Here’s a generalized scenario. I have “Tasks” and I have “Task Events.” I’ve created a database table for each. I’ve also created a model which handles fetching records from the database.
For “Task Events,” I have a few types: Created, Accepted, Comment, Closed.
Currently, I do something simple like $task = new Task($task_id); to grab the task from the database, and $task_events = new Tasks_Events($task_id); which grabs the events for that task. Then, I’ve implemented Iterator so I can do foreach($task_events as $e) { ... } this is all working great.
However, I have found that I need some specialized handlers for a few of the event types. For example, I’ve created Tasks_Events_Comments which extends Tasks_Events, and does some extra processing for Comment event types. What I’ve realized now is that when I gather the collection of events, I really need them to be child types, so if a method is called on the event, the proper overrides from the child type are called.
Here’s a quick example:
class Model {
public function __construct($search = null) {
// Hypothetical example, basically query the DB and populate data.
if (!is_null($search)) { $this->search($search); }
}
protected function onAfterUpdate() { }
}
class Tasks_Events extends Model {
protected function onAfterUpdate() { /* Task Event Specific */ }
}
class Tasks_Events_Comments extends Tasks_Events {
protected function onAfterUpdate() { /* Task Event Comment Specific */ }
}
Then, a hypothetical use case:
class Controller {
public function updateEvent($task_id, $event_id, $params) {
$task = new Tasks($task_id);
$task_event = new Tasks_Events($event_id);
// Some Analysis of Params
$task_event->status = $new_status;
$task_event->save();
}
}
So, HERE is the key. Doing it this way will call Tasks_Events onAfterUpdate()…
My question is, what is a model, paradigm, philosophy, approach that I can use so that when I have a collection of task events, and I action on one, even though I’m using a base class reference, I need to have the child classes functions called.
I really enjoy the simplicity of $e = new Tasks_Events(3); $e->status = 4; $e->save(); one solution I have which I don’t like, is to do something like $e = Tasks_Events::Get($id); where Tasks_Events would query the database, determine the type, then do a “switch” and create the proper type to be returned.
Another reason I don’t like that is because I’ve built up the model to do cool things like $tasks = new Tasks(array('user_id' => 5, 'status' => Tasks::STATUS_OPEN)); and it will build the proper db query, and populate with all of the tasks for user 5 that are open. Then I can do foreach($tasks as $t) { echo $t->subject; } etc. So, I’d love to be able to keep this sort of system…. but I’m not sure if I can if I want to take advantage of inheritance for sub-types.
I’m afraid I may need a Factory pattern, but I’m hoping I might just be missing something.
P.S. If you can think of a better title, feel free to change it.
Thanks @Sam Dufel, your comment made me think deeper and I realized a major flaw in my design.
You made a good suggestion of $task->getEvents(), but I forgot a crucial (flaw?) point in my question. This may also be the reason I’ve gotten into trouble…
Basically the way I implemented the Iterator, and getter/setters is probably the source of the problem. Since it iterates over an array of the raw records.
Then, let’s say I’m at position 2 in the iterator. Calling $item->status, calls __get($name) which checks
$this->records[$this->position][$name]!! So, as you can see, I painted myself into a corner. Even with a factory pattern, this wouldn’t quite work because of the way I implemented the iterator inside of the Tasks_Events (well… the Model) argh.Sorry to bother y’all. Thanks for the thoughts.
Update: What I realize I did was combined a “DAO” with a “Model” with a “Model Collection” in effect. I’m going to separate them. DAO->find() will return a Model Collection (which will be iterable), and DAO->findOne() which will return a Model. It was handy to have all three in one, but as my needs expanded, it was not very extensible.