SitePoint Sponsor

User Tag List

Page 1 of 5 12345 LastLast
Results 1 to 25 of 110
  1. #1
    SitePoint Guru
    Join Date
    Dec 2003
    Location
    oz
    Posts
    819
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Single object or multiple objects per busines object

    I know this has been argued to death, but I've been reconsidering my approach to data access. I currently have a business object and a data mapper for each business object. I'm thinking however that a single object per business object might be the best idea. That way everything is done for a business object is done within the object. It simplifies the system - which I think is an important issue. Alot of systems/frameworks over-complicate things while trying to abstract out layer over layer. I can see the advantage of layers, but can also see the disadvantage with complications of too many.

    The simplicity also means that almost anyone can start modifications on an application becuse it only really has 3-4 layers now. And as far as interchanging a database - well each db has it's own query specifics, so you cant do that anyway.

    What got me to thinking about this was a link to nakedobjects (http://www.nakedobjectsorg/content.html)- which points out that a logical entity should really be an entity. It can have agregate objects to help it do its job/s, but it should be *the* place of contact for all things relating to that entity - with all the processing hidden inside it. This idea really makes sense to me.

    By seperating the data access and the business object, you are seperating the data and the behaviour - which really loses the point of encapsulation. Especially as any modification to the business object also means modification to the mapper.

    Another reason for having a mapper seperately is that a customer object, say, logically shouldn't return an iterator of a list of customer objects.
    Eg BusinessObjectIterator Customer.GetByName(string name).
    But really, why the hell not?

    This of course leaves a couple of issues. Such as if the data access is in the object itself, not every object can have a live connection like each data-mapper. I guess it could initiate a connection each time access to the db is required. A singleton is a problem as you will often require more than one connection at a time - and pooling seems too much work to deal with.

    So I guess my points originally for seperating was:
    1. help make it easy to interchange db's
    2. doesnt make sense for an object to return a iterator/list of objects
    3. keep a class small so it's easy to find aspects you need to find quickly
    4. have a class do just one thing.
    5. a connection in each business object is ALOT of connections.

    Now I'm thinking that these issues are really non-issues (except the last one) and that encapsulation is really the essence of OOP - ie everything is an entity unto which all of its data and behavior are coupled behind an interface.

    Any thoughts/comments?
    And any soln to the problem of having a db conn in each business object?

    Regards,
    Eli

  2. #2
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'll give you a couple of thoughts because you have lots of questions buried in there. First, I think in PHP your goal should be to do the simplest thing that makes sense for the application you are building; second I think your goal should not be to achieve some pattern or abstraction, but to produce a codebase that meets your feature, performance and maintainability targets; and third you should not try to code Java in PHP.

  3. #3
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lazy_yogi
    I know this has been argued to death, but I've been reconsidering my approach to data access. I currently have a business object and a data mapper for each business object. I'm thinking however that a single object per business object might be the best idea. That way everything is done for a business object is done within the object. It simplifies the system - which I think is an important issue. Alot of systems/frameworks over-complicate things while trying to abstract out layer over layer. I can see the advantage of layers, but can also see the disadvantage with complications of too many.
    Ive been researching and toying around with different options for data access in php for a long time and quite enjoy threads about this. . My personal preference is a business object and a mapper. Most people here I think like active records.

    Quote Originally Posted by lazy_yogi
    By seperating the data access and the business object, you are seperating the data and the behaviour - which really loses the point of encapsulation. Especially as any modification to the business object also means modification to the mapper.
    Im not sure I would group data access with the behavior of a business object. To me this is seperate.
    Quote Originally Posted by lazy_yogi
    Another reason for having a mapper seperately is that a customer object, say, logically shouldn't return an iterator of a list of customer objects.
    Eg BusinessObjectIterator Customer.GetByName(string name).
    But really, why the hell not?
    I dont see this as a problem if you are using an active record. This is usually seperated into a new object with common "find" operations for simplicity but should only be done if seperating these operations actually makes things simpler.

    Quote Originally Posted by lazy_yogi
    This of course leaves a couple of issues. Such as if the data access is in the object itself, not every object can have a live connection like each data-mapper. I guess it could initiate a connection each time access to the db is required. A singleton is a problem as you will often require more than one connection at a time - and pooling seems too much work to deal with.
    This is one of my biggest problems with using an active record or similar.
    Quote Originally Posted by lazy_yogi
    So I guess my points originally for seperating was:
    1. help make it easy to interchange db's
    2. doesnt make sense for an object to return a iterator/list of objects
    3. keep a class small so it's easy to find aspects you need to find quickly
    4. have a class do just one thing.
    5. a connection in each business object is ALOT of connections.

    Now I'm thinking that these issues are really non-issues (except the last one) and that encapsulation is really the essence of OOP - ie everything is an entity unto which all of its data and behavior are coupled behind an interface.
    1. Using a mapper or not, isnt going to make changing databases easier. There should be another layer there.
    2. You dont instatiate a new Customer to find Customers, you instantiate a new CustomerFinder to find Customers.

    I think 3,4 and 5 are still issues but of course it depends on what you need you system to do. I wouldnt say a program that follows or doesnt follow those rule is right or wrong.


    My current method for data access is similar to jdo (arborint may not be happy ). I pass an object I call PHPDataMapper ($pdm) through my controller like most people pass a db object. There is a single data mapper for all objects, instead of 1 mapper for each object.

    There are advantages and disadvantages.

    Its alot to explain so heres an example:
    PHP Code:
    // find all customers
    $query $pdm->newQuery('Customer');
    $customers $query->execute();
    foreach(
    $customers as $customer) {
        echo 
    $customer->getName();

    // find all customers named "Joe"
    $query $pdm->newQuery('Customer''name == ?');
    $customers $query->execute('Joe');
    foreach(
    $customers as $customer) {
        echo 
    $customer->getName();

    // find all posts
    $query $pdm->newQuery('Post');
    $posts $query->execute();
    foreach(
    $posts as $post) {
        echo 
    $post->getTitle();

    // find all customers age's and id's in Customer Objects
    $query $pdm->newQuery('Customer');
    $query->setResult('id, age');
    $customers $query->execute();
    foreach(
    $customers as $customer) {
        echo 
    $customer->getId();

    // insert a role
    $role = new Role();
    $role->setDescription('A Role');
    $pdm->insert($role);

    // update a role;
    $role->setDescription('A Role (Revised)');
    $pdm->update($role);

    // delete a customer
    $delete $pdm->newDelete('Customer''id == ?');
    $delete->execute(1); 

  4. #4
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I think we should clarify difference between separatiing what you call "business logic" and what you call "database access" from the implementation of that separation. Putting them in separate classes and putting a shared mapper in between as above may achieve less separation than a simple DAO because the mapper becomes brittle.

    I know the Java folks use fancy terms like "business logic" but I think PHP programmers might divide things more simply as below and implement one or more separations depending on the application.

    SQL - these are the application specific commands sent to the database and the assinging variables into those commands.

    Code - the code that works on data coming from or going to a database.

    DB - this is the library of functions uses to access the database.

    Libraries - reusable code that is not application specific

  5. #5
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I want to point out that its not just the Java people who have fancy terms like business logic. These terms are common to all application development and have a common definition regardless of technology. You have a point though, that implementation differs from technology to technology.

    To be sure we all are on the same page Ill quote fowler on what business logic is and what data access is.

    Quote Originally Posted by Martin Fowler - POEAA pg.20
    Data source logic is about communicating with other systems that carry out tasks on behalf of the application. ... the biggest piece of data source logic is a database ...

    Domain Logic, also referred to as business logic. This is the work that this application needs to do for the domain youre working with. It involves calculations based on inputs and stored data, validation of any data that comes in from the presentation, and figuring out exactly what data source logic to dispatch, depending on commands received from the presentation.
    Quote Originally Posted by arborint
    Putting them in separate classes and putting a shared mapper in between as above may achieve less separation than a simple DAO because the mapper becomes brittle.
    Maybe I should clarify what is going on in the above code. The PHPDataMapper class is using Object-Relational Metadata Mapping. I havent made a seperate "data access" class for each object and then put a shared mapper in between. I only have to describe the relationship between object and database, and then do any* query I want on it using the $pdm object.

    If you still think this is more brittle, please explain.

    *I havent implemented many-to-many etc relationships yet. Still thinking out the best way to do this in php.

  6. #6
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by lazy_yogi
    What got me to thinking about this was a link to nakedobjects (http://www.nakedobjectsorg/content.html)- which points out that a logical entity should really be an entity. It can have agregate objects to help it do its job/s, but it should be *the* place of contact for all things relating to that entity - with all the processing hidden inside it. This idea really makes sense to me.
    I got a chance to quickly skim over the naked objects stuff and it seems like its aimed specifically towards administration "CRUD" type tasks and has alot to do with the UI.

    I couldnt really find much about how the Naked Object framework does data access but this quote shows they recommend a mapper in some cases anyway.
    Quote Originally Posted by http://www.nakedobjects.org/downloads.html
    The SQL Object Store provides a link to a relational database via JDBC. At present this object store will automatically create a database schema and map the objects to it. If you already have tables and they have a one-to-one correspondence with the business objects then these can be mapped in the configuration file. Complicated mappings, however, must be done by writing a mapper class.
    I dont think they are advocating moving every possible thing that a "Customer" could do into a Customer class but to aggregate these objects and have a single interface for commands like you said.

    On top of moving the datasource interface into the Customer class they are also suggesting you move the controller interface into the Customer class.

    Im not sure I like this idea too much.

  7. #7
    SitePoint Guru
    Join Date
    Dec 2003
    Location
    oz
    Posts
    819
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Originally Posted by http://www.nakedobjects.org/downloads.html
    The SQL Object Store provides a link to a relational database via JDBC. At present this object store will automatically create a database schema and map the objects to it. If you already have tables and they have a one-to-one correspondence with the business objects then these can be mapped in the configuration file. Complicated mappings, however, must be done by writing a mapper class.
    Most of what I do in information systems is CRUD, not complicated mappings - which I why I think a mapper in my case is unnecessary.

  8. #8
    SitePoint Addict
    Join Date
    Apr 2004
    Location
    Melbourne
    Posts
    362
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    http://tanus.dotgeek.org/?viewObject=28

    My take on it is to have a business objects as the child of a datamapping object. This datamapping object loads all of the properties (accessible by overloading __get and __set methods) for a certain classtype by reading from the database what properties it has access to.

    For example, the parent EntityObject does basic things like

    Code:
    private $properties;
    private $allowedProperties;
    
    function __construct($classtype)
    { 
        Load from the database property table the properties this classtype can have.
        foreach result
            add the property to the allowedproperties list.
            create a property object which stores the value of this property (this is updated when a __set call is made, and retrieved on a __get call)
    }
    
    function SaveObject()
    {
        if a new object, save it to the database 'class' table, and retrieve its id.
        if a restored object, update its settings (name, path)
        
        iterate through property objects and save them to the 'propertyvalue' table
    }
    
    
    function LoadObject(id)
    {
         loads the specific values for an object from the 'class' table. 
         iterates through this object's properties and loads them from the propertyvalue table
        // performs permiission checks on the object to make sure the current user has access to it (uses unix style permission triplet). Returns false otherwise.
    }
    So the definition for an 'entityfolder' object looks like http://tanus.dotgeek.org/images/xml.png
    This is enough to be able to create an entityobject with all the properties of an 'entityfolder' class and be able to access them as $object->foldername = 'name'; However, to add business logic, I create an actual entityfolder class which extends entityobject;

    Code:
    class EntityFolder extends EntityObject
    {
        // all the business logic for this class goes here. The only interface this 
        // object might have called on it from the parent that relate directly to db usage 
        // is SaveObject(), which writes it.
    }
    
    
    // to load an object, a utility class takes care of loading the type, then loading the object
    
    public function LoadObjectById($id) {
        // get the type of the class this id represents
        $type = self::GetTypeFor($id);
        // Create an object of that type
        $obj = new $type();
        $obj->LoadObject($id);
        
        return $obj;
    }
    It makes creating database aware objects VERY easy (XML definitions!), and simplifies the database access needed to perform most things. I'll be open sourcing it soon, once I get the admin side of things cleaned up a bit.

  9. #9
    SitePoint Zealot sike's Avatar
    Join Date
    Oct 2002
    Posts
    174
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Brenden Vickery
    Maybe I should clarify what is going on in the above code. The PHPDataMapper class is using Object-Relational Metadata Mapping. I havent made a seperate "data access" class for each object and then put a shared mapper in between. I only have to describe the relationship between object and database, and then do any* query I want on it using the $pdm object.

    If you still think this is more brittle, please explain.

    *I havent implemented many-to-many etc relationships yet. Still thinking out the best way to do this in php.
    i am using nearly the same setup as brendan for my businessobjects. i have a single class (i named it gateway) which is able to perform (nearly) any query on my bos. it uses meta descriptions of the structure to build the necessary joins. a short example :
    PHP Code:
       /*
       * Adding Article
       */
       
    $author BusinessObjectGateway::addNew('Author');
       
    $author->name 'bar';
       
    $article BusinessObjectGateway::addNew('Article');
       
    $article->setAuthor($author);
       
    $article->date time();
       
    $article->title 'foo';
       
    $article->save(); // will persist article and author
     
       /*
       * Query Article
       */
       
    $article BusinessObjectGateway::findOne('Article'"id = 1");
     
       
    /*
       * Query Articles
       */
       
    $articles BusinessObjectGateway::findMany('Article'"author.name = 'bar'");
       foreach(
    $articles as $article)
       {
       } 
    Sike

    ps. many-to-many relations give me headaches too

  10. #10
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I agree that many of the implementations listed here are reasonable solutions. They are a style of coding and may achieve separation of program logic or not.

    As to my comment about terms, I think using terms like Data source logic and Domain Logic can obscure more than clarify.

    In PHP the implementations above end up looking more like SQL generators than "business objects." And, they can become brittle when LoadObjectById($id) or findMany('Article', "author.name = 'bar'") are not sufficient as the complexity of the eode and the SQL increases.

  11. #11
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by arborint
    In PHP the implementations above end up looking more like SQL generators than "business objects." And, they can become brittle when LoadObjectById($id) or findMany('Article', "author.name = 'bar'") are not sufficient as the complexity of the eode and the SQL increases.
    They arent business objects, they're data source objects. SQL generation is one part of what they do.

    A problem I have had with using a active record or mapper that is hand coded is that it becomes very unwieldily.

    Lets say we have a active record with this interface:
    PHP Code:
    class Customer {
        
    // all setters / getters
        
    function findAll();
        function 
    findAllWithLastNameLike($lastname);
        function 
    findById($id);

    Then when we are optimising SQL we find that we use findAllWithLastNameLike on 3 pages. One where we need all the customers data, one where we only need the firstname and lastname and one where we need the firstname, lastname and id. So we change the interface to look like this:
    PHP Code:
    class Customer {
        
    // all setters / getters
        
    function findAll();
        function 
    findAllWithLastNameLike($lastname);
        function 
    findAllWithLastNameLikeOnlyFirstLast($lastname);
        function 
    findAllWithLastNameLikeOnlyFirstLastId($lastname);
        function 
    findById($id);

    In a large application when you factor in different variations in the optimised SQL including limits, ordering etc you usually end up with a large amount of functions that get used only on one page in a very specific way. You now have huge business objects that take into account every different way you need to find themselves from the data source.

    If you build a ORMapping solution that does handle every situation you never have to worry about what the database looks like except when you descibe the relationship, which might be as simple as:
    PHP Code:
    <class name="Team" table="TEAM">
           <
    field name="id" column="TEAM_ID" />
           <
    field name="name" column="TEAM_NAME" />
           <
    field name="city" column="TEAM_CITY" />
       </class>
       <class 
    name="Player" table="PLAYER">
           <
    field name="id" column="PLAYER_ID" />
           <
    field name="name" column="PLAYER_NAME" />
           <
    field name="team" column="PLAYER_TEAM_ID">
              <
    join class="Team" field="id" />
           </
    field>
       </class> 
    You get to focus on the business objects and their relationship to each other instead of the database tables relationships.

    In a complete solution you can do very complex queries using 1:*, *:*, *:1, 1:1, bidirectional, ordering, limits, grouping etc using a very simple domain oriented interface.

    So to find all the teams and its players who are in Dallas limited to 20 results offset by 20, orderd by team name we might do this:
    PHP Code:
    $query $pdm->newQuery('Team','Team.city == ?');
    $query->setRange(2040);
    $query->setOrdering('Team.name ascending');
    $teams $query->execute('Dallas');
    foreach(
    $teams as $team) {
       echo 
    $team->getName();
       foreach(
    $team->getPlayers as $player) {
          echo 
    $player->getName();
       }


  12. #12
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    A couple of points. First, I have no problem with "data source objects" as you describe them, not that they tend to grow a number of query specific functions. It should be noted that in PHP it often makes sense to split up these objects so they are focused on specific areas or pages of the applilcation. This is different than a traditional linked executable where the object is usually resident with the code calling it.

    A PHP application is "linked" for each request so a different kind of care needs to be taken in the design. This is where many of the traditional design theories fall short when implemented in PHP for web apps.

    Likewise I think there is a valid question whether the complexity of maintaining OR Mapping XML and code are worth the trouble in PHP. Again, in traditional applications you can have large objects and maps where it makes sense.

    I'd be interested in what you think the decision points are for deciding whether to use the simpler DAO approach or move to a ORMapper?

  13. #13
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by arborint
    I'd be interested in what you think the decision points are for deciding whether to use the simpler DAO approach or move to a ORMapper?
    For me, I would go with the ORMapper first everytime. When optimizing if you find a page that is slow and have found the problem to be the ORMapper (Not the sql) then I would move to a DAO or similar. If there is still a problem go with straight mysql_query($conn, $sql);. Ive never run into this situation in the short time Ive been using an ORMapper but this would be my strategy.

    Quote Originally Posted by arborint
    Likewise I think there is a valid question whether the complexity of maintaining OR Mapping XML and code are worth the trouble in PHP. Again, in traditional applications you can have large objects and maps where it makes sense.
    Ive found that I spend much less time maintaining the XML than I would have spent writing SQL and mapping objects to objects (everything invloved in hand coding a datasource object). I have spent a heck of alot of time writing the ORMapper and its not a complete solution yet but there are open-source products like Propel that probably do a better job.

    My number 1 goal is to get a product to the customer as quick as possible with the least bugs possible. Using an ORMapper has decreased the time needed by a good amount.

    The downside I was expecting was that using the ORMapper would increase load considerably but this hasnt been a problem... yet anyway.

  14. #14
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'd be interested to see more how your code works.

    My experience is that "less time maintaining the XML ... than SQL" plus "a heck of alot of time writing the ORMapper and its not a complete solution yet" equals about the same amount of time both ways but more code to maintain for the mapper. I have also noticed the performance hit of loading the map data on high traffic sites.

  15. #15
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Youre right, at this point I may not have saved any time but I figure Im investing in the future. Once the ORMapper is done I wont have to do anything to it and Ill be saving lots of time.

    Currently Im porting the code to php 5 and doing some re-writing. Here's an early version of the mappings (no joins, types).

    PHP Code:
    class ObjectRelationMap {
        private 
    $doc;
        private 
    $classMaps = array();
        public function 
    __construct($map) {
            if (
    file_exists($map)) {
                
    $this->doc DOMDocument::load($map);
            } else {
                
    $this->doc DOMDocument::loadXML($map);
            } 
        } 
        public function 
    getClassMap($className) {
            if (!
    array_key_exists($className$this->classMaps)) {
                
    $xp = new DomXPath($this->doc);
                
    $nodeList $xp->query(
                    
    "//*[@name = '" $className "']");
                if (
    $nodeList->length == 0) {
                    throw 
    SQLException();
                } 
                
    $classMap = new ClassMap(
                    
    $nodeList->item(0)->getAttribute('name'),
                    
    $nodeList->item(0)->getAttribute('table'));
                
    $this->getFieldMaps($classMap$nodeList->item(0));
                
    $this->classMaps[$className] = $classMap;
            } 
            return 
    $this->classMaps[$className];
        } 
        private function 
    getFieldMaps($classMap$domElement) {
            foreach(
    $domElement->childNodes as $node) {
                if (
    $node instanceof DomElement) {
                    
    $classMap->addField(
                        
    $node->getAttribute('name'),
                        
    $node->getAttribute('column'));
                } 
            } 
        } 

    class 
    ClassMap {
        private 
    $tableName;
        private 
    $className;
        private 
    $fields = array();
        public function 
    __construct($className$tableName) {
            
    $this->className $className;
            
    $this->tableName $tableName;
        } 
        public function 
    addField($fieldName$columnName) {
            
    $this->fields[$fieldName] = new FieldMap(
                
    $fieldName,
                
    $columnName);
        } 
        public function 
    getClassName() {
            return 
    $this->className;
        } 
        public function 
    getTableName() {
            return 
    $this->tableName;
        } 
        public function 
    getFields() {
            return 
    $this->fields;
        } 

    class 
    FieldMap {
        private 
    $fieldName;
        private 
    $columnName;
        public function 
    __construct($fieldName$columnName) {
            
    $this->fieldName $fieldName;
            
    $this->columnName $columnName;
        } 
        public function 
    getFieldName() {
            return 
    $this->fieldName;
        } 
        public function 
    getColumnName() {
            return 
    $this->columnName;
        } 

    I havent done any testing on the effect loading the xml has but the alternative is to not read any xml and just build the maps by hand. Even in a situation where you have 40-50 object-relations its no different than building 50 customer objects without having to go to a database.

    PHP Code:
    class ObjectRelationMap {
        private 
    $classMaps = array();
        public function 
    __construct() {
            
    $classMap = new ClassMap('Team''TEAM');
            
    $classMap->addField('id''TEAM_ID');
            
    $classMap->addField('name''TEAM_NAME');
            
    $classMap->addField('city''TEAM_CITY');
            
    $this->classMaps['Team'] = $classMap;

            
    $classMap = new ClassMap('Player''PLAYER');
            
    $classMap->addField('id''PLAYER_ID');
            
    $classMap->addField('name''PLAYER_NAME');
            
    $classMap->addField('team''PLAYER_TEAM_ID');
            
    $this->classMaps['Player'] = $classMap;
        } 
        public function 
    getClassMap($className) {
            if (
    array_key_exists($className$this->classMaps)) {
                return 
    $this->classMaps[$className];
            } else {
                throw 
    SQLException();
            } 
        } 


  16. #16
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    Brazil,Maringá-PR
    Posts
    128
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    First:
    I would like to say that the discussions on this forums are the best Iīv ever seen. Does anybody knows other forums that has design level discussions like this one?

    Second:
    ORMapper, Data source logic and Domain Logic are new terms to me. Where can I learn more about?

    Now, letīs rock...
    Brenden Vickery, I donīt like this "findAllWithLastNameLikeOnlyFirstLastId". I think itīs like the Ant-Pattern God Object. Your object will become a repository of procedures.
    I donīt like this too:
    PHP Code:
    $query $pdm->newQuery('Team','Team.city == ?'); 
    $query->setRange(2040); 
    $query->setOrdering('Team.name ascending'); 
    $teams $query->execute('Dallas'); 
    For me itīs just a sql builder. The same code is clear to read doing this:
    "SELECT * FROM Team WHERE Team.city == 'Dallas' ORDER BY Team.name ASC LIMIT 20, 40"
    The only reason to do this is if this class interface many databases drivers.

    sike
    Your solution for me is the more plausive. Your code didnīt say its data is coming from a database, it could be a simple text file or an array dump. IMHO Itīs the best abstraction but itīs the less seen in the forums world around. If possible, I would like to discuss more about this one.

  17. #17
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hey Brenden, from now on I'm starting my comments with "Now, letīs rock..."

  18. #18
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    What is the difference between the two ObjectRelationMap classes? Is the lower one supposed to inherit the the upper one?

  19. #19
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Now, letīs rock...

    I like that.

    Quote Originally Posted by neves
    For me itīs just a sql builder. The same code is clear to read doing this:
    "SELECT * FROM Team WHERE Team.city == 'Dallas' ORDER BY Team.name ASC LIMIT 20, 40"
    The only reason to do this is if this class interface many databases drivers.
    Hi Neves,

    I think youve missed the point. Those four lines of code is more equivalent to this in fairly procedural php: (Im leaving out database independance)
    PHP Code:
    function findTeamsNamedWithLimitOrderedByName($city$limit$offset) {
        
    $sql 'SELECT ' 
               
    ' TEAM.TEAM_ID, TEAM_NAME, TEAM_CITY, ' 
               
    ' PLAYER.PLAYER_ID, PLAYER.PLAYER_NAME ' 
               
    'FROM ' 
               
    ' TEAM, PLAYER ' 
               
    'WHERE ' 
               
    ' TEAM.TEAM_ID = PLAYER.PLAYER_TEAM_ID ' 
               
    'AND ' 
               
    ' TEAM.TEAM_CITY = \'' mysql_escape_string($city) . '\'' 
               
    'ORDER BY ' 
               
    ' TEAM.TEAM_NAME ASC ' 
               
    'LIMIT ' . (int)$limit ', ' . (int)$offset;
        
    $teams = array();
        
    $resource mysql_query($conn$sql);
        while (
    $row mysql_fetch_array($resourceMYSQL_ASSOC)) {
            if (
    array_key_exists($row['TEAM_ID']), $teams) {
                
    $team $teams[$row['TEAM_ID']];
                
    $player = new Player();
                
    $player->setName($row['PLAYER_NAME']);
                
    $player->setId($row['PLAYER_ID']);
                
    $team->addPlayer($player);
            } else {
                
    $team = new Team();
                
    $team->setId($row['TEAM_ID']);
                
    $team->setName($row['TEAM_NAME']);
                
    $team->setCity($row['TEAM_CITY']);
                
    $player = new Player();
                
    $player->setName($row['PLAYER_NAME']);
                
    $player->setId($row['PLAYER_ID']);
                
    $team->addPlayer($player);
                
    $teams[$row['TEAM_ID']] = $team;
            } 
        } 
        return 
    $teams;


    $teams findTeamsNamedWithLimitOrderedByName('Dallas'2020);

    foreach(
    $teams as $team) {
        echo 
    $team->getName();
        foreach(
    $team->getPlayers() as $player) {
            echo 
    $player->getName();
        } 

    Quote Originally Posted by arborint
    What is the difference between the two ObjectRelationMap classes? Is the lower one supposed to inherit the the upper one?
    The difference is one gets the ORMap from an xml file and the other lets you define the relationships in php instead of xml.

    You can use one way or the other. Using the php defined map will negate the need to parse xml on each request.
    Last edited by Brenden Vickery; Oct 7, 2004 at 17:52.

  20. #20
    SitePoint Wizard
    Join Date
    Aug 2004
    Location
    California
    Posts
    1,672
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    My changes need to be refactored into cleaner code, but maybe something like:
    PHP Code:
    class ObjectRelationMap {
        private 
    $doc;
        private 
    $classMaps = array();

        public function 
    __construct() {
    // nothing to do now?
        
    }

        public function 
    xmlClassMap($map$className) {
            if (
    file_exists($map)) {
                
    $this->doc DOMDocument::load($map);
            } else {
                
    $this->doc DOMDocument::loadXML($map);
            }
            if (!
    array_key_exists($className$this->classMaps)) {
                
    $xp = new DomXPath($this->doc);
                
    $nodeList $xp->query(
                    
    "//*[@name = '" $className "']");
                if (
    $nodeList->length == 0) {
                    throw 
    SQLException();
                }
                
    $classMap = new ClassMap(
                    
    $nodeList->item(0)->getAttribute('name'),
                    
    $nodeList->item(0)->getAttribute('table'));
                
    $this->getFieldMaps($classMap$nodeList->item(0));
                
    $this->classMaps[$className] = $classMap;
            }
            return 
    $this->classMaps[$className];
        }

        public function 
    arrayClassMap($array) {
            foreach (
    $array as $class) {
                
    $classMap = new ClassMap($class['name'], $class['table']);
                foreach (
    $class['fields'] as $field) {
                    
    $classMap->addField($field['field'], $field['column']);
                }
                
    $this->classMaps[$class['name']] = $classMap;
        }

        public function 
    getClassMap($className) {
            if (
    array_key_exists($className$this->classMaps)) {
                return 
    $this->classMaps[$className];
            } else {
                throw 
    SQLException();
            }
        }

        private function 
    getFieldMaps($classMap$domElement) {
            foreach(
    $domElement->childNodes as $node) {
                if (
    $node instanceof DomElement) {
                    
    $classMap->addField(
                        
    $node->getAttribute('name'),
                        
    $node->getAttribute('column'));
                }
            }
        }


  21. #21
    SitePoint Zealot
    Join Date
    Jul 2004
    Location
    Brazil,Maringá-PR
    Posts
    128
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Now, Letīs rock...
    you are making funny with my old style?

    Brenden Vickery, the code inside the findTeamsNamedWithLimitOrderedByName looks more like a code inside an Action in a MVC framework. Perhaps putting this on the model make it more reusable. Maybe it could go inside a Helper Object, just to donīt full the model object with Action methods.

  22. #22
    SitePoint Guru
    Join Date
    Dec 2003
    Location
    oz
    Posts
    819
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Yea, a single data mapper is a nice idea. It'd clean up the system quite nicely. (And I quite like the metadata idea)

    But as mentioned, the problem of inflexibility with more complex queries is too big an issue to ignore. I think native sql is the way to go becuase sql is pretty simple - especially considering the complex job it does. And it's seperated into a db from a language for a good reason. If I could use the full power of sql and still have only one datamapper that would be idea. I dont know if that is possible tho.

  23. #23
    SitePoint Zealot sike's Avatar
    Join Date
    Oct 2002
    Posts
    174
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    hi,

    nice to see this thread developing in a nice discussion.

    Neves:
    yes you are right, i have written simple drivers for xml and plaintext to ensure i could use them for persisting the bos.

    bredan;
    did you ever think of using uml for modeling your bos? i am writing a xmi reader and some generators (sql, meta, php) which transforms uml to bos. i went this route because describing this in xml gets kind of ugly if the systems grows (imho).

    aborint:
    i think bos will work for most cases without using some special sql stuff. in my experience the only show stoppers are reporting and statistics but thats a problem for most orm systems and i have no problem with using the datasource layer direclty for such tasks.


    brendan has mentioned that he would start every project with orm and go back step by step if needed and i think he is right with this.
    what makes me think at the moment are "lazy read" associations for large collections (e.g. Article->Comments).

    Sike

    ps. for the lurkers : http://www.agiledata.org/essays/mapp....html#AgileDBA
    Last edited by sike; Oct 8, 2004 at 05:31.

  24. #24
    Non-Member
    Join Date
    Sep 2004
    Location
    Florida
    Posts
    19
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Using the php defined map will negate the need to parse xml on each request.
    following the xml route has its advantages of course but i like this idea better is there any chance you could post more script such as the scripts that this example uses?

    PHP Code:
    $query $pdm->newQuery('Team','Team.city == ?'); 
    $query->setRange(2040); 
    $query->setOrdering('Team.name ascending'); 
    $teams $query->execute('Dallas'); 
    thankx

  25. #25
    SitePoint Addict
    Join Date
    May 2003
    Location
    Calgary, Alberta, Canada
    Posts
    275
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by sike
    did you ever think of using uml for modeling your bos? i am writing a xmi reader and some generators (sql, meta, php) which transforms uml to bos. i went this route because describing this in xml gets kind of ugly if the systems grows (imho).
    Interesting, I hadnt thought of that. Currently Im using xslt to transform the xml mapping into business objects and was planning on doing the same sort of thing for sql.
    Quote Originally Posted by sike
    what makes me think at the moment are "lazy read" associations for large collections (e.g. Article->Comments).
    I think lazy loads are quite important aswell. But you need to know when to use them and be sure you arent doing 21 queries where 1 will do.

    For example if you lazy load the comments in your example you would do 1 query for the article and 1 query for the comments. Now, if you want multiple articles/comments and lazy load all the comments, you will do 1 query for the articles and 1 query for each collection of comments which would equal 21 queries if you have 20 articles.

    Another important thing is to be able to specify the results returned. If you only need to display the article title and id, you dont want to pull the body of the article, the author etc.

    Quote Originally Posted by lazy_yogi
    But as mentioned, the problem of inflexibility with more complex queries is too big an issue to ignore. I think native sql is the way to go becuase sql is pretty simple - especially considering the complex job it does. And it's seperated into a db from a language for a good reason. If I could use the full power of sql and still have only one datamapper that would be idea. I dont know if that is possible tho.
    I think we need to remember that you dont have to only use ORM for all queries. You can mix and match if that suits your needs.


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
  •