Welcome Guest, Not a member yet? Register   Sign In
Abstract Models
#1

[eluser]zauber[/eluser]
I'm trying to wrap my head around the codigniter way of doing OOP, and have run into a "textbook example". If anyone can help me understand the best way to solve this, I'm sure I'll have a much deeper understanding of how to work with CI.

I've been working on a model which handles data in a database table, which describes hierarchically ordered components. A few of the functions in the model relate specifically to the component data, but several deal exclusively with the hierarchical organization of the components (moving subtrees to other nodes, what node is the parent of a given node - that type of thing). So the obvious oo approach is: create a an abstract class "TreeModel" which inherits "Model", and have all future models representing tree-data in a database inherit "TreeModel" rather than Model.

Now the design issue is: how do you load the abstract class definition?

It obviously has to be loaded before each inheriting class is defined, and I could just do, for example:

Code:
OrganizationModel.php:
===============

...
require_once("/some/path/TreeModel.php");
class OrganizationModel extends TreeModel{
...

I feel this approach is problematic. It a bit "crude" to use a plain require_once() when there is the lovely loader for everything else. Especially since I will now run into the problem that the loader mainly solves; what is the include path? That obviously depends partially on where I place my "TreeModel.php" - which is also a dilemma for me.

I guess I could just put it in my Models folder, but I anticipate having several applications, and would like to reuse this model in all of my applications, and hence would need some standard folder outside the application folder to keep these.

Similar trouble is sure to pop up with when I start looking at abstracting my controllers. Many of my controllers share a lot of common functionality, and I'm pretty sure I'll want to inherit them too in the same way I'd like to inherit models.

I'm also looking toward inheritance in views - but that's another issue altogether I imagine. More to do with templating. I'm thinking something along the lines of django's templating engine (for those of you who've had a chance to play with that).

There must be several people smarter and more experienced than me who have run into similar problems. I'd be really interested in seeing a few different solutions for these issues, and some discussion for the pro/con's of the various methods.
#2

[eluser]tonanbarbarian[/eluser]
Yes the require_once in the model file is a bit crude and I personally prefer not to do it, although there are numerous examples of people doing just that here in the forums and on the wiki. In fact some of the commonly used CRUD libraries and similar use that method.

Personally I prefer to extend the Loader class myself.

I would do something like this
application/libraries/MY_Loader.php
Code:
<?php
if (!defined('BASEPATH')) exit('No direct script access allowed');

class MY_Loader extends CI_Loader {

  function MY_Loader() {
    parent::CI_Loader();
  } // MY_Loader()

  function treemodel($model, $name = '', $db_conn = FALSE) {
    // load the tree model class but do not instanciate
    if (!class_exists('TreeModel')) {
      load_class('TreeModel', false);
    }
    // load the model
    $this->model($model, $name, $db_conn);
  } // treemodel()

} // class MY_Loader
So then when you want to load a tree model rather than
Code:
$this->load->model('OrganizationModel');
use
Code:
$this->load->treemodel('OrganizationModel');
and it will ensure that the TreeModel class is loaded. You just then need to make sure the TreeModel.php file is in the application/libraries/ folder
#3

[eluser]thurting[/eluser]
There is nothing crude about using require/include. The CI loader is just a wrapper for these functions anyway. I have seen countless people on this board ask a similar question to yours and it amazes me that they are so vehemently against using these methods. Extending the loader is one solution, but if you don't want to do that just use the methods that ship with PHP. Now, if you are going to be storing you models in a folder that breaks from the app structure of CI, you should set your include path in the bootstrap to contain the path these files are stored in. Nothing wrong with that. However, if your models are going to be stored in the models folder using the app structure CI ships with, you don't have to set you include path, and you can append the target files path with the APPPATH constant defined in the bootstrap (e.g. require_once APPPATH . '/models/mymodel.php'Wink. This could be a bitch if you decided to change your dir structure, so I just recommend setting the include path up in the bootstrap.
#4

[eluser]zauber[/eluser]
oooh, tonanbarbarian - that looks brilliant! Do you by chance have references to more 'tricked-out' extended loaders? (curious cause I'm trying to learn Smile )

I'm thinking you could perhaps do something similar, but using function __call($modelname,$args){...} instead of specifically function treemodel($args){...}, that way I'm guessing I won't have to append stuff to the loader for each abstract class I write - I could instead do:

$this->load->model("AbstractestModel");
$this->load->abstractestmodel("FuzzyModel");
$this->load->fuzzymodel("ConcreteModel");

sure, it's not as nice as just going $this->load->model("ConcreteModel") and have the inheritance work perfectly, but it's a sight better.

But come to think of it.... if I do the above, I need to know in my head the inheritance scheme. And if I know it already, I guess I could always write:

$this->load->model("AbstractestModel",DONTCONSTRUCT);
$this->load->model("FuzzyModel", DONTCONSTRUCT);
$this->load->model("ConcreteModel",CONSTRUCT);

(or whatever the syntax for toggling initialization is)

It makes less obvious semantic sense, but is almost as easy to write and doesn't require an extension of the loader. I like your approach better, although it feels like I should only use it for really extending CI with an entirely new type of Model, rather than just for code reuse, since It's an extension of a core class.

Also, do you have any idea of how to solve the path-problem if I'd prefer to keep abstract models outside the application directories?

Thanks again for the inspiring feedback.
#5

[eluser]zauber[/eluser]
[quote author="thurting" date="1198130145"]There is nothing crude about using require/include. The CI loader is just a wrapper for these functions anyway. I have seen countless people on this board ask a similar question to yours and it amazes me that they are so vehemently against using these methods. Extending the loader is one solution, but if you don't want to do that just use the methods that ship with PHP. Now, if you are going to be storing you models in a folder that breaks from the app structure of CI, you should set your include path in the bootstrap to contain the path these files are stored in. Nothing wrong with that. However, if your models are going to be stored in the models folder using the app structure CI ships with, you don't have to set you include path, and you can append the target files path with the APPPATH constant defined in the bootstrap (e.g. require_once APPPATH . '/models/mymodel.php'Wink. This could be a bitch if you decided to change your dir structure, so I just recommend setting the include path up in the bootstrap.[/quote]

Actually, the main reason I was opposed to using require/include was in fact the path-problems. The loader with it's config-dependency solves that so nicely. But yeah, I admit, It feels aesthetically awkward to me to "break the mold". You've shed some light on the path-problem, however. I am grateful, and less averse to using a plain include.
#6

[eluser]tonanbarbarian[/eluser]
At the moment I do not have any more loader extensions like that... I have extended the loader a couple of time but not to load other classes for models.

oh but there is a bug in 1.5.4 of load model that stops the extending of Models if you use MY_Model.php file in your libraries folder.

the fix for that is to add this method to the extended loader class
Code:
function model($model, $name = '', $db_conn = FALSE) {
  // load the Model class correctly so it can be extended
  if (!class_exists('Model'))
    load_class('Model', false);
  
  parent::model($model, $name, $db_conn);
} // model()

But the treemodel loader code I just thought of when I saw your post. I will however be using that approach in a few places in my code because I have a couple of situations where it is useful




Theme © iAndrew 2016 - Forum software by © MyBB