Execute a method on every instantiated object of a certain PHP class
So I have this problem in PHP, I have a class called unit that is a reference from a table called units, so when i update a row on the table units, i have to update my object unit, calling a method called refresh(), like $uni开发者_开发问答t->refresh(). This works ok for me because all the updates to a single row is made from the object unit.
The problem arises when another classes updates the units table. For example lets say i have a class called units (in plural). This class makes massive changes to the units table, changing rows that may be referenced by objects of the unit class.
So i was thinking if maybe an static method on unit class could make all the created objects of the type unit, can make them call they refresh() method, or maybe there is another way to work this (like an ORM, or a design pattern). I have two requisites, 1) i will work on posgtres and i will not change this, 2) I use a lot of user functions and triggers and complex SQL ( lots of time and date calculations, inners selects and so on).
So what could be useful in this kind of situation?
Your Unit sounds like an ActiveRecord. While you could, in theory, have your Units
collection do something like this:
public function refreshAll()
{
foreach ($this->units as $unit) {
if ($unit->isModified) {
$unit->update()
}
}
}
I strongly discourage to do so because that would result in one query per Unit
instance. Roundtrips to the database are most often a bottleneck. Just imagine you have to update a couple hundred or even thousand of instances.
A better approach would be to just collect all the queries required in the next transaction and issue them in one request, e.g. something like
public function refreshAll()
{
$sql = '';
foreach ($this->units as $unit) {
if ($unit->isModified) {
$sql .= $unit->getSql();
}
}
// these are dummy method calls.
// i dont know if postgres supports transactions or multiqueries
$this->dbAdapter->startTransaction();
$this->dbAdapter->multiQuery($sql);
$this->dbAdapter->commitOrRollback();
}
Another option would be to use a dedicated Unit of Work pattern.
Excerpts at Google Books:
- Unit of Work in Martin Fowler's POEAA
- Unit of Work in Matt Zandstra's PHP Objects, Patterns and Practice
Note that when using a Unit of Work, your also might want to consider to remove the database access code from your Unit
instances completely (remove the ActiveRecord), because you are shifting the responsibility to save your objects into other classes then (which is good).
Why not create an object pool and then call the refresh method on every object in the pool when needed? It's by far the simplest and most elegant solution for the problem at hand.
You already said the answer. I would go for an ORM (since working with PHP, your best bet is Doctrine. As Gordon mentioned "Unit of WorK" in the comments, Doctrine uses this pattern for exactly this purpose. To give an example of the refreshing, look at the DOctrine Docs on refreshing Objects/Relations.
If an ORM is to heavy for you, the already mentioned Unit of Work is your answer and you could write your own, light-weight UoW for the project. This example explains the Doctrine 2.0 UoW and what to do with it.
精彩评论