SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 52
  1. #1
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Thumbs up When choosing an ORM, what do you look for

    Hi fellow PHPers.

    I am in the process of writing a PHP object relational mapper (dORM) and was wondering what kind of features are the most important to you as a PHP developer.

    At the moment here are a couple of features that I kept in mind:
    - SQL should be 100% automated
    - Unobtrusive (no class extending or code generation)
    - Support for all data types (scalars, arrays and objects)
    - Support for all relationships (1-to-1, 1-to-many, many-to-many)
    - No naming convention or specific database structure required
    - As little configuration as possible
    - Never have 2 different instances of the same object

    Am I missing something ? What would you add to the list ?

    Before answering, make sure that your idea is not already supported by dORM (http://www.getdorm.com/documentation).

    Your feedback and suggestions are greatly appreciated.

    Good day everyone !

  2. #2
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    This is something I don't understand. Your entire idea here is based on using XML to define information about a model. Yet, you still need to define a class to contain the properties? If that is so the XML or class is merely a extra step. Why not just have one generic data container since the information about the model can be obtained through its XML configuration - correct? Needing to define a XML file and class doesn't sit well with me.

    Code:
    class User {
        Public $followers = array(); // collection of User objects
        Public $following = array(); // collection of User objects
        Public $twits = array(); // collection of Twit objects
        Public $username;
    }
    I was also looking at the documentation and it doesn't seem as if there is a whole lot of control over grouping, having, where, sort and limit clauses. Is there support for things such as having, limit,sort and group or would it be more or less a hack? Is there support for grouped conditions - (t1.a = 1 AND t1.b=2) OR (t1.status = 0)? Lastly is there recursive join support or is the select generation one dimensional? Is all data binded or is it embedded?

    I also believe in a simple interface.

    PHP Code:
    $users $dorm->getUserCollection("user_id > :id", array("id" => 2)); 
    I would prefer placing the comparison operator directly in the array as the key.

    PHP Code:
    $users $dorm->getUserCollection(array('id>' => 2)); 
    The former just seems like a extra step.

    PHP Code:

    $bob 
    = new User();
    $bob->username "bob";

    $john = new User();
    $john->username "john";

    $tim = new User();
    $tim->username "tim"
    Is it possible in this instance to insert these items all in the same query?

    PHP Code:
    $save = new DormSave();
    $save->add($john)->add($tim)->add($bob);
    $save->save(); 
    or must one always act on individual entities?

  3. #3
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Your entire idea here is based on using XML to define information about a model. Yet, you still need to define a class to contain the properties?
    The XML maps PHP classes to database tables/columns. Since I want to allow people to use their own database schema, there is no way to "guess" the mapping, unless some kind of naming convention is imposed or the schema is generated by dORM. Do you think that kind of feature would be interesting ? I'm not sure what you mean by "Yet, you still need to define a class to contain the properties?". Would you like dORM to generate the class for you ? My idea was that people first write their model in PHP and then create the database.

    In other words, the config.xml mapping file is a necessary evil as long as we allow users to use their custom database schema.

    I was also looking at the documentation and it doesn't seem as if there is a whole lot of control over grouping, having, where, sort and limit clauses. Is there support for things such as having, limit,sort and group or would it be more or less a hack? Is there support for grouped conditions - (t1.a = 1 AND t1.b=2) OR (t1.status = 0)? Lastly is there recursive join support or is the select generation one dimensional? Is all data binded or is it embedded?

    I also believe in a simple interface.

    PHP Code:
    $users = $dorm->getUserCollection("user_id > :id", array("id" => 2));

    I would prefer placing the comparison operator directly in the array as the key.

    PHP Code:
    $users = $dorm->getUserCollection(array('id>' => 2));

    The former just seems like a extra step.
    The problem with

    Code:
    $users = $dorm->getUserCollection(array('id>' => 2));
    is that it would make it hard to make custom queries:

    Code:
    $users = $dorm->getUserCollection("user_id > :id OR (username = :username AND email <> :email)", array("id" => 2, "username" => "usernamehere", "email" => "email@domain.com"));
    I must admit that the getCollection method is not very well documented. Actually, the first parameter represents what comes after "WHERE" in the SQL statement. I'll make that information more clear in the documentation.

    Is it possible in this instance to insert these items all in the same query?

    PHP Code:
    $save = new DormSave();
    $save->add($john)->add($tim)->add($bob);
    $save->save();

    or must one always act on individual entities?
    That is an interesting idea. I could easily make the save() method take an unlimited amount of arguments.

    $dorm->save($bob, $tim, $john);
    Did I forget anything ? Would you see automated mapping / database schema generation as a key feature ... or inversely, should it generate the PHP classes / mapping from the database schema ?

  4. #4
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by sysk
    The XML maps PHP classes to database tables/columns. Since I want to allow people to use their own database schema, there is no way to "guess" the mapping, unless some kind of naming convention is imposed or the schema is generated by dORM.
    What do you mean by naming schema? I think its a safe assumption that if the field name is username the object relational property should be username. That makes everything simple. I'm not so sure I like the idea of needing to declare that unless it was a option. For example, by default if a field is named username then the relational property would be username. However, there would be a optional xml element to override that default mapping.

    PHP Code:
    $users $dorm->getUserCollection("user_id > :id OR (username = :username AND email <> :email)", array("id" => 2"username" => "usernamehere""email" => "email@domain.com")); 
    The problem I have with this is it makes it very difficult to remove and add conditions.

    where as something like the below makes it very easy to add and remove certain filters/conditions without repeating code or fancy string manipulation on the users part:

    PHP Code:
    $filters = array(
        
    'username'=>$username
        
    ,'email <>'=>$email
    );

    if(
    $status) {
        
    $filters['status']=0;
    }

    $users $dorm->getUserCollection(
        
    $filters
    ); 
    That is a very simple example, but it supports a more sophisticated interface that doesn't just embed the string allowing the user the ability to control the filter without string manipulation.

    That is an interesting idea. I could easily make the save() method take an unlimited amount of arguments.
    Would they be inserted in a single query though?

    Another thing I was going to ask is the syntax for including related models. For example, if a user has many tweets how could the user and their tweets be brought in at the same time> Furthermore, if each tweet has many comments would it be further possible to bring those in at the same time as well? - or would separate queries need to be run?

  5. #5
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Is something like this possible where in one query a users twits, followings and follows can be found. Along with that the user related to each followings and follows?

    • user
      • twits
      • follows
        • user
      • followings
        • user


    PHP Code:
    // following users name
    $user->follows[0]->user->name
    That is something I feel almost ORM libraries lack. The ability to manage collections of related data through a relatively simple interface without feeling like your hacking the system. Also, to do so in a way supports efficiency rather then running separate queries for each relationship. Is the system capable of resolving that type of hierarchical data in terms of the query and result collection method?

    In regards to the select this a list of options I find useful and have implemented into my own system. The select query is anything but just embedding strings which allows the user a high level of control over everything with tedious string manipulation. The string manipulation is all handled by the system so the user doesn't need to worry about it.

    Find Argument List
    • magicalFilter
    • filter
    • having
    • sort
    • join
    • required
    • condition
    • conditionMap
    • include
    • limit
    • offset
    • dynamic
    • select
    • deselect
    • having
    • require
    • cloak


    Not trying to steal your spotlight just giving you some potential ideas. I can't stand systems that require the user to build the clauses. I also can't stand solutions that just select all fields of the relational table.

  6. #6
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What do you mean by naming schema? I think its a safe assumption that if the field name is username the object relational property should be username. That makes everything simple.
    Most people use the same table names as their class names and same column names as their property names. However, this is not the case for everyone. Furthermore, this still wouldn't tell us how to map our many-to-many relationships (unless we have some sort of table naming convention). That being said, I like the idea of automatically generating the database schema from the PHP model and I'll work on that feature.

    Quote Originally Posted by oddz View Post
    Is something like this possible where in one query a users twits, followings and follows can be found. Along with that the user related to each followings and follows?

    • user
      • twits
      • follows
        • user
      • followings
        • user


    PHP Code:
    // following users name
    $user->follows[0]->user->name
    That is something I feel almost ORM libraries lack. The ability to manage collections of related data through a relatively simple interface without feeling like your hacking the system. Also, to do so in a way supports efficiency rather then running separate queries for each relationship. Is the system capable of resolving that type of hierarchical data in terms of the query and result collection method?
    This is perfectly supported by dORM and doesn't require any sort of "hacking". All you would have to do is $dorm->getUser('id'); and you then have access to all the users properties, including 1-to-1 and 1-to-many relationships.

    i.e:
    PHP Code:
    // you could do
    $user->follows[0]->username;
    $followed $user->follows[0];
    // or even
    $followed->follows[0]->username;

    echo (
    $user->follows[0] instanceof User) ? "This is true !" "Shouldn't return false."
    What dORM does is lazy load "relationships". When you access the property $user->twits, the system makes the appropriate SQL query to load the $user->twits array. Once an object/property is loaded, it is never loaded again so we don't have redundant selects.

    The lazy loading system is already pretty fast. However, it would be a good idea to implement some kind of system that would let developers tell which properties they want to load in advance, so the amount of queries is minimized.

    Quote Originally Posted by oddz View Post
    In regards to the select this a list of options I find useful and have implemented into my own system. The select query is anything but just embedding strings which allows the user a high level of control over everything with tedious string manipulation. The string manipulation is all handled by the system so the user doesn't need to worry about it.

    Find Argument List
    • magicalFilter
    • filter
    • having
    • sort
    • join
    • required
    • condition
    • conditionMap
    • include
    • limit
    • offset
    • dynamic
    • select
    • deselect
    • having
    • require
    • cloak


    Not trying to steal your spotlight just giving you some potential ideas. I can't stand systems that require the user to build the clauses. I also can't stand solutions that just select all fields of the relational table.
    I planned on building a query class that would abstract SQL selects, but I wasn't sure if that was really necessary. I mean, SELECT statements are fairly simple.

    Thanks for your feedback.

  7. #7
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    How many queries would this run with the previous schema?

    PHP Code:
    $dorm->getUser('id'); 
    This is perfectly supported by dORM and doesn't require any sort of "hacking". All you would have to do is $dorm->getUser('id'); and you then have access to all the users properties, including 1-to-1 and 1-to-many relationships.
    That seems very flawed to me. Sometimes only one entity is needed. So if something has a 20 different relationships they would all be resolved? I think its better to able to control that for the sake of efficiency. The last thing I want is to run a query for everything when all I need is the single user. That could potentially be a great deal of queries. Especially if more then one root entity and its relationships are brought at the same time.

    I planned on building a query class that would abstract SQL selects, but I wasn't sure if that was really necessary. I mean, SELECT statements are fairly simple.
    Well just embedding the string requires that the developer builds the string. Breaking all the clauses up into segments and using a array perhaps with options alleviates that. With that said, I more or less prefer the Ruby way of handling it. Furthermore, it also makes it possible to modify the model dynamically. Would it be possible to add dynamic derived columns to the model models within your system? Say results need to be grouped and a counted - how would that be accomplished?

  8. #8
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ironically, for an ORM, a feature that I look for is the ability to customize queries without this being a hack. I have noticed two cases where this is particularly useful: aggregation methods and pre-fetching of related records.
    1. Aggregation: For example, it is often useful to show the number of comments to an article in a listing of summarized articles. A similar example would be to get a listing of tags for that article. Both could be accomplished in the same query that fetches the articles. Some options for accomplishing this are:
      1. Efficiently running a "SELECT COUNT(*) FROM comments WHERE article_id=?"
      2. Requiring the initialization of a record set of comments on which a size() method is called. This is the worst given the obvious performance overhead.
      3. Ideally, inserting a "COUNT(Comment.id)", "LEFT JOIN comments Comment ON (Article.id=Comment.article_id)" and "GROUP BY Article.id".
    2. Pre-fetching: It may be useful in the same blog page to fetch the user information for each blog post in the same query that fetches the articles and any desired aggregates. Most ORMs would require a separate query for each blog post (by a distinct user) to fetch the user. It is nicer to have that fetched with the article itself.


    In code that I use, I have addressed this small subset of problems with the following syntax:
    PHP Code:
    $articles $blog->articles
            
    ->prefetch('user'// Add the necessary inner join and columns to the query
            
    ->aggregate('tags'// Call the aggregateTags method of Article on the Query
            
    ->aggregate('num_comments'// Call the aggregateNumComments method
            
    ->paginate(20// Tweak the Query for pagination and add $pager to $blog->articles
            
    ->orderBy('Article.published DESC'); // Add an order clause 
    This would generate the following query:
    Code MySQL:
    SELECT   
      SQL_CALC_FOUND_ROWS article.id                                   AS id,
                          article.user_id                              AS user_id,
                          article.published                            AS published,
                          article.title                                AS title,
                          article.body                                 AS body,
                          USER.id                                      AS __user__id,
                          USER.username                                AS __user__username,
                          USER.PASSWORD                                AS __user__password,
                          USER.name                                    AS __user__name,
                          Group_concat(DISTINCT tag_id SEPARATOR ', ') AS tags,
                          Count(DISTINCT COMMENT.id)                   AS num_comments
    FROM     article article
             INNER JOIN USER USER
               ON (USER.id = article.user_id)
             LEFT JOIN article_tag at
               ON (article.id = at.article_id)
             LEFT JOIN COMMENT COMMENT
               ON (COMMENT.article_id = article.id)
    GROUP BY article.id
    ORDER BY article.published DESC
    LIMIT    0,20

    In the absence of these two features, I think I would find it very frustrating to code and I doubt that I would consider migration.

  9. #9
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Perhaps a good way to articulate my approach to result finding is query refinement.
    I planned on building a query class that would abstract SQL selects, but I wasn't sure if that was really necessary. I mean, SELECT statements are fairly simple.
    I think that an abstract query object is a very important feature that affords immense flexibility to your code without creating noticeable overhead. To be able to achieve query refinement, you need an abstract query object.

    This relates heavily to lazy loading. If you were to reference $user->twits, a lazy record set would be created although no queries would be executed. The user could then refine the query by where clauses, limits, etc, all the while customizing the query. The query would only be executed once an attempted reference is made to its record set element(s). This could happen through offsetExists(), getIterator(), reset(), etc.

  10. #10
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by b1ind
    If you were to reference $user->twits, a lazy record set would be created although no queries would be executed.
    PHP Code:
    $user->getTwits(array('limit'=>10,'offset'=>5)); 
    That is how the system I am currently building would handle that. The record can essentially become a gateway to other data. When a entity is used in this way it resolves its relationship automatically while allowing the developer to concentrate on everything else. This is a very neat feature because it creates a simple interface to bring in one item and its related objects if they need to be paginated.

    PHP Code:
    $blog = new Blog(2)
    $comments $blog->getComments(array('limit'=>10,'offset'=>15)); 
    in your system to keep the data separate you could do something like:

    PHP Code:
    $blog $dorm->getBlog(2)
    $comments $dorm->getComments($blog,array('limit'=>10,'offset'=>15)); 
    Where you pass in the parent object then resolve the comments association with blog.

    Another thing that I find very useful is a way to count items for pagination.

    PHP Code:
    $total $dorm->getTwitCount(); 
    Quote Originally Posted by b1ind
    1. Aggregation: For example, it is often useful to show the number of comments to an article in a listing of summarized articles. A similar example would be to get a listing of tags for that article. Both could be accomplished in the same query that fetches the articles. Some options for accomplishing this are:
    1. Efficiently running a "SELECT COUNT(*) FROM comments WHERE article_id=?"
    2. Requiring the initialization of a record set of comments on which a size() method is called. This is the worst given the obvious performance overhead.
    3. Ideally, inserting a "COUNT(Comment.id)", "LEFT JOIN comments Comment ON (Article.id=Comment.article_id)" and "GROUP BY Article.id".
    2. Pre-fetching: It may be useful in the same blog page to fetch the user information for each blog post in the same query that fetches the articles and any desired aggregates. Most ORMs would require a separate query for each blog post (by a distinct user) to fetch the user. It is nicer to have that fetched with the article itself.
    This is something that I have always felt is missing from many current ORM solutions. They manage single entities well, but the ability the manage collections of related data is not easily done or achieved in the most efficient way.

    I hate needing to do this:

    $user = new User(2);
    $user->twits;

    I much rather bring it all in together:

    $user = User::find(array('include'=>'twits','id'=>2));

    Where user now has a property twits which is a array of related twit entities. Rather then needing to run separate queries.

  11. #11
    SitePoint Addict
    Join Date
    Aug 2007
    Posts
    365
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I dont see what is wrong with SQL anyway.. Why is everyone always trying to hide SQL inside a number of classes where 9/10 cases it is more simpler and easier to understand ( and probably faster) to use SQL.

    Most database engines use SQL for queries, and SQL is defined as a standard and mosly common between (except some certain features), so why not make use of the tools you have.

    Its just as easy to map data queried with SQL into objects, as it is using a custom query builder. And this mapping is what the ORM concept is based upon.. ??

  12. #12
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    One last thing to think about is the ability to change data between the domain and database. Where this would come in handy is a password. A password on the domain may need to represent the actual password. However, in the database that password may be encrypted. So there needs to be some way to change it. If $user->password is accessed the model the last thing I want is the encrypted string even though that is the true data in the database.

  13. #13
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    How many queries would this run with the previous schema?

    PHP Code:
    $dorm->getUser('id'); 


    That seems very flawed to me. Sometimes only one entity is needed. So if something has a 20 different relationships they would all be resolved? I think its better to able to control that for the sake of efficiency. The last thing I want is to run a query for everything when all I need is the single user. That could potentially be a great deal of queries. Especially if more then one root entity and its relationships are brought at the same time.
    $dorm->getUser('id'); only makes 1 SQL query and doesn't load any relationship. Foreign objects and arrays are assigned a placeholder that will make the appropriate SQL query when they are called. So the answer is no, the "20 different relationships they would all be resolved" wouldn't be resolved.

    You seem to believe that a Query class is crucial for an ORM and I will work on that feature, although I don't believe it is that much important.

    One last thing to think about is the ability to change data between the domain and database. Where this would come in handy is a password. A password on the domain may need to represent the actual password. However, in the database that password may be encrypted. So there needs to be some way to change it. If $user->password is accessed the model the last thing I want is the encrypted string even though that is the true data in the database.
    That wouldn't be complicated at all to do with dORM. Here is one way to do it:

    PHP Code:
    class User {
     private 
    $password;
     public function 
    setPassword($password) {
      
    $this->password $password;
     }
     public function 
    getPassword() {
      return 
    md5($this->password);
     }

    If we have the field password in the table, the map file would include this line:
    Code:
    <password column="password" getter="getPassword" />
    last thing I want is the encrypted string even though that is the true data in the database
    The most common way of storing password is with md5 and I don't really see the use of encrypting password when md5 does a wonderful job. The point of hashing a password is to make it impossible to decrypt and I don't see why someone would need the clear password in its model... It's a very bad idea in terms of security.

    I dont see what is wrong with SQL anyway.. Why is everyone always trying to hide SQL inside a number of classes where 9/10 cases it is more simpler and easier to understand ( and probably faster) to use SQL.

    Most database engines use SQL for queries, and SQL is defined as a standard and mosly common between (except some certain features), so why not make use of the tools you have.

    Its just as easy to map data queried with SQL into objects, as it is using a custom query builder. And this mapping is what the ORM concept is based upon.. ??
    When you use an ORM, you gain a lot in productivity and you make sure that you never load the same object twice. It's a layer of abstraction on top of SQL that can be very useful in terms of productivity. dORM is extremely simple to use.

  14. #14
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    I dont see what is wrong with SQL anyway.. Why is everyone always trying to hide SQL inside a number of classes where 9/10 cases it is more simpler and easier to understand ( and probably faster) to use SQL.
    The purpose isn't to hide SQL. The purpose is to centralize model information in an effort to eliminate repetition and make modifications as simple as changing one file.

  15. #15
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    $dorm->getUser('id'); only makes 1 SQL query and doesn't load any relationship. Foreign objects and arrays are assigned a placeholder that will make the appropriate SQL query when they are called. So the answer is no, the "20 different relationships they would all be resolved" wouldn't be resolved.
    So if I wanted to bring in the user twits there wouldn't be a way to run that in a single query?

    Instead A second query would be run?

    PHP Code:
    $user $dorm->getUser('id'); // 1st query
    $user->twits// 2nd query 
    If that is true how would one count the number of twits for each user without running a number of queries equivalent to the number of users+1.

    • bring in all users
    • need reference twits on each user - results in another query (seems flawed)


    class User {

    private $password;

    public function setPassword($password) {

    $this->password = $password;

    }

    public function getPassword() {

    return md5($this->password);

    }

    }
    I meant change the data on the database side. A common one is to change a the return value of time() via FROM_UNIXTIME(?) in the insert or update query.

  16. #16
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Interesting post b1ind. I do believe dORM covers most of the features you mentioned.

    Quote Originally Posted by b1ind View Post
    Perhaps a good way to articulate my approach to result finding is query refinement.


    I think that an abstract query object is a very important feature that affords immense flexibility to your code without creating noticeable overhead. To be able to achieve query refinement, you need an abstract query object.

    This relates heavily to lazy loading. If you were to reference $user->twits, a lazy record set would be created although no queries would be executed. The user could then refine the query by where clauses, limits, etc, all the while customizing the query. The query would only be executed once an attempted reference is made to its record set element(s). This could happen through offsetExists(), getIterator(), reset(), etc.
    dORM already implements lazy loading on 1-to-1, 1-to-many relationships. However, it does not allow to refine the queries. That is a very good point and I'll implement it ASAP. This is #1 in my priority list.

    Quote Originally Posted by oddz View Post
    I hate needing to do this:

    $user = new User(2);
    $user->twits;

    I much rather bring it all in together:

    $user = User::find(array('include'=>'twits','id'=>2));

    Where user now has a property twits which is a array of related twit entities. Rather then needing to run separate queries.
    I don't see why you like doing this "$user = User::find(array('include'=>'twits','id'=>2));" rather than "$user = $dorm->getUser(2);". The former is longer and whenever you need more than "twits" you have to go modify the parameters. I don't see any benefit to your method apart the fact it runs 1 query instead of two. Probably a gain of 0.0001 second. I believe the productivity gain is largely superior to the performance drawback.

    However, I do agree it would be useful to allow custom queries / row count on many-to-many relationships such as you mentioned:

    PHP Code:
    $comments $blog->getComments(array('limit'=>10,'offset'=>15)); 
    $total $dorm->getTwitCount(); 
    However, I am not sure what would be the best interface for allowing this, considering the fact that I'd like to keep dORM unobtrusive.

    What do you think of the following:
    PHP Code:
    $dorm->load($user->twits)->where('...')->limit('...')->offset('...');
    $row_count $dorm->count($user->twits

  17. #17
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Quote Originally Posted by sysk
    I don't see why you like doing this "$user = User::find(array('include'=>'twits','id'=>2));" rather than "$user = $dorm->getUser(2);". The former is longer and whenever you need more than "twits" you have to go modify the parameters. I don't see any benefit to your method apart the fact it runs 1 query instead of two. Probably a gain of 0.0001 second. I believe the productivity gain is largely superior to the performance drawback.
    Mainly the ability to use aggregates. Otherwise you would need to run separate queries for each one. So if there were a 1000 users and we wanted to count each users comments that would mean 1000 queries + 1 - no? However, being able to include the model makes it possible to run only one query and add group clause for the user id. Then add a dynamic field to count the number of comments.

    PHP Code:
    $users $dorm->getUserCollection();

    foreach(
    $users as $user) echo count($user->twits); 
    Given hundreds or thousands of users that becomes very flawed and inefficient.

  18. #18
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    Mainly the ability to use aggregates. Otherwise you would need to run separate queries for each one. So if there were a 1000 users and we wanted to count each users comments that would mean 1000 queries + 1 - no? However, being able to include the model makes it possible to run only one query and add group clause for the user id. Then add a dynamic field to count the number of comments.
    You're getting something wrong. Even though there were 1000 users, it would be 1 query with dORM. Many-to-many relationships are also loaded with 1 query.

    If you would do the following, there would only be 2 queries (one for the user, and one for the twits) although there might be thousands of twits:
    PHP Code:
    $user $dorm->getUser(1);
    foreach(
    $user->twits as $twit) {
       echo 
    $twit->message;

    As I said earlier, I do agree that being able to refine the many-to-many queries so that we can limit the amount of loaded twits would be a nice feature to implement. That would mean that I would also have to implement a row count method, since count($user->twits) would only return the amount of loaded twits. I will work on that feature !

    Cheers

  19. #19
    PHP/Rails Developer Czaries's Avatar
    Join Date
    May 2004
    Location
    Central USA
    Posts
    806
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I don't want to hijack this threat or steal the spotlight away from dORM, but I am also in the process of creating a php ORM layer called phpDataMapper that handles some of the issues brought up here, and does some things differently than dORM. I thought it would be nice to include it in the discussion so I can get some feedback for improvement as well.

    Things it does differently than dORM:
    - Table fields defined directly in Mapper with PHP (no XML)
    - Uses array-style syntax in finders
    PHP Code:
    $users $userMapper->all(array('id >' => 2'status' => array(123))); 
    - Has query builder for SQL-agnostic queries
    PHP Code:
    $users $userMapper->select()->where(array('name' => 'Bob'))->orWhere(array('name' => 'Tom')); 
    - Allows unlimited query modifications and filters before query is run (using query builder)
    PHP Code:
    $users $userMapper->all(array('id >' => 2));
    $users->orWhere(array('name' => 'Bob'));
    $users->limit(20); 
    Things is does the same as dORM:
    - Supports 1-to-1 and 1-to-many relationships (even same syntax)
    - Lazy loads relations on call
    - Does not execute query until last possible moment

    Things lacking that will be added soon
    - Automatic table migrations based on field definitions
    - Native pagination capabilities
    - Elminination of N+1 queries problem Ruby DataMapper style (the project's inspiration)
    - Lazy loading of specific columns, also from Ruby DataMapper (also with no N+1 penalty)

  20. #20
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    Not trying to steal your spotlight.
    Quote Originally Posted by Czaries View Post
    I don't want to hijack this threat or steal the spotlight away from dORM
    Come on guys Anyways I am confident my library has the best approach to ORM, although it might be lacking some features at the moment

  21. #21
    PHP/Rails Developer Czaries's Avatar
    Join Date
    May 2004
    Location
    Central USA
    Posts
    806
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sysk View Post
    Come on guys Anyways I am confident my library has the best approach to ORM, although it might be lacking some features at the moment
    Haha. But seriously, I can't get over your requirement of XML config files. That's so .NET-like. It's just not the PHP way.

    Doesn't this just seem so much better and easier? Fields defined directly in the model code? It sure does to me:

    PHP Code:
    // Post
    class PostMapper extends phpDataMapper_Model {
        
    // Specify the database table
        
    protected $table "blog_posts";
     
        
    // Define your fields
        
    protected $fields = array(
            
    'id' => array('type' => 'int''primary' => true),
            
    'title' => array('type' => 'string''required' => true),
            
    'body' => array('type' => 'text''required' => true),
            
    'status' => array('type' => 'string''default' => 'draft'),
            
    'date_created' => array('type' => 'datetime')
        );


  22. #22
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    PHP Code:
    $user $dorm->getUser(1);
    foreach(
    $user->twits as $twit) {
       echo 
    $twit->message;

    I understand this. I'm saying count the number of comments for every single user. One user is one user - 2 queries. However, if that is the case then given a hundred users 100+1 queries would need to run in order to count the number of comments for every user. A problem avoided by having a option to include the comments model with the users collection.

    This is how simple the syntax is in my implementation:

    PHP Code:
    $users User::find(
      array(
        
    'include'=>'comments'
        
    ,'group'=>'id'
        
    ,'dynamic'=>array(   
             
    'total_comments'=>'COALESCE(COUNT(Comment.id),0)'
        
    )
      )
      ,array(
        
    'require'=>false
      
    )
    );

    foreach(
    $users as $user) echo '<p>User: ',$user->name,' has ',$user->total_comments,' comments.</p>'
    So what I'm asking is whether that can be achieved without a numerous number of queries. The user and the amount of comments they each have. Not for one but for all users.

  23. #23
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,139
    Mentioned
    16 Post(s)
    Tagged
    3 Thread(s)
    Haha. But seriously, I can't get over your requirement of XML config files. That's so .NET-like. It's just not the PHP way.

    Doesn't this just seem so much better and easier? Fields defined directly in the model code? It sure does to me:
    Quite honestly I agree. I don't see much a benefit in needing to define a XML file alongside and model class. Either or.

    PHP Code:
    $user $dorm->getUser(4); // returns generic container
    $user->status 0;
    $dorm->saveUser($user); // makes sure entity is compatible with User XML configuration schema 

  24. #24
    SitePoint Addict
    Join Date
    Aug 2007
    Posts
    365
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    The purpose isn't to hide SQL. The purpose is to centralize model information in an effort to eliminate repetition and make modifications as simple as changing one file.
    I understand this, but there is no reason why you cant store the SQL inside the model. This also eliminates the repition, and keep modifications easy. If you are going to store mapping between Database field and PHP varible in the model, why not the query string itself?

  25. #25
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Czaries View Post
    Haha. But seriously, I can't get over your requirement of XML config files. That's so .NET-like. It's just not the PHP way.

    Doesn't this just seem so much better and easier? Fields defined directly in the model code? It sure does to me:

    PHP Code:
    // Post
    class PostMapper extends phpDataMapper_Model {
        
    // Specify the database table
        
    protected $table "blog_posts";
     
        
    // Define your fields
        
    protected $fields = array(
            
    'id' => array('type' => 'int''primary' => true),
            
    'title' => array('type' => 'string''required' => true),
            
    'body' => array('type' => 'text''required' => true),
            
    'status' => array('type' => 'string''default' => 'draft'),
            
    'date_created' => array('type' => 'datetime')
        );

    The XML file has many benefits... the main being that it decouples object persistence from domain model. Your domain model is in no way tied to dORM. Decoupling is a core concept in object oriented programming. http://en.wikipedia.org/wiki/Coupling_(computer_science). It also opens the door to automatic mapping generation or GUI tools for generating the mapping, which would be very hard to do with your system.


Tags for this Thread

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
  •