SitePoint Sponsor

User Tag List

Page 2 of 2 FirstFirst 12
Results 26 to 35 of 35
  1. #26
    SitePoint Member
    Join Date
    May 2009
    Posts
    2
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    But essentially it is public, just by-proxy.

  2. #27
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,398
    Mentioned
    65 Post(s)
    Tagged
    1 Thread(s)
    Quote Originally Posted by Lemon Juice View Post
    Only in this case I want $obj->a = 5; to be mapped to $obj->a(5);
    I'll admit to only skim-reading this thread, so what's below might be of no practical use whatsoever.

    PHP Code:

    class MyClass
    {
        public 
    $a 1$b 2$c 3;
        
        public function 
    __call($method$arguments)
        {
            if (
    property_exists($this$method) && count($arguments) === 1)
            {
                return 
    $this->$method $arguments[0];
            }
        }
    }


    $instance = new MyClass;

    $instance->a('x');
    $instance->b('y');
    $instance->c('z');

    print_r($instance);

    /*
    MyClass Object
    (
        [a] => x
        [b] => y
        [c] => z
    )
    */ 
    Salathe
    Software Developer and PHP Manual Author.

  3. #28
    SitePoint Wizard silver trophybronze trophy Stormrider's Avatar
    Join Date
    Sep 2006
    Location
    Nottingham, UK
    Posts
    3,133
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Salathe View Post
    I'll admit to only skim-reading this thread, so what's below might be of no practical use whatsoever.

    PHP Code:

    class MyClass
    {
        public 
    $a 1$b 2$c 3;
        
        public function 
    __call($method$arguments)
        {
            if (
    property_exists($this$method) && count($arguments) === 1)
            {
                return 
    $this->$method $arguments[0];
            }
        }
    }


    $instance = new MyClass;

    $instance->a('x');
    $instance->b('y');
    $instance->c('z');

    print_r($instance);

    /*
    MyClass Object
    (
        [a] => x
        [b] => y
        [c] => z
    )
    */ 
    I think that mapping is the wrong way around - you need to use __get(), not __call().

  4. #29
    @php.net Salathe's Avatar
    Join Date
    Dec 2004
    Location
    Edinburgh
    Posts
    1,398
    Mentioned
    65 Post(s)
    Tagged
    1 Thread(s)
    Absolutely, I should have read the first post.
    Salathe
    Software Developer and PHP Manual Author.

  5. #30
    SitePoint Guru bronze trophy
    Join Date
    Dec 2003
    Location
    Poland
    Posts
    930
    Mentioned
    7 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    Hard coding the properties into the model as public members doesn't make for a very flexible system. Especially if you start to consider the need to overload a single ActiveRecord with other ActiveRecords
    Can you give me an example of overloading a single ActiveRecord with other ActiveRecords?
    or overload calculated columns.
    That's what I mentioned in my previous post that it's very rare that I want to overload columns - I don't want to abstract too much from db structure and have different field values in the db and different (=calculated, modified, translated, etc.) in my application. I think it makes things simpler if I try not to overload columns too much - then when I look straight at db data I see more or less what my application sees. However, sometimes I use overloading for data types like date, datetime, etc. when I want to translate the value to a timestamp for example, but in most other cases I don't need it and plain properties are sufficient. Do you think using both direct properties and getters will make things difficult? Personally I haven't noticed that yet.

    For instance, to create a aggregate calculation when finding data.
    I prefer to use db aggregate functions for such calculations, it's the most efficient way. However, I can create methods for my active records to do that if I want - I don't see how public properties will prevent me from doing that?
    A fundamental advantage of the active record is the flexibility of dynamic properties.
    So I still keep this advantage, it's just that for most db fields I don't find the need for that and I use this advantage optionally. If I need a dynamic property then I create a specific getter and use it. Otherwise I simply use the property directly and then I am aware I'm accessing raw data from db. I haven't found any problem with that in practice.
    $category = new Category(20);
    $category->products[] = new Product(array('name'=>'product'));
    $category->save();
    In this example you are directly modifying public property $products - shouldn't you use a method like addProduct() for it to be consistent with proper active record pattern?

  6. #31
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Quote Originally Posted by Lemon Juice
    Can you give me an example of overloading a single ActiveRecord with other ActiveRecords?
    The below would fetch a blog entry then separetly fetch the blog entries user into a dynamic property called user. This can be seen below with the XML that represents the dynamic property hierarchy.

    PHP Code:
    $blog = new BlogEntry(10);
    $blog->user;
    $blog->toXML(); 
    Code XML:
    <blog_entry model="BlogEntry" table="blog_entries" id="10">
    	<id>10</id>
    	<user_id>12</user_id>
    	<title>Top 10 Truths about Freelancing</title>
    	<created>2009-04-11 03:44:28</created>
    	<entry>content foes here</entry
    	<picture/>
    	<status>1</status>
    	<user model="User" table="users" id="12">
    		<id>12</id>
    		<name>john_doe</name>
    		<first_name>John</first_name>
    		<last_name>Doe</last_name>
    		<email>john@doe.com</email>
    		<pwd>my_password</pwd>
    		<status>1</status>
    		<created>2009-04-10 23:43:35</created>
    		<access>2</access>
    		<site>http://www.john-doe.com</site>
    		<newsletter>1</newsletter>
    	</user>
    </blog_entry>

    For efficiencies sake the previous code also be written as shown to below which would only run one query and return the same data. This is because the users table would joined and the appropriate data related to the user would be redistributed to a new instance of User. The data would be the same as above without the added trip to the database.

    PHP Code:
    $blog =
    BlogEntry::find(
          
    'one'
          
    ,array(
               
    'include'=>'user'
               
    ,'id'=>10
          
    )
    ); 
    That is a simple example of how useful being able to overload a model instance can be. Without dynamic properties each association would also need to be defined as a public member to accomplish a similar functionality.

    Quote Originally Posted by Lemon Juice
    I prefer to use db aggregate functions for such calculations, it's the most efficient way. However, I can create methods for my active records to do that if I want - I don't see how public properties will prevent me from doing that?

    In the previous scenario how would one easily count the number of blogs per a user?

    The solution is to overload the already overloaded model using dynamic fields. In this case a dynamic field named blogs_per_user will be overloaded as a property that contains the aggregate data. That result set data could then be accessible through the dynamic property blogs_per_user on a BlogEntry object. Something that wouldn't be possible at all without dynamic properties because the name and calculation is determined at run time.

    PHP Code:
    $blogs BlogEntry::find(
        array(
            
    'dynamic'=>array(
                
    'blogs_per_user'=>'COALESCE(COUNT(BlogEntry.id),0)'
            
    )
            ,
    'group'=>'user_id'
        
    )
    );
    $blogs->toXML(); 
    A small sample of output. Notice how even though the BlogEntry model doesn't contain a native field named blogs_per_user it has been overloaded and contains the aggregate data - number of blogs per user.
    So using dynamic properties it is possible to overload any model with more data say for aggregate calculations as in the previous scenario.

    Code XML:
    <blog_entries>
    	<blog_entry model="BlogEntry" table="blog_entries" id="9">
    		<id>9</id>
    		<user_id>12</user_id>
    		<title>Freelance Camp Houston: April 11th, 2009</title>
    		<created>2009-04-11 00:25:56</created>
    		<picture/>
    		<status>1</status>
    		<blogs_per_user>3</blogs_per_user>
    		</blog_entry>
    	<blog_entry model="BlogEntry" table="blog_entries" id="11">
    		<id>11</id>
    		<user_id>14</user_id>
    		<title>The Dirty Approach to Marketing</title>
    		<created>2009-04-24 03:21:42</created>
    		<picture/>
    		<status>1</status>
    		<blogs_per_user>4</blogs_per_user>
    	</blog_entry>
    </blog_entries>

    Using a join the system can be even more flexible by getting the number of blogs per user even if the user doesn't have any blogs.

    PHP Code:
    $users User::find(
        array(
            
    'include'=>'blog_entries'
            
    ,'dynamic'=>array(
                
    'blogs_per_user'=>'COALESCE(COUNT(BlogEntry.id),0)'
            
    )
            ,
    'group'=>'id'
        
    )
        ,array(
            
    'deselect'=>'entry'
            
    ,'require'=>false
        
    )
    );
    $users->toXML(); 
    In that case all users are being joined with their blog entries and then using a group calculating a aggregate count based on how many blogs each user has. Information that would now be accessible directly through the user and its dynamic blogs_per_user property:

    PHP Code:
    $users[0]->blogs_per_user || $users[0]['blogs_per_user'] || $users[0]->getProperty('blogs_per_user'); 
    In that case the number of blogs associated with the user at position 0 is easily accessible through a calculated field applied as a dynamic property at run time.

    Quote Originally Posted by Lemon Juice
    In this example you are directly modifying public property $products - shouldn't you use a method like addProduct() for it to be consistent with proper active record pattern?
    PHP Code:
    $category = new Category(20);
    $category->products[] = new Product(array('name'=>'product'));
    $category->save(); 
    Is equal to:

    PHP Code:
    $category = new Category(20);
    $category->addProducts(new Product(array('name'=>'product')));
    $category->save(); 
    The former is just a more convenient way of accomplishing the same thing since all collections implement arrayaccess.

    Laslty, non of this logic is handled inside the model class files. All ActiveRecord functionality is managed through the class which all models extend. The Model files themselves are dormant of any logic besides configuration type info for the model - table name, field, etc. This leads to a very scalable, reusable and lightweight code base then if all logic were to be hard coded into individual model files.

    These are things that I've found make the ActiveRecord system incredibly flexible, easy to use and powerful. I wrote my own implementation based on desire to always use it. Therefore, it supports almost all circumstances which I can think of including subqueries.

    At the same time though I've also broke certain "rules" pertaining to a ActiveRecord to accomplish functionality that would otherwise be almost impossible to implement. So if you believe breaking with tradition will lead to a more useful system then I won't stare you away. ActiveRecord, Gateway, etc all that really matters in the end is what the thing does in my opinion and how efficient, scalable and reusable the solution is. Call it whatever you may if succeeds in those areas pattern or not.
    Last edited by oddz; May 12, 2009 at 13:19.

  7. #32
    SitePoint Guru bronze trophy
    Join Date
    Dec 2003
    Location
    Poland
    Posts
    930
    Mentioned
    7 Post(s)
    Tagged
    0 Thread(s)
    Thanks for taking the time for writeing the detailed explanation, a few comments from me:
    Quote Originally Posted by oddz View Post
    The below would fetch a blog entry then separetly fetch the blog entries user into a dynamic property called user. This can be seen below with the XML that represents the dynamic property hierarchy.

    PHP Code:
    $blog = new BlogEntry(10);
    $blog->user;
    $blog->toXML(); 
    Sure this looks very nice. But all I'm saying is that dynamic properties are not necessary to achieve this functionality. While you do:
    PHP Code:
    $blog->user
    In my system I would write:
    PHP Code:
    $blog->getUser(); 
    which would basically do the same thing. Having public properties does not prevent me from doing this, only I use direct getters while you use getters hidden behind dynamic properties.

    PHP Code:
    $blog =
    BlogEntry::find(
          
    'one'
          
    ,array(
               
    'include'=>'user'
               
    ,'id'=>10
          
    )
    ); 
    That is a simple example of how useful being able to overload a model instance can be. Without dynamic properties each association would also need to be defined as a public member to accomplish a similar functionality.
    I see what you mean - you have this functionality built into a single base class from which all active records extend, right? My approach is a bit different - indeed I use public members to achieve this but I don't see it as a problem because they are auto-generated, they don't add to execution time at all if you have opcode cache, actually they are faster because the base class needs to have some universal functionality to be able to serve all of the active records while the public method (getter) is written (generated) specifically for this active record (table) and can go straight to execution what is necessary. But perhaps the speed difference is not that big depending on your implementation.
    In the previous scenario how would one easily count the number of blogs per a user?
    I simply define a method $blog->countUsers() or setup my generator to generate it for me. Or, I fetch the user object using a join sql like in the example later on.

    The solution is to overload the already overloaded model using dynamic fields. In this case a dynamic field named blogs_per_user will be overloaded as a property that contains the aggregate data. That result set data could then be accessible through the dynamic property blogs_per_user on a BlogEntry object. Something that wouldn't be possible at all without dynamic properties because the name and calculation is determined at run time.
    Again, it's very possible and I do it all the time with methods(). Perhaps dynamic properties will lead to less code in the active record classes but at the expense of slower execution compared to separate methods. But I think I see your point and it doesn't look like a bad system at all.

    Using a join the system can be even more flexible by getting the number of blogs per user even if the user doesn't have any blogs.

    PHP Code:
    $users User::find(
        array(
            
    'include'=>'blog_entries'
            
    ,'dynamic'=>array(
                
    'blogs_per_user'=>'COALESCE(COUNT(BlogEntry.id),0)'
            
    )
            ,
    'group'=>'id'
        
    )
        ,array(
            
    'deselect'=>'entry'
            
    ,'require'=>false
        
    )
    );
    $users->toXML(); 
    This is what I wanted to avoid in my own implementation because for my liking this User::find() abstracts too much from the db and sql and uses a completely different method of accessing data. Why learn and support 2 ways of accessing the same thing? I prefer a more direct approach:
    PHP Code:
    $users UserPeer::doSelect("
        SELECT users.*,
        (SELECT COUNT(*) FROM blog_entries
            WHERE user_id=users.user_id)
        AS blogs_per_user
        FROM users"
    ); 
    This is just my preference and I understand you may like your code better. I'm not really fond of sending complicated criteria to a class so that it can build the right sql for me - it's not really clearer for me because I feel comfortable with sql and I like to see what is being sent to the db and this makes db optimization way easier e.g. when I need to create indexes or optimize queries in some other way. Not to say that direct sql is much faster. But again, I don't need to work with multiple database systems so this is not an issue for me. I would certainly write the proper sql several times faster than the kind of code you showed above - not to say how much easier it would be for me to correct any errors in case I make a mistake.

    The code above may look scary as it allows me to load any data to the $users object with sql, even data from other tables which shouldn't normally be loaded there. While this allows me to abuse the active record by supplying wrong sql I am not going to shoot myself in the foot but use this freedom for something useful.
    These are things that I've found make the ActiveRecord system incredibly flexible, easy to use and powerful. I wrote my own implementation based on desire to always use it. Therefore, it supports almost all circumstances which I can think of including subqueries.

    At the same time though I've also broke certain "rules" pertaining to a ActiveRecord to accomplish functionality that would otherwise be almost impossible to implement. So if you believe breaking with tradition will lead to a more useful system then I won't stare you away. ActiveRecord, Gateway, etc all that really matters in the end is what the thing does in my opinion and how efficient, scalable and reusable the solution is. Call it whatever you may if succeeds in those areas pattern or not.
    I agree with you on this. I certainly broke many rules designing my (semi-?)active record because I felt constrained by too many strict rules of the ones I knew. And besides I wanted something quite fast so I had a bit of efficiency in mind. But it all comes down to coding preferences, I feel comfortable with public properties and plain sql and while I don't need to use them I feel more freedom when they are available. I create public members if I need a dynamic functionality and it works.

  8. #33
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    It doesn't seem like your trying to write a reusable code base. I've been providing recommendations and examples based on writing a reusable library not hard coding everything. My library is based on the idea that anything that extends the ActiveRecord class inherits the same functionality as all other ActiveRecord(s) – nothing is hard wired. So it seems you have a different goal mind in which case you should probably ignore everything I've said.

  9. #34
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    This would not be the most efficient way to run the below query:

    PHP Code:
    $users UserPeer::doSelect("
        SELECT users.*,
        (SELECT COUNT(*) FROM blog_entries
            WHERE user_id=users.user_id)
        AS blogs_per_user
        FROM users"
    ); 
    However, something like this would be:

    Code SQL:
    SELECT 
         t0.`id` AS t0_id
         ,t0.`name` AS t0_name
         ,t0.`first_name` AS t0_first_name
         ,t0.`last_name` AS t0_last_name
         ,t0.`email` AS t0_email
         ,AES_DECRYPT(t0.pwd,SHA1(CONCAT(?,t0.created))) AS t0_pwd
         ,t0.`status` AS t0_status
         ,t0.`created` AS t0_created
         ,t0.`access` AS t0_access
         ,t0.`site` AS t0_site
         ,t0.`newsletter` AS t0_newsletter
         ,COALESCE(COUNT(t1.id),0) AS t0_blogs_per_user
         ,t1.`id` AS t1_id,t1.`user_id` AS t1_user_id
         ,t1.`title` AS t1_title,t1.`created` AS t1_created
         ,t1.`picture` AS t1_picture,t1.`status` AS t1_status 
      FROM 
         users AS t0 
      LEFT 
      JOIN 
          blog_entries AS t1
        ON t0.id = t1.user_id 
     GROUP 
         BY t0.id

  10. #35
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Here is another simple use for overloading. This would not be possible without overloading because the release_day,release_year and release_month are calculated at run time. These fields are not native to the MusicAlbum model.

    MusicAlbum Model
    PHP Code:
    <?php
    class MusicAlbum extends MusicAlbumModelStub {

        public static 
    $fields = array('id','artist','title','release_date');
        public static 
    $requiredFields = array('artist','title');

    }
    ?>
    Application Code
    PHP Code:
    $album MusicAlbum::find(
        array(
            
    'dynamic'=>array(
                
    'release_year'=>'YEAR(MusicAlbum.release_date)'
                
    ,'release_day'=>'DAY(MusicAlbum.release_date)'
                
    ,'release_month'=>'MONTH(MusicAlbum.release_date)'
            
    )
        )
    );
    $album->toXML(); 
    XML Representation of MusicAlbum Object
    Code XML:
    <music_albums>
    	<music_album model="MusicAlbum" table="music_albums" id="1">
    		<id>1</id>
    		<artist>Angels &amp; Airwaves</artist>
    		<title>I-Empire</title>
    		<release_date>2007-11-06</release_date>
    		<release_year>2007</release_year>
    		<release_day>6</release_day>
    		<release_month>11</release_month>
    	</music_album>
    </music_albums>

    Notice how these properties have been overloaded into the model as if they were native to it:

    Code XML:
    <release_year>2007</release_year>
    <release_day>6</release_day>
    <release_month>11</release_month>

    I think that feature is well worth the use of dynamic properties. I'm sure there has come a time when you or anyone else for that matter have needed access to fields that were not native to the model. Using overloading makes that partly possible. Otherwise that exception would need to be hard coded into class. However, by making everything dynamic and generic the model files never need to be touched.

    This is what I wanted to avoid in my own implementation because for my liking this User::find() abstracts too much from the db and sql and uses a completely different method of accessing data. Why learn and support 2 ways of accessing the same thing? I prefer a more direct approach:
    There are two main problems I've come to realize when moving information from the database to the domain. Those two problems are creating the query and collecting the result into meaningful structure. In a one to many or many to many relationship the result set will always have repeating data. Collecting that data into a meaningful hierarchical structure while eliminating repeating data on the application level requires a way to know what depth each table/model refers to and a way to identify a unique row. This requires that the query be built dynamically because the collection object needs to know how to recreate a tree like structure from a one-dimensional result set.

    So for simple queries this isn't really needed. However, you can only get by with that one dimensional relationship for so long. besides its very restrictive. Only being able to manage one model at a time just doesn't cut my requirements and seems like laziness. Its very easy to make a ActiveRecord or ORM that only manages one relational table. However, its practical use doesn't travel very far in my opinion and experience.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •