How get custom routes to show up in the google sitemap? - silverstripe

We are using the google sitemap module: https://github.com/wilr/silverstripe-googlesitemaps
We have a TourPage.php which has custom routes on its controller, in our sitemap.xml it shows the parent page fine, but no routes are showing up:
e.g At the moment it just shows:
website.com/tours/tour-name-1/
website.com/tours/tour-name-2/
but we want it to show:
website.com/tours/tour-name-1/
website.com/tours/tour-name-1/photos
website.com/tours/tour-name-1/details
website.com/tours/tour-name-1/reviews
...
website.com/tours/tour-name-2/
website.com/tours/tour-name-2/photos
website.com/tours/tour-name-2/details
website.com/tours/tour-name-2/reviews
etc
How do we achieve this?
class TourPage_Controller extends Page_Controller {
private static $allowed_actions = array('BrochureForm', 'brochure', 'brochure_thanks', 'details', 'photos', 'reviews', 'book', 'downloadItineraryFile', 'map', 'overview', 'getSlugName');
public function photos() {
if(!$this->canViewPage()) {
return $this->redirect(ToursPage::getFirstPageLink());
}
return array(
'MetaTitle' => $this->resolveMetaTitle('MetaTitlePhotos'),
'Photos' => $this->TourPhotos(),
'MetaImage' => $this->resolveMetaImagePhotos(),
'MetaVideo' => false,
'ImageSlug' => $this->getSlugName(),
'SluggedImage' => $this->getImageBySlug(),
'AbsolutePhotoURL' => $this->getAbsolutePhotoURL()
);
}
public function index() {
if(!$this->canViewPage()) {
return $this->redirect(ToursPage::getFirstPageLink());
}
// Have to return something...
return array();
}
public function init() {
parent::init();
if($this->request->param('Action') == 'brochure') {
Requirements::themedCSS('bootstrap.min');
Requirements::javascript(THIRD_PARTY_PATH . 'javascript/chosen.jquery.min.js');
}
}
public function overview() {
if(!$this->canViewPage()) {
return $this->redirect(ToursPage::getFirstPageLink());
}
return array();
}
public function details() {
if(!$this->canViewPage()) {
return $this->redirect(ToursPage::getFirstPageLink());
}
// Have to return something...
return array(
'MetaTitle' => $this->resolveMetaTitle('MetaTitleDetails'),
'Photos' => $this->TourPhotos(),
'MetaImage' => $this->resolveMetaImageDetails(),
'MetaVideo' => false
);
}
public function reviews() {
if(!$this->canViewPage()) {
return $this->redirect(ToursPage::getFirstPageLink());
}
return array(
'MetaTitle' => $this->resolveMetaTitle('MetaTitleReviews'),
'Reviews' => $this->data()->Reviews()->Filter('Disabled', 0),
'MetaImage' => $this->resolveMetaImageReviews(),
'MetaVideo' => false
);
}
public function book() {
// If we don't book, then head to the contact page
if($this->ContactFormToBook) {
FlashMessage::add('Please use the Contact Us form if you\'d like to book a ' . $this->Title, 'success');
return $this->redirect(ContactPage::getFirstPageLink());
}
return $this->redirect(BookingPage::getFirstPageLink() . '?Tour=' . $this->ID);
}
/*
* brochure page - with a check to ensure that they are allowed to view the page
*/
public function brochure() {
if(!$this->canViewPage()) {
return $this->redirect(ToursPage::getFirstPageLink());
}
return array();
}
public function map(SS_HTTPRequest $request) {
if(!$this->isAllowedMap()) {
return $this->redirect($this->Link());
}
Requirements::javascript('https://maps.googleapis.com/maps/api/js?v=3.exp&signed_in=true&key=AIzaSyCNCBX_mLK0uuDElVttJVJgM2fuXIynv6E');
Requirements::javascript(THIRD_PARTY_PATH . 'javascript/map.js');
return array(
'MetaImage' => $this->resolveMetaImageMap(),
'MetaVideo' => false
);
}
}

That module provides the ability to include custom routes as described in the docs.
Example below:
GoogleSitemap::register_routes(array(
'/my-custom-controller/',
'/Security/',
'/Security/login/'
));

Related

Wordpress API with Vue js return 401

I'm trying to create a WordPress plugin with vueJs and custom endpoint. Then try to make some request. The problem comes with authentification request (the simple get request works fine). I don't know why but i only get 401 error. And i think is all correct. Hope you can help me.
Other details, when i return true the permission_callback and try to get the user by get_current_user(), i get 0. It seems that the nonce is not working ...
Here my vue code :
addNewBike() {
this.$http.post(window.wpApiSettings.root+'lkbiking/v1/bike/add', {
headers: {
'X-WP-Nonce': window.wpApiSettings.nonce
},
data: {
name: 'test',
}
})
.then(res => {
console.log(res)
})
.catch(err => console.log(err))
}
And the php code :
namespace LkBiking\Api;
use WP_REST_Request;
class Api
{
private $version;
private $namespace;
private $plugin_dir_path;
private $plugin_name;
public function __construct($plugin_name)
{
$this->plugin_name = $plugin_name;
$this->version = '1';
$this->namespace = $plugin_name . '/v' . $this->version;
$this->plugin_dir_path = plugin_dir_path($plugin_name);
}
public function run()
{
add_action('rest_api_init', array($this, 'registerBikeList'));
add_action('rest_api_init', array($this, 'registerBikeAdd'));
}
public function registerBikeList()
{
register_rest_route(
$this->namespace,
'/bike',
[
'methods' => 'GET',
'callback' => array($this, 'getBikeList'),
'permission_callback' => function () {
return true;
},
]
);
}
public function registerBikeAdd()
{
register_rest_route(
$this->namespace,
'/bike/add',
[
'methods' => 'POST',
'callback' => array($this, 'addNewBike'),
'permission_callback' => function() {
return current_user_can('manage_options');
}
]
);
}
public function getBikeList()
{
return wp_json_encode(['test api'=> 'ok']);
}
public function addNewBike(WP_REST_Request $request)
{
return wp_json_encode(['test add'=> 'ok']);
}
public function isAdmin(WP_REST_Request $request)
{
return true;
}
Of course the nonce is correctly add by the admin wordpress area. No need now to create (i can see it with a console.log(wpApiSettings).
So what i'm doing wrong guys ?

Dynamic ChoiceType (select2 + AJAX)

I need a form field to choose from thousands of entities, so a dynamic choice system like select2 (with AJAX) is perfectly suited.
My AJAX endpoint works fine, but the custom form type does not work:
class Select2AjaxDataCategoryType extends AbstractType
{
/**
* #var EntityManagerInterface
*/
private $entityManager;
/**
* #var RouterInterface
*/
private $router;
public function __construct(EntityManagerInterface $entityManager,
RouterInterface $router)
{
$this->entityManager = $entityManager;
$this->router = $router;
}
public function getParent()
{
return ChoiceType::class;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->resetModelTransformers();
$builder->resetViewTransformers();
$builder->addModelTransformer(new CallbackTransformer(
function (?DataCategory $dc) {
dump('model transform is called ' . ($dc ? $dc->getId()->toString() : 'null'));
return $dc ? $dc->getId()->toString() : '';
},
function ($id) : ?DataCategory{
dump('model reversetransform is called ' . $id);
$dc = $this->entityManager->getRepository(DataCategory::class)->find($id);
if($dc === null)
throw new TransformationFailedException("Konnte keine Datenkategorie mit ID $id finden");
return $dc;
}
));
$builder->addViewTransformer(new CallbackTransformer( // Identity !!!
function ($dc) {
dump('view transform is called ' . $dc);
return $dc;
},
function ( $id) {
dump('view reversetransform is called ' . $id);
return $id;
}
));
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) { // makes validation pass
$data = $event->getData();
dump($data); // select2'd id, correct
dump($event->getForm()->getName()); // name of my form field
$event->getForm()->getParent()->add( // so this is lik "overwriting"? Documented nowhere :-/
$event->getForm()->getName(),
ChoiceType::class,
['choices' => [$data => $data]]);
$event->getForm()->getParent()->get($event->getForm()->getName())->setData($data);
});
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setRequired('currentDataCategory');
$resolver->setAllowedTypes('currentDataCategory', [DataCategory::class]);
$resolver->setDefaults([
'attr' => [
'data-ajax' => '1',
'data-ajax-endpoint' => $this->router->generate('data-category-manage-select2')
]
]);
}
}
When using this form type, it seems to work, but finally no entity object is returned, but null. According to symfony debug toolbar however, the value is received:
Also the dumps indicate that the view and model transformers were called:
For the sake of completeness (I hope we'll find a perfect solution and help others), here is my js code (it works):
$('select[data-ajax=1]').select2({
theme: "bootstrap4",
placeholder: "Bitte wählen",
ajax: {
url: function() { return $(this).data('ajax-endpoint');},
dataType: 'json',
data: function (params) {
var query = {
search: params.term,
page: params.page || 0
}
// Query parameters will be ?search=[term]&page=[page]
return query;
}
}
});
I have solved the problem, here is my complete solution:
$('select[data-ajax=1]').select2({
theme: "bootstrap4",
placeholder: "Bitte wählen",
ajax: {
url: function() { return $(this).data('ajax-endpoint');},
dataType: 'json',
data: function (params) {
var query = {
search: params.term,
page: params.page || 0
}
// Query parameters will be ?search=[term]&page=[page]
return query;
}
}
});
The new form type is fixed for one class DataCategory, and works both for single and multiple select's.
I have build-in a distinction between select2 frontend and the standard EntityType (mainly for testing reasons, because the new select2 based approach does not allow PHPUnit tests that use symfony's Client (WebTestCase)): If there are less than 50 DataCategory entities in the DB, the field falls back to EntityType
class Select2AjaxDataCategoryType extends AbstractType
{
/**
* #var EntityManagerInterface
*/
private $entityManager;
/**
* #var RouterInterface
*/
private $router;
private $transformCallback;
public function __construct(EntityManagerInterface $entityManager,
RouterInterface $router)
{
$this->entityManager = $entityManager;
$this->router = $router;
$this->transformCallback = function ($stringOrDc) {
if (is_string($stringOrDc)) return $stringOrDc;
else return $stringOrDc->getId()->toString();
};
}
public function getParent()
{
if($this->entityManager->getRepository(DataCategory::class)->count([]) > 50)
return ChoiceType::class;
else
return EntityType::class;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
if($this->entityManager->getRepository(DataCategory::class)->count([]) > 50) {
$builder->addModelTransformer(new CallbackTransformer(
function ($dc) {
/** #var $dc DataCategory|DataCategory[]|string|string[] */
/** #return string|string[] */
dump('model transform', $dc);
if($dc === null) return '';
if(is_array($dc)) {
return array_map($this->transformCallback, $dc);
} else if($dc instanceof Collection) {
return $dc->map($this->transformCallback);
} else {
return ($this->transformCallback)($dc);
}
},
function ($id) {
dump('model reversetransform', $id);
if (is_string($id)) {
$dc = $this->entityManager->getRepository(DataCategory::class)->find($id);
if ($dc === null)
throw new TransformationFailedException("Konnte keine Datenkategorie mit ID $id finden");
dump($dc);
return $dc;
} else {
$ret = [];
foreach($id as $i){
$dc = $this->entityManager->getRepository(DataCategory::class)->find($i);
if ($dc === null)
throw new TransformationFailedException("Konnte keine Datenkategorie mit ID $id finden");
$ret[] = $dc;
}
return $ret;
}
}
));
$builder->resetViewTransformers();
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
$dataId = $event->getData();
dump('presubmit', $dataId, $event->getForm()->getConfig()->getOptions()['choices']);
if(empty($dataId))
return;
$name = $event->getForm()->getName();
if (is_array($dataId)) { // multiple-true-case
if (!empty(array_diff($dataId, $event->getForm()->getConfig()->getOptions()['choices']))) {
$options = $event->getForm()->getParent()->get($name)->getConfig()->getOptions();
$options['choices'] = array_combine($dataId, $dataId);
$event->getForm()->getParent()->add($name, Select2AjaxDataCategoryType::class, $options);
$event->getForm()->getParent()->get($name)->submit($dataId);
$event->stopPropagation();
}
} else { // multiple-false-case
if($dataId instanceof DataCategory){
$dataId = $dataId->getId()->toString();
throw new \Exception('Hätte ich nicht erwartet, sollte string sein');
}
if (!in_array($dataId, $event->getForm()->getConfig()->getOptions()['choices'])) {
$options = $event->getForm()->getParent()->get($name)->getConfig()->getOptions();
$options['choices'] = [$dataId => $dataId];
$event->getForm()->getParent()->add($name, Select2AjaxDataCategoryType::class, $options);
$event->getForm()->getParent()->get($name)->submit($dataId);
$event->stopPropagation();
}
}
});
// $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
// dump("pre set data", $event->getData());
// });
} else {
}
}
public function configureOptions(OptionsResolver $resolver)
{
if($this->entityManager->getRepository(DataCategory::class)->count([]) > 50) {
$resolver->setDefaults([
'attr' => [
'data-ajax' => '1',
'data-ajax-endpoint' => $this->router->generate('data-category-manage-select2')
],
'choices' => function (Options $options) {
$data = $options['data'];
dump('data', $data);
if($data !== null) {
if(is_array($data) || $data instanceof Collection){
$ret = [];
foreach ($data as $d) {
$ret[$d->description . ' (' . $d->name . ')'] = $d->getId()->toString();
}
dump($ret);
return $ret;
} else if ($data instanceof DataCategory){
return [$data->description . ' (' . $data->name . ')' => $data->getId()->toString()];
} else {
throw new \InvalidArgumentException("Argument unerwartet.");
}
} else {
return [];
}
}
]);
} else {
$resolver->setDefaults([
'class' => DataCategory::class,
'choice_label' => function ($cat, $key, $index) { return DataCategory::choiceLabel($cat);},
'choices' => function (Options $options) {
return $this->entityManager->getRepository(DataCategory::class)->getValidChildCategoryChoices($options['currentDataCategory']);
}
]);
}
}
}
It is very important to set the 'data' option when using this new type, otherwise the choices option is not correctly set:
$builder->add('summands', Select2AjaxDataCategoryType::class,[
'currentDataCategory' => $mdc,
'data' => $mdc->summands->toArray(),
'multiple' => true,
'required' => false,
'label' => 'Summierte Kategorien',
]);

