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:
- Static methods are not desirable.
- Separation of Concerns
- 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
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
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
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
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
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.
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