SitePoint Sponsor

User Tag List

Results 1 to 6 of 6
  1. #1
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    A different slant on OQL ?

    (First marcus, excuse me for imitating your topic name.)

    Ok, this is basicly a follow up to the "Building an OQL Lexer/Parser" thread a while back. I've spent quite some time working with trying to get a criteria/oql library to got with my orm "Model"(If you're interested in the orm check my signature). I didn't wanna build a Lexer/Parser as it's quite slow to use a parser/lexer in php (atleast from my experiences) - And I didn't want a pure a raw Criteria/QueryObject tedious to do complex queries (joins, etc.) with them.

    My solution is something that could be described as an OQL that only uses Objects and no query-strings. You basicly describe what you want with objects and the database gets it - sort of an advanced QBE - OQL combination. The few testcases I got look like this: (If you don't like the way sitepoint.com messes up the syntax by wrapping it check this link instead: http://pastebin.se/2358)

    PHP Code:
    <?php
        
    class QueryTest extends ReadingTests{
        
            function 
    testSimpleQuery(){
                
    $person $this->manager->query("Person");
                
    $person->name "Fredrik";
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");
            }
            
            function 
    testMultipleFields(){
                
    $person $this->manager->query("Person");
                
    $person->name "Fredrik";
                
    $person->savings 20000;
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");
            }
            
            function 
    testOtherConditions(){
                
    $person $this->manager->query("Person");
                
    $person->name = array('!=',"Fredrik");
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");
            }
            

            function 
    testSimpleJoin(){
                
    $person $this->manager->query("Person");
                
    $person->presentation->text "This is fredriks presentation, have a beer and take a seat ;).";
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");
            }

            function 
    testSplitCondition(){
                
    $person $this->manager->query("Person");
                
    $person->name = array('!=',"Fredrik");
                
    $person->memberships->group->name "Users";
                
                
    $person->split();
                
    $person->name = array("!=","Maddis");
                
    $person->memberships->group->name "BOFH";
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");
            }
            
            function 
    testSelfJoin(){
                
    $person $this->manager->query("Person");
                
    $person->parent->name "Madeleine";
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");
            }
            
            function 
    testComplexJoin(){
                
    $person $this->manager->query("Person");
                
    $person->membership->group->name "BOFH";
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");            
            }
            
            function 
    testVeryComplexJoinGetPersonsThatHaveAParentInBOFHGroup(){
                
    $person $this->manager->query("Person");
                
    $person->parent->membership->group->name "BOFH";
                
                
    $persons $person->fetch();
                
    $this->assertIsA($persons[0], "Person");
                
    $this->assertEqual($persons[0]->name"Lillis");    
            }

            function 
    testMathOperations(){
                
    $person $this->manager->query("Person");
                
    $person->name = array('like'"%i%");
                
                
    $this->assertEqual($person->sum("savings"), 22100);
                
    $this->assertEqual($person->avg("savings"), 7366);
                
    $this->assertEqual($person->min("savings"), 100);
                
    $this->assertEqual($person->max("savings"), 20000);
                
    $this->assertEqual($person->count(), 3);
            }
        
        }
    ?>
    As you can see you "describe" your joins thru something like this: $query->join1->join2->field = condition. The idea is quite simple, I figured out that you can use __get and __set (there are methods named join() and field() which do the exact same thing for the people that doesn't like using __magic). all __get always represents a join as you're trying to get something from another object/table (they have to be joined) and all __set are conditions. You can write extensive joins such as(to find a person who's has the text "hej" in his presentation and a parent that is the member of admin group and):

    PHP Code:
    <?php
    $query 
    $model->query("Person");
    $query->presentation->text = array('like','%hej%');
    $query->parent->membership->group->name "admin");
    $mathces $query->fetch();
    ?>
    As I've only worked about ~4-5h on this there are currently some limitations(for example you can't join the same table two different times, but it does support Self joins (as you can see in the examples I join a parent) - the're handled differently).

    Well, open for comments as always

  2. #2
    SitePoint Guru dbevfat's Avatar
    Join Date
    Dec 2004
    Location
    ljubljana, slovenia
    Posts
    684
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,

    I must admit this looks very sweet, self-explaining and easy.

    I've only had time to read your post and I don't know what's going on behind the scenes, but I will dig deeper into this when I have more time. I also have some questions, which I'll post if I don't find the answers to them on your page.

    Just a quick one:
    what kind of an object is $person? It is returned from a manager::query(), so I'm guessing it's a Query: a criteria collection with enough knowledge of a 'Person' domain model to tell simple and relational fields apart, and to assemble (and execute) an SQL statement for 'person' table.

    So, $person->name maps to name column in person table. But in case of a join, how exactly does this work? When you write $person->membership->group->name, is membership another Query that is created by the manager? In which case, so is 'group'. How are these (standalone) queries mapped together in an SQL?

    I apologize if the answers seem obvious - didn't really dig into it yet.

    Another thing: a comment on the conditions. While simple string values and arrays may seem at least easy if not sufficient, things would be improved if the condition (criterion?) itself is an object.

    Example:
    PHP Code:
    $person->birthdate = new ValueBetween('1.1.1990''1.1.2000');
    // instead of
    $person->birthdate = array('>=''1.1.1990');
    $person->birthdate = array('<=''1.1.2000');
    // heh .. i've just realized that this probably wouldn't work :)
    // except if you append the conditions instead of overwriting them 
    ... or something of that kind.

    Best regards

  3. #3
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok, let me explain. I gave it a realy hard thought and realized that all __get ($person->membership->group->name) calls are joins and all __set ($person->membership->group->name) are conditions. A query could be written such as this to be more explicit:
    Code:
    $person->join("membership")->join("group")->field("name","admin");
    it's always the same instance which is returned, so join() returns $this, and it keeps a track record of all joins that have been done in one single call so $person->membership->group->name has a "joinque" of membership_group so it can keep a track of which joins should connect to which and and which table the last __set(field) call is assoiciated with.

    The $person is an ObjectQuery and there's only ever one of this as it returns itself from the join()/__get methods(__get is just a masqurade over join as it just relays it's call to it).

    For more advanced field checks you can do stuff such as this:
    PHP Code:
    <?php
    $person
    ->name = array("fredrik","thr"); // matches (name = 'fredrik' OR name ='thr');
    $person->name = array('!=','fredrik','thr'); // matches (name != 'fredrik' OR name = 'thr');
    ?>
    You can do more advanced stuff ofc.

    This ObjectQuery-thing is just very early and I've put maybee max 8h work into it.

  4. #4
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    If you wanna dig into how I've done check these two (very very early "proof of concep" classes on my orms svn repos: http://model.serverside.se:83/repos/...odel/Criteria/) and here are some more extensive tests: http://model.serverside.se:83/repos/...query_test.php

  5. #5
    SitePoint Evangelist ghurtado's Avatar
    Join Date
    Sep 2003
    Location
    Wixom, Michigan
    Posts
    591
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I have to admit, this looks very cool! Can't wait to see further development on it. Only one quick question comes to mind. Do you have "deep updates" working?

    IE
    PHP Code:
    $user->group->name 'New Group Name';
    $user->commit(); 
    Garcia

  6. #6
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    ah, yes that works.. but that's not part of the Object Query language, that's part of the ORM solution


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
  •