__construct() must be an instance of Post\Model\PostTable

Try add new module? but got are error:
Argument 1 passed to Post\Controller\PostController::__construct()
must be an instance of Post\Model\PostTable, none given, called in
W:\domains\zend_blog\skeleton-application\vendor\zendframework\zend-servicemanager\src\Factory\InvokableFactory.php
namespace Post\Controller;
use Post\Model\Post;
// Add the following import:
use Post\Model\PostTable;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
class PostController extends AbstractActionController
{
private $table;
/**
* Execute the request
*
* #param MvcEvent $e
* #return mixed
*/
// Add this constructor:
public function __construct(PostTable $table)
{
$this->table = $table;
}
public function indexAction()
{
return new ViewModel([
'post' => $this->table->fetchAll()
]);
}
}
Post Table:
<?php
namespace Post\Model;
use RuntimeException;
use Zend\Db\TableGateway\TableGatewayInterface;
use Zend\Db\TableGateway\TableGateway;
class PostTable
{
private $tableGateway;
public function __construct(TableGateway $tableGateway)
{
// $this->tableGateway = $tableGateway;
$this->tableGateway = $tableGateway;
}
public function fetchAll()
{
return $this->tableGateway->select();
}
public function getPost($id)
{
$id = (int) $id;
$rowset = $this->tableGateway->select(['id' => $id]);
$row = $rowset->current();
if (! $row) {
throw new RuntimeException(sprintf(
'Could not find row with identifier %d',
$id
));
}
return $row;
}
public function savePodt(Post $album)
{
$data = [
'artist' => $album->artist,
'title' => $album->title,
];
$id = (int) $album->id;
if ($id === 0) {
$this->tableGateway->insert($data);
return;
}
if (! $this->getPost($id)) {
throw new RuntimeException(sprintf(
'Cannot update album with identifier %d; does not exist',
$id
));
}
$this->tableGateway->update($data, ['id' => $id]);
}
public function deletePost($id)
{
$this->tableGateway->delete(['id' => (int) $id]);
}
}
Module:
namespace Post;
use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\ModuleManager\Feature\ServiceProviderInterface;
class Module implements ConfigProviderInterface,ServiceProviderInterface
{
public function getConfig()
{
return include __DIR__ . '/../config/module.config.php';
}
/**
* Expected to return \Zend\ServiceManager\Config object or array to
* seed such an object.
*
* #return array|\Zend\ServiceManager\Config
*/
public function getServiceConfig()
{
return [
'factories' => [
Model\PostTable::class => function($container) {
$tableGateway = $container->get(Model\PostTableGateway::class);
return new Model\PostTable($tableGateway);
},
Model\PostTableGateway::class => function ($container) {
$dbAdapter = $container->get(AdapterInterface::class);
$resultSetPrototype = new ResultSet();
$resultSetPrototype->setArrayObjectPrototype(new Model\Post());
return new TableGateway('post', $dbAdapter, null, $resultSetPrototype);
},
],
];
}
public function getControllerConfig() {
return [
'factories' => [
Controller\PostController::class => function($container) {
return new Controller\PostController(
$container->get(Model\PostTable::class)
);
},
],
];
}
}
Most probably you have a duplicate setting for the controller factory in the module.config.php. Check:
'controllers' => [
'factories' => [
Controller\PostController::class => InvokableFactory::class,
...
],
],
Remove line:
Controller\PostController::class => InvokableFactory::class,
so that the factory method in your Module.php is used.

