Create a backend module – 3. Code the module

Date: June 16, 2010
Oxid version: 4.3.1

Having set up the module framework in the first two parts of this tutorial, we will now dive into the actual coding. This part will guide you in the implementation of your module and give some pointers on best practices and pitfalls to avoid. The examples presented here will be quite simple, in keeping with the KISS principle.

Plan your work

Before you write one line of code, you should decide on what you want to accomplish and what steps are involved. Planning your work helps to keep your goal in view and check your progress. If the problem you need to solve turns out to be complex, break down your implementation into smaller units. Each unit or function should do just one task, and do it well. Embracing the KISS principle also means that your functions should not depend on each other or have hidden side effects. An example: function A calls function B to do a task, B does this and also sets some variables that A, in turn, uses to accomplish its task. Such dependencies lead to unmanagable code with bugs that are notoriously hard to find.

The example module we will create, “bpx_genpics” is supposed to help in importing a large product list into the Oxid shop. It will generate pictures for the imported articles. The scenario is as follows:

  1. The Oxid import module is used to feed the oxarticles table with a prepared CSV list. The field oxarticles.oxpic1 contains the name of the image file for the article; oxarticles.oxpicsgenerated is set to 0.
  2. The images for the imported articles are uploaded via FTP to the folder out/pictures/master/1. The name of the image file matches the name set in oxarticles.oxpic1.
  3. Our module is called up in the admin section of the Oxid shop (Service – Generate Pictures).
  4. Before starting the picture generation the module shows the number of articles that don’t have generated pictures and displays a link (or button) for starting the generation.
  5. Pressing the link starts the actual work of the module.
  6. After the generation is done the module again shows the number of articles without generated pictures (this should be zero) and the number of processed articles.

Use Oxid functions

Picture generation is, of course, done by the Oxid system itself. If you select the “Pictures” tab under Administer Products – Products, enter an image file for slot 1 and press “Save” the icon, thumb and zoom pictures are generated for the selected article. The same thing happens when a user views an article without generated pictures on the frontend. So knowing how Oxis works helps you in deciding if you can use already existing functions.

Finding the actual class and function names can be a bit more challenging. The Firebug extension shows the “Save” button code:

<input type = "submit" onclick = "Javascript:document.myedit.fnc.value='save'" value = "Save" name = "save" class = "edittext" >

We need to find the “myedit” element in the page source. The source code of the frame containing the “Save” button has a form with id=”myedit”:

<form name="myedit" id="myedit" enctype="multipart/form-data" action=";" method="post">
<input type="hidden" name="MAX_FILE_SIZE" value="2097152">
<input type="hidden" name="stoken" value="xxxxxxxx">
<input type="hidden" name="force_admin_sid" value="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx">
<input type="hidden" name="cl" value="article_pictures">
<input type="hidden" name="fnc" value="">
<input type="hidden" name="oxid" value="art001">
<input type="hidden" name="editval[article__oxid]" value="art001">
<input type="hidden" name="voxid" value="art001">
<input type="hidden" name="oxparentid" value="">
<input type="hidden" name="masterPicIndex" value="">

The hidden input element with name “cl” reveals the Oxid class: “article_pictures”. So what we’re looking for is the function article_pictures::save(). Firing up the Oxid eShop API documentation we search for “article_pictures” and find the class “Article_Pictures” with the save() function. The documentation says: “Saves (uploads) pictures to server.” Well, that’s rather sparse. And, looking at the source code of the save() function shows that we cannot pass an article object to the function. But we want to invoke the save() function repeatedly for a whole list of articles! The save() function calls a set of utility functions that look enticing, but these are all protected and cannot be accessed outside of the class. Now what?

For your own module you may have found the solution with the detective work above. As for us, we’ll have to poke around in the documentation some more. It’s all about generating pictures, so why not try searching for “generate” in the documentation? Whoa! I found “generateArticlePictures” in the class “oxPictureHandler” – “Generates article pictures (icon, thumbnail, zoom picture) from master picture.” – that’s more like it! The arguments passed to generateArticlePictures are the article object and the index of the master picture (in our case this will be 1). Before generating the pictures we will need to delete any previously generated ones. The same class also provides the function “deleteArticleMasterPicture” – “Deletes master picture and all images generated from it. If third parameter is false, skips master image delete, only all generated images will be deleted.”

Now we have the Oxid picture functions for our module. To get the list of articles we will need some database utitlities.

Database utilities

Oxid provides several utility functions for database operations:

  • To get a single value: $myval = oxDb::getDb()->getOne( $sQuery )
  • For a set of rows: $rows = oxDb::getDb(true)->Execute( $sQuery )

Remember to call getDb(true) if you want to iterate over the rowset! The getDb() function returns an ADOConnection object; the members getOne() and Execute() are documented in the ADOdb library for PHP.

Here is an example of row iteration:

$rows = oxDb::getDb(true)->Execute( $sSelect );
if( $rows != false && $rows->recordCount() > 0 ) {
	while ( !$rows->EOF ) {
		// get a field value of the current row
		$oxid = $rows->fields["oxid"];
		// do something
		// move to the next row!!

Write the functions

Continuing with the bpx_genpics module created in part 1, we will add the function genpics() to do the heavy lifting. Here is the final code:


class bpx_genpics extends oxAdminView
  protected $_sThisTemplate = 'bpx_genpics.tpl';
  public function render()
    // count articles without generated pictures
    $this->_aViewData["nogen_count"] = oxDb::getDb()->getOne( $this->_getCountSelect() );
    return $this->_sThisTemplate;
  public function genpics()
    // count articles without generated pictures before the generation
    $iCountBefore = oxDb::getDb()->getOne( $this->_getCountSelect() );
    $rows = oxDb::getDb(true)->Execute( $this->_getArticleSelect() );
    if( $rows != false && $rows->recordCount() > 0 ) {
      while ( !$rows->EOF ) {
        $oxid = $rows->fields["oxid"];
        //echo "oxid=" . $oxid;
        // create and load the article object
        $oArticle = oxNew( "oxarticle" );
        $oArticle->load( $oxid );
        // generate pictures for the current article
        $oxph = oxPictureHandler::getInstance();
        $oxph->deleteArticleMasterPicture( $oArticle, 1, false );
        $oxph->generateArticlePictures( $oArticle, 1 );
    // count articles without generated pictures after the generation
    $iCountAfter = oxDb::getDb()->getOne( $this->_getCountSelect() );
    // save some data for use in the template
    $this->_aViewData["gen_count"] = $iCountBefore - $iCountAfter;  
    $this->_aViewData["gen_done"] = true;  
  protected $_sCountQ;
  protected function _getCountSelect(){
      $sViewName = getViewName('oxarticles');
      $this->_sCountQ = "SELECT COUNT(*) FROM $sViewName WHERE $sViewName.oxpicsgenerated = 0 and $sViewName.oxpic1 IS NOT NULL";
    return $this->_sCountQ;
  protected $_sSelectQ;
  protected function _getArticleSelect(){
      $sViewName = getViewName('oxarticles');
      $this->_sSelectQ = "SELECT OXID FROM $sViewName WHERE $sViewName.oxpicsgenerated = 0 and $sViewName.oxpic1 IS NOT NULL";
    return $this->_sSelectQ;  

Most of this should be familiar from the discussion above. Note that I called ini_set(‘display_errors’, true) in both functions. This is helpful in analyzing bugs and should be removed when the module goes productive. Another gotcha is the call to member functions of oxPictureHandler: you must create an instance of the class before using non-static members!

Modify the template

The template file, bpx_genpics.tpl, needs some extra code to render the link for generating the pictures. Again, the complete code is shown before discussing it:


[{include file="headitem.tpl" title="GENERAL_ADMIN_TITLE"|oxmultilangassign box=" "}]
<script type="text/javascript">
    if (top)
        top.sMenuItem    = "[{ oxmultilang ident="bpx_genpics_menuitem" }]";
        top.sMenuSubItem = "[{ oxmultilang ident="bpx_genpics_menusubitem" }]";
        top.sWorkArea    = "[{$_act}]";
<form name="myedit" id="myedit" action="[{ $shop->selflink }]" method="post">
[{ $shop->hiddensid }]
<input type="hidden" name="cl" value="bpx_genpics">
<input type="hidden" name="fnc" value="">
<input type="hidden" name="oxid" value="[{ $oxid }]">
<input type="hidden" name="voxid" value="[{ $oxid }]">
<input type="hidden" name="oxparentid" value="[{ $oxparentid }]">
<input type="hidden" name="editval[oxarticles__oxid]" value="[{ $oxid }]">
</form><br /><br />
<div class="center">
<h2>[{$nogen_count}] articles without generated pictures</h2>
<a href="#" onclick="document.myedit.fnc.value='genpics';document.myedit.submit();">Generate Pictures</a>
[{if $gen_done}]
<h3>Pictures for [{$gen_count}] articles were generated.</h3>

I lifted the form#myedit from the admin/systeminfo.tpl mentioned in part 1. We need this form for the “Generate Pictures” link with its onclick handler. Here the input element fnc gets the value ‘genpics’ and the form is submitted. The Oxid system then in turn calls bpx_genpics::genpics(), invoking the actual generation code.

In the DIV the Smarty tags display the number of articles without generated pictures and the number of processed articles. When the onclick handler returns, the page is rendered again, and should show 0 articles without generated pictures.

What’s next?

The complete source code is available as a download from the author’s website:

Please note that this module is only an example for your own module coding. Although I have used it successfully, you use it at your own risk.

Before it is suitable for production use, some issues need to be considered:

  • When processing a large number of articles you may encounter script timeout errors.
  • A progress bar and a Cancel button should be displayed during processing.
  • An option to revert to the original state may be desirable

I may adress some of these concerns in the coming weeks. Stay tuned.

Again, if you have any problems with this tutorial, please post to my thread in the forum:

Happy Coding!