ExpressionEngine CMS
Open, Free, Amazing

Thread

This is an archived forum and the content is probably no longer relevant, but is provided here for posterity.

The active forums are here.

Gas ORM

October 23, 2011 4:00pm

Subscribe [45]
  • #166 / Jan 10, 2012 6:34pm

    Carlos Mora

    13 posts

    Then you could do more-less same with above, to unshift or adding some value into set data collection, in other hooks point like before_save(), eg : the ‘somefield’ variable above into ‘hashed_password’ field.

    But, to be honest, i think Shawn lil bit non effective in those example. I mean, why we need a pseudo-variable in the first place, to create a user entries? I will sent the data directly, then hash it on before_save (without shifting and unshifting the password variable at all). Dont adding unecessary complexity into something which actually simple, without a good reason. Sound make sense? 😉

    Now I see! I didn’t find out of the use of filled_fields() and set_fields.
    Thanks!

  • #167 / Jan 11, 2012 4:15am

    Pieter

    34 posts

    I completely follow the schema created by the gasunittest controller and when I run below code

    $jobs = Gas::factory('job')->all();
    
    foreach($jobs as $job)
    {
     echo 'Job: '. $job->name . '
    '; 
     
     if($job->user)
     {
      foreach($job->user as $user)
      {
       echo 'user: '. $user->name . '
    '; 
      }
     } 
     echo '<hr >';
    }

    I get an error: Table ‘gasorm.user_job’ doesn’t exist.

    I know I can corrent this with the custom table in the job model, but in my opinion I shouldn’t have to as I use the default naming convention. So I hope the alphabetical order makes it into the 1.x and 2.x verions.

     

  • #168 / Jan 11, 2012 6:24am

    toopay

    1583 posts

    @Pieter,
    In version 1.4.2 or earlier, you will get the decent result by above syntax. In version 1.4.3, i remove those “automatically-guess-for-you” mapped, for pivot table name in many-to-many relationship. The reason was, its too much consuming memory. Since the process will take this step :
    1. The relationship dispatcher, first will create an array contain 2 possible pivot table name. By your example above, the possible pivot table name were : job_user or user_job.
    2. Then it will loop over those “guessed” pivot table name, to find existed table, and will break if the valid table name found.

    Did you think, those 2 extra step, while translate our many-to-many relationship, didnt too much? It make the many-to-many goes slower than it should. So in 1.4.3, it only works for child_parent pattern table, as you experience by now. No more extra job, we can (as you already aware) adding more line in child table (in this case, job table) to tell the pivot table name. This convention assume, we only (or mostly) need to fetch the relationship of the parent table (in this case user table), and did not need to fetch the otherwise.

    Also, there are other disadvantage, to use the many-to-many relationship directly like the earlier approach of this ORM(use has_and_belongs_to rule), since by this kind of relationship type, we could not process the pivot table itself. I will recomend, to use ‘has_many’ and ‘through’ options, to set up a many-to-many relationship. That will not only provide more convinient way to work with all tables included in this relationship, but also will work vice versa (parent<->child), one thing you need for your task. ‘has_and_belongs_to’ rule, perhaps, would be removed in the next version, so we could avoid this confused issue.

  • #169 / Jan 12, 2012 12:42pm

    Pieter

    34 posts

    Hello Toopay,

    Thanks for your response. I see what you mean. I don’t want to push you, I’m just interested to know why you make certain choices. Though I still don’t see any overhead in naming the pivot table in alphabetical order, as most ORM’s do.

    I was was writing an relate method for making relationships easy to implement. It was supposed to work like this:

    // relate user to job
    $job = Gas::factory('job')->find(2);
    $user = Gas::factory('user')->find(3);
    $job->relate($user);
    
    // or relate multiple users to same job
    $users = Gas::factory('user')->all();
    $job->relate($users);
  • #170 / Jan 13, 2012 4:21am

    toopay

    1583 posts

    @Pieter,
    What hold you to eagerly-load all related resource, instead doing another check within your extension? (thats what ‘relate’ method about, right?) Iam interested, if some of you, need something which not provided by internal method within this ORM (because possibly, i could include those additional functionality in new version, if i could see potential benefits of having it).

    About your proposal in the many-to-many naming convention, i am still not seeing the significant benefits to adding more logic within relationship hydrator.

    In new version, you would define model relationship as follow : (eg, we use the classic example of user <-> wife)

    user model will be something like :

    <?php namespace Model;
    
    use \Gas\Core;
    use \Gas\ORM;
    
    class User extends ORM {
       function _init()
       {
          // Define relationships
          self::$relationships = array(
             'wife' => ORM::has_one('\\Model\\Wife', NULL, array('select:id,name')),
          );
    
          // Define fields definition
          self::$fields = array(
             'id'       => ORM::field('auto[3]'),
             'name'     => ORM::field('char[40]'),
             'email'    => ORM::field('email[40]'),
             'username' => ORM::field('char[10]', array('required', 'callback_username_check')),
          );
       }
    }

    Then wife model will be something like :

    <?php namespace Model;
    
    use \Gas\Core;
    use \Gas\ORM;
    
    class Wife extends ORM {
       function _init()
       {
          // Define relationships
          self::$relationships = array(
             'user' => ORM::belongs_to('\\Model\\User'),
          );
    
          // Define fields definition
          self::$fields = array(
             'id'         => ORM::field('auto[3]'),
             'user_id'    => ORM::field('int[3]'),
             'name'       => ORM::field('char[40]'),
             'hair_color' => ORM::field('email[20]'),
          );
       }
    }

    Furthermore, if you look closer for those new way of how this ORM define its relationship between entities(look at the develop branch, in unit testing section, for complete usage tests), there are 4 new things already :
    1. Relationship aliasing :
    Mean you could name your relationship entity with something else (this will handy for long-named of table(s)).

    2. Tier models configuration, still with custom key(for custom relationship which could not follow convention). So in many to many, we could have something like :

    self::$relationships = array(
       'job' => ORM::has_many('\\Model\\Job_user => \\Model\\Job', 'custom_key', array('select:id,name'),
    );
    // '\\Model\\Job_user => \\Model\\Job' above represent the flow to reach the relationship entity
    // so ORM first will take intermediate records from the first tier (Model\Job_user) then next tier(Model\Job)
    // This way, it will possible to set up a relationship between several tiers of entity.

    Which i think will resolve your many-to-many proposal above (and actually, also remove the necessary of ‘through’ option). The reason why is really simple : to avoid stacking “i-will-guess-for-you” logic within this ORM, and let developer choose how and what they want to do with his each entities.

    3. Pre-process query for relationship. So you could tell how your relationship entity would fetched by adding more SQL clause. This can be done both in _init method or/and when we try to reach those relationship method (on the fly).

    4. Regarding how we reach related entity. In this case (user <-> wife) instead the old syntax, which posibly something like :

    $user1 = Gas::factory('user')->find(1);
    $user1_wife = $user1->wife;

    We will have something like

    $user1 = Model\User::find(1);
    $user1_wife = $user1->wife();
    // or if we try to pre-process the wife entity,
    // let say we only want to fetch the id and name:
    $user1_wife = $user->wife('select:id,name');

    So seriously, did we need more, after this changes? 😉

  • #171 / Jan 13, 2012 8:01am

    Pieter

    34 posts

    @Toopay
    Your new version looks promising. (I still have to get used to PHP5.3, guess I’ll have to upgrade my PHP skill a bit)

    I think you misunderstood my relate method as I intend to use it to save relations (not get related entities).
    I wanted to create a more elegant method to for an issue we discussed in this (http://ellislab.com/forums/viewthread/202669/P100/#958479) post.
    Also wanted to create an unrelate() method to do the opposite. This could be used before you delete a record so other items don’t point to a non existent row.

  • #172 / Jan 13, 2012 10:12am

    splaq

    13 posts

    I’m having the “model *whatever* located, but missing relationship properties” issue..I’ve looked over the documentation and I just do not get what’s going on..

    My tutorials_gas.php model looks like this..

    <?php
    class Tutorial extends Gas {
     
     public $relations = array(
      'has_one' => array('user' => array()),
      'has_many' => array('comments' => array())
     );
     public $table = 'tutorials';
    
     public function get_tut_list($num_posts){
      $list = Gas::factory('tutorial')->order_by('id', 'desc')->limit($num_posts)->all();
      return $list;
     }
    }

    It seems to be setup correctly as in each tutorial has one user but can also have many comments…

    my user_gas.php model looks like so..

    <?php 
    class User extends Gas {
     
     public $relations = array(
      'has_many' => array(
       'blogs' => array(), 
       'showcases' => array(), 
       'tutorials' => array()
      )
     );
     public $table = 'users';
    
     public static function create_user($fname, $lname, $email, $pwd){
      $user = new User;
      $user->first_name = $fname;
      $user->last_name = $lname;
      $user->email = $email;
      $user->password = self::hash_password($pwd);
      $user->joined_date = date('Y-m-d H:i:s', time());
    
      $user->save();
     }
    
     function hash_password($password){
      
      $salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
      $hash = hash('sha256', $salt . $password);
    
      return $salt . $hash;
     }
    }

    Again seems to be setup correctly but for some reason the relationships seem to be a moot point.. relationship is each user has many blogs, showcases and tutorials but I just get “Model tutorial located, but missing relationship properties”...What am I doing wrong or what am I missing?..I’ve been up all night screwing around with this and it’s starting to drive me mildly nuts so any help or light shed on this issue would be of great use!

    Thanks

  • #173 / Jan 13, 2012 11:43am

    toopay

    1583 posts

    @pieter,
    Could you share a real-scenario, about your ‘relate’ and ‘unrelate’ method? 😉

    @splaq,

    From what i’ve seen above, already found something wrong within your setup. At least, this line :

    'tutorials' => array()

    in your user model, shoudl be

    'tutorial' => array()

    Because your ‘tutorials’ table holds by ‘tutorial’ model. In Gas ORM, you point a relationship between entities by its models, not by the real table names. Then, you should check Relationship section of documentation. If this are the first time you works with relational database concept, then i suggest you use ‘gasunittest.php’ to auto-generate all necessary stuff, as an example. It would create several table, models and files, which would give you an idea about that.

  • #174 / Jan 13, 2012 3:40pm

    splaq

    13 posts

    Hey toopay I replaced what you suggested “tutorials” to “tutorial” I was used to inflection on other ORM’s such as PHP-ActiveRecord..I’m getting a different error now..1054 tutorials_id not found in the user table..which is correct because the user has many tutorials and I’m not going to fill the user table with a bunch of duplicates for the tutorial ID…I’ve looked through the documentation..What am I not understanding? in other ORM’s I’ve used such as PHP-ActiveRecord what I have configured as for the relations would be correct..here’s the contents of my files again…

    tutorial_gas.php

    <?php
    class Tutorial extends Gas {
     
     public $relations = array(
      'has_one' => array('user' => array()),
      'has_many' => array('comment' => array())
     );
     public $table = 'tutorials';
    
     public function get_tut_list($num_posts){
      $list = Gas::factory('tutorial')->order_by('id', 'desc')->limit($num_posts)->all();
      return $list;
     }
    }

    Again correct in my mind relation mapping each tutorial has one user associated with it and each tutorial can have many comments..

    user_gas.php

    <?php 
    class User extends Gas {
     
     public $relations = array(
      'has_many' => array(
       'blog' => array(), 
       'showcase' => array(), 
       'tutorial' => array()
      )
     );
     public $table = 'users';
    
     public static function create_user($fname, $lname, $email, $pwd){
      $user = new User;
      $user->first_name = $fname;
      $user->last_name = $lname;
      $user->email = $email;
      $user->password = self::hash_password($pwd);
      $user->joined_date = date('Y-m-d H:i:s', time());
    
      $user->save();
     }
    
     function hash_password($password){
      
      $salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
      $hash = hash('sha256', $salt . $password);
    
      return $salt . $hash;
     }
    }

    Each user can have many or multiple blogs, showcases and tutorials associated with them.

    And just encase the issue is in my view here’s my view…It’s a smarty template..
    tuts.tpl

    <div id="tuts">
     <h2>Newest Tutorials</h2>
    <p> {foreach $tuts as $tutorial}<br />
     <div class="tutorial"><br />
      </p><h3><a >id}">{$tutorial->title}</a></h3>
    <p>  {$tutorial->description}<br />
      <small>Posted By: {$tutorial->user->email}</small><br />
     </div><br />
     {/foreach}<br />
    </div>

    Also, here’s the database error I’m getting exactly..

    Error Number: 1054
    
    Unknown column 'tutorials_id' in 'where clause'
    
    SELECT * FROM (`users`) WHERE `tutorials_id` IN ('9')
    
    Filename: /home/splaq/www/caseartdesigns/sparks/Gas-ORM/1.4.3/libraries/Gas.php
    
    Line Number: 4935

     

  • #175 / Jan 13, 2012 4:41pm

    toopay

    1583 posts

    First, how you set up your table entities between tutorial and user?
    1. Are you use pivot table which mean there are 3 tables involve :

    +-------+   +-----------------+   +------------+
    | users |   | tutorials_users |   | tutorials  |
    +-------+   +-----------------+   +------------+
    | id    |<->| users_id        |   |            |
    | ...   |   | tutorials_id    |<->| id         |
    +-------+   +-----------------+   | ...        |
                                      +------------+

    or, 2. Is it referenced directly?

    +-------+   +-----------------+
    | users |   | tutorials       |
    +-------+   +-----------------+
    | id    |<->| users_id        |
    | ...   |   | ...             |
    +-------+   +-----------------+

    If you set up it “through” a pivot table, then you should add “through” option within your relationship (read relationship section on how to set up this one) and you should have “tutorials_users” model too.

    If you set up your entities directly, then one of your entities should have “belongs_to” property. In above case, tutorials (logically) should “belongs_to” users, so users could have several tutorial but each tutorial always belongs to a user.

    When something like that(“plural->singular” naming convention) was very trivial thing, you would especially noticed(soon or later) how this ORM different compare to other ORM in general, on queries approaches. Because while others stacking “JOIN” statement into their entity relationhips hydrator logic, this one not.

    I am not going to argue about why “JOIN” is evil. Instead, see for yourself : do a benchmark against other ORM(especially one you refer above) in several scenario(s). While other ORM dead by thousand of queries try to do a “black magic” for you, or try to mimic other programming languange behaviour, you will see another differences with this “ORM”.

  • #176 / Jan 14, 2012 6:29am

    Pieter

    34 posts

    @Toopay
    In my opinion the biggest advantage of ORM’s is working with relations. Not only accessing related objects, but also making or removing relations between rows. I prefer not having to look in the model to see how I named the through table and having to write sql myself.

    // new kid is born…
    $new_kid = Gas::factory('kid')->fill($_POST)->save();
    
    // add relations between user and kid
    Gas::factory('user')->find(1)->relate($new_kid);
    // I went to the library and borrowed 2 books and returned 1
    $borrowed = Gas::factory('book')->find(12,56);
    $returned = Gas::factory('book')->find(24);
    
    // apply to user
    // parameter of relate and unrelate method can be an object or an array of objects
    Gas::factory('user')
     ->find(1)
     ->relate($borrowed)
     ->unrelate($returned);

    Making the relate method work for many-to-many relationships can be more challenging as you first have to check if the relations doesn’t already exist in the pivot table.

    Or you could do it like Datamapper and use the save (http://datamapper.wanwizard.eu/pages/save.html) and delete (http://datamapper.wanwizard.eu/pages/delete.html) method for adding and removing relationships.

     

  • #177 / Jan 14, 2012 2:51pm

    toopay

    1583 posts

    @Pieter,

    I could see cascade delete (to avoid having orphan records within some intermediate/pivot table) was a missed feature to earlier version (1.X), despite there are already hooks point available (before_delete or after_delete) to resolve this thing.

    But while above feature could considered as something to add on feature list, i am hardly to see that saving a relationship in those un-transparent way would be a handy feature, at least for 2 reasons :

    1. It is very looong way, to save a data. Those process would require to make sure data integrity between entities has been passed (which in your above case, was reflecting the opposite situation : you know exactly what entities record involved).
    2. It will most likely become non uniformal method, among supported relationship. Since those will not works for composite table (which now, has been planned, supported by new version).

    There are enough hooks point (observers) to resolve those problem, which i believe several time faster than above process.

  • #178 / Jan 15, 2012 11:16am

    Pieter

    34 posts

    @Toopay
    Ok, what about the way the ORM of FuelPHP makes relations (http://docs.fuelphp.com/packages/orm/relations/many_many.html):

    // both main and related object are new:
    $post = new Model_Post();
    $post->users[] = new Model_User();
    $post->save();
    
    // both main and related object already exist
    $user = Model_User::find(8);
    $user->posts[1] = Model_Post::find(1);
    $user->save();
    
    // break the relationship established above
    $post = Model_Post::find(1);
    unset($post->users[8]);
    $post->save();
  • #179 / Jan 17, 2012 1:46pm

    toopay

    1583 posts

    Just let you guys all know…Gas ORM 2 (initial) is now LIVE.

    There are several trivial tasks to fully create ‘2.0’ tag for it, but it would not be long until those time came 😊

  • #180 / Jan 18, 2012 2:47pm

    sqwk

    83 posts

    Just discovered Gas and figured out how to use it within a couple of minutes. Great job. One thing though? Is there a way to select what fields are being selected from the DB?

    $buyers = Gas::factory('users')->with('emails')->all()

    Can I tell Gas that I only want the ‘id’ and ‘username’ fields from the users table and the ‘id’ and ‘email’ field from the emails table? I found out that I can add ->select() to tell Gas what to get from the users table, but what about the email table?

    Also, can I eager load multiple levels? For example if there is another m:m relationship after emails?

.(JavaScript must be enabled to view this email address)

ExpressionEngine News!

#eecms, #events, #releases