How to set a custom value for records in a Report?

The code below is a custom report I'm putting together, using SilverStripe 3.1.
The Title and ClassName values are working fine, but though I can get the Status for each Page I'm not sure how to set the Status value against each Page in the DataList. How can I do that?
Once that's done, the Status column should be populated.
class PageListByType extends SS_Report {
function title() {
return 'Page List by Type';
}
function description() {
return 'List all the pages in the site, along with their page type';
}
public function sourceRecords($params = array(), $sort = null, $limit = null) {
$pages = Page::get()->sort($sort);
foreach ($pages as $pagenum=>$page) {
$flags = $page->getStatusFlags();
if ($flags) {
foreach ($flags as $status) {
// if (isset($pages[$pagenum]->Status)) die(array($pages[$pagenum]->Status, $status)); #detect multiple statuses; not sure if this will happen
/////////////////////////
// The following line needs fixing:
/////////////////////////
$pages[$pagenum]->Status = "{$status['text']} ({$status['title']})";
}
}
}
// die($pages->debug());
return $pages;
}
public function columns() {
return array(
'Title' => _t('PageListByTypeReport.PageName', 'Page name'),
'ClassName' => _t('PageListByTypeReport.ClassName', 'Page type'),
'Status' => _t('PageListByTypeReport.Status', 'Status')
);
}
}
Edit: Thanks #Turnerj for your answer! My final working code is as follows:
class PageListByType extends SS_Report {
function title() {
return 'Page List by Type';
}
function description() {
return 'List all the pages in the site, along with their page type';
}
public function sourceRecords($params = array(), $sort = null, $limit = null) {
$pages = DataObject::get("SiteTree", "", "");
return $pages;
}
public function columns() {
return array(
'Title' => _t('PageListByTypeReport.PageName', 'Page name'),
'ClassName' => _t('PageListByTypeReport.ClassName', 'Page type'),
'Status' => _t('PageListByTypeReport.Status', 'Status')
);
}
}
and in Page.php:
public function getStatus() {
$flags = $this->getStatusFlags();
$result = array();
if ($flags) {
foreach ($flags as $status) {
$result[] = "{$status['text']} ({$status['title']})";
}
} else {
$result[] = 'Published';
}
return implode(', ', $result);
}
After further investigation, I recreated the issue and found the solution.
Overall, my solution involves what I suggested in the comments about bringing the status fetching to the actual Page by adding a getStatus function.
I described essentially the following:
public function getStatus()
{
return $this->getStatusFlags();
}
This technically is correct, it will get the status flags but you are right, it doesn't display them in the report. This is due to this function returning an array which the report does not understand to render.
My solution is to change this function to return a string so with a few simple edits combining what you wrote with what I wrote, we get the following:
public function getStatus()
{
$flags = $this->getStatusFlags();
$result = array();
if ($flags)
{
foreach ($flags as $status)
{
$result[] = "{$status['text']} ({$status['title']})";
}
}
return implode(', ', $result);
}
I've got one unique twist on combining our code, I add each status to an array and implode it back to a single string. This can seem a little excessive, by default getStatusFlag will return one key in the array. If however you have a DataExtension that has the updateStatusFlags method, you could add additional keys to the result.
Basically, I would leave the implode handling in if in the future you do have code that messes with the status flags.
Now, you might be able to do something similar using the $casting property on the Page but given you were essentially adding this function just for the report, it was cleaner to just update it directly.
I did notice that the status flags array is actually empty if the page is published which means your report won't have anything next to published pages. If that is your intention, great!
If not, you could do another little alteration:
public function getStatus()
{
$flags = $this->getStatusFlags();
$result = array();
if ($flags)
{
foreach ($flags as $status)
{
$result[] = "{$status['text']} ({$status['title']})";
}
}
else
{
$result[] = 'Published (The page has been published)';
}
return implode(', ', $result);
}
The if ($flags) will evaluate to false when there are no current statuses (aka. the page is published) due to automatic casting.
Have you tried with an anonymous function inside columns()?
class PageListByType extends SS_Report {
function title() {
return 'Page List by Type';
}
function description() {
return 'List all the pages in the site, along with their page type';
}
public function sourceRecords($params = array(), $sort = null, $limit = null) {
return Page::get()->sort($sort);
}
public function columns() {
return array(
'Title' => _t('PageListByTypeReport.PageName', 'Page name'),
'ClassName' => _t('PageListByTypeReport.ClassName', 'Page type'),
'Status' => array(
'title'=>_t('PageListByTypeReport.Status', 'Status'),
'formatting' => function($value, $item) {
$flags = $item->getStatusFlags();
$status = '';
if ($flags) {
foreach ($flags as $status) {
$status = "{$status['text']} ({$status['title']})";
}
}
return $status ? : _t('PageListByTypeReport.PagePublished', 'Page published');
}
)
);
}
}

