SitePoint Sponsor

User Tag List

Page 1 of 3 123 LastLast
Results 1 to 25 of 105

Hybrid View

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

    Feedback on current ORM-implementation.

    I couldn't dig up my old thread about this, and I've been inactive on this forum for the past 2 months or so. But here's my current ORM-implementation(or well, what will BE an ORM-implementation when it's done). I just wanted some feedback on the testcases to see if the API/etc. seems ok, so I'll post it here. The whole codebase inc. tests is also attached to the post.

    PHP Code:
    <?php
    require_once('./Helpers/devfunc.php');
    ra('./Classes/');
    ra('./DBClasses/');

    require_once(
    'simpletest/unit_tester.php');
    require_once(
    'simpletest/reporter.php');
    require_once(
    'simpletest/reporter_showpasses.php');

    class 
    TestUnitOfWork extends UnitTestCase{

        public function 
    __construct(){
            
    $this->= new UnitOfWork(new MySQLConnection("localhost","user","password","database"));
            
    $this->UnitTestCase();
        }
        
        public function 
    testCreation(){
            
    $person $this->u->create("Person");
            
    $this->assertIsA($person,"Person");
        }

        public function 
    testCreationsAreUnique(){
            
    $p1 $this->u->create("Person");
            
    $p2 $this->u->create("Person");
            
    $this->assertIsA($p1,"Person");
            
    $this->assertIsA($p2,"Person");
            
    $this->assertNotEqual(strval($p1),strval($p2));
            
    $this->u->insert->purge();
        }
        
        public function 
    testIdentityMapWorks(){
            
    $p1 $this->u->getByPk("Person",1);
            
    $p2 $this->u->getByPk("Person",1);
            
    $this->assertIsA($p1,"Person");
            
    $this->assertIsA($p2,"Person");
            
    $this->assertEqual(strval($p1),strval($p2));
        }
        
        public function 
    testTwoDifferentObjectsActuallyDiffer(){
            
    $p1 $this->u->getByPk("Person",1);
            
    $p2 $this->u->getByPk("Person",2);
            
    $this->assertIsA($p1,"Person");
            
    $this->assertIsA($p2,"Person");
            
    $this->assertNotEqual(strval($p1),strval($p2));
            
    $this->assertNotEqual($p1->pk,$p2->pk);
        }
        
        public function 
    testInsertObjectActuallySaved(){
            
    $p $this->u->create("Person");
            
    $p->firstname "Anders";
            
    $p->age 18;
            
    $this->u->commit($p);
            
    $u2 = new UnitOfWork(null,true);
            list(
    $lillis) = $u2->getBySQL("select * from Person where Firstname = %s",'Anders');
            
    $this->assertIsA($lillis,"Person");
            
    $this->assertNotEqual(strval($p),strval($lillis));
        }
        
        public function 
    testDeleteBySQL(){
            
    $this->assertTrue($this->u->deleteBySQL("delete from Person where Firstname = %s",'Anders'));
        }
        
        public function 
    testCreateMultipleAreSaved(){
            
    $this->u->insert->purge();
            
    $p1 $this->u->create("Person");
            
    $p2 $this->u->create("Person");
            
    $p1->firstname "Dummy #1";
            
    $p2->firstname "Dummy #2";
            
    $p1->age 10;
            
    $p2->age 12;
            
    $this->u->insert->commit();
            
    $u2 = new UnitOfWork(null,true);
            list(
    $pn1,$pn2) = $u2->getBySQL("select * from Person where Firstname like %s order by id",'Dummy%');
            
    $this->assertEqual($p1->pk,$pn1->pk);
            
    $this->assertEqual($p2->pk,$pn2->pk);
            
    $this->assertNotEqual(strval($p1),strval($pn1));
            
    $this->assertNotEqual(strval($p2),strval($pn2));
        }
        
        public function 
    testCreatedObjectGetsPk(){
            
    $p $this->u->create("Person");
            
    $p->firstname "Dummy #3";
            
    $this->assertNull($p->pk);
            
    $this->u->commit();
            
    $this->assertIsA($p->pk,"Integer");
        }
        
        public function 
    testDeleteBySQLMultiple(){
            
    $this->assertTrue($this->u->deleteBySQL("delete from Person where Firstname like %s",'Dummy%'));
        }
        
        public function 
    testDeleteObject(){
            
    $p $this->u->create("Person");
            
    $p->firstname "Dummy #4";
            
    $p->age 14;
            
    $this->u->commit($p);
            
    $this->assertIsA($p->pk,"Integer");
            
    $this->u->delete($p);
            
    $this->u->commit();
            
    $p $this->u->getBySQL("select * from Person where Firstname = %s",'Dummy #4');
            
    $this->assertEqual(count($p),0);
        }
        
        public function 
    testGetBySQL(){
            
    $persons $this->u->getBySQL("select * from Person");
            foreach(
    $persons as $person){
                
    $this->assertIsA($person,"Person");
            }
        }
        
        public function 
    testGetBySQLCondition(){
            
    $persons $this->u->getBySQL("select * from Person where Id = %s",1);
            
    $this->assertIsA($persons,"Array");
            foreach(
    $persons as $person){
                
    $this->assertIsA($person,"Person");
            }
        }
        

    }

    $test = new TestUnitOfWork;
    $test->run(new ShowPasses());
    ?>
    And here follows the whole codebase(~180kb, inc. simpletest and tests):
    Attached Files Attached Files

  2. #2
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi.

    Looks cool just looking at the tests. I like the way the object type can be inferred from the SQL. How reliable is this?

    Minor point on the test case - I wouldn't use __construct() to set things up. It's run ony once (as is __destruct) for the entire test case. By using the setUp() method you create a fresh UnitOfWork for each test. Just in case you ended up with some test interference.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  3. #3
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    I like the way the object type can be inferred from the SQL.
    Me too. I'd like to see support for other mapping conventions though. A configurable callback that accepts a table name and returns an inflected value would be enough for me.

  4. #4
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by michel
    Me too. I'd like to see support for other mapping conventions though. A configurable callback that accepts a table name and returns an inflected value would be enough for me.
    Could you explain this in some code? I think I know what you're after but not 100% sure.

    More generally about mappings, the thing is that I'm trying soo soo hard to not have any mapping files at all. Currently it works without mappings but that requires insanley strict table/field names. I'm trying to come up with some nameing standard that makes it possible for some type of parser to parse the complete DB, check vs. the classes that exists and "just make it worK". The current naming "rules" are as follows:


    * Table and Class (map one to one)
    * Fields/Property names are matched so that if a Class / Table exists with the name "Group" and you create a field / property that is named "Group" that field/property will only accept a Group object (or int PK in the actuall DB)

    Many to Many relations are not yet solved and I'm still pondering a solution for it.

  5. #5
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Could you explain this in some code? I think I know what you're after but not 100% sure.
    I think it would be nice if you could extend UnitOfWork. Something like this would be sufficient for me:

    PHP Code:
    class ApplicationUnitOfWork extends UnitOfWork {
     
         protected 
    $table_mapping_method 'method';
         
    // or
         
    protected $table_mapping_method = array('class_name''method');
      
     }
     
     class 
    UnitOfWork {
     
         public function 
    getBySQL($sql){
             
    $args func_get_args(); unset($args[0]);
             if(
    preg_match('~from[\t\s\n]+`?([_a-z0-9]+)~i',$sql,$match)){
                 
    $type $match[1];
                 if (isset(
    $this->table_naming_callback)) {
                     if (
    is_callable($this->table_mapping_method)) {
                         
    $class_name call_user_func($this->table_mapping_method$type);
                     } elseif (
    is_string($this->table_mapping_method) &&
                         
    method_exists($this$this->table_mapping_method))
                     {
                         
    $class_name $this->$this->table_mapping_method();
                     }
                     if (!isset(
    $class_name) || empty($class_name)) {
                         throw ...
                     }
                 } else {
                     
    $class_name $type;
                 }
                 ...
             }
         }
     
     } 

  6. #6
    SitePoint Evangelist
    Join Date
    Jun 2003
    Location
    Melbourne, Australia
    Posts
    440
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    here's my current ORM-implementation
    Just browsing the code, this looks rather clever. Lastcraft's changes library is maybe a bit cleverer, but only a bit!

    I can't quite understand what the identify map is for. Since it assigns an id to an object by
    PHP Code:
    strval($object); 
    it seems to be giving each object a unique id, but beyond that, I don't appreciate how that information is used. Would you explain it, please, thr?
    Zealotry is contingent upon 100 posts and addiction 200?

  7. #7
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by auricle
    Just browsing the code, this looks rather clever. Lastcraft's changes library is maybe a bit cleverer, but only a bit!

    I can't quite understand what the identify map is for. Since it assigns an id to an object by
    PHP Code:
    strval($object); 
    it seems to be giving each object a unique id, but beyond that, I don't appreciate how that information is used. Would you explain it, please, thr?
    I've looked some at Lastcraft's and the other libs and gotten some inspiration, and yes... his is a bit more cleaver ;p.

    The "strval($object);" thing is atm. not needed realy, and is a leftover from and old version of the code. What it does is that for the dirty-new objects (ie: the one's that are just created, and doesn't have reference in the DB) it does a strval($object); on them to get a Key to use in an assc. array.

    All the "real" objects(the ones that have a reference in the DB) are saved in the identitymap with a "TypePKValue" key.


    Edit:
    The code i posted above is the result of ~3 weeks work, I got a working criteria implementation to got with it... but I didn't realy like it all that much and seperated the two. Still working on the Many<>Many relationship which is not working fully yet, but it will . I'll make sure to post some updated code when I get it.

    I'm aiming to make this into something like Changes or some of the other smaller orm's out there. No new propel here Keep it simple


    Quote Originally Posted by lastcraft
    I like the way the object type can be inferred from the SQL. How reliable is this?
    This due to the simple fact that it atm. runs the following regexp on the sql: "~select.*from\s+(\w+)~i" (or something like that, can't remember the exact regexp from the top of my head. And matches the (\w+) part on the available classes, It's not the best solution realy, but it's that type of functionallity I want in the end - with a cleaner solution.


    Edit 2: Atm. I'm considering some design decisions, and I decided to consult you guys. If you look at the following code:

    PHP Code:
        public function testGetBySQLCondition(){
            
    $persons $this->u->getBySQL("select * from Person where Id = %s",1);
            
    $this->assertIsA($persons,"Array");
            foreach(
    $persons as $person){
                
    $this->assertIsA($person,"Person");
            }
        } 
    The SQL above always returns either 0 or 1 Person objects, because you either have a person with the Id you're searching for or you dont. But this doesn't apply to all fields as you might search for something "Name LIKE 'F%'" My "problem" here is the following: What should I return?

    1. First situation, you don't find any persons at all - return either "false" or "array()" ?

    2. Second situation, you find one person, return "object" or "array(object)" ?

    3. Third situation, you find more then one person - only one solution in my eyse, return "array(object,object,etc)" ?

    Why I'm pondering this is that if you look on the code above, I do the following:
    PHP Code:
            foreach($persons as $person){
                
    $this->assertIsA($person,"Person");
            } 
    If i always return an array, no matter how many i find/don't find - the looping comes natural as you always get an array back. I've always liked to only return one type of result from an method/function as it gives easy to read and comprehendable code. The "only" downside I see is that if you want to know if you find any persons you have to do:
    PHP Code:
    $persons $this->u->getBySQL("select * from Person where Id = %s",1);
    if(
    count($persons) > 0){ /* ... */ 
    instead of being able to do:

    PHP Code:
    if(($persons $this->u->getBySQL("select * from Person where Id = %s",1)) !== false){

    (Altho, I have to say that I find the first of the two codesnippest to be the easiest to understand and first glance).

  8. #8
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by thr
    The "strval($object);" thing is atm. not needed realy, and is a leftover from and old version of the code. What it does is that for the dirty-new objects (ie: the one's that are just created, and doesn't have reference in the DB) it does a strval($object); on them to get a Key to use in an assc. array.
    A hash key system instead of a dirty flag was discussed in one of the many persistence threads on this forum. I think we ended up deciding that it was the way to go. Is this PHP5 only? Can you notuse the __serialize() method?

    Quote Originally Posted by thr
    Still working on the Many<>Many relationship which is not working fully yet, but it will . I'll make sure to post some updated code when I get it.
    I ducked that one, simply because I never believed in it. To me many to many meant the data model needs a refactor.

    Quote Originally Posted by thr
    I'm aiming to make this into something like Changes or some of the other smaller orm's out there. No new propel here Keep it simple
    Propel is pretty simple. With Zend dipping their toes in this arena, ActiveRecord implementations have a lot of competition. This is one step up (RowDataGateway+, DataAccessor, DAO). That's good market positioning .

    Quote Originally Posted by thr
    This due to the simple fact that it atm. runs the following regexp on the sql: "~select.*from\s+(\w+)~i" (or something like that, can't remember the exact regexp from the top of my head. And matches the (\w+) part on the available classes, It's not the best solution realy, but it's that type of functionallity I want in the end - with a cleaner solution.
    If you hammer the hell out of it with test cases, then you should be able to evolve some kind of Lexer/Parser combination. I reckon you can make it fast and reliable, and possibly get the joins in too. I think it's a brilliant idea. Much cleverer than Changes .

    Quote Originally Posted by thr
    1. First situation, you don't find any persons at all - return either "false" or "array()" ?

    2. Second situation, you find one person, return "object" or "array(object)" ?

    3. Third situation, you find more then one person - only one solution in my eyse, return "array(object,object,etc)" ?
    Yuk. You are going to throw a load of type checking code straight back onto the caller. That's code that will be duplicated over and over. Return array() from all those options, but add a syntactic sugar method findOneBySql(). I also prefer findBySql as it's more emphatic, but I guess that's personal choice .

    I take it exceptions are your error handling mechanism?

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  9. #9
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    A hash key system instead of a dirty flag was discussed in one of the many persistence threads on this forum. I think we ended up deciding that it was the way to go. Is this PHP5 only? Can you notuse the __serialize() method?
    Yes, PHP5 (ofc ). I'm going to go for the strval($object)-aproach in the end for dirty objects and the "TypePkVal" for non-dirty objects.
    Quote Originally Posted by lastcraft
    I ducked that one, simply because I never believed in it. To me many to many meant the data model needs a refactor.
    Just one example is the Person <> Group relation. One person can have many Groups and one Group can have many Persons(members).
    Quote Originally Posted by lastcraft
    Propel is pretty simple. With Zend dipping their toes in this arena, ActiveRecord implementations have a lot of competition. This is one step up (RowDataGateway+, DataAccessor, DAO). That's good market positioning .
    Ah well, propel is nice but the thing that makes me go *ouhf* when I read the source / used it is the unending XM(hel)L. XML is nice, but well... sometimes it's just to much. And I've never realy liked the ActiveRecord as it locks you so hard with your current DB layout after a couple of 1k lines of code.
    Quote Originally Posted by lastcraft
    If you hammer the hell out of it with test cases, then you should be able to evolve some kind of Lexer/Parser combination. I reckon you can make it fast and reliable, and possibly get the joins in too. I think it's a brilliant idea. Much cleverer than Changes .
    I like the idea because it makes you able to just write your queries and get what you want, and it's very intuative(spelling?). The joins are not first priority atm.
    Quote Originally Posted by lastcraft
    Yuk. You are going to throw a load of type checking code straight back onto the caller. That's code that will be duplicated over and over. Return array() from all those options, but add a syntactic sugar method findOneBySql(). I also prefer findBySql as it's more emphatic, but I guess that's personal choice .
    Yes, I decided to go for always returning an array().
    Quote Originally Posted by lastcraft
    I take it exceptions are your error handling mechanism?
    Ofcourse.

    A couple of more questsions/notes/thoughts/ideas here:


    1. I'm going to go for the "virtual" properties sollution(I think atleast, unless someone can put a realy good argument to why I shouldn't), the base DataObject class will look something like this:

    PHP Code:
    <?php
        
    class DataObject{

            protected 
    $_Pk;

            public function 
    __get($property){
                
    $method "get".ucfirst($property);
                if(
    method_exists($this,$method)){
                    return 
    $this->$method();
                }
            }
                
            public function 
    __set($property,$value){
                
    $method "set".ucfirst($property);
                if(
    method_exists($this,$method)){
                    return 
    $this->$method($value);
                }
            }
            
            public function 
    getPk(){
                return 
    $this->_Pk;
            }
            public function 
    setPk($value){
                
    $this->_Pk $value;
            }

           }
    ?>
    And the use of it will look like this:

    PHP Code:
    <?php
        $do 
    = new DataObject;
        
    $do->pk 1;
        echo 
    $do->pk;
    ?>
    This looks ok?

    Edit #1:

    2. How about writing SQL vs. criteria/query objects? I've got a working criteri/query object implementation for this, but well.. it's just so... BIG for so little. I realy do prefer to just write the SQL, but that again binds me to my DB and makes changes very hard.

    For now I'll settle to use SQL as it's easier to develop, because dev:ing both a query/critera implementation and an ORM-mapper at the same time is to much . But I'm still very interested in your thoughts about what's the best aproach.

  10. #10
    SitePoint Zealot johno's Avatar
    Join Date
    Sep 2003
    Location
    Bratislava, Slovakia
    Posts
    184
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Just one example is the Person <> Group relation. One person can have many Groups and one Group can have many Persons(members).
    Yes, but there is always another association table. Something like Categorization, Occurrence or something like that. When you are looking for groups a specific person belong to, you are actually looking for Occurrences of this specific Person in Groups.

    The idea (your stuff, Changes, Propel, ActiveRecord) looks pretty good to me, but if I am doing something more than simple select/insert/update/delete I always end up with writing specific finder/gateway methods. Isn't there a way how to simplify it?

    What about complex queries and performance issues?
    http://www.sitepoint.com/forums/show...6&postcount=19 (Ignore my solution, it's only an idea.)

    Pretty good stuff anyways. I don't want to scare or stop you.
    Annotations support for PHP5
    TC/OPT™ Group Leader

  11. #11
    SitePoint Zealot DerelictMan's Avatar
    Join Date
    Oct 2005
    Posts
    123
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ah well, propel is nice but the thing that makes me go *ouhf* when I read the source / used it is the unending XM(hel)L. XML is nice, but well... sometimes it's just to much. And I've never realy liked the ActiveRecord as it locks you so hard with your current DB layout after a couple of 1k lines of code.
    I've used Propel for a project and I only had to create one XML file to describe my mapping scheme. I wouldn't exactly call that "unending". Since Propel doesn't infer much at runtime there has to be some mechanism to describe the schema and define the mapping to classes/fields/etc. What approach would you prefer over XML? (Not that I take issue with your distaste for Propel; it worked nicely for me but I know that it's not for everyone...)

    Also, Propel is not an ActiveRecord implementation. I know you probably didn't intend to say that, but reading that paragraph others may get the idea that it is.
    Off Topic:

    (That's the main reason I chose it for my project...I was working with a legacy database that was (is) being used by other applications that are difficult to change so cleaning up the schema wasn't really an option, and in my case an ActiveRecord approach wouldn't have been much clearer than the spaghetti SQL approach...)

  12. #12
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by thr
    Just one example is the Person <> Group relation. One person can have many Groups and one Group can have many Persons(members).
    I think better is...

    Person (1)-->(*) Membership (*)<--(1) Group

    ...as Jason pointed out. N:M relations will get you into all sorts of trouble with changing group names from inside a Person class. In these situations, you actually want to pull apart the link table.

    I've never needed N:M, and skimming "Data Model Patterns" (Hay) and "The Data Model Resource Book" (Silverston) I cannot find a single N:M relation. All are 1:N with named intermediaries.

    I think it's low value.

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things

  13. #13
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Just glanced quickly. API looks fine and seems pretty intuitive to me.
    Quick detail:
    PHP Code:
        class MySQLConnection{
            
            public function 
    vsprintfExecute($sql,$args){
                foreach(
    $args as &$arg){
                    if(!
    is_numeric($arg)){
                  
    $arg "'".mysql_real_escape_string($arg,$this->resource)."'";
                    }
                }
                return 
    $this->execute(vsprintf($sql,$args));
            }
            
        } 
    You're likely to run into trouble with is_numeric() and literalizing numeric values. Take a value starting with one or more zeroes (i.e. a phone number with digits only). You'll need a string literal, but is_numeric() will return true so you get a number literal instead. MySQL will strip the first zeroes, with both string and numeric column types. I'd prefer configurable literalizing.

  14. #14
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by michel
    You're likely to run into trouble with is_numeric() and literalizing numeric values. Take a value starting with one or more zeroes (i.e. a phone number with digits only). You'll need a string literal, but is_numeric() will return true so you get a number literal instead. MySQL will strip the first zeroes, with both string and numeric column types. I'd prefer configurable literalizing.
    Thanks for the heads up, I'll look into how to solve it.

  15. #15
    SitePoint Evangelist
    Join Date
    Jun 2003
    Location
    Melbourne, Australia
    Posts
    440
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    The "strval($object);" thing is atm. not needed realy, and is a leftover from and old version of the code. What it does is that for the dirty-new objects (ie: the one's that are just created, and doesn't have reference in the DB) it does a strval($object); on them to get a Key to use in an assc. array.

    All the "real" objects(the ones that have a reference in the DB) are saved in the identitymap with a "TypePKValue" key.
    Yes, I understand that. But what I don't understand is what role the IdentityMap plays in the interactions between the UnitofWork object and the various Units.

    This doesn't seem to challenge others here (or they don't care). If you're standing next to me, just put on that t-shirt that says "I'm with Stupid"!

    Later...
    Oh, I get it. It acts as a central repository of objects.
    Last edited by auricle; Jan 27, 2006 at 03:12. Reason: Enlightenment!
    Zealotry is contingent upon 100 posts and addiction 200?

  16. #16
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hmm, wondering if rather than parsing the SQL its easier to determine the class by the first row query result.

    Just glancing at the code something like FieldMap::determineType()

    PHP Code:
        public function determineType($row)
        {
            foreach(
    $this->map as $type => $map)
            {
                if (isset(
    $row[$map['pk']]) && count($map['fields']) == count($row) - 1)
                {            
                    
    $r array_diff($map['fields'], array_keys($row));
                    unset(
    $r[$map['pk']]);
                    if (empty(
    $r))
                        return 
    $type;
                }
            }
            return 
    FALSE;
        } 
    Completely untried, but makes no assumptions on the SQL string, just assumes fields uniquely identify a class.

  17. #17
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren
    Hmm, wondering if rather than parsing the SQL its easier to determine the class by the first row query result.

    Just glancing at the code something like FieldMap::determineType()

    [...codesnippet...]

    Completely untried, but makes no assumptions on the SQL string, just assumes fields uniquely identify a class.
    This has the problem that it'll get insanley slow when you start doing long queries with loads of fields or many queries.

  18. #18
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    This has the problem that it'll get insanley slow when you start doing long queries with loads of fields or many queries.
    Dont need to run it for every row, just the first row returned. And perhaps even only once per prepared query

    Eg.
    'SELECT * FROM Person WHERE name LIKE %s' is always going to return a Person, no matter what parameters are inserted, so after first run can cache
    the type.

  19. #19
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren
    Dont need to run it for every row, just the first row returned. And perhaps even only once per prepared query

    Eg.
    'SELECT * FROM Person WHERE name LIKE %s' is always going to return a Person, no matter what parameters are inserted, so after first run can cache
    the type.
    Ah yes, I'm aware of that - but if you select from a table with many cols, you're going to get maybee 15-20 or more fields to parse vs. the table type. And if you start doing several queries on several different tables, you're getting quite many loops and such that can be avoided.

    The other thing is that this (imho) is harder to code then a small regex-lexer/parser.

  20. #20
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Ah yes, I'm aware of that - but if you select from a table with many cols, you're going to get maybee 15-20 or more fields to parse vs. the table type. And if you start doing several queries on several different tables, you're getting quite many loops and such that can be avoided.

    The other thing is that this (imho) is harder to code then a small regex-lexer/parser.
    Parsing SQL is way harder than seeing if two arrays array_keys($row) & $map['fields'] are the same, imho. Plus it doesn't prevent 'CALL spPersonByName %s'

  21. #21
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A solution to mapping N:M relations "automagicly" just sprung into my mind, example:

    You have a Person table and a Group table. One Person can have many Groups and one Group can have many Persons as it's member. The table to "map" this relation would be called Group_Person (alphabetical order, G > P). Now when the database parser finds a table that consists of X_Y (where X and Y are two different tablenames of already existing tables) it will automagicly set up $person->group and $group->person and mapp the relations - what do you guys feel about this aproach? to strict?

  22. #22
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    A solution to mapping N:M relations "automagicly" just sprung into my mind, example:

    You have a Person table and a Group table. One Person can have many Groups and one Group can have many Persons as it's member. The table to "map" this relation would be called Group_Person (alphabetical order, G > P). Now when the database parser finds a table that consists of X_Y (where X and Y are two different tablenames of already existing tables) it will automagicly set up $person->group and $group->person and mapp the relations - what do you guys feel about this aproach? to strict?
    To strict in my mind, I would probably have named it something like Memberships. Perhaps you could take a cue from the Rails ActiveRecord implementation and establish a default but allow for an easy override?
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  23. #23
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sweatje
    To strict in my mind, I would probably have named it something like Memberships. Perhaps you could take a cue from the Rails ActiveRecord implementation and establish a default but allow for an easy override?
    Good idea there. I don't think that there's any way to escape some typ of mapping in the end =/.

    What do you feel about the "Person::Group >> Group" ? (That the field Group is locked onto the table Group - to strict?

  24. #24
    eschew sesquipedalians silver trophy sweatje's Avatar
    Join Date
    Jun 2003
    Location
    Iowa, USA
    Posts
    3,749
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Good idea there. I don't think that there's any way to escape some typ of mapping in the end =/.

    What do you feel about the "Person::Group >> Group" ? (That the field Group is locked onto the table Group - to strict?
    It does make sense to me that $person->Groups would point to an array of Group objects for which the person is associated with (perhap even automatically lazy loaded?) Of course now you are descending into the whole pluralzation issue
    Jason Sweat ZCE - jsweat_php@yahoo.com
    Book: PHP Patterns
    Good Stuff: SimpleTest PHPUnit FireFox ADOdb YUI
    Detestable (adjective): software that isn't testable.

  25. #25
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ah, neat.


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
  •