Advanced Add-on Tutorial

This tutorial demonstrates some advanced techniques in add-on development, like using hooks and post-controllers.

Note

Hooking is a very powerful technique, and we use it a lot in CS-Cart. PHP hooks can be used to perform additional pre- and post-processing of data, or to override default processing routines. TPL hooks are used to handle data on rendering, e.g. to display additional data with no need to modify the original template.

A pre- or post-controller is a special PHP file which (depending on its name and location in the add-on directory structure) are called before or after a particular standard controller is executed.

In this tutorial, we will create an add-on, which will utilize both PHP and TPL hooks.

The add-on will collect information about the categories being shown to a signed in user and store it in the database. This data will be then shown on the admin panel dashboard as a table of users and according number of viewed categories.

Requirements

You will need an installed and functioning CS-Cart store to be able to follow this tutorial. You can download CS-Cart from our site and use it in the Free mode.

Basic knowledge of PHP, Smarty, and getting familiarized with the CS-Cart add-on directory structure is required. It is recommended that you follow the Hello World tutorial before proceeding with this one.

Add-on Initialization: addon.xml, init.php

Go to the directory app/addons in your CS-Cart installation root directory and create the advanced_addon subdirectory. In this directory, create the file addon.xml with the following content (download):

addons/advanced_addon/addon.xml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?xml version="1.0"?>
<addon scheme="2.0">
	<id>advanced_addon</id>
	<name>Advanced Add-on</name>
	<description>Advanced Add-on</description>
	<version>1.0</version>
	<priority>100500</priority>
	<status>active</status>
	<queries>
        <item for="install">DROP TABLE IF EXISTS ?:advanced_addon_data;</item>
		<item for="install">
            CREATE TABLE `?:advanced_addon_data` (
				`user_id` int(11) unsigned NOT NULL DEFAULT 0,
				`categories` text NOT NULL DEFAULT '',
				PRIMARY KEY (`user_id`)
			) Engine=MyISAM DEFAULT CHARSET UTF8;
		</item>
        <item for="uninstall">DROP TABLE IF EXISTS ?:advanced_addon_data;</item>
	</queries>
</addon>

In the same directory, create the file init.php with the following content (download):

addons/advanced_addon/init.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
/***************************************************************************
*                                                                          *
*   (c) 2004 Vladimir V. Kalynyak, Alexey V. Vinokurov, Ilya M. Shalnev    *
*                                                                          *
* This  is  commercial  software,  only  users  who have purchased a valid *
* license  and  accept  to the terms of the  License Agreement can install *
* and use this program.                                                    *
*                                                                          *
****************************************************************************
* PLEASE READ THE FULL TEXT  OF THE SOFTWARE  LICENSE   AGREEMENT  IN  THE *
* "copyright.txt" FILE PROVIDED WITH THIS DISTRIBUTION PACKAGE.            *
****************************************************************************/

if ( !defined('AREA') ) { die('Access denied'); }

fn_register_hooks(
	'get_category_data_pre'
);

?>

Hint

Function names are usually self-explanatory in CS-Cart (e.g. get_products to get products). Hooks are normally named after the function they are placed in, with the _pre or _post suffix for a pre- and post-processing hook accordingly (e.g. get_products_pre and get_products_post).

In this file, we declare that we are going to connect to the hook get_category_data_pre, which is called before getting category data by a certain query. You can find the information about this and other hooks in our Hook base.

Getting Data: func.php

In the add-on directory (app/addons/advanced_addon), create the file func.php. It will contain the actual function to be embedded to the hook (download):

addons/advanced_addon/func.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
/***************************************************************************
*                                                                          *
*   (c) 2004 Vladimir V. Kalynyak, Alexey V. Vinokurov, Ilya M. Shalnev    *
*                                                                          *
* This  is  commercial  software,  only  users  who have purchased a valid *
* license  and  accept  to the terms of the  License Agreement can install *
* and use this program.                                                    *
*                                                                          *
****************************************************************************
* PLEASE READ THE FULL TEXT  OF THE SOFTWARE  LICENSE   AGREEMENT  IN  THE *
* "copyright.txt" FILE PROVIDED WITH THIS DISTRIBUTION PACKAGE.            *
****************************************************************************/

if ( !defined('AREA') ) { die('Access denied'); }

function fn_advanced_addon_get_category_data_pre($category_id, $field_list, $get_main_pair, $skip_company_condition, $lang_code)
{
	//Getting authentication data to identify user
	$auth = $_SESSION['auth'];

	//Checking if the user is logged in and resides at the customer area
	if (!empty($auth['user_id']) && AREA == 'C') {
		//Checking if the database has data on the user.
		//Creating new record if necessary, appending existing data if possible.

		$viewed_categories = db_get_field('SELECT categories FROM ?:advanced_addon_data WHERE user_id = ?i', $auth['user_id']);

		if (!empty($viewed_categories)) {
			$viewed_categories = unserialize($viewed_categories);
		}

		$viewed_categories[$category_id] = true;
		$viewed_categories = serialize($viewed_categories);

		db_query('REPLACE INTO ?:advanced_addon_data VALUES (?i, ?s)', $auth['user_id'], $viewed_categories);
	}
}

?>

The function fn_advanced_addon_get_category_data_pre will get the currently displayed categories and store this data to the database linked with the user who is browsing the store.

Important

It is essential to follow the naming convention: fn_ + [addon id] + _ + [hook name].

A function named out ot this convention will be ignored.

Showing Data in the Admin Panel: index.post.tpl, index.post.php

In order to show the collected data in the admin panel, we are going to append a new data block to the TPL hook index in the admin panel dashboard template (design/backend/templates/views/index/index.tpl).

Go to the directory design/backend/templates/addons and create a directory named advanced_addon. In this directory, create a subdirectory hooks and inside it another subdirectory index.

In this directory, create the file index.post.tpl with the following content (download):

design/backend/templates/addons/advanced_addon/hooks/index/index.post.tpl

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="statistics-box">
	<div class="statistics-body">
		<p class="strong">Viewed categories</p>
		<div class="clear">
			{if $viewed_categories}
				<ul>
					{foreach from=$viewed_categories item="category_data"}
						<li><strong><a href="{"profiles.update?user_id=`$category_data.user_id`"|fn_url}">{$category_data.user_name}</a></strong>:&nbsp;
							{foreach from=$category_data.categories key="category_id" item="category_name"}
								<a href="{"categories.update?category_id=`$category_id`"|fn_url}">{$category_name}</a>, 
							{/foreach}
						</li>
					{/foreach}
				</ul>
			{else}
				<ul>
					<li>No data found</li>
				</ul>
			{/if}
		</div>
	</div>
</div>

Important

Unlike PHP hooks, TPL hooks should not be explicitly declared. It is sufficient to just place a properly named template in the properly named directory.

The location convention is as follows:

  • design/backend/templates/addons/<addon name>/hooks for the admin panel template hooks
  • var/themes_repository/basic/templates/addons/<addon name>/hooks for the customer area template hooks
  • var/themes_repository/basic/mail/templates/addons/<addon name>/hooks for the customer area template hooks

Inside the hooks directory, the hooks should be located and named as follows: <template name>/<hook name>.[pre|post].tpl

The template cannot gather data from the database itself, this is performed by a post-controller for the index.php controller.

Go to the directory app/addons/advanced_addon and create the subdirectories controllers/backend. Switch to this directory and create the file index.post.php with the following content (download):

app/addons/advanced_addon/controllers/backend/index.post.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
/***************************************************************************
*                                                                          *
*   (c) 2004 Vladimir V. Kalynyak, Alexey V. Vinokurov, Ilya M. Shalnev    *
*                                                                          *
* This  is  commercial  software,  only  users  who have purchased a valid *
* license  and  accept  to the terms of the  License Agreement can install *
* and use this program.                                                    *
*                                                                          *
****************************************************************************
* PLEASE READ THE FULL TEXT  OF THE SOFTWARE  LICENSE   AGREEMENT  IN  THE *
* "copyright.txt" FILE PROVIDED WITH THIS DISTRIBUTION PACKAGE.            *
****************************************************************************/

if (!defined('BOOTSTRAP')) { die('Access denied'); }

use Tygh\Registry;

$viewed_categories = db_get_array('SELECT * FROM ?:advanced_addon_data');

if (!empty($viewed_categories)) {
    foreach ($viewed_categories as $key => $category_data) {
        $category_data['user_name'] = fn_get_user_name($category_data['user_id']);
        $category_data['categories'] = unserialize($category_data['categories']);
        $category_data['categories']  = fn_get_category_name(array_keys($category_data['categories']));

        $viewed_categories[$key] = $category_data;
    }

    Registry::get('view')->assign('viewed_categories', $viewed_categories);
}

?>

Double check all the file paths, names, and file contents to guarantee that the add-on will work properly.

Result

To see the add-on in action, install it first. To do that, go to Add-ons → Manage add-ons in the CS-Cart admin panel. Find the item Advanced Add-on and click Install near the title. You should see a successful installation notification.

Now switch to the dashboard and to the bottom of the page. You should see a new section looking similar to this:

Advanced Add-on, Viewed Categories, empty

As you see, there are no data so far, but the section is shown properly.

Switch to the customer area, log in, and surf a bit around the store. Just go over some random categories. You can also try browsing under several different accounts.

Refresh the dashboard page of the admin panel and check the state of the Viewed categories section:

Advanced Add-on, Viewed Categories with data

The section should now indicate the categories you have just surfed through, which is exactly as planned.