SitePoint Sponsor

User Tag List

Results 1 to 12 of 12
  1. #1
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Reflection and object/relational mapping

    I was browsing the reflection docs in the php manual, and noticed that you can get and set properties of an object:

    PHP Code:
        public mixed getValue(stdclass object)
        public 
    void setValue(stdclass objectmixed value
    What do you guys think about using this to construct and deconstruct objects in a data mapper? It's breaking encapsulation, but the data mapper should know about the internals of the object anyways. That would mean that if the object isn't used heavily by the view, you wouldn't necessarily need a getter and setter for every single property in a given object... right? Or am I missing something?

  2. #2
    SitePoint Zealot Amenthes's Avatar
    Join Date
    Oct 2006
    Location
    Bucharest, Romania
    Posts
    143
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'll assume you were talking about non-public members, otherwise setters and
    getters would make no sense. So, if that's the case, setValue() won't work as
    expected as far as I know. A week ago or so, Sebastian Bergmann wrote
    on php.internals that he couldn't use setValue() with non-public members and
    reported it as a bug. I haven't tested it though. It may have been solved for 5.3

    On breaking encapsulation I would say that, yes, the mapper should know about
    the structure of the object it constructs, nevertheless, there may be business
    logic in there too. Just to save you from repeating that logic in the mappers.
    Like validating a certain business rule on a field or something.
    I'm under construction | http://igstan.ro/

  3. #3
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Amenthes View Post
    I'll assume you were talking about non-public members, otherwise setters and
    getters would make no sense. So, if that's the case, setValue() won't work as
    expected as far as I know. A week ago or so, Sebastian Bergmann wrote
    on php.internals that he couldn't use setValue() with non-public members and
    reported it as a bug. I haven't tested it though. It may have been solved for 5.3
    It looks like you need to call $prop->setAccessible(true); before you can access private properties.

    Quote Originally Posted by Amenthes View Post
    On breaking encapsulation I would say that, yes, the mapper should know about
    the structure of the object it constructs, nevertheless, there may be business
    logic in there too. Just to save you from repeating that logic in the mappers.
    Like validating a certain business rule on a field or something.
    That's a good point.

  4. #4
    SitePoint Zealot Amenthes's Avatar
    Join Date
    Oct 2006
    Location
    Bucharest, Romania
    Posts
    143
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by allspiritseve View Post
    It looks like you need to call $prop->setAccessible(true); before you can access private properties.
    It appears the bug concerns exactly the aspect you mentioned.

    http://bugs.php.net/bug.php?id=46718
    I'm under construction | http://igstan.ro/

  5. #5
    SitePoint Wizard Ren's Avatar
    Join Date
    Aug 2003
    Location
    UK
    Posts
    1,060
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think using Reflection is a bit overkill.

    You can simple cast an object to an array to get all its attributes. Its just a matter of filtering, and rekeying that array to turn it into something you can use with PDO.

    PHP Code:
    <?php

    class TinyObjectMapper
    {
        protected 
    $className;
        protected 
    $mapOut;

        function 
    __construct($className$mapOut) { $this->className $className$this->mapOut $mapOut; }

        function 
    getClassName() { return $this->className; }

        function 
    mapOut($object)
        {
            
    $array = (array)$object;
            return 
    array_combine($this->mapOutarray_intersect_key($array$this->mapOut));
        }

        function 
    mapIn($row)
        {
            
    $r = array();
            foreach(
    $this->mapOut as $propertyName => $columnName)
                
    $r[substr($propertyName3)] = $row[$columnName];
            return 
    call_user_func(array($this->className'__set_state'), $r);
        }
    }

    class 
    Person
    {
        protected 
    $id;
        protected 
    $firstName;
        protected 
    $lastName;

        function 
    __construct($firstName$lastName$id null)
        {
            
    $this->id $id;
            
    $this->firstName $firstName;
            
    $this->lastName $lastName;
        }

        static function 
    __set_state(array $values)
        {
            return new 
    self($values['firstName'], $values['lastName'], $values['id']);
        }
    }


    class 
    PersonMapper extends TinyObjectMapper
    {
        function 
    __construct($className 'Person', array $mapOut = array())
        {
            
    $mapOut["\0*\0id"] = 'p_ID';
            
    $mapOut["\0*\0firstName"] = 'p_FirstName';
            
    $mapOut["\0*\0lastName"] = 'p_LastName';
            
    parent::__construct($className$mapOut);
        }
    }

        
    $personMapper = new PersonMapper();

        
    $homer = new Person('Homer''Simpson');

        
    var_dump($personMapper->mapOut($homer));

        
    $homer $personMapper->mapIn(array('p_ID' => 1'p_FirstName' => 'Homer''p_LastName' => 'Simpson'));

        
    var_dump($homer);

  6. #6
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren View Post
    You can simple cast an object to an array to get all its attributes.
    What about settings it's attributes?

  7. #7
    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 allspiritseve View Post
    What about settings it's attributes?
    Used the __set_state() static method, as it takes an array of attribute values and returns an new instance.

  8. #8
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren View Post
    Used the __set_state() static method, as it takes an array of attribute values and returns an new instance.
    If I used reflection I wouldn't need any such method in my domain objects though...

  9. #9
    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 allspiritseve View Post
    If I used reflection I wouldn't need any such method in my domain objects though...
    Its just not worth using Reflection, imo. Though it is easier now that can set private/protected attributes via Reflection, I suppose.

  10. #10
    Spirit Coder allspiritseve's Avatar
    Join Date
    Dec 2002
    Location
    Ann Arbor, MI (USA)
    Posts
    648
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Ren
    Its just not worth using Reflection, imo. Though it is easier now that can set private/protected attributes via Reflection, I suppose.
    I guess I'm just a fan of keeping all non-business logic out of my domain objects... what you described above sounds like an array implementation of the builder pattern. It's introducing a little complexity into the domain object; I'd rather keep all that complexity in the mapper.

  11. #11
    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 allspiritseve View Post
    It looks like you need to call $prop->setAccessible(true); before you can access private properties.
    Note that it's not available until 5.3

  12. #12
    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 allspiritseve View Post
    I guess I'm just a fan of keeping all non-business logic out of my domain objects... what you described above sounds like an array implementation of the builder pattern. It's introducing a little complexity into the domain object; I'd rather keep all that complexity in the mapper.
    I have written a Reflection based mapper.

    The mapper had a method with mapped attribute names to SqlColumn objects or another Mapper for value objects (things like Money).

    For setting search the constructor for argument of with the same name as the attribute, then look for get<AttributeName>() and set<AttributeName>() methods trying to determine how to build the object.

    Also had a subclass of the Mapper which did class table inheritance (ie when a domain object is split over multiple database tables.)

    Nice exercise, but all pretty slow, and pointless.

    There is no reason why couldn't move the __set_state() method to the Mapper. The only reason it is there, is because var_export()ing model objects would work too.


Tags for this Thread

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •