Part of the EllisLab Network
   
1 of 2
1
Using truly object-oriented models?
Posted: 13 August 2007 03:52 PM   [ Ignore ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  329
Joined  05-07-2006

I’ve just started learning Ruby on Rails, and one of the things that stands out for me is the way it seems to use Models in a more truly object-oriented fashion than Code Igniter does.

In RoR, you (for example) would call the new() method of your Article model, and it will return an object representing the new Article, complete with methods and properties:

@article = Article.new # => the variable @article is now an instance of the Article class
@article.title = "My latest blog entry"
puts @article.title # => prints the title

CI, by comparison, seems more like a bunch of procedural functions that just happen to be namespaced into a particular model - there’s no object-relationship between the model and the ‘thing’ it represents:

$article = $this->article_model->new(); // we can assign the result to a variable, but it isn't an instance of the Article class
$this->article_model->setTitle("My latest blog entry");
echo
$this->article_model->getTitle(); // prints the title, assuming we kept a reference to the row in the database

Is this something I’m misunderstanding about CI, or was it never supposed to be truly OO in respect of models?

 Signature 

The Watchmaker Project | My EE add-ons | Follow me on Twitter

Profile
 
 
Posted: 13 August 2007 04:07 PM   [ Ignore ]   [ # 1 ]  
Summer Student
Total Posts:  6
Joined  03-30-2007

I believe you can do it in a similar way with CodeIgniter.
E.G.

$myModel = new $this->MyModel();
$myModel->mymethod();
// or to get a property
$myModel->my_property;

Given you have loaded the model this would work just fine.

Oscar

Profile
 
 
Posted: 13 August 2007 06:20 PM   [ Ignore ]   [ # 2 ]  
Research Assistant
RankRankRank
Total Posts:  558
Joined  06-17-2006

Models are not autogenerated, they have to be created by you,

You can achieve similar results using ane returning database results or using the activerecord library.

 Signature 

CodeCrafter - Open Source Code Generation for CI

Profile
 
 
Posted: 14 August 2007 12:51 AM   [ Ignore ]   [ # 3 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  329
Joined  05-07-2006
Crafter - 13 August 2007 06:20 PM

Models are not autogenerated, they have to be created by you.

I know that (although they do inherit from the Model superclass) - CI’s models just seem very anaemic compared to Rails’ default feature-ful ActiveRecord implementation.

 Signature 

The Watchmaker Project | My EE add-ons | Follow me on Twitter

Profile
 
 
Posted: 14 August 2007 02:09 AM   [ Ignore ]   [ # 4 ]  
Research Assistant
RankRankRank
Total Posts:  558
Joined  06-17-2006
Buddy Bradley - 14 August 2007 12:51 AM

CI’s models just seem very anaemic compared to Rails’ default feature-ful ActiveRecord implementation.

I agree.

I’ve convrted Jake Grimley’s MyActiveRecord which is a “Rail-like” implementation of AR.. It’s a work around, and it’s not complete, but it works for me, but I’m not sure if its ready for public consumption.

I also do beleive there is an ORM project announced in this forum that comes close to your desired usage, although I haven’t quite followed the project.

 Signature 

CodeCrafter - Open Source Code Generation for CI

Profile
 
 
Posted: 14 August 2007 02:45 AM   [ Ignore ]   [ # 5 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  329
Joined  05-07-2006

Interesting. It wouldn’t take much work to build an actual ActiveRecord superclass that inherits from the base Model class, and then extend that with a lot of useful methods to mirror the Rails ActiveRecord implementation. I might have a play with it today - have you got a link to the ORM project you mentioned?

 Signature 

The Watchmaker Project | My EE add-ons | Follow me on Twitter

Profile
 
 
Posted: 14 August 2007 03:45 AM   [ Ignore ]   [ # 6 ]  
Summer Student
Total Posts:  25
Joined  06-22-2007

Takeing the RoR approach to the model I guess that model means something else in CI than in RoR.

But you can make a class that acts like a model in RoR, I believe.

You make a class that represents an article, in that you include the CI-instance, and you then go on with your functions. I think it can be done quite easily, you just can not load it with $this->load->model but you either load it manually or extend the load-class so that you can load a ‘propper’ model.

Although I might have misunderstood you, and for that I apologize.

 Signature 

Good luck and happy CI-ing!
.:8):.

Profile
 
 
Posted: 16 August 2007 10:31 AM   [ Ignore ]   [ # 7 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  329
Joined  05-07-2006

Thanks, Henrik - actually I think it does work okay when using the Loader class.

It’s pretty easy to create an ActiveRecord class that you can then use with dynamic methods - here’s my prototype code, if anyone’s interested:

class ActiveRecord {

    
var $_parent_name = '';

    function
ActiveRecord()
    
{
        $this
->_assign_libraries( (method_exists($this, '__get') OR method_exists($this, '__set')) ? FALSE : TRUE );
        
$this->_parent_name = ucfirst(get_class($this));
        
log_message('debug', "ActiveRecord Class Initialized");
    
}

    
function _assign_libraries($use_reference = TRUE)
    
{
        $CI
=& get_instance();                
        foreach (
array_keys(get_object_vars($CI)) as $key)
        
{
            
if ( ! isset($this->$key) AND $key != $this->_parent_name)
            
{            
                
if ($use_reference == TRUE)
                
{
                    $this
->$key = '';
                    
$this->$key =& $CI->$key;
                
}
                
else
                
{
                    $this
->$key = $CI->$key;
                
}
            }
        }        
    }
    
    public
function __call($method, $args)
    
{
        
if (stristr($method, 'find_by_')) {
            eval(
'return $this->find_by("' . str_replace('find_by_', '', $method) . '", "' . $args[0] . '");');
        
}
        
if ( ! isset($args) ) eval('return $this->' . $method . ';');
        
eval('$this->' . $method . ' = "' . $args[0] . '";');
    
}
    
    
function find($id)
    
{
        $this
->db->where('id', $id);
        
$this->db->from(TABLE);
        
$query = $this->db->get();
        
$found = $query->row();
        
$query = $this->db->query('SHOW COLUMNS FROM ' . TABLE);
        foreach(
$query->result() as $column)
        
{
            eval(
'$this->' . $column->Field . ' = $found->' . $column->Field . ';');
        
}
    }

    
function find_by($column, $query)
    
{
        $this
->db->where($column, $query);
        
$this->db->from(TABLE);
        
$query = $this->db->get();
        
$found = $query->row();
        
$query = $this->db->query('SHOW COLUMNS FROM ' . TABLE);
        foreach(
$query->result() as $column)
        
{
            eval(
'$this->' . $column->Field . ' = $found->' . $column->Field . ';');
        
}
    }
    
}

Then by declaring the TABLE constant in your model, you automagically have access to dynamic methods like find_by_name() or find_by_postcode() - basically any fields that are in the table. You also have dynamic getter and setter methods for every table column.

Here’s a basic model:

class Person extends ActiveRecord {

    
function Person()
    
{
        parent
::ActiveRecord();
        if ( !
defined('TABLE') ) define('TABLE', 'people');
    
}
    
    
function full_name()
    
{
        
return $this->name . ' ' . $this->surname;
    
}
    
    
function save()
    
{
        
if ($this->db->insert(
            
TABLE,
            array(
                
'name' => $this->name,
                
'surname' => $this->surname
            
)
        ))
        
{
            $this
->id = $this->db->insert_id();
           
}
        
else
        
{
            log_message
('error', $this->db->last_query());
        
}
    }
    
    
function hello()
    
{
        
echo '<p>Hello from ' . $this->full_name() . '!</p>';
    
}
    
}

Now you can do things like this:

$me = new Person();
$me->name = 'Matthew';
$me->surname = 'Pennell';
$me->save();

$foobar = new Person();
$foobar->find_by_name('Matthew');
        
echo
'Found ' . $foobar->full_name() . ' in the ' . TABLE . ' database!';

Obviously you can then build a lot more functionality into the ActiveRecord superclass to do complex finding, autosaving, etc. - fun stuff. smile

 Signature 

The Watchmaker Project | My EE add-ons | Follow me on Twitter

Profile
 
 
Posted: 17 August 2007 01:43 AM   [ Ignore ]   [ # 8 ]  
Research Assistant
RankRankRank
Total Posts:  558
Joined  06-17-2006

Thank you Buddy, your solution is so ..., elegant. I’m definitely going to use this going forward.

 Signature 

CodeCrafter - Open Source Code Generation for CI

Profile
 
 
Posted: 17 August 2007 03:15 AM   [ Ignore ]   [ # 9 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  329
Joined  05-07-2006

Thanks - it’s nowhere near ready for use yet, though. For one thing it doesn’t currently cope with returning arrays of objects, so I need to refine the find_by_ functions to be aware of what is being requested.

I’ll write it all up when I’m done. smile

 Signature 

The Watchmaker Project | My EE add-ons | Follow me on Twitter

Profile
 
 
Posted: 17 August 2007 03:38 AM   [ Ignore ]   [ # 10 ]  
Grad Student
Rank
Total Posts:  84
Joined  07-24-2007

You could also use make the object use the its own class name to determine the table name it should use, as in the object Person knows it should use the database table Persons. Perhaps something better would be to let the object try and figure the table name out by itself but allow the programmer to over-ride the table name with his own if he so wishes. Your example is a pretty good one: The object would think its class it Persons but you could over-ride it to use the table People because its just… nicer. On the other had an object User would come up with the table Users which is fine.

Profile
 
 
Posted: 17 August 2007 03:47 AM   [ Ignore ]   [ # 11 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  329
Joined  05-07-2006

True - maybe I’ll add that in (I’ll need to find a table of ‘common ways to pluralise English words’ first)...

 Signature 

The Watchmaker Project | My EE add-ons | Follow me on Twitter

Profile
 
 
Posted: 17 August 2007 07:57 AM   [ Ignore ]   [ # 12 ]  
Grad Student
Rank
Total Posts:  84
Joined  07-24-2007

Pfffft, just be lazy and add ‘s’ wink

Profile
 
 
Posted: 20 August 2007 07:37 PM   [ Ignore ]   [ # 13 ]  
Lab Assistant
Avatar
RankRank
Total Posts:  150
Joined  03-19-2007
Buddy Bradley - 13 August 2007 03:52 PM

In RoR, you (for example) would call the new() method of your Article model, and it will return an object representing the new Article, complete with methods and properties:

@article = Article.new # => the variable @article is now an instance of the Article class
@article.title = "My latest blog entry"
puts @article.title # => prints the title

CI, by comparison, seems more like a bunch of procedural functions that just happen to be namespaced into a particular model - there’s no object-relationship between the model and the ‘thing’ it represents:

$article = $this->article_model->new(); // we can assign the result to a variable, but it isn't an instance of the Article class
$this->article_model->setTitle("My latest blog entry");
echo
$this->article_model->getTitle(); // prints the title, assuming we kept a reference to the row in the database

Is this something I’m misunderstanding about CI, or was it never supposed to be truly OO in respect of models?

I’m thinking you’re mixing apples and oranges (a little bit) here.  In RoR, you’ve got this getter/setter paradigm to set values.  In CI, you work directly with the view’s code rather than as an object:

view1.php

<html>
<
head>
<
title><?= $title ?></title>
</
head>
<
body>
<
h1>I like <?= $flavor ?> ice cream</h1>
</
body>
</
html>

the controller:

$info = array(
  
"title"=>"My favorite ice cream",
  
"flavor"=>"Strawberry"
);
$this->load->view('view1',$info);

So while you aren’t specifically saying something like:

$view1->setTitle("My favorite ice cream");

You’re still programmatically setting your values; except in CI they are in an associative array that’s passed to a view.

The CI method provides a “relationship” between the controller and the view - but the view doesn’t have any kind of built-in methods to utilize.  The controller and model(s) have to do all the heavy lifting.

 Signature 

$me = null;

Profile
 
 
Posted: 20 August 2007 07:38 PM   [ Ignore ]   [ # 14 ]  
Administrator
Avatar
RankRankRankRankRankRank
Total Posts:  7337
Joined  03-23-2006

Yes!  I very much like this idea.  Matt, this has very good potential.  I can tell you one thing though, most CI users won’t pick it up and play with it unless its clearly documented.  I’d love to see this keep evolving.  If you put up a tutorial, or even a “here’s where I am with this” on the watchmakerproject, I bet you’d have some takers (me included).

Neat, and thanks.  I started playing with RoR once upon a day, and I do think it has lots to offer.

 Signature 

DerekAllard.com - CodeIgniter, ExpressionEngine, and the World of Web Design
BambooInvoice - Open Source, CodeIgniter powered invoicing.

Profile
MSG
 
 
Posted: 21 August 2007 05:22 AM   [ Ignore ]   [ # 15 ]  
Research Assistant
Avatar
RankRankRank
Total Posts:  329
Joined  05-07-2006
JayTee - 20 August 2007 07:37 PM

The CI method provides a “relationship” between the controller and the view - but the view doesn’t have any kind of built-in methods to utilize.  The controller and model(s) have to do all the heavy lifting.

I think you’re misunderstanding where I’m going with this. I’m not concerned with how the retrieved data will be sent to the view yet, I’m just playing with a way to make retrieving and interacting with data from your models a lot easier (or more confusing, depending on your point-of-view). smile

 Signature 

The Watchmaker Project | My EE add-ons | Follow me on Twitter

Profile
 
 
   
1 of 2
1
 
Post Marker Legend
New Topic New posts Hot Topic Hot Topic with new posts New Poll New Poll Moved Topic Moved Topic Sticky Topic Sticky topic
Old Topic No new posts Hot Old Topic Hot Topic with no new posts Old Poll Old Poll Closed Topic Closed Topic Announcement Announcements
Theme
Change Theme
Visitor Statistics
The most visitors ever was 819, on March 11, 2010 11:15 AM
Total Registered Members: 120434 Total Logged-in Users: 43
Total Topics: 126527 Total Anonymous Users: 4
Total Replies: 665302 Total Guests: 373
Total Posts: 791829    
Members ( View Memberlist )