Separating data from execution in osCommerce boxes
multimixer | learn | Monday January 3 2011I was in the need to be able to access the data of each box in osCommerce without having the box being “executed”, that means in this case, added to the left or right column. Why this, you may want to ask. There are several reasons for, lets say I want to add the contents of the box into an other group and to have it displayed in the footer, header or in the middle of the page, whatever.
Let’s take things from the beginning on a concrete example and let’s understand first how things work by default before doing any changes. Thats always a good idea.
Let’s take a simple box, the one that displays the links to “contact us”, “shipping and returns”, “conditions of use” etc. This box, the “information” box is getting created in file catalog/includes/modules/boxes/bm_information.php
Opening the file, we see that it is a class with various functions in it. We are interested in function “execute()” that looks like this
function execute() { global $oscTemplate; $data = '<div class="ui-widget infoBoxContainer">' . ' <div class="ui-widget-header infoBoxHeading">' . MODULE_BOXES_INFORMATION_BOX_TITLE . '</div>' . ' <div class="ui-widget-content infoBoxContents">' . ' <a href="' . tep_href_link(FILENAME_SHIPPING) . '">' . MODULE_BOXES_INFORMATION_BOX_SHIPPING . '</a><br />' . ' <a href="' . tep_href_link(FILENAME_PRIVACY) . '">' . MODULE_BOXES_INFORMATION_BOX_PRIVACY . '</a><br />' . ' <a href="' . tep_href_link(FILENAME_CONDITIONS) . '">' . MODULE_BOXES_INFORMATION_BOX_CONDITIONS . '</a><br />' . ' <a href="' . tep_href_link(FILENAME_CONTACT_US) . '">' . MODULE_BOXES_INFORMATION_BOX_CONTACT . '</a>' . ' </div>' . '</div>'; $oscTemplate->addBlock($data, $this->group); }
Two things are going on here. The first is that $data is getting defined, so we say that $data is this and this and this, in this example $data is a collection of links to various pages
Second thing that happens is to add this data to a group. What group? The one that got defined just before in function “bm_information()” :
$this->group = ((MODULE_BOXES_INFORMATION_CONTENT_PLACEMENT == 'Left Column') ? 'boxes_column_left' : 'boxes_column_right');
This here says that the group is either the left or the right column, depending on what the value in the configuration table of our database is. Thats in other words the setting we have when configuring the boxes in the admin area, setting the option to “left column” or “right column”
Back to the execution function, and the part where the $data is getting added to a group
$oscTemplate->addBlock($data, $this->group);
We see that a function of class $oscTemplate is getting used, thats in the file catalog/includes/classes/osc_template.php, and the function looks like this
function addBlock($block, $group) { $this->_blocks[$group][] = $block; }
It’s simply adding our $data (of the box file) to the array of blocks, the group, again defined in the box file.
To complete the picture, we should also know that the whole “execution system” is getting activated via the function buildBlocks() (file catalog/ includes/classes/osc_template.php starting at line 70) and this function again is called in file catalog/includes/template_top.php, just at the beginning, at line 13
Now, that we know how the system works, we can go back to the original question, that is: To separate the “data” (in our example the creation of the list of links) from the “execution” (in our example to add this list of links to the left or right column)
I hope that now, the reasons why to do this are more clear than before: I want simply to have the option to access the plain (well, ok, including it’s html) data of each box, so that I can do with it whatever I want, without having to access each time the box file.
There is for example a addon available (Box content placement), that is adding 3 more optional positions for the boxes, and for this it is altering each box file. Thats all fine, but what if you want to add one more position? And what if you want to have some boxes to appear under some conditions only? For example jut on some pages? Or to have the boxes appearing on all pages but the front page? And so on and so forth.
Thats why I ended up with the approach I’m presenting here, to separate data from execution. In our box example (file includes/modules/boxes/bm_information.php), all I do is to split the function execute into 2 separate functions, so it looks like this
function dataF() { $data = '<div class="ui-widget infoBoxContainer">' . ' <div class="ui-widget-header infoBoxHeading">' . MODULE_BOXES_INFORMATION_BOX_TITLE . '</div>' . ' <div class="ui-widget-content infoBoxContents">' . ' <a href="' . tep_href_link(FILENAME_SHIPPING) . '">' . MODULE_BOXES_INFORMATION_BOX_SHIPPING . '</a><br />' . ' <a href="' . tep_href_link(FILENAME_PRIVACY) . '">' . MODULE_BOXES_INFORMATION_BOX_PRIVACY . '</a><br />' . ' <a href="' . tep_href_link(FILENAME_CONDITIONS) . '">' . MODULE_BOXES_INFORMATION_BOX_CONDITIONS . '</a><br />' . ' <a href="' . tep_href_link(FILENAME_CONTACT_US) . '">' . MODULE_BOXES_INFORMATION_BOX_CONTACT . '</a>' . ' </div>' . '</div>'; return $data; } function execute() { global $oscTemplate; $oscTemplate->addBlock($this->dataF(), $this->group); }
You see that I created a new function, called “dataF()” (could be “kuku()”) where the “data creation process” happens and then I use the output of this function for the function execute(), that is now just executing any data that was “created” before. Same of course I need to do for all boxes
After done once, things are getting really easy. Each time I need a box (out of the regular left/right schema), all I need to do is to “activate” the class of the box I need and to output the contents of dataF() anywhere I want. Or, I can create a new group of boxes, called eg “boxes_footer, add to it any boxes I like, and then display the whole thing in the footer, or wherever I want. Or I can move all boxes together to the left or to the right. In general: I have the total control over the boxes display, without doing anything to the boxes files themselves
This approach is used in mini template system, for example when moving all boxes to the left or to the right, or when making the “information” or “search” box to disappear when a certain template is activated
Update on 5/5/2010: Because some of you asked how to output the content of dataF(), here is the way to do it.
Let’s say you want to display the contents of box “shopping cart” at the bottom of file shipping.php. That’s not very realistic, it’s just an example to demonstrate that you can display anything anywhere
What you need to do in this case? first thing is to “activate” the class in this way
$boxdata = new bm_shopping_cart;
Next step is to echo the variable $boxdata wherever you want, not that the variable could be called anything, eg $kuku
echo $boxdata->dataF();
Now, there is the “danger” to get an error on the page in case the shopping cart box is not present, that means installed. To avoid this, we need to check if the class exists and to activate it only if this is the case, like this
if (class_exists(bm_shopping_cart)) { $boxdata = new bm_shopping_cart; echo $boxdata->dataF(); }
The result on shipping.php looks like this
In the same way you can output any box at any place of your store, for example the cart in the header, or the information box in the footer, or whatever
You will want of course to wrap everything into proper html and/or to use some css to style everything. Enjoy
Very helpful, but how do I “activate” the class of the box I need and output the contents of dataF() anywhere I want?
as fore mentioned i too am having problems outputting the content of dataF
Just like @couba, I woudl also like to know how to “activate” the class of the box. I have tried a number of different things but have had no luck to output the content of dataF().
Hi people. Please find my updated post above, there is a simple example on how to output dataF() on your page
Great article. One question does this work with all the boxes, I tried it with manufacturer_info, it doesn’t thow up any errors but the info doesn’t show?
here’s the code.
function dataA() {
if (isset($HTTP_GET_VARS['products_id'])) {
$manufacturer_query = tep_db_query(“select m.manufacturers_id, m.manufacturers_name, m.manufacturers_image, mi.manufacturers_url from ” . TABLE_MANUFACTURERS . ” m left join ” . TABLE_MANUFACTURERS_INFO . ” mi on (m.manufacturers_id = mi.manufacturers_id and mi.languages_id = ‘” . (int)$languages_id . “‘), ” . TABLE_PRODUCTS . ” p where p.products_id = ‘” . (int)$HTTP_GET_VARS['products_id'] . “‘ and p.manufacturers_id = m.manufacturers_id”);
if (tep_db_num_rows($manufacturer_query)) {
$manufacturer = tep_db_fetch_array($manufacturer_query);
$manufacturer_info_string = ”;
if (tep_not_null($manufacturer['manufacturers_image'])) $manufacturer_info_string .= ” . tep_image(DIR_WS_IMAGES . $manufacturer['manufacturers_image'], $manufacturer['manufacturers_name']) . ”;
if (tep_not_null($manufacturer['manufacturers_url'])) $manufacturer_info_string .= ‘- ‘ . sprintf(MODULE_BOXES_MANUFACTURER_INFO_BOX_HOMEPAGE, $manufacturer['manufacturers_name']) . ‘‘;
$manufacturer_info_string .= ‘- ‘ . MODULE_BOXES_MANUFACTURER_INFO_BOX_OTHER_PRODUCTS . ‘‘ .
”;
$data = ” .
‘ ‘ . MODULE_BOXES_MANUFACTURER_INFO_BOX_TITLE . ” .
‘ ‘ . $manufacturer_info_string .
”;
return $data;
}
}
}
function execute() {
global $HTTP_GET_VARS, $languages_id, $oscTemplate;
$oscTemplate->addBlock($this->dataA(), $this->group);
}
***** added this to the page *********
dataA();
}
?>
Hi Steve
From a first sight I think that you need to do following to get it working
1) Add all “globals” that you have now in function execute() to function dataA(), except $oscTemplate that should stay in function execute()
2) Add to the page the complete piece of code like it is done for the shopping cart example. Simply “dataA()” says nothing
3) make sure you have the manufacturer info box installed in admin. It has not to be enabled, but it need to be active
Hope it helps
Yep it was point 1 that solved it, I had the code correct in the data A call, it just didnt show when I posted it.
thanks alot
This all makes great sense and thank you for posting this tutorial.
However I am confused where to split the function in the bm_categories.php file so that I can call it where I would like to?
Thanks for any help in advance.
There is no difference for box bm_categories.php, its the same process like for any other boxes: You slit the function execute() into 2 functions, the one is dataF() that return the output, the other execute() that add the box to a “group”
Concrete, in bm_categories.php you have
function execute() {
global $SID, $oscTemplate;
if ((USE_CACHE == ‘true’) && empty($SID)) {
$output = tep_cache_categories_box();
} else {
$output = $this->getData();
}
$oscTemplate->addBlock($output, $this->group);
}
This you can replace with following
function dataF() {
global $SID;
if ((USE_CACHE == ‘true’) && empty($SID)) {
$output = tep_cache_categories_box();
} else {
$output = $this->getData();
}
return $output;
}
function execute() {
global $oscTemplate;
$oscTemplate->addBlock($this->dataF(), $this->group);
}
Thank you very much, I understand better now.
Hello, well thank you for this posting this article.
i understood very well how to split the execute function. but i want to know:
-should i leave the rest of the code ? (function isEnabled(), function check(), ect…)
-after editing the file (bm_manufacturer_info), should i upload and replace the existing file?
never mind, its done… thank you for the information, sir!!
Very Useful topic.
Thanks for writing this.
Hi George,
I followed your instructions and was able to separate data from the bm_information box and output the data into another file.
I am trying to separate data now for bm_currencies. I get a Fatal error: Call to undefined method bm_currencies::execute()
This is what I have done. Any idea as to what I have done wrong? Thank you in advance.
function dataF() {
global $PHP_SELF, $currencies, $HTTP_GET_VARS, $request_type, $currency;
if (substr(basename($PHP_SELF), 0, 8) != ‘checkout’) {
if (isset($currencies) && is_object($currencies) && (count($currencies->currencies) > 1)) {
reset($currencies->currencies);
$currencies_array = array();
while (list($key, $value) = each($currencies->currencies)) {
$currencies_array[] = array(‘id’ => $key, ‘text’ => $value['title']);
}
$hidden_get_variables = ”;
reset($HTTP_GET_VARS);
while (list($key, $value) = each($HTTP_GET_VARS)) {
if ( is_string($value) && ($key != ‘currency’) && ($key != tep_session_name()) && ($key != ‘x’) && ($key != ‘y’) ) {
$hidden_get_variables .= tep_draw_hidden_field($key, $value);
}
}
$data = ” .
‘ ‘ . MODULE_BOXES_CURRENCIES_BOX_TITLE . ” .
‘ ‘ .
‘ ‘ . tep_draw_form(‘currencies’, tep_href_link(basename($PHP_SELF), ”, $request_type, false), ‘get’) .
‘ ‘ . tep_draw_pull_down_menu(‘currency’, $currencies_array, $currency, ‘onchange=”this.form.submit();” style=”width: 100%”‘) . $hidden_get_variables . tep_hide_session_id() . ” .
‘ ‘ .
”;
return $data;
}
function execute() {
global $oscTemplate;
$oscTemplate->addBlock($this->dataF(), $this->group);
}
}
}
Ok, I figured it out. :)
This part should be below the curly braces
return $data;
}
}
}
function execute() {
global $oscTemplate;