SitePoint Sponsor

User Tag List

Page 2 of 3 FirstFirst 123 LastLast
Results 26 to 50 of 52
  1. #26
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by oddz View Post
    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.
    As I told you dORM doesn't currently have a COUNT() method. However, it could easily be implemented. Also, what you fail to understand is the following:

    PHP Code:
    $users $dorm->getUserCollection(); 
    is ONE SQL query even though there is thousands of users. Now if you want to load every comments for every user, you are right, it will be one query for every user. I do plan on implementing a method that will tell which properties to load in advance, so we can minimize the amount of queries.

    The problem with that approach is the following situation:

    PHP Code:
    BookShelf {
        public 
    $books = array();
    }

    Book {
       public 
    $authors = array();
    }

    Author {
       public 
    $name;
    }

    // how can I tell the ORM that I want to load all books from bookshelf, but also authors of book

    // this is how your system would look like
    $bookshelf BookShelf::find(
      array(
        
    'include'=>'books'
        
    ,'group'=>'id'
      
    )
    );

    // Now how do I tell the ORM that I also wanted to use "authors" in books, so that the following works?
    // Do we need another SQL query ?

    echo 'First author name: ' $bookshelf->books[0]->authors[0]->name
    I wonder what would be a nice interface for loading all relationships in a single queries including deep relationships like in the example above.

    What about the following ?
    PHP Code:
    $dorm->getBookShelf('id', array('books' => array('authors'))); 

  2. #27
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    Well I use a tree. That is really the only way. Every branch represents a model and children represent either a has one,has many, many to many or belongs to association. Which makes it possible but not advised to include an infinite number of relationships.

    PHP Code:
    Bookshelf::find(
        array(
            
    'include'=>'books'
        
    )
        ,array(
            
    'include'=>'authors'
        
    )
    ); 
    The system then builds a tree based on hierarchical relationship:

    • BookShelf
      • book
        • author (user)


    Then a separate object tracks what fields pertain to which node and their alias. Which makes it possible to collect the raw one dimensional SQL result back into a meaningful hierarchical structure to an infinite depth.

    PHP Code:
    echo $bookshelf[1]->book[2]->author[0]->name
    Everything is based on a tree including updates, deletes and inserts. This makes it possible to manage collections of related data as one entity.

  3. #28
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    How does your system know that "authors" is a property of books ? BookShelf could have a property with the same name... although it is very unlikely in this case.

    Wouldn't that be more accurate ?

    PHP Code:
    Bookshelf::find(
        array(
            
    'include'=> array(
                
    'books' => array('authors')
            )
        )
    ); 

  4. #29
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    PHP Code:
    $bookshelf BookShelf::find(

      array(
        
    'include'=>'books'
        
    ,'group'=>'id'
      
    )
      ,array(
        
    'include'=>'authors'
      
    )

    ); 
    The next array represents the "book". Books belong to and has many authors so authors may be included.

  5. #30
    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 sysk
    How does your system know that "authors" is a property of books
    That information is stored inside each model. The look up model is then included in the query, but cloaked/hidden when the result is collected. So that you don't need to do this: $books[0]->authorizations[0]->author->name, but rather: $books[0]->author[0]->name; The look up model essentially doesn't exist.

    PHP Code:
    class Book extends ActiveRecord {

        public static 
    $belongsToAndHasMany = array('authors','authorizations');

    }

    class 
    Author extends ActiveRecord {

        public static 
    $belongsToAndHasMany = array('books','authorizations');

    }

    class 
    Authorization extends ActiveRecord {

        public static 
    $foreignKeys = array(
            
    'book_id'=>array('Book','id')
            ,
    'author_id'=>array('Author','id')
        );


    If a model can be related to another model then either can be singular or plural property of the other.

    Quote Originally Posted by sysk
    BookShelf could have a property with the same name... although it is very unlikely in this case.
    If the field name was author then all would be well. However, if the field name was authors then there would be a problem. A many to many relationship resolves to a plural property name. Only has one and belongs to relationships resolve to a singular name. Singular relationships are represented as the object and plural relationships are represented as a collection of objects. If there was a property names authors that represented a actual field on the books table then it would be overrided by the authors collection if authors where included. At least currently.

  6. #31
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    @sysk: does dORM allow mapping to getters/setters instead of direct properties? I like to keep my properties protected/private.

    Also, I noticed you mentioned all relationships are lazy loaded. Do you allow the option to eager load some relationships? Can you specify batch lazy loads? How are you lazy loading? Do you use a proxy, or runtime subclassing to stay type safe?

  7. #32
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Uh... your system "guesses" that the authors you are talking about in the array is the authors property of Book. This is a kinda twisted and that would be horrible to manage when you have multiple properties that are deeply nested ! Anyways, good luck with your ORM !

  8. #33
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    @sysk: does dORM allow mapping to getters/setters instead of direct properties? I like to keep my properties protected/private.

    Also, I noticed you mentioned all relationships are lazy loaded. Do you allow the option to eager load some relationships? Can you specify batch lazy loads? How are you lazy loading? Do you use a proxy, or runtime subclassing to stay type safe?
    dORM does allow mapping using getters/setters:

    Code:
    <propertyName field="fieldName" setter="setterMethod" getter="getterMethod" />
    As mentioned earlier in the thread, every property that is a relationship in the database (1-to-many, many-to-many) is lazy loaded. When dORM loads an object, it assigns its properties that maps to relationships a Dorm_Placeholder object which holds the information on how to load the property. Using __get() __set() and __call(), whenever one of those is called by the user, the property is dynamically replaced by the actual object/array that the property is supposed to contain. I guess that would be called a proxy. Do you think runtime sub classing would be a better option ? It would be an interesting thing to experiment since I try to make dORM as less obtrusive as possible.

    I plan on making a method that would tell dORM which properties to load in advance so we can avoid the lazy loading process and minimize the amount of SQL queries.

    i.e.:
    PHP Code:
    $dorm->getUser('id', array('array of properties to load')); 
    That's the most interesting comment so far!

  9. #34
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sysk View Post
    dORM does allow mapping using getters/setters
    Cool.

    Quote Originally Posted by sysk View Post
    As mentioned earlier in the thread, every property that is a relationship in the database (1-to-many, many-to-many) is lazy loaded. When dORM loads an object, it assigns its properties that maps to relationships a Dorm_Placeholder object which holds the information on how to load the property. Using __get() __set() and __call(), whenever one of those is called by the user, the property is dynamically replaced by the actual object/array that the property is supposed to contain. I guess that would be called a proxy.
    Yeah, I'd call that a proxy.

    Quote Originally Posted by sysk View Post
    Do you think runtime sub classing would be a better option ? It would be an interesting thing to experiment since I try to make dORM as less obtrusive as possible.
    I think so... though others with more experience than I do may think otherwise.

    Quote Originally Posted by sysk View Post
    I plan on making a method that would tell dORM which properties to load in advance so we can avoid the lazy loading process and minimize the amount of SQL queries.
    I would prefer setting those options beforehand (probably in your XML file, though I favor keeping the mappings within hand-coded mapper classes myself). Though it may optimize queries to specify what object graph you pull initially, it's just too darn verbose to set it all up when you want data. I'd rather set up defaults that could be modified at runtime if necessary.

    The options you should allow are eager load, lazy load, and batch lazy load. If you don't know what batch lazy load is, let's say you are retrieving the latest 10 articles from the database. You are then going to loop through each article and display some information about it, including it's author. If you are lazy loading normally, you'd have to do a query for every associated user. If you batch lazy load, you'd do one query that loaded all users for all 10 articles. First access on any user object would execute that query.

  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)
    Quote Originally Posted by sysk
    Uh... your system "guesses" that the authors you are talking about in the array is the authors property of Book. This is a kinda twisted and that would be horrible to manage when you have multiple properties that are deeply nested ! Anyways, good luck with your ORM !
    Well no its not a guess.

    • 1.) bookshelf
      • 2.) book
        • 3.) author



    array(
    array( // represents bookshelf
    )
    ,array( // represents book
    )
    ,array( // represents author
    )
    )

    You need to explicitly use the keyword include similar to Luke Bakers system. The only difference here is that its recursive. That is how its possible to avoid the need to lazy load and build the appropriate join sequence. Which makes it possible to use aggregates and group data in one query.

    PHP Code:
    $bookshelf BookShelf::find(

      array( 
    // this array pertains to bookshelf
        
    'include'=>'books'
      
    )
      ,array( 
    // this array pertains to books
        
    'include'=>'authors'
       
    ,'group'=>'id'
        
    ,'dynamic'=>array(
            
    'author_names'=>'GROUP_CONCAT(Author.name)'
            
    ,'author_total'=>'COUNT(Author.id)'
        
    )
      )
      ,array( 
    // this array pertains to authors
        
    'require'=>false // left join so that all books are returned regardless if any authors are defined for that book
      
    )

    ); 

  11. #36
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    Cool.


    Yeah, I'd call that a proxy.


    I think so... though others with more experience than I do may think otherwise.


    I would prefer setting those options beforehand (probably in your XML file, though I favor keeping the mappings within hand-coded mapper classes myself). Though it may optimize queries to specify what object graph you pull initially, it's just too darn verbose to set it all up when you want data. I'd rather set up defaults that could be modified at runtime if necessary.

    The options you should allow are eager load, lazy load, and batch lazy load. If you don't know what batch lazy load is, let's say you are retrieving the latest 10 articles from the database. You are then going to loop through each article and display some information about it, including it's author. If you are lazy loading normally, you'd have to do a query for every associated user. If you batch lazy load, you'd do one query that loaded all users for all 10 articles. First access on any user object would execute that query.
    I will explore the idea of runtime sub classing objects, although it won't be necessary for arrays (impossible actually) since the current place holder already implements SPL interfaces that make it behave like an array.

    I love the idea of eager loading and batch lazy loading, although I'll have to think on how to implement it. I could simply add an attribute to all properties in the xml file that would tell whether this property should be loaded by default. I will also consider replacing the XML config by a PHP class since people seem to like that option better.

    I'll keep you all updated when I've done some progress!

    This is my current priority list:

    1. Implement batch lazy loading
    2. Implement eager loading
    3. Implement a way to refine many-to-many queries (where, limit)
    4. Write a Query class

  12. #37
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I have brainstormed over eager loading and query refinement and this is what I have come with. I think it's pretty much complete.

    PS: none of the following is currently implemented.

    PHP Code:
    <?php
    /////////////////////////////////////
    // LAZY LOADING
    /////////////////////////////////////

    // get and getCollection return a placeholder that holds information on how to load the object
    $book $dorm->getBook('book_id');
    $books $dorm->getBookCollection();

    // Whenever a user property is accessed (__get(), __set() or __call()), dynamically load the object from the db
    // and replace the placeholder by the loaded object. i.e:
    echo $book->title;
    foreach (
    $books as $book) {echo $book->title;}

    // skip lazy loading: execute the query now
    $book->commit(); // book should now be an instance of Book instead of Dorm_Placeholder


    /////////////////////////////////////
    // QUERY REFINING
    /////////////////////////////////////

    // Note: it is also possible to refine queries on lazy loaded properties.
    // Note: in most cases, it is possible to replace an id by the object itself or an associative array
    // Note: unless specified, the order of methods has no importance
    // get the book where id = book_id
    $book $dorm->getBook()->id('book_id');
    $book $dorm->getBook('book_id'); // equivalent
    // get book where title = 'The Bible'.
    // Notice: if book titles aren't unique, you should use getBookCollection since it might return many rows.
    $book $dorm->getBook()->where("book_title = 'The Bible'");
    // you can also use the following syntax to prevent SQL injection
    $book $dorm->getBook()->where('book_title = ?''The Bible');
    // if you can't remember what the column name is, you can use the property name instead
    $book $dorm->getBook()->whereProperty('title = ?''The Bible');
    // You can add the "and" and "or" prefix to where and whereProperty
    $book $dorm->getBook()
        ->
    where('book_title = ?''The Bible')
        ->
    andWhere('book_id = ?''id');
    $book $dorm->getBook()->where('book_title = ? AND book_id = ?', array('The Bible''book_id')); // equivalent of above

    $book $dorm->getBook()->orWhere('book_title = ?''The Bible');
    $book $dorm->getBook()->andWhereProperty('title = ?''The Bible');

    // if you know your query will return more than one row, replace getBook() by getBookCollection() (the same methods are available)
    $books $dorm->getBookCollection(); // the placeholder should implement an SPL iterator to behave like an array
    // limit the resultset
    $books $dorm->getBookCollection()->limit(10100);
    $books $dorm->getBookCollection()->limit(100)->offset(10); // equivalent of the above
    // order the result (the order in which you call orderBy tells the priority)
    // desc() is optional. if not specified, the order will be ascendant
    $books $dorm->getBookCollection()->orderBy('book_title')->desc();
    $books $dorm->getBookCollection()->orderBy('book_title')->desc()->orderBy('book_id');
    $books $dorm->getBookCollection()->orderByProperty('title'); // if you can't remember the column name
    // get all publishers of a book. useful for relationships
    $publishers $dorm->getPublisherCollection()->parentBook('book_id');
    // get the author of a book
    $author $dorm->getAuthor()->parentBook('book_id');

    // custom SQL query. Notice: this will override any previous query refinement
    $books $dorm->getBookCollection()->sql('SELECT * FROM books');

    // Once an object is accessed/comitted, it is no longer possible to refine the query on it.
    $book->title// or
    $book->commit();
    // this is how to do if you want to "reload" an object
    $dorm->load($book)->where('...')->otherMethod('...')->commit(); //etc.

    /////////////////////////////////////
    // EAGER LOADING
    /////////////////////////////////////

    /*
     * Eager  load consists of loading object/array properties in the same query
     * as the parent object. Prevents lazy loading which would mean more queries.
     * You can specify which property to eager load by default in the map file
     * <property loadType="eager" />. This is useful when an object's property is
     * accessed very often.
     */

    // eager load the publishers property of Book. this is the property name, not the table name
    $book $dorm->getBook('id')->join('publishers');
    // eager load publishers and their address property
    $book $dorm->getBook('id')->join('publishers''publishers.address');
    $book $dorm->getBook('id')->join('publishers')->join('publishers.address'); // equivalent as above
    // eager load ALL properties (not recursive)
    $book $dorm->getBook('id')->join('*');
    // disable eager load on a given property (useful if a property is set to default eager load in the map file)
    $book $dorm->getBook('id')->unjoin('publishers');
    // disable eager load on all properties
    $book $dorm->getBook('id')->unjoin('*');


    /////////////////////////////////////
    // CONCLUSION
    /////////////////////////////////////

    // I have pretty much covered lazy loading, query refinement and eager loading
    // Here is how a complex query could look like
    $books $dorm->getBookCollection()
        ->
    join('publishers')
        ->
    join('publishers.address')
        ->
    whereProperty('title = ? OR publishers.address.city = ?', array('The Bible''Vatican'))
        ->
    orderByProperty('title')->asc()
        ->
    offset(2)
        ->
    limit(4);

  13. #38
    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
    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.
    LOL. Thank you for defining "Decoupling" for me. That was SO helpful .

    I don't see why you would want to decouple your table structure definition from the model that's working with it. My setup is different than yours. phpDataMapper requires one mapper per table, so the mapper you are working with is directly tied to a specific table. So in that setup, there is no reason to decouple the table structure definition from the actual mapper working with that table. For my project, it's much better to just avoid additional file reads and XML parsing, because it's just not necessary.

    phpDataMapper takes queues from the Ruby DataMapper project (that's the whole goal). Ruby DataMapper has the field definitions directly in the mapper working with that table, so that's the way it is in phpDataMapper too. Just because something is setup differently than dORM, doesn't mean it's wrong.

  14. #39
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    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?
    When I started my last project, I tried this approach. I really tried the pure KISS. But as my requirements changed and queries changed, it was a real pain. Furthermore, as the site grew, the number of different, but specific queries needed grew and so every time I made a change to the model (SQL table), I had to maintain X highly specific queries. This just resulted in too many bugs so I abandoned the approach.

    Personally, I am not a big fan of the array syntax that you guys prefer, but that's just a matter of taste. You raised an interesting requirement though. Prefetching the Authors of Books in a Bookshelf. I think the syntax that I would prefer would be this:

    PHP Code:
    // Because author is a 'child' of book, the prefetching of book would also be implicit
    // For fun, we'll make it a tad challenging and find all bookshelves that contain a
    // book by Dickens
    $shelves findAll('Bookshelf')
        ->
    prefetch('book.author')
        ->
    aggregate('Author.num_books')
        ->
    where('Author.last_name=?''Dickens'); 
    What do you think of the usability of this syntax?

  15. #40
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sysk View Post
    I have brainstormed over eager loading and query refinement and this is what I have come with. I think it's pretty much complete.
    Why lazy load books? If I'm requesting a book, I'm going to be using it. Lazy loading should be used when you are not sure if an associated object is going to be used. Default should generally be eager loading, but you're probably going to want to strategically place lazy loads just so you don't grab the whole database at once. As I said before, I'd really prefer specifying these with the mapping specs and not when I'm retrieving objects.

  16. #41
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Czaries View Post
    LOL. Thank you for defining "Decoupling" for me. That was SO helpful .

    I don't see why you would want to decouple your table structure definition from the model that's working with it.
    If you fail to understand this, don't roll your eyes when I define decoupling for you

    Quote Originally Posted by allspiritseve View Post
    Why lazy load books? If I'm requesting a book, I'm going to be using it. Lazy loading should be used when you are not sure if an associated object is going to be used. Default should generally be eager loading, but you're probably going to want to strategically place lazy loads just so you don't grab the whole database at once. As I said before, I'd really prefer specifying these with the mapping specs and not when I'm retrieving objects.
    The reason we have to lazy load is that you don't have to explicitly call "->commit()" every time you load an object. I'd like to keep the syntax as simple as possible and make the query refinement optional.

    PHP Code:
    // this normally returns a Query object so that we can add more filters like
    $book $dorm->getBookCollection(); 
    // i.e.
    $book $dorm->getBookCollection()->limit(10);
    // Now we would have to commit in order to load $book...
    $book->commit();

    // By lazy loading, we can skip this step
    echo $book->title
    Quote Originally Posted by b1ind View Post
    When I started my last project, I tried this approach. I really tried the pure KISS. But as my requirements changed and queries changed, it was a real pain. Furthermore, as the site grew, the number of different, but specific queries needed grew and so every time I made a change to the model (SQL table), I had to maintain X highly specific queries. This just resulted in too many bugs so I abandoned the approach.

    Personally, I am not a big fan of the array syntax that you guys prefer, but that's just a matter of taste. You raised an interesting requirement though. Prefetching the Authors of Books in a Bookshelf. I think the syntax that I would prefer would be this:

    PHP Code:
    // Because author is a 'child' of book, the prefetching of book would also be implicit
    // For fun, we'll make it a tad challenging and find all bookshelves that contain a
    // book by Dickens
    $shelves findAll('Bookshelf')
        ->
    prefetch('book.author')
        ->
    aggregate('Author.num_books')
        ->
    where('Author.last_name=?''Dickens'); 
    What do you think of the usability of this syntax?
    I like this idea: Because author is a 'child' of book, the prefetching of book would also be implicit. This is totally true and I will definitely implement it.

    I also like the "prefetch" word instead of "join". However, how would you name "unjoin"... "unprefetch" ?

    Also, what is "->aggregate('Author.num_books')" supposed to do? Return the count of author rows for the book? I thought I would make this implicit somehow, or in a distinct method.

  17. #42
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Also, what is "->aggregate('Author.num_books')" supposed to do? Return the count of author rows for the book? I thought I would make this implicit somehow, or in a distinct method.
    Sorry, that is a bit of noise that is pretty specific to the way I do things. aggregate('user.num_articles') is just a fun test to see if the syntax for prefetching would be limiting.

    What ->aggregate('user.num_articles') does is simply call
    PHP Code:
    User::aggregateNumArticles($query'__user__num_articles'); 
    where $query is the Query object that will be modified and the second parameter is the alias of the column in the query that is namespaced to user.

    I chose to make defining aggregate something that can either be done manually by modifying the query, or by proxy, through a named, predefined method of the model definition.

  18. #43
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sysk View Post
    The reason we have to lazy load is that you don't have to explicitly call "->commit()" every time you load an object. I'd like to keep the syntax as simple as possible and make the query refinement optional.
    I don't think you understand what lazy loading is used for-- it's not a gimmicky thing to save a method call, it's a performance optimization technique that becomes necessary as your object graph gets large enough to noticeably slow down execution. Basically: if your domain is large enough to require an ORM, proper lazy/eager loading is essential.

  19. #44
    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 b1ind
    hen I started my last project, I tried this approach. I really tried the pure KISS. But as my requirements changed and queries changed, it was a real pain.
    Tell me about it. I also started by placing the SQL directly in the model, but as I added features it was imperative that code be refactored.

    Quote Originally Posted by b1ind
    Personally, I am not a big fan of the array syntax that you guys prefer, but that's just a matter of taste. You raised an interesting requirement though. Prefetching the Authors of Books in a Bookshelf. I think the syntax that I would prefer would be this:
    Array syntax is so much simpler and easy to use in my opinion.

    PHP Code:
    $shelves findAll('Bookshelf')
        ->
    prefetch('book.author')
        ->
    aggregate('Author.num_books')
        ->
    where('Author.last_name=?''Dickens'); 
    Would it be possible to join the same tables together in this approach?

    Quote Originally Posted by Czaries
    I don't see why you would want to decouple your table structure definition from the model that's working with it.
    Honestly, decoupling the table structure from the model seems like over complication. I don't see how that supports the idea of a simple interface. Its much simpler, straightforward and time saving to use the field names as the default getter and setters.

    I much rather use the syntax here:

    PHP Code:
    $twit->created
    rather then the syntax below:

    PHP Code:
    $twit->getCreated(); 
    Granted I do believe there should be way to avoid the magical methods also though:

    PHP Code:
    $twit->getProperty('created');
    $twit->setProperty('created',time()); 
    To be quite honest this is a waste of time and space:

    PHP 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;
    }

    class 
    Twit {
        Public 
    $time;
        Public 
    $message;

    When developing a library you need to make decisions based on the fact that programmers are lazy and hate repetition. Needing to define the above and a XML configuration doesn't support that notion. I see what your attempting to accomplish but in reality needing to define those classes is extra fluff considering the configuration information is inside the XML file.

    PHP Code:
    $user $dorm->getUser(8); 
    However, have the dynamic method get[User] always return a generic data container with the appropriate configuration information which it is linked. You could even access that information though a static method of the Dorm class or configure is directly on generic data container. This would eliminate the need for model class definitions my the developer. Instead ALL that information would be contained inside the XML file. The a class definition COULD be optional perhaps.

  20. #45
    SitePoint Enthusiast
    Join Date
    Feb 2004
    Location
    Montreal
    Posts
    77
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Would it be possible to join the same tables together in this approach?
    That is the major drawback with the (IMHO) easier syntax. Self-joins are possible, but at a cost. To get around the limitation, I make a subclass of the base class and the self-join references that derived class. This is only a partial solution, however, because each additional level of self-reference would require a new class.

    This was a limitation that I was prepared to live with from the beginning because of one of the design choices I made early on. I wanted the views to be able to easily refine record sets without knowing the intimate details of the query aliasing scheme.

    I chose to have the table aliases generated in the queries correspond to the classname of the record. So, for example, in my application (online classes) you pass in a course object to the view and let the view have at it.

    Here is an abbreviated example:
    Code PHP:
    <?php if ($course->tests->reset()->where("Test.state='draft'")): ?>
    <?php foreach ($course->tests as $test): ?>
    <!-- Show a list of test drafts -->
    <?php endforeach; ?>
    <?php else: ?>
    <!-- Show a message saying that there are no drafts -->
    <?php endif; ?>
     
    <?php if ($course->tests->reset()->where("Test.state='finished'")): ?>
    <?php foreach ($course->tests as $test): ?>
    <!-- Show a list of test that need to be graded -->
    <?php endforeach; ?>
    <?php else: ?>
    <!-- Show a message saying that there are no un-graded tests -->
    <?php endif; ?>

    Now suppose that students can create and grade tests, just not their own, the same view code would work, but the controller would have this:

    PHP Code:
    // Change the base query that will be reverted-to upon future calls to ::reset()
    $response->course->tests->where("Test.student_id<>?"$student->id)->lock(); 
    In theory, yes this could be seen as a very poor design choice on my part, but in practice, I have never found myself limited by this choice. It is true that if I really wanted to spend the time, I could parse (or perform simple replaces) the query fragments and change the passed-in column aliases to the internal aliases, but... honestly... meh, I don't have the time, the patience or the budget. Regardless, I am impressed by your algorithms for query building, but my preferences just wouldn't allow me to use the syntax.

  21. #46
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    I don't think you understand what lazy loading is used for-- it's not a gimmicky thing to save a method call, it's a performance optimization technique that becomes necessary as your object graph gets large enough to noticeably slow down execution. Basically: if your domain is large enough to require an ORM, proper lazy/eager loading is essential.
    I perfectly understand what lazy loading is and the only reason I implement lazy loading even when you explicitly load an object is a technical reason: not having to commit all queries with ->commit();

    Quote Originally Posted by oddz View Post
    Tell me about it. I also started by placing the SQL directly in the model, but as I added features it was imperative that code be refactored.



    Array syntax is so much simpler and easy to use in my opinion.

    PHP Code:
    $shelves findAll('Bookshelf')
        ->
    prefetch('book.author')
        ->
    aggregate('Author.num_books')
        ->
    where('Author.last_name=?''Dickens'); 
    Would it be possible to join the same tables together in this approach?



    Honestly, decoupling the table structure from the model seems like over complication. I don't see how that supports the idea of a simple interface. Its much simpler, straightforward and time saving to use the field names as the default getter and setters.

    I much rather use the syntax here:

    PHP Code:
    $twit->created
    rather then the syntax below:

    PHP Code:
    $twit->getCreated(); 
    Granted I do believe there should be way to avoid the magical methods also though:

    PHP Code:
    $twit->getProperty('created');
    $twit->setProperty('created',time()); 
    To be quite honest this is a waste of time and space:

    PHP 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;
    }

    class 
    Twit {
        Public 
    $time;
        Public 
    $message;

    When developing a library you need to make decisions based on the fact that programmers are lazy and hate repetition. Needing to define the above and a XML configuration doesn't support that notion. I see what your attempting to accomplish but in reality needing to define those classes is extra fluff considering the configuration information is inside the XML file.

    PHP Code:
    $user $dorm->getUser(8); 
    However, have the dynamic method get[User] always return a generic data container with the appropriate configuration information which it is linked. You could even access that information though a static method of the Dorm class or configure is directly on generic data container. This would eliminate the need for model class definitions my the developer. Instead ALL that information would be contained inside the XML file. The a class definition COULD be optional perhaps.
    Seriously man, I hope you are drunk. Could you stay in your thread from now on please? I'm writing an ORM to abstract the database, not the PHP models. You seem to think that an ORM should abstract the PHP model which is completely ridiculous. Also, try to understand dORM before making false assumptions. dORM doesn't rely on setters/getters or magic methods, although it can use them. Also, the XML file is necessary because I want allow developers to use their own database schema. If you think that's such a crime, I could simply make a PHP object instead of an XML but in my opinion, XML is much more friendly. Decoupling is something you fail to understand it seems.

  22. #47
    SitePoint Wizard bronze trophy
    Join Date
    Jul 2006
    Location
    Augusta, Georgia, United States
    Posts
    4,194
    Mentioned
    17 Post(s)
    Tagged
    5 Thread(s)
    I understand decoupling. However, when dealing with 50+ tables the last thing I need to do is define a class and XML configuration for each one. That is doubling the amount of work I need to do with no practical benefit the way I see it.

  23. #48
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Having to modify the database schema every time you change the name of a property is a pain in the *** too.

  24. #49
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sysk View Post
    dORM does allow mapping using getters/setters:

    Code:
    <propertyName field="fieldName" setter="setterMethod" getter="getterMethod" />
    I noticed this maps directly to a field in the database. Can you also map an object or collection of objects to a property or getter/setter?

  25. #50
    SitePoint Enthusiast
    Join Date
    Dec 2007
    Posts
    36
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    I noticed this maps directly to a field in the database. Can you also map an object or collection of objects to a property or getter/setter?
    Sure. However, the property has to be an array of the same object type.

    <propertyName pivot="pivotTableName" class="objectClassName" />

    By the way, I will update dORM soon. I am currently implementing query refining and prefetching.


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
  •