5

I have a class named GuestbookEntry that maps to the properties that are in the database table named "guestbook". Very simple!

Originally, I had a static method named getActiveEntries() that retrieved an array of all GuestbookEntry objects. Each row in the guestbook table was an object that was added to that array. Then while learning how to properly design PHP classes, I learned some things:

  1. Static methods are not desirable.
  2. Separation of Concerns
  3. Single Responsibility Principle

If the GuestbookEntry class should only be responsible for managing single guestbook entries then where should this getActiveEntries() method most properly go?

Update:

I am looking for an answer that complies with the SOLID acronym principles and allows for test-ability. That's why I want to stay away from static calls/standard functions.

DAO, repository, ...? Please explain as though your explanation will be part of "Where to Locate FOR DUMMIES"... :-)

4
  • I've changed your title a bit, as a senior developer would probably go for the more efficient way, that might not necessarily be the "purest" way, design wise. Since you are trying to understand and apply the principles, looking for the "purest" way is a good thing, but keep in mind that perfectionism isn't a goal in itself, at least not in a professional context.
    – yannis
    Nov 4, 2012 at 5:22
  • Took at look at the link! By any chance do you happen to know of an awesome PHP book that uses the SOLID acronym principles? Or a small open source project that I can study and learn from that is not a framework?
    – darga33
    Nov 4, 2012 at 8:36
  • PHP Objects, Patterns, and Practice is a good read, but it's a bit old (2007). phpmaster.com has some excellent tutorials, most are a bit advanced but they have at least a few for beginners. Alejandro Gervasio has a series of tutorials there that are mostly about object orientation, but check the other authors as well. I was also going to suggest our sister site, Code Review, but you've already found it ;)
    – yannis
    Nov 4, 2012 at 8:40
  • Hey thanks for pointing me back to Alejandro! I saw some of his tutorials on devshed, they were awesome but they were OLD as can be! The phpmaster ones are new, thanks so much! So is that PHP objects, patterns and practice the newest "good read"? Also, what do you think of the TableGateway pattern as someome suggested as an answer to this getActiveEntries() question?
    – darga33
    Nov 4, 2012 at 16:14

6 Answers 6

0

This use case calls for a data access object (DAO), which handles the responsibility of retrieving objects from the underlying data store.

For example, you could define a GuestbookEntryDao with methods such as getById(...), getActiveEntries(...sort options...), etc.

Edit: In response to your comment:

GuestbookEntryDao would be an interface, ultimately implemented by a MySqlGuestBookEntryDao class or so. The point is that you can later switch implementations (drop in a FlatXmlGuestBookEntryDao class) without affecting the rest of the app.

GuestbookEntry would not extend GuestbookEntryDao, and IMHO should not depend on it either (although some people disagree): They should be separate objects entirely, because there are 2 separate responsibilities in play (the model and data access responsibilities).

The result is that, somewhere in your app, you will have a high-level process representing a use case of your application which might looks something like (pseudo-code):

function browseGuestBook() {

    // ... determine which entries to retrieve ...

    // retrieve entries
    entries = guestBookEntryDao.getActiveEntries(...);

    // store entries in output model
    // (this part may vary wildly depending on your app)
    guestbookBrowserOutput.entries = entries;

    // ...

}

