SitePoint Sponsor

User Tag List

Results 1 to 12 of 12
  1. #1
    SitePoint Member
    Join Date
    Oct 2003
    Location
    Williamsburg VA
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    OOP Design Issues

    Hello,

    I'm beginning work on a fairly large PHP application. This time, I've decided to go the OOP route. Rather than have a ton of SQL queries on each page, I'm referring to everything as objects.

    This all seemed great as I was planning the app, and developing an architecture. It even worked well in my little test cases. Now when I'm writing code; however, things are looking kludgy.

    For instance: If I load a User object, it loads data out of the database. Now I want to loop through the Roles the User is in... More queries to load these. Now what Permissions do these roles have... More queries again. If I wanted a list of all the Users with their Permissions, this could quickly become hundreds of queries. In the pre-OOP days, this would have been a single query.

    I could theoretically load all this information every time I load User (thus compacting it down to one query), but that would be overkill for the 99% of pages that don't need all this extra information.

    Are there any good sites that explain strategies for developing OO web apps? I have no problem with the syntax and nuts-n-bolts. It's putting objects to good use that I need help with.

    Thanks for any pointers...

    Norman

  2. #2
    ********* 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 ElNormo
    I'm beginning work on a fairly large PHP application.
    Great time to start a new approach .

    Quote Originally Posted by ElNormo
    Rather than have a ton of SQL queries on each page, I'm referring to everything as objects.
    You are actually trying to do two things at once. One is learn OO and the other is moving from data driven to a three tier architecture (model driven). You will need to learn the OO via the Design Patterns book (Gamma et al) and Refactoring (Martin Fowler). The architecture patterns have books too...

    Quote Originally Posted by ElNormo
    For instance: If I load a User object, it loads data out of the database. Now I want to loop through the Roles the User is in... More queries to load these. Now what Permissions do these roles have... More queries again. If I wanted a list of all the Users with their Permissions, this could quickly become hundreds of queries. In the pre-OOP days, this would have been a single query.
    There is nothing wrong with having complex queries in there. The OO part allows you to hide this aspect so that you can concentrate more on the business problem. OO also gives you more flexibility in development as databases are lousy at encapsulation. Databases are very good at searching whereas objects aren't, because...er...they are very good at encapsulation. You will be trading a little performance for flexibility. I'll trade two queries instead of one for this simple structure...
    PHP Code:
    class LoginFinder() {
        ...
        function &
    findLoginByUsername($username) {
            
    $query "select * from logins where username='$username'";
            
    $iterator = &$this->_connection($query);
            
    $row = &iterator->next();
            if (
    $row) {
                return new 
    Login($row);
            }
            return 
    false;
        }
    }
    class 
    PermissionsFinder() {
        ...
        function 
    findPermissionsByLogin(&$login) {
            
    $login_id $login->getId();
            
    $query "select * from permissions, roles, logins where ...";
            
    $permissions = new PermissionsSet();
            
    $iterator = &$this->_connection($query);
            while (
    $row $iterator->next()) {
                
    $permissions->allow($row['action']);
            }
            return 
    $permissions;
        }
    }
    class 
    Login {
        function 
    Login($row) { ... }
        function 
    getId() { ... }
        function 
    getPermissions() {
            if (! 
    $this->_permisions) {
                
    $finder = &new PermissionsFinder();
                
    $this->_permissions $finder->findPermissionsByLogin($this);
            }
            return 
    $this->_permissions;
        }
        ...

    Quote Originally Posted by ElNormo
    ...but that would be overkill for the 99% of pages that don't need all this extra information.
    The code above only loads the permissions when needed. When you do...
    PHP Code:
    $finder = &new LoginFinder();
    $login = &$finder->findLoginByUsername('Me');
    $permissions $login->getPermissions(); 
    It's a second query, but the client code, the code you write at the application level, is more expressive.

    Quote Originally Posted by ElNormo
    Are there any good sites that explain strategies for developing OO web apps? I have no problem with the syntax and nuts-n-bolts. It's putting objects to good use that I need help with.
    You need a whole bunch of books. Your existing data modelling books will help too. The most immediate data/OO ones are...

    1) Data Access Patterns (Nock)
    2) Patterns of Enterprise Application Architecture (Fowler)
    3) Domain Driven Design (Evans)

    The Martin Fowler one is closest to what you want right now. The code above he calls an ActiveRecord although it could be morphed into DataMapper quite easily depending on you apply it. In the longer term take a look at Roles, Responsibilities and Collaborations (Wirfs-Brock).

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

  3. #3
    SitePoint Evangelist
    Join Date
    Dec 2003
    Location
    Arizona
    Posts
    411
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Is there more information on this "Finder" pattern that is described here? I am looking for an alternative to the Data Access Object and it appears that the "Finder" pattern is quite analogous.

    Thanks,

    JT

  4. #4
    SitePoint Enthusiast Sasa's Avatar
    Join Date
    Mar 2003
    Location
    Cologne, Germany
    Posts
    60
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi ElNormo,

    in addition to what Marcus is saying I might add that there is an elegant way to hide the sql you are using in a mor oop way. The concept behind this is called object-relational mapping. And the library I would like to recommend is http://pear.php.net/package/DB_DataObject. It allows you to completely hide the way you access the database in a oo way. The samples they provide allow for a quick learning about what it is all about (I worked through them in 60 minutes). And it is exactly what Martin Fowler recommends using in Patterns of Enterprise Application Architecture for this matter. So Marcus is dead on to recommend this book as a first reading. Of course, buying Harry's books on this this site couldn't hurt either, because they contain very good advice in your situation (even a chapter on the package I recommended above.

    Have Fun
    Sasa

  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 Sasa
    And the library I would like to recommend is http://pear.php.net/package/DB_DataObject.
    I ran into some data destroying bugs in an earlier version, 0.17 I think, and didn't dare touch it after that. It is basically RowDataGateway without the finder, so it is clearly relevant, but I am a bit suspicious of inheriting from this class. How has it worked out for you?

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

  6. #6
    SitePoint Enthusiast Sasa's Avatar
    Join Date
    Mar 2003
    Location
    Cologne, Germany
    Posts
    60
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi Marcus,

    I have not used it that much, especially since I am porting all my code to Ruby since I have a server where I can install my own stuff. It seems like I won't be using PHP not this much anymore in the future to be using it now, since I only discovered it reading Harry's book... I have only tested it a little bit and I am confident that HarryF (maybe he can comment on it?) has tested it enough to give a clear recommendation for production use. Of course you would have to extensively (unit)test it yourself (but you would that anyway, right? ) It seems like a lot of improvements have been made, the current version is 1.53. A version numbre of 0.17 doesn't really suggest a stable build.

    Yours
    Sasa

  7. #7
    SitePoint Member
    Join Date
    Oct 2003
    Location
    Williamsburg VA
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Thanks

    Thanks for all your help... I'm looking at DB_DataObjects now, and it's certainly intriguing. I'll look through my Software Engineering books for more strategies, and keep an eye out for the other books you mentioned.

    It's a shame that there are TONS of great tutorials on how to make simple objects, but ignore strategies for implementing them successfully.

    Norman

  8. #8
    SitePoint Member
    Join Date
    Oct 2003
    Location
    Williamsburg VA
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Marcus,

    Quote Originally Posted by lastcraft
    Great time to start a new approach .
    I agree, that's why I want to get started right rather than fix something later.

    Apologies up front... I'm going to cheat and ask you a quick question before fully doing my research into PEAR:B_DataObject and reading some books.

    In your example, you said that finding the permissions for a user would take a second query. I agree. Now what if you wanted to get a list of all the users, and their associated permissions? Wouldn't this be one query to load all the users, then one query for each user to load the permissions? What if you wanted something one level deeper? This quickly becomes an ugly mess.

    I'm sure this issue has been tackled. Perhaps one of the documents you pointed me to has a clean solution?

    Thanks,

    Norman

  9. #9
    Non-Member
    Join Date
    Jan 2004
    Location
    Planet Earth
    Posts
    1,764
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    It's not directly what you want, though it'll help you get started (design patterns), and that of course, is Harry's own site

    www.phppatterns.com

    Check it out

  10. #10
    ********* 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 ElNormo
    In your example, you said that finding the permissions for a user would take a second query. I agree. Now what if you wanted to get a list of all the users, and their associated permissions?

    Wouldn't this be one query to load all the users, then one query for each user to load the permissions? What if you wanted something one level deeper? This quickly becomes an ugly mess.
    There would be two approaches. Either always join the permisions to the login (conceptually ugly, once authenticated you won't need the login again) or place such statistics gathering operations into a different class or library. Reporting and statistics tends to favour a more database like approach, and so having these as separate services (even hiding users and permissions) will save cluttering the authentication module.

    I don't know any of this for sure without seeing your actual application.

    O/R mapping is a complex job as they are two opposing philosophies.

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

  11. #11
    SitePoint Member
    Join Date
    Oct 2003
    Location
    Williamsburg VA
    Posts
    21
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Marcus (and others),

    Thanks again for your help. I've setup a demo of DB_DataObject, and am currently reading Fowler's book (so far, it's pretty good).

    I'm still having an issue dealing with multiple sets of objects, especially related to DB_DataObject. Two instances I've come up with so far:

    1. Related objects. Loading a "tree" of objects will take too many queries, potentially one per object. For instance, Buildings consist of Floors, which consist of Rooms. If I want a list of all the Rooms and their associated Buildings, that's a LOT of queries. Using straight SQL, I could just INNER JOIN them all together.

    2. Deleting (or updating) a set of objects. If I want to delete 100 accounts, I have to load them, then delete them individually.

    I realize I could make additional methods to handle these cases, but this creates unnecessary code reuse. Why do I need a method to delete a set of accounts when I can delete them individually?

    Have these kinds of issues been addressed by the enterprise architecture thinktanks? I'm not coming up with any clean solutions...

    Thanks again for your advice,

    Norman

  12. #12
    SitePoint Guru dagfinn's Avatar
    Join Date
    Jan 2004
    Location
    Oslo, Norway
    Posts
    894
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ElNormo
    Marcus (and others),

    Thanks again for your help. I've setup a demo of DB_DataObject, and am currently reading Fowler's book (so far, it's pretty good).

    I'm still having an issue dealing with multiple sets of objects, especially related to DB_DataObject. Two instances I've come up with so far:

    1. Related objects. Loading a "tree" of objects will take too many queries, potentially one per object. For instance, Buildings consist of Floors, which consist of Rooms. If I want a list of all the Rooms and their associated Buildings, that's a LOT of queries. Using straight SQL, I could just INNER JOIN them all together.

    2. Deleting (or updating) a set of objects. If I want to delete 100 accounts, I have to load them, then delete them individually.

    I realize I could make additional methods to handle these cases, but this creates unnecessary code reuse. Why do I need a method to delete a set of accounts when I can delete them individually?
    You need it if you need it . What I mean is, if you do those operations often enough--and they're slow enough--that it's worth your while to optimize them, then you add the extra queries. And you might as well wrap the queries in methods. Optimization increases complexity, as usual, and you don't want to do it unless it's necessary.
    Dagfinn Reiersøl
    PHP in Action / Blog / Twitter
    "Making the impossible possible, the possible easy,
    and the easy elegant"
    -- Moshe Feldenkrais


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
  •