Welcome Guest, Not a member yet? Register   Sign In
ActiveRecord inspired ORM
#1

[eluser]m4rw3r[/eluser]
I've finished an ORM model which I would like to have some comments on.
It is inspired by ruby's ActiveRecord and it should be very customizable (setting tablenames, foreign key columns, etc.), yet easy to use (assumes some default values if you don't explicitly supply them).
It has currently support for Belongs To, Has Many, Has One and Has And Belongs To Many relationships.
It should be completely PHP 4 compatible (Haven't got the opportunity to test, can somebody please verify that it woks on PHP 4?)
When I am confident that everything works as it should, I will integrate MPTtree and this ORM (I will still have an ORM-less variant of MPTtree).

I currently regard this model as a release candidate, so I have not written any manual or anything, but I think you can figure it out from all the comments in the file.

Wiki download: ORM_by_m4rw3r_RC1

Example:
Code:
// model 1
class thread extends ORM{
    var $__has_many = 'posts'; // you need to define relationships before you call the constructor of ORM
    // it assumes that the model is 'post' (if it exists) and that the foreign key is 'threads_id'
    function page(){
        parent::ORM();
    }
}
// model 2
class post extends ORM{
    // here I specifically define a model and a foreign key for the users table
    var $__belongs_to = array('threads','users' => array('table' => 'users','col' => 'author_id'));
    function post(){
        parent::ORM();
    }
}
// controller
$this->load->model('ORM'); // or include the Model class and orm.php in another way
$this->load->model('thread'); // no need to load the post model, the ORM class loads it if needed
$obj = $this->thread->find(1); // find by id
$obj->load_rel(); // loads all relationships for this record
$obj->posts[0]->load_related('threads'); // loads all related threads in the first post (array is empty / false if no related are found)
#2

[eluser]nmweb[/eluser]
Nice contribution to CI. How do you plan to integrate MPTtree? Using acts_as=mptt like behaviours.
#3

[eluser]m4rw3r[/eluser]
That is a good idea, but I don't know how easy it is to integrate, PHP 4 is missing the __call() method (which I could have used), but I'll look into it.

If I on the other hand let MPTtree extend ORM I don't break the API, I can make two versions of base classes (one extending ORM, the other doesn't) so you can choose if you need ORM, and you then don't lock yourself to using ORM all the time (MPTtree is still there).
#4

[eluser]nmweb[/eluser]
Extending ORM is the other possibility. If you want MPTtree with and without ORM, you need to base classes though which leads to double code. With acts as this might be circumvented.

__call() is an option for php5, but without it you could do in php4:
<code>
$menu=Some_Model();
$children=$menu->tree->get_children();
</code>
The tree property would be an instance of your MPTtree class.
#5

[eluser]m4rw3r[/eluser]
I thought of something like this:
Code:
define('MPTtreeORM',true);
if(MPTtreeORM == true){
    class MPTtree_base extends ORM{
        function MPTtree_base(){
            parent::ORM();
        }
    }
}
else{
    class MPTtree_base{
        // some vars
        function MPTtree_base(){
            // some init
        }
    }
}
class MPTtree extends MPTtree_base{
    ...
I can admit that act_as would be nice, I'll look at it (I would like to get rid of the need of assigning MPTtree to a var in the model (var tree overwrites other == bad), and also an easier way for behaviors to add their methods to the child objects).
In the mean time, you can submit suggestions for what types of behaviors you want (tree, list, etc.).
#6

[eluser]nmweb[/eluser]
Conditional classes are not very neat I think. I would be reluctant to using it.

You could look at implementing an observer pattern in your model so you can do

<code>
$model=new Model;
$model->attach('ORM_Tree_Observer');
</code>
Perhaps this is something. Without __call I feel a bit crippled Smile

Common behaviours are: tree (adjacency lists), nested_sets(mpttree), list and versioned (to have multiple versions of an article etc.)
#7

[eluser]James Pax[/eluser]
This is really nice m4rw3r :O

I thought I was happy with a model interface I developed but this is much richer!

I learnt quite a bit just by looking at the layout, kudos for sharing this! Awesome stuff Big Grin

Btw one conern... is the price in performance relevant?
#8

[eluser]m4rw3r[/eluser]
NOTE: The measurements presented in this post were horribly wrong, in a later post I have posted the correct results.

I think the only thing that slows it down is the loading of data into objects, because it fetches the data from the db and then CI converts it to an array and then the ORM populates an object with that data, the same applies to loading of relations (if anyone has suggestions on how to improve performance, mail me).

Anyway, here are a quick benchmark:
Fetching a row and loading one relation and then another on that one, this was repeated 1000 times for both the ORM and for CI's Active Record db:

Code:
ORM      5.2980
CI AR    3.5974

Result: My ORM class is about 47.27 % slower (Rubys ActiveRecord is also about 50% slower).

Used software:
OS: Windows XP Media Center
Server: Apache 2.0.61
PHP: 5.2.5

Code used:
Code:
$this->benchmark->mark('ORM_start');
$this->load->model('ORM');
$this->load->model('thread');
$this->load->model('post');
for($i = 0; $i < 1000; $i++){
    $obj = $this->thread->find(1);
    $obj->load_rel();
    $obj->posts[0]->load_rel();
}
$this->benchmark->mark('ORM_end');
$this->benchmark->mark('CI_db_start');
for($i = 0; $i < 1000; $i++){
    $q = $this->db->get_where('threads',array('id' => 1),1);
    $d = $q->row_array();
    $q = $this->db->get_where('posts',array('threads_id' => $d['id']));
    $d_p = $q->result_array();
    $q = $this->db->get_where('threads',array('id' => $d_p[0]['threads_id']),1);
    $d_p_t = $q->row_array();
}
$this->benchmark->mark('CI_db_end');

The relevance of the price in performance always depends on what you get for sacrificing it. So if you only need to fetch some data without relations (and the site is used very frequently) I'd probably say no, it's not worth it. But if it's a bigger app with a lot of db work and relations, it would simplify a lot.
But it's like comparing PHP with C++, PHP makes making web services easy and C++ is a lot faster but more difficult.

I'm currently checking on how to implement behaviors in the best way, but suggestions are still welcome Tongue
#9

[eluser]m4rw3r[/eluser]
Two questions:
Are 50% slower too slow?
Are there anyone who want to help me with implementing behaviours, increasing preformance, etc.?
#10

[eluser]xwero[/eluser]
I think the AR library result could be faster if it is only one query now you have 3 Wink

Why not use a file or files instead of a database table to store your meta data?




Theme © iAndrew 2016 - Forum software by © MyBB