The classes where such functions/processes reside are usually called services in domain-driven design (I'm referring to an application-level service in this case, not a domain service). This object will depend on GuestbookEntryDao to retrieve the needed entries; GuestbookEntry does not know GuestbookEntryDao exists.

This also relates to dependency injection and providing objects with the references they need to do their job - nothing more and nothing less - but that's a different topic...

6
  • 1
    So would GuestbookEntryDao be an asbract class and would GuestbookEntry extend GuestbookEntryDao? Or would I inject GuestbookEntryDao into the GuestbookEntry class in the constructor? I really am trying to follow as best as I can!
    – darga33
    Nov 4, 2012 at 4:42
  • Updated answer in response to your comment. Look further into DDD and read up on dependency injection (principles apply even when not using a DI framework) to potentially revolutionize the way you design your apps! Good luck.
    – Torious
    Nov 4, 2012 at 13:32
  • This solutions is overly complex and could make someone desist from OO design. Nov 4, 2012 at 17:24
  • 1
    Hey Torious! I completely understand what you are saying! I am reading all about DDD here: sosa.ucsd.edu/teaching/cse294/fall2007/dddbook.pdf It is REALLY AWESOME. It even takes it a step further and uses repositories so that even if you change your database later down the road, you can without having to change all of your code! It is really awesome! I would up vote your answer, but I need a reputation of 15 before it will let me lol
    – darga33
    Nov 5, 2012 at 6:11
  • 1
    and for anyone that wants the complete book: I found it here to download (although I haven't read it yet. It is by Eric Evans the one who basically originated with the Domain Driven Design. daem0n.org/stuff/…
    – darga33
    Nov 5, 2012 at 6:13
1

Where did you learn that static methods are not desirable? I have a question for you: how many guestbook tables do you have? Obviously, you only have 1.

Sometimes it makes a lot of sense to use a plain old function or static method to perform meta-operations that are a "level above".

Some people would advocate a GuestbookEntryCollection class which manages a set of GuestBookEntry objects, but ...

... here is where purism vs. pragmatism come into play.

If all you need is a list of GuestBookEntry objects, then put that code in a callable (be it a function, or static method), and just call it.

Do you need advanced features, like building a custom query, sorting them, filtering them, etc...? Maybe that warrants a separate class for managing it.


What's the difference between objects and functions? Objects can maintain individual state, while functions are stateless. If you are performing functional operations and do not have state to manage, then functions are great!

Here is another thing to consider... In PHP, functions need to be manually included before they can be used, but classes have the ability to autoload.

include('/path/to/function.php')
$x = GuestbookEntryList()

vs.

$x = GuestbookEntry::GetList()

vs.

$x = new GuestbookEntryCollection()

Food for thought :)

2
  • 1
    This is bad advice: In most cases (including this one), statics/global functions hurt testability, maintability, extensibility and code transparency (of dependencies).
    – Torious
    Nov 4, 2012 at 4:31
  • 1
    Hey Gahooa, thanks for the info but I am going for high cohesion and low coupling. I want to learn the absolute best way. I forgot to include that in my original post. Updating now...
    – darga33
    Nov 4, 2012 at 4:43
0

Here's how I would do it. For row data just use arrays, so:

$entry = array(
    'entry_id' => '23',
    'user_id' => '4',
    'creation_date' => '2012-11-03',
    'content' => 'blah blah.');

Your database model calls will return row data in the above format - this is also how PHP database plugins return data so you don't really have to do anything. Then for getting the data just have a model object:

class EntryModel {
    public function getEntries() {
        //
    }

    public function getEntry($entryID) {
        //
    }
}

So as you can see it doesn't matter if you're getting a single entry or a bunch of entries - just have your method return the desired data set.

2
  • Right that is how I currently had it, as all part of the GuestbookEntry model that mapped directly to the DB. But notice that EntryModel should only deal with single entries. So the method getEntries() doesn't belong, according to the Single Responsibility Principle and the Separation of Concerns :-) That's why I'm looking for the best way too do it!
    – darga33
    Nov 4, 2012 at 4:48
  • It's okay for EntryModel to have getEntries(). You're grouping logic in the sense that EntryModel will only deal with entries and UserModel will only deal with users. But wait...what about getUserEntries($userID)? I'd stick that in EntryModel as you're returning entries.
    – Ryan
    Nov 4, 2012 at 5:56
0

Since you probably are looking for a purism kind of answer, and aren't just about what would work, then the Guestbook shouldn't even depend on a database.

Rather, you have a Guestbook, you have a Database and you have a GuestbookGateway. The gateway retrieves rows from the database in the form of PHP objects, and when something needs to change, the gateway has a save method that accepts a Guestbook. That's a proper separation of concerns.

ORM can lead to many issues from the tight coupling it's based on. I've had to deal with some of them in the past, and it wasn't pretty. However, it should be noted that for many applications (especially web applications that usually work the same way), ORM is good enough.

1
  • I see. Is that the tableGateway pattern that I heard about? do you happen to know of a good tutorial on it?
    – darga33
    Nov 4, 2012 at 8:27
0

Personally I'd go for a class, 'Guestbook', that contains the array of GuestbookEntries and have the function as a member of that class. The function can still return an array of entries as desired.

0

If I understand your question right ...

you are searching for the "correct" place to put you getActiveEntries() method. Cause from a OO-Design "point-of-view" you want to adhere to the principles of:

  • Separation of Concerns
  • Single Responsibility Principle

(FYI: I never heard avoid statics, so I skipped that one)

The cause of your "problem":

Every single entry should only be concerned about itself, and only responsible for itself. Therefore it can not be responsible for a "collection" of entries.

The principles you mentioned above are Object Oriented principles. Solutions for problems involving OO principles demand that you create many objects and many classes so that together they work as a solution.

Now if the method "getEntries" can not go into the DAO class (Data Access Object) it

... could go into a factory type of class. A Factory is responsible for "creating" objects. You could argue that a Factory for GuestBookEntries has 2 methods: createEntryFromId(int $entryId) and createActiveEntries().

... could go into a controller type of class. Same as factory above, just named differently. The controller type of class could do anything, even more than a factory. If you ever need something like "verify()" or "isDirty" for your GuestBookEntry, the factory would again not be the place to put those methods. Cause factories are only concerned about creation (they kind of have the duty to guarantee creation or fail quick) where as controller type of classes can carry verify() or isXYZ() methods without hurting their "purpose".

... in the end it doesn't matter what you call "it". But you will need another "class/object" to take the responsibility of restoring a collection of active GuestBookEntries. Most likely this class will make use of the single GuestBookEntry class that you already have.

When it get's down to implementing the methods that interact with each other (the new class and the GuestBookEntry) you are welcome to use array() as a container to transport GuestBookEntry objects. You could create a special "typed" container that guarantees to only carry GuestBookEntry objects, but that's again up to you. (Plus it yet another oo-design decision, hehe)

To sum it up:

Create another class with the responsibility to retrieve "collections of GuestBookEntries based on their status (active|inactive)" that uses (composition) your existing GuestBookEntry class.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.