Love Fuel?    Donate

FuelPHP Forums

Ask your question about FuelPHP in the appropriate forum, or help others by answering their questions.
Kohana's ORM ported to Fuel
  • If anyone was interested in using it, I've ported Kohana's ORM over to a fuel package. Usage is identical to Kohana. Feel free to fork on github and improve if you want. We'll be adding magic methods (__call and __callStatic) so you can do fancy things like find_by_username_and_password('ben', 'somepassword'); see https://github.com/TJS-Technology/fuel-kohana-orm for link
  • Nice Ben, but can you explain what Kohana ORM can do and Fuel AR cannot? To me it is almost exactly the same. Using has_many, has_one, belongs_to, etc. What is missing from AR that is a musthave for example? Overriding table name, primary key? Or is this port ment for people who are used to Kohana?
  • Hey Mike, Fuel's AR does simple databases fine, however once you start getting complex table structures it can't process these. See this thread for more info. It also doesn't support lazy loading. The main reason we're using it is that we've got a web application we're starting that is going to have a complex database structure, similar to that of Magento (magento has 300+ tables on a blank install). We want to use Fuel but can't with AR the way it is so this will fix it. We want fuel's OIL and migrations, so we just ported this over. Maybe when the architecture of the AR class is changed (and it's only one function that has to be rewritten and a couple adapted), maybe we'll port our model interactions back to AR.
  • Hey Ben, Nice work, could you move it out of the "orm" folder and rename to something prefixed with "fuel-"? Check out https://github.com/fuel-packages/fuel-phpactiverecord for an example. I already forked it into fuel-packages when I noticed it wouldn't work right. Once you update it I'll refork it and it can be installed using Oil after that (when named "fuel-kohana-orm" )
    php oil package install kohana-orm
    
  • Nice Ben, did you finish:
    We'll be adding magic methods (__call and __callStatic) so you can do fancy things like find_by_username_and_password('ben', 'somepassword');
  • Hi guys, I've added a few methods to the ORM (the usage is similar to Fuel's AR):
    // Additionally, you can use the orm as follows:
    $clients = ORM::factory('client')->find_all_by_last_name('corlett');
    
    foreach ($clients as $client)
    {
     echo nl2br($client->get_first_name() . PHP_EOL);
    }
    
    the find_all_by you'll recognise but i've also added a method where you can call get_ on a property. Not only does it return false if the property isn't set (as opposed to throwing an error, but it also returns false if empty). For example:
    if ($client->get_first_name())
    {
         // Do something
    }
    

    You can download it from https://github.com/TJS-Technology/fuel-kohana-orm or install it as a package once @jelmer has updated the fork on my repo
  • Nice, Does the following also work?
    find_first_by_first_name('ben');
    find_first_by_last_name('corlett');
    
    find_all_by_first_name_and_last_name('ben', 'corlett');
    find_first_by_first_name_and_last_name('ben', 'corlett');
    
    find_all_by_first_name_or_last_name('ben', 'corlett');
    find_first_by_first_name_or_last_name('ben', 'corlett');
    
    // etc.
    
    $clients = ORM::factory('client')->find_all_by_last_name('corlett');
    $client->put_first_name('mike')
    

    Shouldn't it return null on empty?
  • Yeah mate all of that works. None of the methods are static, you just use them once you've already called the static factory method; that is:
    $client = ORM::factory('client')->{{now put the magic method}}
    

    Only one I haven't done is the put_ it'll be set_ but i've not implemented that yet. So for now it'll be
    $client = ORM::factory('client')->find_by_first_name_and_last_name('ben', 'corlett');
    
    // pointless 'if' statement, just for demo
    if ($client->get_first_name() == 'ben')
    {
         $client->first_name = 'joe';
    }
    
    $client->save();
    
    It will soon be:
    $client = ORM::factory('client')->find_by_first_name_and_last_name('ben', 'corlett');
    
    // pointless 'if' statement, just for demo
    if ($client->get_first_name() == 'ben')
    {
         $client->set_first_name('joe');
    }
    
    $client->save();
    

    of course you don't have to do the get_ or set_ (when available) methods. they're just for convenience
  • You'r quick... Setting empty will be: ?
    $client->set_first_name('');
    

    What about database functions, they get passed through?
    $client->set_password("MD5('somepassword')");
    
    $client->set_post_edited('NOW()');
    
  • Fully wrote up a response and then deleted it. haha. I think that your functions as a param to the set function will work fine, but i'd use the model to validate and filter data (keeping controllers and views as dumb as possible). Anyway when jelmer updates the package fork from my github you can install it and test it for me. I've spent all afternoon having a play with it, seems to work fine. it's been rewritten so that i haven't touched the original kohana_orm.php file, just extended it with a bunch of wrapper functions. magic method set_ (ie $client->set_name('ben') ) is now in, and all find_by and find_all_by methods are either static or non-static. you call models by the actual model now, not by orm::factory() - ORM::factory() can't allow for namespacing. anyway it's all in the README below. README for new (rewritten) version is below.
    -------------------------------------------------------------------------------------
    To install this package:
    
    1. Through OIL:
    php oil package install kohana-orm
    
    2. Manually:
    1. Download from https://github.com/TJS-Technology/fuel-kohana-orm
    2. Copy to DOCROOT/packages/
    3. Go to DOCROOT/app/config.php and find:
    'packages' => array(
    //'activerecord',
    ),
    
    And add 'orm' to array:
    'packages' => array(
    //'activerecord',
    'orm',
    ),
    4. Ensure correct database settings are provided in DOCROOT/app/config/db.php
    
    
    
    -------------------------------------------------------------------------------------
    
    
    
    Using the ORM:
    
    // DOCROOT/app/class/controller/welcome.php
    class Controller_Welcome extends Controller {
    
    public function action_index()
    {
    $clients = Model_Client::factory()->where('last_name', '=', 'Corlett')->find_all();
    // Or:
    // $clients = new Model_Client();
    // $clients = $clients->where('last_name', '=', 'Corlett')
    // ->find_all();
    
    foreach ($clients as $client)
    {
    echo nl2br($client->first_name . PHP_EOL);
    
    $client->first_name = 'Another name';
    
    $client->save();
    }
    }
    
    // Additionally, you can use the orm with magic methods as follows
    $clients = Model_Client::find_all_by_last_name('Corlett');
    // Or:
    // $clients = Model_Client::factory()->find_all_by_last_name('Corlett');
    // Or:
    // $clients = new Model_Client();
    // $clients = $clients->find_all_by_last_name('Corlett');
    
    foreach ($clients as $client)
    {
    // Same as $client->first_name except it doesn't throw
    // An error if not existent, just returns false.
    if ($client->get_first_name())
    {
    // As opposed to $client->first_name = 'Another name';
    $client->set_first_name('Another name');
    
    $client->save();
    }
    }
    }
    
    // DOCROOT/app/classes/model/client.php
    class Model_Client extends \ORM
    {
    
    }
    
    See http://kohanaframework.org/guide/orm for full usage instructions (magic methods are not available in original ORM).
    
    
    -------------------------------------------------------------------------------------
    
    Note:
    
    The reason the original method of loading an ORM model ($something = ORM::factory('client'))
    has been removed is because of namespacing. Without making a heap of assumptions, we cannot
    load the correct model.
    
    Say you've got the following app structure:
    
    app/
    ---/classes/
    -----------/models/client.php
    
    // And under modules
    -----------/somemodule/
    ----------------------/classes/
    ------------------------------/models/client.php
    
    Q: If you went ORM::factory('client'), which module would it load?
    A: The one in the same namespace (only if that namespace is the global namespace - no module support).
    
    The way it's structured now, you can load a model from any module, for example (assuming you have the same
    structure as above):
    
    // DOCROOT/app/classes/controller/welcome.php
    class Controller_Welcome extends Controller {
    
    public function action_index()
    {
    // This will use the model under app/classes/models/client.php
    $clients = Model_Client::factory()->find_by_first_name('ben');
    
    // now let's say you've got a module called `somemodule`, and it's
    // also got a model called `client`. How do we load this? this is
    // how:
    $clients = Client\Model_Client::factory()->find_by_first_name('ben');
    // ^^^^ notice the namespace
    }
    }
    
    For the namespacing (hmvc) support you need to enable your module in DOCROOT/app/config/config.php (around line 130
    at the time of writing this).
    
    If you have any questions, you can reach me on twitter @ben_corlett or on fuel forums (http://fuelphp.com/users/profile/view/29).
    
  • @driftz I fixed it about an hour after dan's commit however the kohana-packages repo hasn't been updated to reflect that. see here
  • First of all, nice job on the port! I just have one thing to add though. Shouldn't the "factory" method be called "instance"? Since it doesn't opperate like a factory anymore and gives you an instance?
  • Cool nice one, have manually installed and am testing it out. Though I had to change package name in config to 'kohana-orm' from just 'orm'. Out of interest will the in-built validation methods work with Fuel?
  • I've made some changes to prevent php warning logged by fuel, github didn't let me send a pull reguest so here is the commit: https://github.com/FrenkyNet/fuel-kohana-orm/commit/09551f353d969bc698760dc36660785923294268
    It fixed 3 php errors. Errors are included in the commit message.
  • Edit from previous post. Updated README in the following area:
         // now let's say you've got a module called `somemodule`, and it's
         // also got a model called `client`. How do we load this? this is
         // how:
         $clients = Somemodule\Model_Client::factory()->find_by_first_name('ben');
         //                     ^^^^ notice the namespace
       }
    }
    

    Hi mate. Thanks for the feedback. There is a good reason that factory now doesn't support parameters and that is that you can't provide a namespace and a model as a string parameter and have php deterimine namespaces. ie:
    // This will load model client (in same namespace as caller)
    ORM::factory('client');
    
    // This won't work
    ORM::factory('\Client'); // Global namespace
    ORM::factory('Somemodule\Client'); // calling another namespace from a global namespace
    

    Because fuel is centralised around namespaces (which is a GREAT thing), I needed to override this function. So basically the only use that factory has now is it allows chaining on to occur - ie:
    $something = Model_Client::factory()->where('age', '=', 19)->find_all();
    
    // or from a different namespace
    $something = Somemodule\Model_Client::factory()->where('age', '=', 19)->find_all();
    

    I've put in
    public function __call($method, array $arguments)
    

    as requested. I've actually updated the class so that if it is called by ORM (not the child) it actually throws an error. If you can find a way to support namespacing in the factory method i'd love that and i'd put it in but at this stage it renders the kohana-orm module useless in the realm of fuel. of course, if you really want the ability to use factory from the orm, add to DOCROOT/app/bootsraph.php the following
    Fuel\Core\Autoloader::add_classes(array(
     // Add classes you want to override here
     // Example: 'View' => APPPATH.'classes/view.php',
     'ORM'    => APPPATH . 'classes/orm.php',
    );
    

    and under DOCROOT/app/classes/ create a file ORM.php and make that either extend ORM\ORM, or ORM\Kohana_ORM. You'll just need to override the methods you want. but once again I wouldn't recommend doing it this way as it's not supported by the nature of fuel. Look at fuel's AR class, you load the child model, not the ActiveRecord class. that way you can load any model from any namespace. if you'd like me to explain it further i'd be more than happy to go through it with you. Also, updated the README on github.
  • @Logicbox updated README for the correct installation - kohana-orm as the package, not orm.
  • shit @FrenkyNet sorry i just realised what you were saying. you were talking about the error:
    Error - 2011-02-04 10:36:06 --> 2048 - Declaration of ORM\ORM::factory() should be compatible with that of ORM\Kohana_ORM::factory() in /Users/ben/Sites/Projects/tjs/wfm/fuel/packages/kohana-orm/classes/orm.php on line 14
    
    weren't you? I fixed that just then by instead of returning new static it returns new ORM. My apologies I thought you didn't understand the whole namespace fix. I've since implemented your fixes and will push to github in a minute.
  • @FrenkyNet do you know if there is a way to not allow any parameters to the factory method (or just the one, id) and not get the following error?
    Error - 2011-02-04 10:44:28 --> 2048 - Declaration of ORM\ORM::factory() should be compatible with that of ORM\Kohana_ORM::factory() in /Users/ben/Sites/Projects/tjs/wfm/fuel/packages/kohana-orm/classes/orm.php on line 14
    
    

  • @Logicbox I'm not sure about the validation, haven't got that far yet. I'll try it and get back to you. If it doesn't, i'll make a wrapper validation class (like i did with the inflecctor class that just liinks back to fuel's inflector class). basically at this point i'm up to i've not modified a single line of code in the original ORM class, which allows for easy upgradiing when kohana updates.
    @FrankyNet, with factory being called instance(), were you talking about using the singleton method or just renaming it? the problem with the singleton method is you don't ever want to use it with models. Say in a request, you save some data about a model (provided by the user through a form) and then in a view give feedback on something else that uses the same model. because it's in the same request, you can't get a new instance of it (using the singleton method).
  • Okay. Another change (sorry about how many there are haha). But.... It now supports the ORM::factory() method. You need to remember though, because
    the ORM is in a namespace ORM, it needs to know the namespace of the file you're in,
    so usage is as follows:
    // NOTE THE DOUBLE BACK SLASHES LIKE IN ALL BOOTSTRAP.PHP CLASSES!!!! IMPORTANT
    // Think about it, \n is a new line.
    $clients = \ORM::factory('\\Model_Client')->find_all();
    
    // load out of a module
    $clients = \ORM::factory('Somemodule\\Model_Clients')->find_all_by_last_name('corlett');
    

    Anyway, download and have a play. Once again, I'd recommend NOT TO USE the factory method, but rather get your descendent models
    and call the init() function on them. it's actually better practise. Updated README.md file:
    // DOCROOT/app/class/controller/welcome.php
       class Controller_Welcome extends Controller {
    
        public function action_index()
        {
         $clients = Model_Client::init()->where('last_name', '=', 'Corlett')->find_all();
         // Or:
         // $clients = new Model_Client();
         // $clients = $clients->where('last_name', '=', 'Corlett')
         //        ->find_all();
         
         foreach ($clients as $client)
         {
          echo nl2br($client->first_name . PHP_EOL);
          
          $client->first_name = 'Another name';
          
          $client->save();
         }
        }
        
        // Additionally, you can use the orm with magic methods as follows
        $clients = Model_Client::find_all_by_last_name('Corlett');
        // Or:
        // $clients = Model_Client::factory()->find_all_by_last_name('Corlett');
        // Or:
        // $clients = new Model_Client();
        // $clients = $clients->find_all_by_last_name('Corlett');
        
        foreach ($clients as $client)
        {
         // Same as $client->first_name except it doesn't throw
         // An error if not existent, just returns false.
         if ($client->get_first_name())
         {
          // As opposed to $client->first_name = 'Another name';
          $client->set_first_name('Another name');
          
          $client->save();
         }
        }
        
        // Due to popular demand, you can use the old factory method,
        // Although this is NOT recommended. For example
        $clients = \ORM::factory('\\clients')->find_all();
        
        // Note, you need to provide the namespace for the model to be
        // loaded using the factory method, replacing single slashes with
        // double slashes (the same as in all your bootstrap.php files).
       }
    
       // DOCROOT/app/classes/model/client.php
       class Model_Client extends \ORM
       {
        // Relationships are defined as follows:
        
        // Same namespace
        protected $_has_many = array(
         'cars' => array(
          // Yes, I know in Kohana you would
          // have just put 'Client_Car', but
          // the orm now works out what namespace
          // you're in if you don't provide it one,
          // and having to add the 'Model_' prefix
          // is just too many assumptions.
          'model' => 'Model_Client_Car'
        ));
        
        // Different namespace
        protected $_has_many = array(
         'cars' => array(
          // Notice the double backslashes!
          'model' => 'Somemodule\\Model_Client_Car'
        ));
       }
    
  • @Ben Corlett There is no way to extend it but not declare the parameters. You'll have to declare them in order to comply with the extended class. Not using them is the extended function own choice so that's the best way so solve it. It might feel like a hacky way to do so, but it's actualy quite correct if you think about it. And for the instance method, i'd be best to make it a new instance since you can have multiple instances of the same object.
  • @Ben Corlett About the instance method, i thought it would be used like this:
    Model_User::instance()->directly_chainable();
    

    So the function is what factory was before the change.
    static function instance(){
    return new static;
    }
    

    What's your thought about this?
  • @FrenkyNet Just about every other class in fuel (and kohana) uses "instance" with the singleton pattern (only one instance), that's why we were avoiding it. Ben and I actually had an argument about it because I thought the same thing as you. However it would be good to stay consistent with the rest of the framework and stick to "init" or "factory".
  • @Thomas I see. While i don't think it's the exact correct naming for the functions, it probably comes down to taste. Since they've made the choice to do it this way, it's best to stick with it. As long as there is a way to chain directly from a static function, i'm all in. Thanks
  • Hey guys, In response to the release of Kohana 3.1.0 I've updated the ORM package just now to suite it. Because i'm not modifying the actual ORM class at all, It was a very easy upgrade. Had to change a couple of lines in the wrapper classes, but aside from that it was a sweet upgrade. See the actual class for changes and new features. Once again, until the fuel repo is updated, you can download it from https://github.com/TJS-Technology/fuel-kohana-orm

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion