CodeIgniter Forums

Full Version: Load model with a constructer - possible?
You're currently viewing a stripped down version of our content. View the full version with proper formatting.

El Forum

[eluser]Unknown[/eluser]
Hi I'm new to CI and i've done a search for this question but can't seem to get a satisfactory answer.

When using the code e.g. "$this->load->model('Cart')" is it possible to pass data to the constructor? Or do I have to initialise the required data separately?

thanks, mo

El Forum

[eluser]phpoet[/eluser]
I have the exact same question. In particular, many of my models load data from a row in a database table. It would be cool if I could pass in the id of the database table row when loading the model in the controller.

For example, it would be neat if you could write something like this.

Code:
$this->load->model("Product", $product_id);

Of course this would not work as the 2nd parameter is an alias name for the model you are loading.

I have considered creating my own Loader class by extending the existing Loader class in CI then modifying the model() function (in system/libraries/Loader.php) so that it takes an array as an optional 4th parameter that could be exploded into parameters when loading the model. Then you could at least write something like:

Code:
$this->load->model("Product", '', '', array('id'=>12));

What I'd probably do is make that array the 2nd parameter since it's rare that I provide an alternative name. I also autoload my db connection in almost all my apps. Then in my new Loader class's model function I'd check to see if the 2nd parameter was an array or an integer. If it was an integer I would assume that the number was the id (primary key) in the database table for the row that should be loaded. If it was an array I'd explode the array into the parameters for the model's constructor.

I'm very interested in other people's thoughts on this topic as well. Thanks!

El Forum

[eluser]Michael Wales[/eluser]
Unfortunately, this could get kind of tricky, the way CI currently has models implemented. The model object doesn't really describe one instance of data (for example: one user). Instead, it describes one type of data, all instances of it.

For example, let's say you have a page in which you are listing 3 users. You don't instantiate the user model 3 times, loading their unique variables into class variables. Instead, the model is instantiated once and that same instance of the model is used to retrieve data as you see fit.

So, technically, yes it would be really easy to setup the Model class to accept constructor parameters but you would be severely limit in how that model would behave with our seriously planning ahead and probably changing the majority of your calls to models within your application.

I just finished this piece of the Datamapper ORM Class today and it was a bit of a headache... definitely takes a complete rethinking of CI's model class once you go in this direction.

El Forum

[eluser]phpoet[/eluser]
That is very interesting. It's almost like CI Models are sort of more like pseudo-controllers or database controllers. The documentation for Models has a function that returns an array of objects representing rows in a database. If all you need to do is access the data in a table row, this works great. But when you need to perform a calculation on the data it seems like things get a little funny.

Suppose, like you said, you have a CI model for Users and you have a database table that holds all the data for the users such as name, and date of birth. Then you want to list all of the users and their ages. The age, however, needs to be calculated based on the date of birth. I'd want to create a function in the model to calculate age. Then I would have the controller load up an array of User models and pass that array of Users to the view. In the view, I would loop through the array and do something like...

Code:
<?php foreach($users as $user) ?>
  <p>&lt;?= $user->name ?&gt; is &lt;?= $user->calculate_age() ?&gt; years old.</p>
&lt;?php endforeach; ?&gt;

That seems like a more reasonable approach than only having one User instance and reloading it's data attributes for each user in the list. If I understand you correctly, it seems like you would suggest having the controller load an array of User id numbers and pass the array of ids to the view. Then, in the view, doing something like this...

Code:
&lt;?php foreach($users as $user_id) ?&gt;
  &lt;?php $this->User->load_from_database($user_id); ?&gt;
  <p>&lt;?= $user->name ?&gt; is &lt;?= $user->calculate_age() ?&gt; years old.</p>
&lt;?php endforeach; ?&gt;

The problem I have with this approach is that it hits the database much more. First, there is a query to get all the User ids. Then another query for each User in the system. What are your thoughts about that? Have I misunderstood the recommendation of having only one instance that represents a type of data?

El Forum

[eluser]Michael Wales[/eluser]
Definitely not enough coffee in me yet to wrap my head around this.

Here's the short and dirty of what I was trying to get at with the way CodeIgniter is currently setup - all Models are Singletons:

Code:
// Loads the correct file, instantiates the object
$this->load->model('user');
// Just returns the previously instantiated object
$this->load->model('user');

So - something like this, assuming the 2nd parameter is a constructor variable, isn't really possible:

Code:
$user = $this->load->model('user', 2);
$user->username = 'walesmd'; // Set a new username
$user2 = $this->load->model('user', 4);
$user2->username = 'dallard'; //
$user3 = new User(4);
$user3->username = 'djones';

// Can you guess what this line returns?
echo $user->username;
// Result:  dallard

/* This should work properly, but I think the loading of the model, then just ignoring that object completely is ugly as sin
*/
echo $user3->username;

El Forum

[eluser]phpoet[/eluser]
Thank you, Michael, for all of your insight into this. I really appreciate it. I completely agree that loading the model, then just ignoring that object is ugly.

If all Models are Singletons, what is the best way to work with classes that you don't want to be Singletons but do interact heavily with the database? Perhaps the idea is to create "Library" classes and call get_instance() to gain access to the CI resources. Normally I think of Library classes as those classes which are reusable among projects rather than classes designed to work with project specific data. I suppose you could simply create project specific Libraries. This is not how I normally think of an MVC framework though. I think of M as Models.

In your CI Applications do you write initialize() or load() functions that take the id (primary key) from the db table as a parameter and then pull in the data? So do you have a lot of code in your applications that looks like this?

Code:
$this->load->model('User');

$this->User->load(4);
echo $this->User->username;

$this->User->load(5);
echo $this->User->username;

El Forum

[eluser]Michael Wales[/eluser]
Yes, typically, in the past I would write a get() method for each model that accepted an array of search parameters. So, I could do things like this:

Code:
$this->load->model('user');
$u = $this->user->get(array('id'=>4));
echo $u->email;
$this->user->set_email($new_email);

Now, I do something like this (coming soon):
Code:
$this->datamapper->model('user');
$u = new User(array('id'=>4));
echo $u->email;
$u->email = $new_email;
$u->save();

El Forum

[eluser]Unknown[/eluser]
Hi all, thanks for all the replies.

I think the problem was initially I was going down a rails, etc type scenario where typically one object represents 1 row in a table whereas in CI the model is just a front-end facade for the whole table *i think*.

I've resorted to returning an array of "row" objects from methods such as "find_by_description" etc as described in the manual which works fine and I'm happy with it, except it does make some of the code more "terse" as a result - but some of that is due to constraints in the language itself.

@Michael the datamapper looks very interesting - is it for public consumption?