/lazy_model

Lazy model loading for CakePHP 1.2 & 1.3.

Primary LanguagePHP

LazyModel plugin

Used to build CakePHP model chains when then they are actually used. This COULD increase the speed of
your application and decrease it's memory usage significantly. It will be done fully transparent and
will take only a single line of code in your application.

Facts:

-   100% compatible with CakePHP 1.2 and 1.3 (all model core tests pass).
-   Works on PHP 5.2 and 5.3.
-   Only loads models when they are needed.
-   Works fine with Containable and OneQuery (and probably Linkable).

Setup:

1.  Clone the plugin to your APP/plugins directory (or as a git submodule in that folder).
2.  Put the following code in APP/app_model.php:

    <?php
    App::import('Lib', 'LazyModel.LazyModel');
    class AppModel extends LazyModel {
    }
    ?>

3.  See the magic happen.

Q&A:

Q:  I have this page and it isn't really faster, doesn't it work?
A:  It probably does, but the speed increase can vary a lot. The more models and associations you
    have and the less you use them, the higher the speed increase will be. If you use every model,
    they will be instantiated just like CakePHP itself would do. Therefor it is important to use
    Containable or any other behavior that limits the number of models that is used. Using
    'recursive' isn't advised.

Q:  What were you thinking when you made a Model as a Lib?
A:  This is by design. When importing a Model using App::import() CakePHP will automagically load
    AppModel for you. This is fine when loading a normal model, but if you want to apply this model
    to AppModel it will be causing an endless loop. This Model essentially is designed to be
    applied on every model and therefore implemented as a Lib.
    (See: http://github.com/cakephp/cakephp/blob/1.3/cake/libs/configure.php#L1154)

    The libs folder was introduced in CakePHP 1.3, but it was already possible to use Libs in
    CakePHP 1.2. It all boils down to some Inflector magic which was implemented earlier.

Q:  My HABTM isn't completely lazy loaded, is this a bug?
A:  No, full lazy loading for HABTM never works, because CakePHP needs to fill all sorts of
    settings in the associations and it needs at least the join model for that. You can optimize
    this though. It will be only half loaded when you set the 'with', 'associationForeignKey' and
    'joinTable' keys in your HABTM association. In this case CakePHP will only load the join model.

    See the test cases for details on how to set it up.

    You will notice it will make a bit of a mess. So I suggest you only do this when you really
    need to. It is probably not even worth it. Another way to speed up HABTM is to not use it at
    all. You can split things up as hasMany and belongsTo associations. More on this can be found
    in the book under heading "What to do when HABTM becomes complicated?".
    (See: http://book.cakephp.org/view/1034/Saving-Related-Model-Data-HABTM)

Q:  ACL breaks on CakePHP 1.2, is this a bug?
A:  Yes, but not one of this plugin. It has to do with the PHP4 compatibility of CakePHP. The
    problem is fixed in CakePHP 1.3 though. So if you can, upgrade fast! If you can't, just copy
    the ACL behavior to your application, find the line of the problem (see the error) which says:

    $model->{$type} =& ClassRegistry::init($type);

    Change it to:

    if (PHP5) {
        $model->{$type} = ClassRegistry::init($type);
    } else {
        $model->{$type} =& ClassRegistry::init($type);
    }

    This will get rid of the error.

Q:  When I use Model::deleteAll() on a 'with' model with only two fields directly, where the 'with'
    model is the primary model of the Controller, CakePHP still wants to delete records using the 
    'id' field. It used to work before. What can I do?
A:  Because CakePHP creates the models all at once by default, the primary key is set to one of
    the foreign IDs through the association of the parent HABTM model. LazyModel prevents the
    loading of those models, so the primary key isn't set. I tried to fix this, but it is
    unfixable without loading the other models first. There is a simple way to make it work
    though. Set the primary key to the right field before calling Model::deleteAll() like this:

    $this->Model->primaryKey = 'foreign_id';
    $this->Model->deleteAll(array('foreign_id' => $id, 'other_foreign_id' => $fid));

    This doesn't mean you always need to set the primary key before calling Model::deleteAll(). It
    only needs to be done in this specific case.

Q:  Hey, the 'required' classes in my forms are gone! Can I fix this?
A:  Duuh! The FormHelper checks whether a Model is set in the ClassRegistry or not and if it is, it
    will check the validation rules if it needs to add the 'required' class somewhere. So the
    reason the classes are gone is because the Models aren't in the ClassRegistry anymore.

    Fixing this is quite easy. Just call the Models you use in your action like this:

    public function admin_add() {
        $this->Model->AssociatedModel;
        $this->Model->OtherAssociatedModel;

        // Some data checking and saving.
    }

    This will instantiate the Models and place them in the ClassRegistry. Of course this will
    make the action slightly slower and bigger, but (in my opinion) it will still beat loading
    the entire model chain.

Q:  What the hell?! My form had a checkbox/textarea/whatever for a specific datatype and with
    LazyModel it has become just another text input. Where did it go?
A:  This issue is related to the previous one and can be fixed the same way. You can also add
    'type' => 'textarea' to the options of FormHelper::input() for example to fix it.

Q:  Was this your own idea?
A:  Not really, there are more tries at this, but I couldn't really make them work like I had in
    mind and so started my own little project using a slightly different approach. However, I
    used the other attempts by Matt Curry and José Lorenzo Rodríguez as inspiration.
    (See: http://github.com/mcurry/lazy_loader/)
    (See: http://github.com/lorenzo/lazy_loader/)
    (See: http://bin.cakephp.org/saved/39855/)