custom module slowing drupal

I have worked on a custom shipping module to match my commerce module shipping needs. I have noticed that the module is slowing drupal a lot on a lot the pages even-though I am basically my module should only be hooking to drupal commerce checkout.
Here is my module code:
function myips_commerce_shipping_method_info() {
$shipping_methods = array();
$shipping_methods['IPS'] = array(
'title' => t('IPS'),
'description' => t('Quote rates from IPS'),
);
return array($shipping_methods);
}
function myips_commerce_shipping_service_info() {
$shipping_services = array();
$shipping_services['myips_shipping_service'] = array( //arbitrary name w/ 'service' in there
'title' => t('Custom Shipping Service'), //title for the interface
'description' => t('Variable rates based on the book origin '),
'display_title' => t('IPS Shipping'),
'shipping_method' => 'IPS',
'price_component' => 'shipping', //from commerce_shipping
'callbacks' => array(
'rate' => 'myips_service_rate_order'),
);
return $shipping_services;
}
function myips_service_rate_order($shipping_service, $order) {
$order_number=$order->order_number;
$currency_code='USD';
$shipping_total=getordershipmentvalue($order_number);
$rates['myips_shipping_service']=array(
'amount' => commerce_currency_decimal_to_amount($shipping_total, $currency_code),
'currency_code' =>$currency_code,
'data' => array(),
);
return $rates[$shipping_service['name']];
}
function getcustomershippingcountry($order_id) {
$order=commerce_order_load($order_id);
$profile_id=$order->commerce_customer_shipping['und']['0']['profile_id'];
$profile=commerce_customer_profile_load($profile_id);
$country= $profile->commerce_customer_address[LANGUAGE_NONE]['0']['country'];
return $country;
}
function getordershipmentvalue($order_id) {
$order=commerce_order_load($order_id);
$total=0;
foreach($order->commerce_line_items[LANGUAGE_NONE] as $line) {
$line_item_id=$line['line_item_id'];
$total=$total+getitemshipmentvalue($line_item_id);
}
return $total;
}
function getitemshipmentvalue($line_item_id){
$line_item=commerce_line_item_load($line_item_id);
$line_quantity=$line_item->quantity;
$order_id= $line_item->order_id;
$destination= getcustomershippingcountry($order_id);
$product_id=$line_item->commerce_product[LANGUAGE_NONE]['0']['product_id'];
$product=commerce_product_load($product_id);
$product_weight=$product->field_book_weight[LANGUAGE_NONE]['0']['weight'];
$product_origin=$product->field_book_origin[LANGUAGE_NONE]['0']['value'];
$line_total=getshippingrates($destination, $product_origin, $product_weight*$line_quantity);
return $line_total;
}
function calculateshippment($shipping_type, $zone, $product_weight) {
$query=new EntityFieldQuery();
$query->entityCondition('entity_type', 'node');
$query->entityCondition('bundle', $shipping_type);
$query->fieldCondition('field_up_to_weight', 'value',$product_weight,'>=');
$query->fieldCondition('field_zone_number', 'value',$zone);
$query->fieldorderby('field_up_to_weight','value','ASC');
$query->range(0,1);
$result=$query->execute();
if(!empty($result)){
$nodes=node_load_multiple(array_keys($result['node']));
foreach($nodes as $node) {
$price=$node->field_shipment_price[LANGUAGE_NONE][0]['value'];
}
}
return $price;
}
function getdestinationzone($destination, $shipping_type) {
$query=new EntityFieldQuery();
$query->entityCondition('entity_type', 'node');
$query->entityCondition('bundle', 'countries_zones');
$query->fieldCondition('field_country_abbreviation', 'value',$destination);
$result=$query->execute();
if(!empty($result)) {
$nodes=node_load_multiple(array_keys($result['node']));
foreach($nodes as $node) {
if($shipping_type=='out_us_zone_prices') {
$zone=$node->field_us_zone[LANGUAGE_NONE]['0']['value'];
}elseif($shipping_type=='mail_zone_prices') {
$zone=$node->field_mail_zone[LANGUAGE_NONE]['0']['value'];
}elseif($shipping_type=='dhl_zone_prices') {
$zone=$node->field_dhl_zone[LANGUAGE_NONE]['0']['value'];
}
}
}
return $zone;
}
function getshippingrates($destination, $product_origin, $product_weight) {
if($product_origin=='US') {
if($destination==$product_origin) {
$shipping_type='us_zone_prices';
}else {
$shipping_type='out_us_zone_prices';
}
/* End of Product Origin US */
}elseif($product_origin=='LB'){
if($destination==$product_origin) {
$shipping_type='mail_zone_prices';
}else {
$shipping_type='dhl_zone_prices';
}
}
$zone=getdestinationzone($destination,$shipping_type);
return calculateshippment($shipping_type, $zone, $product_weight);
}
Why would my module slowing drupal performance?
I think the main problem of your module is that your database queries are in a foreach-loop (in getordershipmentvalue())
Ok you maybe can't see it immediately, but if you look behind the getitemshipmentvalue()-call, you can see, that there are many other function calls there.
And over multiple corners the functions calculateshippment() and getdestinationzone() will be called. In these functions there are database-queries.
Thats the reason, why your module is so slow: Because over multiple corners you query the database in a loop. This is similar to a DOS-Attack on the database-server (A bigger problem with great arrays of "commerce_line_items").
The solution: Think about your code-design. Try to put your database-query-code outside of the foreach-loop.
I would also prefer to cache the data after the first request. (For example with variable_set())

Resources