SitePoint Sponsor

User Tag List

Page 1 of 5 12345 LastLast
Results 1 to 25 of 105
  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 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?

  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 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).

  5. #5
    ********* 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

  6. #6
    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.

  7. #7
    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.

  8. #8
    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.

  9. #9
    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.

  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 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.

  12. #12
    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.

  13. #13
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Thanks for the heads up, I'll look into how to solve it.
    I have been toying with this as well. I think column names would have to be configured inside DataObject implementations, i.e.
    DataObject::numeric_strings. Unit::commitObject() can match these against column names returned by FieldMap::getFields() to determine which literals to use.

  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 johno
    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.
    Yes, but you still have to have support for N:M relations. Because N:M relations exists and are sometimes inevitable.

    Quote Originally Posted by johnoThe 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.)
    Well, I have an insanley complex QueryObject/QueryEngine sollution that abstracts the SQL into objects. It also implements a SQL-cache so that queries that can be cached are(yes it auto-sense what queries contain dynamic variables and those who don't). The problem with this is the simple fact that, well... it's not practical realy, it realy isn't. And as I wrote a post or two up I'm sticking with pure SQL because it's easier during development of the ORM. Imho. criteria/query objects are overkill most of the time.

    Quote Originally Posted by johno
    I don't want to scare or stop you.
    That's not possible ;p

  15. #15
    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...)

  16. #16
    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.

  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 DerelictMan
    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...)
    My current implementation resolves the names of tables/fields/classes/properties automagicly by parsing the database(atm. the result of the parsing is not saved anywhere so it's redone on every call to the page - ofc. this will be cached, but I'm just going for functionallity/API now - not performance). I've solved all the mapping issues atm. except N:M relations, which I've not found a good way to do without some type of mappings file.

    Quote Originally Posted by DerelictMan
    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.
    Ah no, soz if it came out wrong.

  18. #18
    SitePoint Zealot DerelictMan's Avatar
    Join Date
    Oct 2005
    Posts
    123
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    My current implementation resolves the names of tables/fields/classes/properties automagicly by parsing the database...
    Ah, I see. I know my situation is probably unusual, but in my case I'm glad Propel allows me to be explicit in the mapping, since I'm stuck in a situation where I have to interface with a database that I cannot make schema changes to. (That's the reason that Ruby on Rails, as cool as the language Ruby is, never really blew me away...ActiveRecord just doesn't cut it for me.) But, as I said, I know I'm in the minority group, and I can definitely see the appeal of not having to bother creating a mapping file if you can get away with it...

  19. #19
    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.

  20. #20
    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;
                 }
                 ...
             }
         }
     
     } 

  21. #21
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    Imho. criteria/query objects are overkill most of the time.
    I'm currently working with a simple Finder whose methods accept SQL fragments and primary key values. Seems to work well for simple queries on one or two tables:
    PHP Code:
      find(135);
      
    find('1-3');
      
    findAll(
          
    'age>:age AND height<=:height',
          array(
    'age' => $age'height' => $height)
      );
      
    findAll(array(
          
    'conditions' => 'column IN(?)',
          
    'order' => 'created_on DESC',
          
    'limit' => 10),
          
    $params
      
    ); 

  22. #22
    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 DerelictMan
    Ah, I see. I know my situation is probably unusual, but in my case I'm glad Propel allows me to be explicit in the mapping, since I'm stuck in a situation where I have to interface with a database that I cannot make schema changes to. (That's the reason that Ruby on Rails, as cool as the language Ruby is, never really blew me away...ActiveRecord just doesn't cut it for me.) But, as I said, I know I'm in the minority group, and I can definitely see the appeal of not having to bother creating a mapping file if you can get away with it...
    Ah yes, if you're working on an old database where the names of tables/fields can't be changed I see why you chose propel - perfect for the job imho.

    What I'm working on atm. is only for "new" projects, and it's not supposed to be adaptable on older/legacy databases.

  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 michel
    I'm currently working with a simple Finder whose methods accept SQL fragments and primary key values. Seems to work well for simple queries on one or two tables:
    PHP Code:
      find(135);
      
    find('1-3');
      
    findAll(
          
    'age>:age AND height<=:height',
          array(
    'age' => $age'height' => $height)
      );
      
    findAll(array(
          
    'conditions' => 'column IN(?)',
          
    'order' => 'created_on DESC',
          
    'limit' => 10),
          
    $params
      
    ); 
    The thing is tho... why do you do this? As imho there is no gain at all. As soon as you take the step into writing the col/field names out in the query you loose the only thing that makes criterias worth it, namely abstraction. The ability to change your database w/o changing your code.

  24. #24
    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.

  25. #25
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    The thing is tho... why do you do this?
    I needed a way to parameterize data fetching queries and reduce the amount of repetitive elements in SQL statements. It's not SQL abstraction I'm after.
    Quote Originally Posted by thr
    The ability to change your database w/o changing your code.
    Sounds like utopia to me


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
  •