Using custom libraries in Opencart
Opencart shoppingcart software is great, but not perfect. What I don't like at all is that when writing an extension (what Wordpress would call a plugin) the code shatters all over the place. And with modifications you must give the results a place somewhere in all those views.
A library can ease the pain...somewhat
A library in Opencart is not in Admin neither is in Catalog, it is an in system located library. It is placed in the system/library folder. You can even load your custom library automatically so that it everywhere available.
I use libraries to have functionality that I would easily execute like:
$drink = $this->myCustomLib->takeAColdBeverageFromTheFridge('random');
and that it will return "Diet Coke" or "Milk".
But there is much more. You can even use models or something really crazy like rendering your own Twig templates (only useful if you are using version 2.x of Opencart, for version 3 has a Twig engine by default).
Autoloading a library
Let's assume that we have a custom library (in the system/library folder) named Megalib. How we create the library and what to do with it we'll discuss in a moment, but first how to autoload it?
Customization for an library autoload
We create a new customization XML named autoload-megalib.ocmod.xml (Opencart version 2.x) with the following content:
<?xml version="1.0" encoding="utf-8"?>
<modification>
<code>RegisterMegalibLib</code>
<name>Register Megalib Lib</name>
<version>1.0</version>
<author>Your name</author>
<link>https://www.yourlink.local</link>
<file path="catalog/controller/startup/startup.php">
<operation>
<search>
<![CDATA[
$this->registry->set('openbay', new Openbay($this->registry));
]]>
</search>
<add position="after">
<![CDATA[
$this->registry->set('megalib', new Megalib($this->registry));
]]>
</add>
</operation>
</file>
</modification>
When this customization is installed and cached it can be accessed as:
$drink = $this->megalib->getMeAColdBeverage();
Sample: the Megalib library
In the system/library folder a new file named megalib.php must be created. A library is nothing more than a usual PHP class. It is no extension of a "library" class. Just a simple class.
<?php
use Megalib\Base;
class Megalib extends Base
{
private $lang;
public function __construct($registry)
{
parent::__construct($registry);
$this->load->model('megalib/product');
$this->lang = (int) $this->config->get('config_language_id');
}
/**
* @param string $template
* @param null $id
* @param null $lang
*
* @return string
*/
public function getProductDocuments($template = 'hello-world', $id = null, $lang = null)
{
$lang_id = isset($lang) ? $lang : $this->lang;
$product_id = isset($id) ? $id : $this->request->get['product_id'];
$documents = $this->model_megalib_product->getDocuments($product_id, $lang_id);
$data = [
'dir_download' => DIR_DOWNLOAD,
'language_id' => $lang_id,
'product_id' => $product_id,
'documents' => $documents
];
$view = $this->twig($template, $data);
return $view;
}
/**
* @param null $id
* @param null $lang
*
* @return bool
*/
public function hasDocuments($id = null, $lang = null) {
$lang_id = isset($lang) ? $lang : $this->lang;
$product_id = isset($id) ? $id : $this->request->get['product_id'];
$documents = $this->model_megalib_product->getDocuments($product_id, $lang_id);
return count($documents) > 0;
}
}
For the moment this library has two functions:
- getProductDocuments, renders a Twig template and returns a string
- hasDocuments, returns a boolean (true/false) to inform if this product has documents
Extending a library class
use Megalib\Base;
class Megalib extends Base
...
Our Megalib is an extension of a Base class that can be found in file base.php inside the system/library/megalib folder. Within that folder you can have all kinds of things related to your library, like the Twig templates we use later on.
Loading models in your library class
$this->load->model('megalib/product');
$documents = $this->model_megalib_product->getDocuments($product_id, $lang_id);
is loading the model from catalog/model/megalib/product.php.
base.php with Twig loader
<?php
namespace Megalib;
use Twig\Loader\FilesystemLoader as TwigLoader;
class Base
{
private $defaultTpl = 'hello-world';
var $response;
var $logger;
protected $product_id = null;
/**
* @param object $registry Registry Object
*
* You could load some useful libraries, few examples:
*
* $registry->get('db');
* $registry->get('cache');
* $registry->get('session');
* $registry->get('config');
* and more...
*/
function __construct($registry)
{
$this->autoload();
$this->registry = $registry;
$this->db = $registry->get('db');
$this->request = $registry->get('request');
$this->session = $registry->get('session');
$this->language = $registry->get('language');
}
public function __get($key)
{
return $this->registry->get($key);
}
function output($output = [])
{
return json_encode($output);
}
function getProductId()
{
// current Product ID
$this->product_id = $this->request->get['product_id'];
return $this->product_id;
}
function twig($_template = '', $_data = []) {
$_template = is_string($_template) ? $_template : $this->defaultTpl;
if (strpos($_template, '.twig') === false) {
$_template .= '.twig';
}
$twigLoader = new TwigLoader(dirname(__FILE__) . '/twig');
$twig = new \Twig\Environment($twigLoader, [
'cache' => dirname(__FILE__) . '/twig/cache'
]);
$template = $twig->load($_template);
return $template->render($_data);
}
private function autoload()
{
if (is_file(DIR_SYSTEM . '../vendor/autoload.php')) {
require_once(DIR_SYSTEM . '../vendor/autoload.php');
}
}
}
Opencart version 2.x is not supporting Twig templating by default. But I hate that spagetti mix of HTML with PHP. So I applied Twig support with this library, in our case to create an product itemlist with Bootstrap.
To make Twig work, it is important that you have a composer.json in the root of your Opencart installation with following content (at least):
{
"require": {
"katzien/php-mime-type": "^2.1.0",
"twig/twig": "^3.x-dev"
}
}
Then you have to execute:
composer update
After that a vendor folder is created with all the dependencies.
How to use Twig from this library
In the Megalib class we already have seen the following function:
public function getProductDocuments($template = 'hello-world', $id = null, $lang = null)
{
$lang_id = isset($lang) ? $lang : $this->lang;
$product_id = isset($id) ? $id : $this->request->get['product_id'];
$this->load->model('megalib/product');
$documents = $this->model_megalib_product->getDocuments($product_id, $lang_id);
$data = [
'dir_download' => DIR_DOWNLOAD,
'language_id' => $lang_id,
'product_id' => $product_id,
'documents' => $documents
];
$view = $this->twig($template, $data);
return $view;
}
The Twig related part is related to this snippet of code:
$documents = $this->model_megalib_product->getDocuments($product_id, $lang_id);
$data = [
'dir_download' => DIR_DOWNLOAD,
'language_id' => $lang_id,
'product_id' => $product_id,
'documents' => $documents
];
$view = $this->twig($template, $data);
return $view;
In this piece of code the following steps are executed:
- the $documents are loaded from the database (by use of the model)
- the $data array is assembled
- the $this->twig function is executed and its result returned in variable $view
- the $view is returned
Now pay attention to the following lines in the Base class:
<?php
namespace Megalib;
use Twig\Loader\FilesystemLoader as TwigLoader;
class Base
{
...
function __construct($registry)
{
$this->autoload();
...
}
...
function twig($_template = '', $_data = []) {
$_template = is_string($_template) ? $_template : $this->defaultTpl;
if (strpos($_template, '.twig') === false) {
$_template .= '.twig';
}
$twigLoader = new TwigLoader(dirname(__FILE__) . '/twig');
$twig = new \Twig\Environment($twigLoader, [
'cache' => dirname(__FILE__) . '/twig/cache'
]);
$template = $twig->load($_template);
return $template->render($_data);
}
private function autoload()
{
if (is_file(DIR_SYSTEM . '../vendor/autoload.php')) {
require_once(DIR_SYSTEM . '../vendor/autoload.php');
}
}
}
- In the __construct the autoload is loading the dependencies. As this is a require_once, other custom libraries will not create redundancy.
- The twig function accepts a template and a data array. The templates are stored in a folder named twig which is within the system/libraries/megalib folder.
- The twig function renders a template into a string and returns the outcome $template->render($_data).
Where to use your libraries?
You can use your library functions in controllers, models and even views. Here is a sample how I have used the functions described earlier:
Controller:
$product_id = $this->request->get['product_id'];
$has_documents = $this->megalib->hasDocuments($product_id);
$product_documents = $has_documents ? $this->megalib->getProductDocuments('documents', $product_id ) : false;
$data['has_documents'] = $has_documents;
$data['product_documents'] = $product_documents;
View:
<?php if (isset($has_documents) && $has_documents) { ?>
<?php echo $product_documents ?>
<?php } ?>
Tip
If you don't like Twig, you could also experiment to modify your library and support Mustache, Handlebars or even Smarty.
see also: