3

I am newbie in PHP, I have faced with weird thing for me in opencart PHP engine.
There is file called catalog/controller/module/slideshow.php.
This class extends Controller class

class ControllerModuleSlideshow extends Controller {
        protected function index($setting) {
                static $module = 0;

                $this->load->model('design/banner');
                $this->load->model('tool/image');
.....
                 $this->model_design_banner->getBanner($setting['banner_id']);
.....

Hm $this->model_design_banner there is no such member in this class, oh probably it is in parent class.
Let's check this, cd....

<?php
abstract class Controller {
        protected $registry;
        protected $id;
        protected $layout;
        protected $template;
        protected $children = array();
        protected $data = array();
        protected $output;

Hm .... ??!!! WTF (sorry)

There is no such member in this class too.......

I guessed that is shortcut for catalog/controller/module/banner.php

// Here are a lot questions how , where ?

Let's open catalog/model/design/banner.php

<?php
class ModelDesignBanner extends Model {
        public function getBanner($banner_id) {
                $query = $this->db->query("SELECT * FROM " . DB_PREFIX . "banner_image bi LEFT JOIN " . DB_PREFIX . "banner_image_description bid ON (bi.banner_image_id  = bid.banner_image_id) WHERE bi.banner_id = '" . (int)$banner_id . "' AND bid.language_id = '" . (int)$this->config->get('config_language_id') . "'");

                return $query->rows;
        }
}

Okey it without previous questions it looks normal... stop !

If we can call method shortcut it should be static ... .

$this->db->query

In this case should point to nothing ....

So a lot of weird things for me

How does this really work. How does shortcut is mapped to function, why function is not static and so on.

Please explain this, I will be grateful for any help.

EDIT

If load object has method that loads model in my class where does load method is declared,there is also this so it should be in same class.

tereško
  • 56,151
  • 24
  • 92
  • 147
cdxulkqy
  • 33
  • 4

1 Answers1

5

Using $this->model_design_banner fails, because $this->load->model('design/banner'); fails.

It fails, because you mixed up "model" and "module".

You have a model file in a module folder:

catalog/module/design/banner.php

This should be

catalog/model/design/banner.php

The function call $this->load->model('design/banner'); tries to load the model from a specific location: "catalog/model/design/banner.php". But it can't find it in your case and so the "magic shortcut" is not working. Just move the file to the correct folder. $this->model_design_banner->getBanner(); should work, when the model is found.

How does this work internally?

The model() function expects the model file with a model class in a certain folder. It will then load this file (if it exists) and instantiate the model class as a class property of your current class. To build the name of the class property, the name of the model class is modified from ModelAAABBB to model_aaa_bbb and that's $this->model_aaa_bbb - for easier usage. This is not really documented in depth (http://docs.opencart.com/developer/loading), but it's working like this internally.

Take a look at the Loader.php with model(): https://github.com/opencart/opencart/blob/master/upload/system/engine/loader.php#L15

This is static file loading combined with a Registry Pattern and Magic property access from the Controller. There is an "easier", "non-magic" way: to simply rely on Autoloading. That would allow to use $model = new ModelDesignBanner(); directly in the controller. The autoloader would resolve the classname to filename via it's classmap. It really depends.. and it's a design decision of the OpenCart core team. I like the Autoloading approach more, because it doesn't hide so much of whats really going on. It might be slower compared to direct includes, if the map is really big.

Magic is bad - http://www.infoq.com/presentations/8-lines-code-refactoring

Jens A. Koch
  • 34,456
  • 12
  • 100
  • 117
  • Sorry this is my mistake. It works, but I don't understand how. Please explain this – cdxulkqy Apr 19 '15 at 10:37
  • I have corrected my mistake please explain how does this work – cdxulkqy Apr 19 '15 at 10:39
  • 1
    I've updated my answer to explain how the model loading works. – Jens A. Koch Apr 19 '15 at 10:44
  • 1
    Thank you for answer but where is load member is declared ? It it has method that loads model. I haven't found it anywhere in class hierarchy – cdxulkqy Apr 19 '15 at 10:54
  • 1
    Updated my answer with a link to system/engine/loader.php. The model() function is inside it and you see that it uses a fixed dir for model loading. This is a case of direct requiring/loading of a file and not autoloading. – Jens A. Koch Apr 19 '15 at 10:55
  • Thank you a lot for the help, but the load object is pretended to be class member because it is used with $this keyword , is it loaded to every class instance somewhere – cdxulkqy Apr 19 '15 at 10:59
  • 1
    Correct! The loader is always needed and is instantiated itself and set to the global registry object during the bootstrap of OpenCart. You find it in various `index.php` files - always near the top. See https://github.com/opencart/opencart/blob/master/upload/index.php#L22 – Jens A. Koch Apr 19 '15 at 11:02
  • 1
    Why can you access it via $this->load? Well, AbstractController uses Magic Functions: __get and __set. https://github.com/opencart/opencart/blob/master/upload/system/engine/controller.php That makes a class property available for direct access. – Jens A. Koch Apr 19 '15 at 11:06
  • Thank you for answer, I am getting it slower. But one last question I have. Is it really good design ? I have came from Java/Android/ and for me it seems very tangled. One object adds fields to another objects ,which in turn adds fields to another object and so on. I got lost in all this. So maybe PHP developers have some other design patterns to make it unreadable for people unfamiliar with this engine. Or maybe I am stupid to understand these approaches. – cdxulkqy Apr 19 '15 at 11:06
  • 1
    Glad i could help! You are right: this is static file loading combined with a Registry Pattern and Magic property access from the Controller. There is an easier way: to simply rely on Autoloading. That would allow to use `$model = new ModelDesignBanner()` directly in the controller and a real autoloader would resolve the classname to filename via mapping. It really depends.. and it's a design decision of the OpenCart core team. I like the Autoloading approch more, because it doesn't hide so much of whats really going on. It might be slower compared to direct includes, if the map is really big. – Jens A. Koch Apr 19 '15 at 11:11
  • Thank you a lot, you really helped me Please add your answer from comments to answer, I think it would be useful for others. And there is last question, how to use this model from another custom class I have created ? Without being modified by Loader... – cdxulkqy Apr 19 '15 at 11:27
  • 1
    This question popped up before and is answered over here: http://stackoverflow.com/questions/13512865/opencart-load-model-outside-controller – Jens A. Koch Apr 19 '15 at 11:41