In my drupal 8 custom module I use block to show the next and previous links of current article page. However, links do not change when switching nodes due to the caching. How can I limit the caching for this block?
I can't wrap my head around this.
public function build() {
/**
* {#inheritdoc}
*/
$node = \Drupal::request()->attributes->get('node');
$created_time = $node->getCreatedTime();
$nextprevlinks ="";
$nextprevlinks .= $this->generateNext($created_time);
$nextprevlinks .= $this->generatePrevious($created_time);
return array('#markup' => $nextprevlinks);
}
Just in case someone else brain farts like I just did.
This is how my return now looks:
return array('#markup' => $nextprevlinks,
'#cache' => array("max-age" => 0),
);
Related
I am new at Drupal 7 and I'm creating a Block by code, following this tutorial.
So I create a new module folder at drupal/sites/all/modules and created two files:
block_square_menu.info: it has the info of the module:
name = Block Square Menu
description = Module that create a Block for Square menu, menu shown only in home page
core = 7.x
package = custom
block_square_menu.module: it contains the PHP code:
<?php
/**
* Implements hook_block_info().
*/
function block_square_block_info() {
$blocks = array();
$blocks['block_square'] = array(
'info' => t('Block Square'),
'cache' => DRUPAL_CACHE_PER_ROLE,
);
return $blocks;
}
/**
* Implements hook_block_view().
*/
function block_square_block_view($delta = '') {
$block = array();
switch ($delta) {
case 'block_square':
$block['subject'] = t('block Title');
$block['content'] = t('Hello World!');
break;
}
return $block;
}
After save the files, I go to Admin/Modules, I activate the new module and save the configuration. Now I go to Structure/Blocks and it should list my new Block, but it doesn't do.
I have followed all the tutorial steps and I cleaned Drupal cache, but I'm still having the problem.
First solve your mistake: change the function name where you implemented hook_block_view(), you need to change it as function blocks_square_block_view()
/**
* Implements hook_block_view().
*/
function blocks_square_block_view($delta = '') {
$block = array();
......
After also if not solve then remove 'cache' attribute from hook_block_info() it is optional.
Then follow 2 steps if you missed.
1) Clear all cache (/admin/config/development/performance).
2) Enable your custom module (/admin/modules).
After trying again, your block should appear in (/admin/structure/block).
Solved, the problem was the name of the functions. So the names started with "block_square" which it have the word "block" and it causes some trouble so I changed the all the names with menu_square.
So the functions are now:
menu_square_block_info()
menu_square_block_view($delta = '')
And the files are:
menu_square.info
menu_square.module
The code of the files are:
info:
name = Menu Square
description = Module that create a Block for Square menu, menu shown only in home page
core = 7.x
package = custom
module:
<?php
/**
* Implements hook_block_info().
*/
function menu_square_block_info() {
$blocks['menu_square'] = array(
'info' => t('Block Square'),
//'cache' => DRUPAL_CACHE_PER_ROLE,
);
return $blocks;
}
/**
* Implements hook_block_view().
*/
function menu_square_block_view($delta = '') {
$block = array();
switch ($delta) {
case 'menu_square':
$block['subject'] = t('block Title');
$block['content'] = t('Hello World!');
break;
}
return $block;
}
I want the sales_order_create grid to show both the price and special price in the same column, and I've done so by adding:
->addAttributeToSelect('special_price')
To the _prepareCollection() function, and then adding:
$this->addColumn('special_price', array(
'header' => Mage::helper('sales')->__('Special Price'),
'sortable' => false,
'index' => array('price', 'special_price'),
'type' => 'concat',
'separator' => ' -- ',
'width' => '140px',
));
To the _prepareColumns() function.
This works! The result is a new column which, as an example, displays:
79.9800 -- 34.9900
How do I format this so it's in a currency format? £xx.xx
Also, is it possible to style it? So it looks like this: £79.98 (£34.99)
If it's not possible to style, just in currency format would be great.
I think it's something to do with the renderer but I'm new to Magento so would need it explaining in a basic way if thats ok.
Thanks
I've managed to get this working, my code is horrible, but it may help anyone who stumbles across this in the future.
I looked at the price column and saw:
'renderer' => 'adminhtml/sales_order_create_search_grid_renderer_price',
After navigating to app/code/core/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer I noticed a concat renderer.
I copied Concat.php from this directory and created a local version, I named it Special.php to avoid any conflicts:
/app/code/local/Mage/Adminhtml/Block/Widget/Grid/Column/Renderer/Special.php
I then added this renderer to my special_price column:
'renderer' => 'adminhtml/sales_order_create_search_grid_renderer_special',
My Special.php code is as follows (warning: this code is pretty horrible, but it works so I'm happy):
class Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Special
extends Mage_Adminhtml_Block_Widget_Grid_Column_Renderer_Abstract
{
/**
* Renders grid column
*
* #param Varien_Object $row
* #return string
*/
public function render(Varien_Object $row)
{
$dataArr = array();
foreach ($this->getColumn()->getIndex() as $index) {
if ($data = $row->getData($index)) {
//$dataArr[] = $data;
$dataArr[] = number_format((float)$data, 2, '.', '');
}
}
$data = join($this->getColumn()->getSeparator(), $dataArr);
// TODO run column type renderer
$price = '';
$special = '';
if (strlen($dataArr[0]) > 0) {
if (strlen($dataArr[1]) > 0) {
$price = '<span style="text-decoration:line-through">£' . $dataArr[0] . '</span>';
$special = ' £' . $dataArr[1];
}
else {
$price = '£' . $dataArr[0];
$special = '';
}
}
return $price . $special;
}
}
The result is if there is no price (grouped product) the entry is blank, if there is no special the entry is the RRP and if there is a special the entry is RRP special price
The code I'm sure can be improved but it works
I need to put some code into the site on drupal and i need this code to work on every page of my site. How can i do this? I wanted to find the file of footer and put some code inside, but i can't find it.
There are 3 ways to add PHP code in to the footer.
1> Turn on the PHP filter & enter the code into a block that is positioned in the footer left region.
2> Put the code in the appropriate template file in the sub-theme.
3> Make a module that outputs the code to a block; activate and place the block.
Suppose that you want to add following line to footer area :
©<?php print date('Y');?> Your Company Name - Address of your company.
So best way to do it is make small module like this:
copyright_block.info
name = Copyright Block
description = Shows the (incrementing) current year and company information.
package = Other
core = 7.x
files[] = copyright_block.module
copyright_block.module
<?php
/**
* #file
* This module shows the copyright year and company information.
*/
/**
* Implements hook_help().
*/
function copyright_block_help($path, $arg) {
if ($path == 'admin/help#copyright_block') {
return t('Manually edit to change company information');
}
}
/**
* Implements hook_block_info().
*/
function copyright_block_block_info() {
$blocks = array();
$blocks['show_copyright'] = array(
'info' => t('Company Information'),
'cache' => DRUPAL_NO_CACHE,
);
return $blocks;
}
/**
* Implements hook_block_view().
*/
function copyright_block_block_view($block_name = '') {
if ($block_name == 'show_copyright') {
$content = "<p>©" . date('Y') ." Your Company Name - Address of your company</p>";
$block = array(
'subject' => t('Company Information'),
'content' => $content,
);
return $block;
}
}
NOTE: Do not put PHP end tag ?> at the end.
All,
Background of how problem was detected
My question concerns the performance of a web app, mainly the index page. I noticed the problem when I was giving a demonstration at a local branch of my company that has slow internet (I don't know the exact speeds, or ping rate) judged by the fact that Google took about 10 seconds to load. My index page took ~10-20 times longer to load. I was under the assumption that my app did most of the work on the server side (as php is making all of the database queries...). But this led me to look at the network tool of Chrome and see the latency times of these 4 divs being loaded by ajax (I'll elaborate in a bit). Interestingly, the scripts being called appear to run sequentially, but not necessarily in the order I invoked the ajax calls (sometimes they do, other times they don't).
What are these divs / ajax requests?
Here is a code snippets of a request:
Yii::app()->clientScript->registerScript('leftDiv', '
$( "#left_dash" ).load(
"'.$this->createUrl("/site/page?view=leftDashLoad") .'",
function(){
$("#left_dash p a").click(function() {
$(this).parent().parent().find("div.scroll100").slideUp();
$(this).parent().next().stop(false, false).slideDown();
});
$("p:first-child").next().slideDown();
}
);
' );
Here is the page requested:
$this->widget('widgets.ScrollList',array(
'condition'=>
function($muddJob,$scrollList)
{
$job = $muddJob->Job;; //returns a job or empty array
if(!empty($job) )
{
if( $muddJob->uploadArtwork == null && $muddJob->uploadData == null ) {
array_push($scrollList->_models,$job);
$scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1);
return true;
}
}
return false;
},
'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Dealer Name"),
'name'=> "Print New Ticket",
'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),
)
);
Imagine that page (the page that ajax has called) having 6 similar declarations that create widgets. The goal is to return html to put back in place of a loading gif on the index page.
Here is the scroll widget:
<?php
Yii::import('widgets.ScrollListBase');
include_once Yii::app()->extensionPath . "/BusinessDay.php";
class ScrollList extends ScrollListBase
{
private $_content;
public $columns = array();
public $columnValues;
private $_listInfo;
public $name;
public $_models = array();
public $condition;
public $muddJobs; //object to pass
public $jobsMailingTodayArray = array();
public function init()
{
//$this->init();
$this->_listInfo = $this->generateListInfo($this->columns);
//$muddJobs = $this->getCurrentMuddExchanges();
$listInfo = $this->newScrollList($this->muddJobs);
$contents = $this->createContent($listInfo,$this->name);
$this->_content = $contents[0];
// $this->_fullTableContent = $contents[1];
//$this->_listInfo = $contents[2];
}
public function run()
{
//if($this->data['isVisible'])
echo $this->_content;
Yii::app()->session["exploded_content_{$this->name}"] = $this->_models;
}
private function newScrollList($muddJobs)
{
$listInfo = $this->_listInfo;
$tempCount = 0;
foreach($muddJobs as $muddJob)
{
$condition = $this->condition;
if($condition($muddJob,$this) && empty($this->jobsMailingTodayArray) ) //if no job exists for the muddExchange...
{
$tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
$listInfo = $tempArray[0];
$tempCount = $tempArray[1];
}
elseif ( !empty($this->jobsMailingTodayArray ) )
{
foreach ($this->jobsMailingTodayArray as $jobMailingToday) //change to for loop over the length of the jobsMailingToday
{
$tempArray = $this->createWidgetLinks($tempCount,$listInfo,$muddJob,$this->columnValues);
$listInfo = $tempArray[0];
$tempCount = $tempArray[1];
}
$this->jobsMailingTodayArray = array();
}
}
return array($listInfo,$tempCount);
}
}
?>
Here is it's parent:
<?php
class ScrollListBase extends CWidget
{
private $content = "<p>";
private $divDeclaration = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n";
private $headTag = "<th>";
private $headTagClose = "</th>\n";
private $theadTagClose = "</thead>\n";
private $bodyTag = "<tbody>\n";
private $listInfo = "<div class='scroll100'>\n<table class='quickInfoTable'>\n<thead>\n<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
/**
* Initializes the widget.
*/
public function createContent($listInfo,$name)
{
$largeHref = Yii::app()->request->baseUrl . '/index.php/site/fullTableView';
$this->content .= "<span class='badge' >{$listInfo[1]} </span> <a href='#'>{$name} </a> <a href='$largeHref/Name/{$name}'> <small>(view larger)</small> </a> </p>";
if( $listInfo[1] > 0 )
{
// $this->fullTable .= substr($listInfo[0],22);
// $this->fullTableContent= $this->fullContent .= $this->fullTable . "</tbody>\n</table>\n</div>";
$this->content .= $listInfo[0] . "</tbody>\n</table>\n</div>";
}
return array($this->content);
}
//Helper Methods
/**
*
* #param type $attributeArray. send an accociative array
* #return type = either a job or an empty array
*/
protected function getJobByAttributes($attributeArray)
{
return Jobs::model()->with('MuddExchange')->findByAttributes($attributeArray);
}
protected function createWidgetLinks($tempCount,$listInfo,$muddJob,$columnValues,$url="/MuddExchange/")
{
$tempCount++;
$viewIndex = $muddJob->exchange_id;
$model = $muddJob;
$job = $muddJob->Job;
if ( isset($job ))
{
$model = $job;
$url = "/Jobs/";
$viewIndex = $model->job_id;
}
$link = CHtml::link("$model->jobNumber",array("{$url}{$viewIndex}"));
$listInfo .= "<tr>\n<td>$link</td>\n";
foreach ($columnValues as $columnValue)
{
$listInfo .= "<td>{$columnValue}</td>\n";
}
$listInfo .= "</tr>";
return array($listInfo,$tempCount);
}
protected function getListInfo()
{
return $this->listInfo;
}
/**
* Takes an array of strings to generate the column names for a particular list.
* #param array $heads
* #return string
*
*/
protected function generateListInfo($heads)
{
//<th>Job#</th>\n<th>Package#</th>\n<th>Entry Date</th>\n</thead>\n<tbody>\n";
$htmlScrollStart = $this->divDeclaration;
foreach ($heads as $tableColumn => $name)
{
$htmlScrollStart .= $this->headTag . $name . $this->headTagClose;
}
$htmlScrollStart .= $this->theadTagClose . $this->bodyTag;
return $htmlScrollStart;
}
public function calculateDueDate($jobsMailDate,$job)
{
$package = PackageSchedule::model()->findByAttributes(array('package_id'=>$job->packageID));
$projectedDays = $package->projected_days_before_mail_date;
$dropDate1 = $jobsMailDate->projected_mail_date;
$dropDate = wrapBusinessDay($dropDate1); //use this for actual command...
$toSec = 24*60*60;
$dayInt =0;
$secDropDate = strtotime($dropDate1);
do{
$dayInt +=1;
$daysInSec = ($dayInt) * $toSec ;
$secGuessDueDate = $secDropDate - $daysInSec;
$dueDate = date('Y-m-d',$secGuessDueDate);
$difference = $dropDate->difference($dueDate);
}while( $difference != $projectedDays);
return $dueDate;
}
}
?>
Why I think this behavior is odd
The whole slow internet thing is a beast in and of itself, but I don't think that is in the scope of StackOverflow. I'm more concerned about the loading of these divs. The div that loads last, i.e., takes on average 1.5 to 2 seconds, is an ajax request to a page that creates a single widget. The logic behind it is here:
<?php
include_once Yii::app()->extensionPath . "/CurrentExchanges.php";
$currentExchanges = Yii::app()->session['currentExchanges'];
$this->layout = 'barebones';
$this->widget('widgets.ScrollList',array(
'condition'=>
function($muddJob,$scrollList)
{
if ($muddJob->dropDate1 != null && $muddJob->dropDate1 != '0000-00-00')
{
$job = $muddJob->Job;;
if(!empty($job) && $job->packageID != null) //if job exists for the muddExchange and has a package
{
if($job->uploadArtwork == null )
{
$jobsMailDate = JobsMailDate::model()->findByAttributes(array("job_id"=>$job->job_id,'sequence_num'=>1));
if(!empty($jobsMailDate))
{
$calculatedDueDate = $scrollList->calculateDueDate($jobsMailDate,$job);
if (strtotime($calculatedDueDate) <= strtotime(date("Y-m-d")) )
{
array_push($scrollList->_models , $job);
$scrollList->columnValues = array($muddJob->jobDescription,$muddJob->dropDate1,$jobsMailDate->projected_mail_date);
return true;
}
}
}
}
}
return false;
},
'columns' => array('col1'=>"MuddJob#",'col2'=>"Desc",'col3'=>"Drop Date", 'col4' =>'Projected Drop Date'),
'name'=> "Artwork Due Today",
'muddJobs' => $currentExchanges->getCurrentMuddExchanges(),
)
);
?>
The calculateduedate method makes 2 additional calls to the server.
What I'm failing to understand is why the left div (with the most proccessing to do) is usually the first to return and the artLoad is usually the last to load (by a substantial difference). Here are some times returned by chromes network tool:
leftDashLoad: 475ms
rightDashLoad: 593ms
dataLoad: 825ms
artLoad: 1.41s
dataLoad: 453ms
rightDashLoad: 660ms
leftDashLoad: 919ms
artLoad: 1.51s
rightDashLoad: 559ms
leftDashLoad: 1.17s
dataLoad: 1.65s
artLoad: 2.01s
I just can't fathom why the left/right dashloads return so much faster than the artLoad. The code for artLoad and dataLoad are nearly identical save the actual comparison (the one if statement). If this were truly asynchronous, I'd expect the order to be art/dataLoad, rightDashLoad and leftDashLoad based purely on the amounts of computation done in each page. Perhaps the server isn't multithreading, or there is some weird configuration, but if that were the case, I don't see why the effects of the loading would be hit so hard by slow internet.
If I have overlooked something obvious or failed to use google appropriately, I do apologize. Thanks for any help you can offer!
Language/other tech info
The app was developed using the Yii framework. PHP 5.3. Apache Server. INNODB Tables. Server is hosted in dreamhost.
Update
I've changed the view page so that the ajax calls are now calling a controller action. It seems to have made the loading times more similar (asynchronous?) on my local dev environment, but really slowed it down on the QA environment (hosted on dreamhost...). Here is screen shot of the local network tools info:
dev environment
and the qa site (note, that the databases have about the same amounts of data...)
qa environment
Thoughts? It seems to me that my original problem may be solved, as the return times (on my local dev) look more like I expect them to.
Also, my own ignorance as to how NetBeans debugs was playing a part in this synchronous loading thing as xdebug is using a session. I believe this was forcing the ajax calls to wait their turn.
Thanks to #Rowan for helping me diagnose this strange behavior. PHP was trying to request a session before the session was closed in hopes to prevent a race hazard. There were session requests in my code, and there was a session started by my IDE (NetBeans). Removing all references to sessions in the ajax called pages and having ajax call actions that use renderPartial() proved to return the expected behavior (and much better code IMO). Let me know how to properly thank users in terms of StackOverflow (can I up comments, or what is there available? ). Thank you all!
Here is my custom.info
name = Custom
description = Custom module
core = 7.x
package = Own
and custom.module
<?php
/**
* #file
* An example custom module for selecting, updating and deleting query
*/
/**
* Implementation of hook_block_info()
*/
echo 'Today: \n';
echo date('m/d/Y');
function custom_block_info() {
$block['custom'] = array('info' => t('Custom block'))
return $block;
}
/**
* Implements hook_block_view.
*/
function custom_block_view($delta = '') {
global $user;
$block['content'] = t('Hello #user from IP #host',array(
'#user' => format_username($user),
'#host' => $user->hostname`enter code here`
));
$result = db_select('node','a')
->fields('a', array('title'))
->execute();
foreach($result as $node) {
$items[] = array(
'data' => t($node->title)
);
}
$block['content'] .= theme('item_list', array(
'items' => $items
));
return $block;
}
But this custom module is not displaying data in the sidebar where had i put the block. i have place the echo statement above the code it's not even displaying that echo statement in block can any one tell me how to resolve this????
P.S. I have installed drupal i ve changed nothing in database!
Check if your module is active in the modules list (admin/modules) if so try to put your echo statements in a hook_init like this:
function custom_init(){
echo 'Today: \n';
echo date('m/d/Y');
}
then clear all drupal cache Configuration > Performance > Clear alla caches (admin/config/development/performance)