Welcome Guest, Not a member yet? Register   Sign In
Using truly object-oriented models?
#1

[eluser]Matthew Pennell[/eluser]
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:

Code:
@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:

Code:
$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?
#2

[eluser]Unknown[/eluser]
I believe you can do it in a similar way with CodeIgniter.
E.G.
Code:
$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
#3

[eluser]Crafter[/eluser]
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.
#4

[eluser]Matthew Pennell[/eluser]
[quote author="Crafter" date="1187065219"]Models are not autogenerated, they have to be created by you.[/quote]
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.
#5

[eluser]Crafter[/eluser]
[quote author="Buddy Bradley" date="1187088683"]
CI's models just seem very anaemic compared to Rails' default feature-ful ActiveRecord implementation.[/quote]

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.
#6

[eluser]Matthew Pennell[/eluser]
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?
#7

[eluser]Henrik Pejer[/eluser]
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.
#8

[eluser]Matthew Pennell[/eluser]
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:
Code:
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:
Code:
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:

Code:
$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
#9

[eluser]Crafter[/eluser]
Thank you Buddy, your solution is so ..., elegant. I'm definitely going to use this going forward.
#10

[eluser]Matthew Pennell[/eluser]
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




Theme © iAndrew 2016 - Forum software by © MyBB