SitePoint Sponsor

User Tag List

Page 3 of 5 FirstFirst 12345 LastLast
Results 51 to 75 of 105
  1. #51
    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
    Problem with that is, if write some method

    $person = $..->getBySQL('SELECT id, name FROM Person WHERE ... ');

    And if later alter the Person table, by adding a column, the above should raise an error, imo. As its not retrieving all required fields.
    THe only field that is required is the PK(in this case ID). The current code checks for the PK, if the PK is there and you only need the nick from a user(example: displaying the author-name on a comment/news post), you can do:

    "SELECT Id,Nick FROM Person WHERE ...." and this is fully acceptable. If you then try to access for example the $person->email field, it's fetched automagicly(yes it requires one more SQL query, and yes this should be avoided most of the time, if you know you're going 2 need the email-field, just fetch it from the start)

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

  3. #53
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Don't know if it's of any interest, but here's the current "DataObject" class (I could use a better name maybe, any suggestions?) and the "Person"-class that extends it to show how it's suposed to be used:

    DataObject.php
    PHP Code:
    <?php
    abstract class DataObject{

        public function 
    __construct($row = array()){
            if(
    is_array($row) && count($row) > 0){
                foreach(
    $row as $field => $value){
                    
    $this->{'_'.$field} = $value;
                }
            }
        }

        public function 
    __get($property){
            
    $method "get".ucfirst($property);
            if(
    method_exists($this,$method)){
                return 
    $this->$method();
            }else{
                throw new 
    RuntimeException("Undefined property: ".get_class($this)."::\$$property (get)");
            }
        }
        
        public function 
    __set($property,$value){
            
    $method "set".ucfirst($property);
            if(
    method_exists($this,$method)){
                return 
    $this->$method($value);
            }else{
                throw new 
    RuntimeException("Undefined property: ".get_class($this)."::\$$property (set)");
            }
        }
        
        abstract public function 
    getPk(){}
        
        abstract public function 
    setPk($value){}
        
    }
    ?>
    Person.php
    PHP Code:
    <?php
    class Person extends DataObject{

        protected 
    $_id null;
        protected 
    $_name null;
        protected 
    $_age null;
        
        public function 
    getPk(){
            return 
    $this->_id;
        }
        
        public function 
    setPk($value){
            if(!isset(
    $this->_id)){
                return 
    $this->_id $value;
            }else{
                throw new 
    RuntimeException("Can't change already set pk (id)");
            }
        }
        
        public function 
    getId(){
            return 
    $this->getPk();
        }
        
        public function 
    setId($value){
            return 
    $this->setPk($value);
        }
            
        public function 
    getName(){
            return 
    $this->_name;
        }
        
        public function 
    setName($value){
            return 
    $this->_name = (string)$value;
        }    
        
        public function 
    getAge(){
            return 
    $this->_age;
        }
        
        public function 
    setAge($value){
            return 
    $this->_age = (int)$value;
        }

    }
    ?>
    And here's how the Person class would be used:
    PHP Code:
    <?php
    $person 
    $uow->create("Person");
    $person->name "thr";
    $person->age 20;
    ?>

  4. #54
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Also if want to remove the strval($object) fun

    http://www.wiki.cc/php/SplObjectStorage

    It appears its available in 5.X and not just 6.

  5. #55
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    DataObject.php
    How about optional getters and setters? (I know, I'm terribly lazy )
    PHP Code:
    abstract class DataObject{
     
         public function 
    __set($property,$value){
             
    $method "set".ucfirst($property);
             if(
    method_exists($this,$method)){
                 return 
    $this->$method($value);
             }else{
                 
    $this->{"_$property"} = $value;
             }
         }
     
     } 
    ucfirst() seems a little limiting to me. What if I want to use camelcased getter and setter method names and column names are lowercase_and_underscored? With single-syllable column names ucfirst() will work fine, but anything_longer... Configurable inflection?

  6. #56
    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
    How about optional getters and setters? (I know, I'm terribly lazy )
    How do you mean? The code in Person.php is autogenerated by the DB-parser/lexer.


    Quote Originally Posted by michel
    How about optional getters and setters? (I know, I'm terribly ucfirst() seems a little limiting to me. What if I want to use camelcased getter and setter method names and column names are lowercase_and_underscored? With single-syllable column names ucfirst() will work fine, but anything_longer... Configurable inflection?
    i've debated the ucfirst() thing with myself so many times. I've decided to go with uc-first on the get/set:ers names b/c it gives nicer names, such as:

    public function getId(); instead of public function getid(); There's no difference to how you access the variables or anything. Just that the names of the get/set-functions are UCed on the first letter.

  7. #57
    SitePoint Guru
    Join Date
    Nov 2002
    Posts
    841
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren
    I had no idea what this class did till I read the wiki page. The name for this in Java and in mathematics is a Set. "A collection that contains no duplicate elements." The name implies that its limited to objects, but why couldn't any PHP value be a member of a set?

  8. #58
    SitePoint Guru 33degrees's Avatar
    Join Date
    May 2005
    Posts
    707
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by michel
    A separate table storing relations? I have used this before. I wouldn't know how to (sanely) store many to many relations another way.
    A sperate table is the standard way of doing many to many in a relational database, and any ORM layer has to support that.

    Quote Originally Posted by michel
    Came accross an interesting Hibernate - Rails ActiveRecord comparison by the way.
    Eh, comparing an activerecord implementation to a datamapper implementation is comparing apples to oranges. Also, some of his comments are purely subjective and show a fair amount of bias; at the beginning, he complains about the lack of explicitness in ActiveRecord, saying that you have to go to your database to see what your fields are. He overlooks the benefit of this approach (which is that you can change your schema without having to change any code), and ignores the fact that you can explicitely declare the fields in ActiveRecord if you want to, you just don't have to. Another thing is that he trots out examples like:

    Code:
    (Java)
    public class Miner {
       // Other fields/methods omitted
    
        private GoldClaim goldClaim;
        /**
         * @hibernate.many-to-one column="gold_claim_id"
         *         cascade="save"
         */
        public GoldClaim getGoldClaim() { return goldClaim; }
        public void setGoldClaim(GoldClaim goldClaim) {
            this.goldClaim = goldClaim;
        }
    }
    
    (Rails)
    class Miner < ActiveRecord::Base
        belongs_to :gold_claim
    end
    and says "Not much real difference here", ignoring the fact that the Java way takes four times as much code. Basically, he keeps pressing the point about how much more powerfull Hibernate is, without giving much importance to how much simpler ActiveRecord is.

  9. #59
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    How do you mean? The code in Person.php is autogenerated by the DB-parser/lexer.
    Didn't know about code generation in your tool. Didn't examine your Helpers/tableGen.php script. My bad.

  10. #60
    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
    Didn't know about code generation in your tool. Didn't examine your Helpers/tableGen.php script. My bad.
    the codegeneration is insanley simplified just for dev-purposes, just so u know .

  11. #61
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    The Netherlands
    Posts
    170
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by 33degrees
    A sperate table is the standard way of doing many to many in a relational database, and any ORM layer has to support that.
    That's what I thought.

    Quote Originally Posted by 33degrees
    Eh, comparing an activerecord implementation to a datamapper implementation is comparing apples to oranges.
    Not entirely so. Maybe Rails' ActiveRecord name is misleading:
    Quote Originally Posted by http://api.rubyonrails.org/files/vendor/rails/activerecord/README.html
    Active Record’s main contribution to the pattern is to relieve the original of two stunting problems: lack of associations and inheritance. By adding a simple domain language-like set of macros to describe the former and integrating the Single Table Inheritance pattern for the latter, Active Record narrows the gap of functionality between the data mapper and active record approach.
    Quote Originally Posted by 33degrees
    Also, some of his comments are purely subjective and show a fair amount of bias;
    Sure. No problem. For me it's just interesting to see code samples next to each other.
    Quote Originally Posted by 33degrees
    he complains about the lack of explicitness in ActiveRecord, saying that you have to go to your database to see what your fields are. He overlooks the benefit of this approach (which is that you can change your schema without having to change any code)
    This is why I'd prefer a decent ActiveRecord with sensible defaults. No code generation, no mapping files. But maybe my apps are a little less demanding on an ORM tool than heavyweight ones the author refers to.

  12. #62
    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 Selkirk
    I had no idea what this class did till I read the wiki page. The name for this in Java and in mathematics is a Set. "A collection that contains no duplicate elements." The name implies that its limited to objects, but why couldn't any PHP value be a member of a set?
    Who knows..

    I suspect the necessity came from SPLs subject/observer pattern, to prevent an observer getting registered multiple times.

  13. #63
    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
    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.
    How would you make this look code-wise in the worker? How would you assign different relations, etc. (Just had to quote this once more to ask this question, soz.)


    Edit: Got some more API ideas, I'll paste the commented code here and if anyone feels like it I'd appriciate comments(especially about the API for 1:N > Intermediate < 1:N relations):

    PHP Code:
    <?php
    ##### Instancing UoW
    /* UnitOfWork::__construct(MySQLConnection mysqlConnection); */
    $uow = new UnitOfWork(new MySQLConnection('localhost','dev','devnull','development'));

    ##### Object Creating
    /* UnitOfWork::create(str className[, int amount]); */
    $thr $uow->create("Person");
    $thr->name "Fredrik";
    $thr->age 20;

    ##### Object Saving
    /* UnitOfWork::commit(int mode); */
    $uow->commit();                     // int 0
    $uow->commit(UnitOfWork::ALL);         // int 0
    $uow->commit(UnitOfWork::INSERT);     // int 1
    $uow->commit(UnitOfWork::UPDATE);     // int 2
    $uow->commit(UnitOfWork::DELETE);     // int 3

    ##### Retrivieing Object
    /* UnitOfWork::getBySQL(str sql[, str argument [...] ]); */
    /* UnitOfWork::getOneBySQL(str sql[, str argument [...] ]); */
    list($madeleine) = $uow->getBySQL("select * from Person where name = %s",'Madeleine');
    $fredrik $uow->getOneBySQL("select * from Person where name = %s",'Fredrik');

    ##### Object assignment (In this example you can only be the member of one group)
    $admin $uow->getOneBySQL("select * from Group where name = %s",'Admin');
    $fredrik $uow->getOneBySQL("select * from Person where name = %s",'Fredrik');
    $fredrik->group $admin;

    ##### (1 > N) Multiple Relations (Object Assignment)
    $latestNews $uow->getOneBySQL("select * from Newspost where order by date desc limit 1");
    $commentOne $uow->create("Comment");
    $commentTwo $uow->create("Comment");
    $latestNews->comment[] = $commentOne;
    $latestNews->comment[] = $commentTwo;

    ##### User 1 > * Membership * < 1 Group - Simulated N:M Relations (Object Assignemnt)
    $admin $uow->getOneBySQL("select * from Group where name = %s",'Admin');
    $user $uow->getOneBySQL("select * from Group where name = %s",'User');
    $fredrik $uow->getOneBySQL("select * from Person where name = %s",'Fredrik');
    list(
    $adminM,$userM) = $uow->create("Membership",2);
    $adminM->person $fredrik;
    $adminM->group $admin;
    $userM->person $fredrik;
    $userM->group $user;
    /* I'm  not realy happy about the above API, it's WAY to complicated to create the "middle" table
    first and then assign the tables having the relation. Sure it seems logical to create the "Membership". */

    ##### Lazy Loading
    $news $uow->getBySQL("select * from Newspost order by date desc limit 1");
    /* If you refer to  the whole $news->comment it will load all of the associated objects at once */
    foreach($news->comment as $comment){
           echo 
    $comment->text;
    }
    /* If you refer to the $news->comment[1] or some other individual object, it will only load that object */
    $news->comment[1];
    ?>

  14. #64
    SitePoint Evangelist
    Join Date
    Jun 2003
    Location
    Melbourne, Australia
    Posts
    440
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    How do you mean? The code in Person.php is autogenerated by the DB-parser/lexer.
    I notice that tableGen.php includes a primary key set/get template. Both your examples of generated files ('Person' and 'Group') use this, and, unless one were going to change the tableGen script, the getPk() and setPk() functions would always take this form.

    So why are these methods abstract in DataObject.php and "implemented" them in the template? Couldn't you just put them in the DataObject class?
    Last edited by auricle; Jan 27, 2006 at 20:53. Reason: Clarity
    Zealotry is contingent upon 100 posts and addiction 200?

  15. #65
    ********* 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
    How would you make this look code-wise in the worker? How would you assign different relations, etc. (Just had to quote this once more to ask this question, soz.)
    Pretty much how you've done it. The value comes not in loading the objects, but in writing them.

    Say you want to change a group that a person belongs to. With the intermediate table it's obvious that you delete the membership and then create another membership attached to another group, creating a new group if needed. Or, if you want to change the group name for everyone on the other hand, you just change the group name. All very explicit and clear. If you try to hide all of those options behind some fancy N:M interface, it all gets terribly messy .

    Link tables are also a sign of a half finished data model to me, but I think I'm just being snooty .

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

  16. #66
    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
    I notice that tableGen.php includes a primary key set/get template. Both your examples of generated files ('Person' and 'Group') use this, and, unless one were going to change the tableGen script, the getPk() and setPk() functions would always take this form.

    So why are these methods abstract in DataObject.php and "implemented" them in the template? Couldn't you just put them in the DataObject class?
    They're abstract in the DataObject because they have to be implemented.

    It's not possible to create the set/getPk() in the DataObject as I don't know the name of the PK in every table that will be created. In all my cases I use Id for Primary Key - but some people use "id", "pk", "key", etc.

  17. #67
    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
    Pretty much how you've done it. The value comes not in loading the objects, but in writing them.

    Say you want to change a group that a person belongs to. With the intermediate table it's obvious that you delete the membership and then create another membership attached to another group, creating a new group if needed. Or, if you want to change the group name for everyone on the other hand, you just change the group name. All very explicit and clear. If you try to hide all of those options behind some fancy N:M interface, it all gets terribly messy .

    Link tables are also a sign of a half finished data model to me, but I think I'm just being snooty .

    yours, Marcus
    Ok cheers, I think I just missunderstood you from the begining then - as the way you describe now and the way I showed is for me the "standard" way to simulate/create N:M relations in Relational Databases .

    One last question: You do believe that the Intermediate table should be an object, and not just be "abstracted"-away and never shown?

  18. #68
    ********* 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
    One last question: You do believe that the Intermediate table should be an object, and not just be "abstracted"-away and never shown?
    Yes, this is what I'm getting at. DataAccessor ORM tools make the infrastructure cleaner by giving a pseudo object interface, but you still need domain objects to manipulate them.

    Regarding the "DataObject" name, I would go with "DataAccessor" (Nock's book) or "Persistent" (Ambler and many others).

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

  19. #69
    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
    Yes, this is what I'm getting at. DataAccessor ORM tools make the infrastructure cleaner by giving a pseudo object interface, but you still need domain objects to manipulate them.
    Now when I think about it "logically" it seems much better the way you proposed then trying to hide the intermediate table, yes.

    Quote Originally Posted by lastcraft
    Regarding the "DataObject" name, I would go with "DataAccessor" (Nock's book) or "Persistent" (Ambler and many others).
    Ah nice, DataAccessor sounds better then "Persistent" imho.

    Hm, another thing that just sprung to mind - take these two tables for example:


    Code:
    #Person
    - id (pk)
    - name
    - age
    
    #Order
    - id (pk)
    - person (fk > Person.id)
    - item
    - amount
    Should both objects be aware of the relation, or should it be one way? In code:

    PHP Code:
    <?php
    // Both-Way:
    echo $person->order[1]->item// Works - Echos the item of the 1st order
    echo $order->person->name// Works - Echos the name of the person that placed the order
    // One-way
    echo $person->order[1]->item // Doesn't work as "Person" isn't aware of the relation to Order
    echo $orders->person->name// Works - Echos the name of th eperson that placed the order
    ?>
    My question is: Should a "fake/virtual" property be set up for the Table/Object that doesn't hold the FK in a 1:* relationship?

  20. #70
    ********* 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
    Should both objects be aware of the relation, or should it be one way?
    I did it one way only in Changes and regretted it. You need a getParent() type accessor occasionally.

    I decided to join in read only data from the parent to cut down on this type of (inefficient) parent fetching, and that worked. The problem is that you pass a collection child into a method and it suddenly wants the parent again. If the ID is known, you can shortcut the database lookup, so adding a getParent() type method (renamed to something domain like in Changes) will be an early fix once we get back to the code.

    The problem is when this method is called in a loop, without the parents being loaded. One solution is to do a count(*) on the parent table after the second such query. If it looks feasible, load the lot.

    Anyway, users just expect this facility. It's part of making them comfortable.

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

  21. #71
    SitePoint Evangelist
    Join Date
    Jun 2003
    Location
    Melbourne, Australia
    Posts
    440
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by thr
    It's not possible to create the set/getPk() in the DataObject as I don't know the name of the PK in every table that will be created.
    PHP Code:
        public function getPk(){
            return $this->_<?php echo $f?>;
        }
    Yes, of course. I misread the template file.
    Zealotry is contingent upon 100 posts and addiction 200?

  22. #72
    SitePoint Guru thr's Avatar
    Join Date
    Jun 2003
    Location
    Sweden
    Posts
    664
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    After much trouble I've realized that the "autosensing" of what table fields are objects(those that should hold FKs in the relational db) is impossible, because situations such as:


    Code:
    #Person
     - id(pk)
     - name
    
    #Message
     - id (pk)
     - from (fk > Person.id)
     - to (fk > Person.id)
     - message
     - date
    Are possible, and there is no logical way to determine the relation of the from/to fields to the Person.id as far as I can see. With this in mind I realized that my dream to be able to do this without much configuration is impossible - XML here I come, I haven't decided on the best aproach yet tho - I've come up with two different solutions:

    1. Define everything in XML and nothing in SQL and no Classes. On every run the "engine"(UnitOfWork) checks for classes / databases and creates them as needed from the schema. A simple version of this could look like (at the moment I'm doing MySQL only so the types on the schema is thier exact MySQL versions):
    Code:
    <?xml version="1.0" encoding="iso-8859-1"?>
    <model>
    	<class name="Person" key="id">
    		<field name="name" type="varchar" />
    		<field name="age" type="integer" />
    		<field name="email" type="varchar" />
    		<collection name="memberships" type="Membership" key="member" />
    	</class>
    	<class name="Group" key="id">
    		<field name="name" type="varchar" />
    		<field name="comment" type="varchar" />
    		<collection name="members" type="Membership" key="group" />
    	</class>
    	<class name="Membership" key="id">
    		<field name="member" type="Person" />
    		<field name="group" type="Group" />
    		<field name="joinDate" type="int" />
    	</class>
    	<class name="Message" key="id">
    		<field name="sender" type="Person" />
    		<field name="recipient" type="Person" />
    		<field name="date" type="int" />
    		<field name="text" type="text" />
    	</class>
    </model>
    2. Define only the "collection" and "fk" relations in the XML + Create the SQL and Database as you usually do, a simple version of this could look something like(only XML here ofc. as I bet all of you already know SQL):
    Code:
    <?xml version="1.0" encoding="iso-8859-1"?>
    <model>
    	<class name="Person" key="id">
    		<collection name="memberships" type="Membership" key="member" />
    	</class>
    	<class name="Group" key="id">
    		<collection name="members" type="Membership" key="group" />
    	</class>
    	<class name="Membership" key="id">
    		<field name="member" type="Person" />
    	</class>
    	<class name="Message" key="id">
    		<field name="sender" type="Person" />
    		<field name="recipient" type="Person" />
    	</class>
    </model>
    So, which one would you people believe is the most efficient/good/best/etc (and why do you think it's the best/etc.) ?

  23. #73
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It'd be nice if it was abstracted, so can load a schema from any source, so could have it incoming from XML, or the RDBMs.

    Think most RDBMs are becoming SQL:2003 compliant with information_schema tables for extracting this information, and I've been experimenting with a set of DataAccessor(s) being used to gather this information.

    I'm using PDO, and its named parameter queries, as think it simplifies things abit.

    PHP Code:
    class Table extends DataAccessor
    {
        function 
    getSchemaName() { return $this->state['SCHEMA_NAME']; }
        function 
    getTableName() { return $this->state['TABLE_NAME']; }
            
        function 
    getColumns()
        {
            return 
    $this->unitOfWork->findBySql('Column',
                
    'SELECT * FROM information_schema.COLUMNS c
                WHERE c.TABLE_SCHEMA = :TABLE_SCHEMA AND c.TABLE_NAME = :TABLE_NAME'
    $this->state);
        }

        function 
    getConstraints()
        {
            return 
    $this->unitOfWork->findBySql('Constraint',
                
    'SELECT c.* FROM information_schema.TABLE_CONSTRAINTS c
                    WHERE c.TABLE_SCHEMA = :TABLE_SCHEMA 
                        AND c.TABLE_NAME = :TABLE_NAME'
    $this->state);
        }
        
        function 
    getReferences()
        {
            return 
    $this->unitOfWork->findBySql('Reference',
                
    'SELECT * FROM information_schema.TABLE_CONSTRAINTS c 
                    INNER JOIN information_schema.KEY_COLUMN_USAGE k
                        ON c.CONSTRAINT_SCHEMA = k.CONSTRAINT_SCHEMA
                        AND c.CONSTRAINT_NAME = k.CONSTRAINT_NAME
                    WHERE c.CONSTRAINT_TYPE = \'FOREIGN KEY\'
                            AND c.TABLE_SCHEMA = :TABLE_SCHEMA
                            AND c.TABLE_NAME = :TABLE_NAME'
    $this->state);
        }
        
        function 
    getReferencedBy()
        {
            return 
    $this->unitOfWork->findBySql('Reference',
                
    'SELECT * FROM information_schema.TABLE_CONSTRAINTS c
                    INNER JOIN information_schema.KEY_COLUMN_USAGE k
                    ON c.CONSTRAINT_SCHEMA = k.CONSTRAINT_SCHEMA
                        AND c.CONSTRAINT_NAME = k.CONSTRAINT_NAME    
                WHERE c.CONSTRAINT_TYPE = \'FOREIGN KEY\'
                        AND k.REFERENCED_TABLE_SCHEMA = :TABLE_SCHEMA
                        AND k.REFERENCED_TABLE_NAME = :TABLE_NAME
                        AND k.ORDINAL_POSITION = 1'
    $this->state);
        }
    }

    class 
    Column extends DataAccessor
    {
        function 
    getColumnName() { return $this->state['COLUMN_NAME']; }
    }

    class 
    Constraint extends DataAccessor
    {
        function 
    getConstraintType() { return $this->state['CONSTRAINT_TYPE']; }
        function 
    getConstraintName() { return $this->state['CONSTRAINT_NAME']; }
        
        function 
    getTableName() { return $this->state['TABLE_NAME']; }
        
        function 
    getColumns()
        {
            return 
    $this->unitOfWork->findBySql('ConstraintColumn',
                
    'SELECT * FROM information_schema.KEY_COLUMN_USAGE k 
                            WHERE k.TABLE_SCHEMA = :TABLE_SCHEMA AND k.TABLE_NAME = :TABLE_NAME AND k.CONSTRAINT_NAME = :CONSTRAINT_NAME
                ORDER BY k.ORDINAL_POSITION ASC'
    $this->state);
        }
    }

    class 
    Reference extends Constraint
    {
        function 
    getReferencedTableName() { return $this->state['REFERENCED_TABLE_NAME']; }
        function 
    getReferencedColumnName() { return $this->state['REFERENCED_COLUMN_NAME']; }
    }

    class 
    ConstraintColumn extends DataAccessor
    {    
        function 
    getColumnName() { return $this->state['COLUMN_NAME']; }
        
        function 
    getReferencedColumnName() { return $this->state['REFERENCED_COLUMN_NAME']; }


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

    You have many more options than this.

    1) As you've pointed out, code generate the SQL and also the classes on demand. This is tricky and you may end up with a build step just to ease the pain. In most commercial environments though, this extra work is no problem and you get actual generated files to look at when youare not sure what's happening. That's the way most persistence layers do it.

    2) The problem you presented is not a show stopper. You have a "from" attribute and a "to" attribute and they both give back a person. Assuming you can introspect the foreign keys (with MySQL 5 you can do most of SQL2003), I don't see the issue. The Identity maps covers you against "from" and "to" being the same person. What am I missing ?

    3) Mixing XML and SQL is just yucky unles you embed the SQL in the XML. It's still yucky.

    4) Use the doclet/annotations system in the way hibernate does...
    PHP Code:
    class SaveMe {

        
    /**@ persistent varchar(255) username primary key */
        
    function getUsername() { ... }

    The Reflection system reads these in PHP 5.1+.

    5) Use the variable names...
    PHP Code:
    class PersonAccessor extends DataAccessor {
        private 
    $username_varchar_primarykey;

    A bit freaky, but it could work. Every string is a potential programming language .

    6) Have a separate schema object...
    PHP Code:
    $schema = new Schema('PersonAccessor');

    class 
    PersonAccessor {
        ...
        static function 
    getMapping() {
            
    $mapping = new Mapping('people');
            
    $mapping->primaryKey('username')->varChar(255);
            
    $mapping->name->varChar(255)->notNull;
            
    $mapping->is_registered->boolean->default(true);
            return 
    $mapping;
        }

    I bet there are lot's more besides.

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

  25. #75
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lastcraft
    5) Use the variable names...
    PHP Code:
    class PersonAccessor extends DataAccessor {
        private 
    $username_varchar_primarykey;

    A bit freaky, but it could work. Every string is a potential programming language .
    Arg.


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
  •