The problem with the code is that there are many interdependencies between the models that grow exponentially each time features are added. To put it another way, lets say the system receives an order for a car. There can be many types of car. Each car has thousands of parts. Each part needs to be ordered, customized (painted etc.), assembled, tested, cleaned and any number of things. Even identical cars may require different parts, different assembly processes etc depending on the options. Thousands of things need to happen in the correct order when an order is received.
Obviously, putting all this into models would be a nightmare and duplicate code would start to suddenly appear everywhere. I searched in vain and pondered, what would be the best way to add some sort of layer where I could monitor all the method calls to the models and inject code on the fly. There's no elegant solution to this problem in PHP. I'm not sure there is in any programming language.
Next I looked into the observer pattern. It didn't quite "fit" into what I needed to accomplish nor did it feel right, but I think it put me on the right track.
I think the best solution to this problem is to be able to inject "drivers" or "modules" into the code on the fly in order to keep the code understandable by our feeble minds. I like calling them drivers but I guess technically a driver is a hardware interface so I called them modules.
Here is the class I wrote that stores modules which are executed when events are triggered. I wanted to type hint the $class coming in through the attach method in order to ensure it was implementing a specific interface but this requires sending the event handler the object. I didn't want to do this for performance reasons.
Code: Select all
<?php
class event_handler {
private static
$_events = array();
private function __construct() { }
/**
* Adds an event handler to the event stack
*
* @param string $event The event name
* @param string $class The class name to trigger
* @param string $method The method name to trigger
*/
public static function attach($event, $class, $method) {
self::$_events[$event][$class] = $method;
}
/**
* Executes all event handlers assigned to the specified event
*
* @param string $event The event name
* @return bool true or false if no events were executed
*/
public static function trigger($event) {
if (!isset(self::$_events[$event])) {
return false;
}
$args = array_slice(func_get_args(), 1);
foreach (self::$_events[$event] as $class => $method) {
$i = new $class();
$i->$method($args);
}
return true;
}
/**
* Removes an event handler from the event stack
*
* @param string $event The event name
* @param string $class The class to detatch
*/
public static function detach($event, $class) {
unset(self::$_events[$event][$class]);
}
/**
* Removes all event handlers from the specified event
*
* @param string $event The event name
*/
public static function detachAll($event) {
unset(self::$_events[$event]);
}
/**
* Clears all events
*
*/
public static function reset() {
self::$_events = array();
}
}
Code: Select all
event_handler::attach('ADD_CAR_ORDER', 'add_car_parts_module', 'exec');The models trigger events with this which can take any number of arguments.
Code: Select all
event_handler::trigger('ADD_CAR_ORDER', $data);Code: Select all
<?php
class add_car_parts_module {
public function exec($data) {
$eam = new add_car_parts_model();
if (isset($data[0]['parts_list']) && is_array($data[0]['parts_list']) && count($data[0]['parts_list']) > 0) {
foreach ($data[0]['parts_list'] as $row) {
$ins = array(
'order_id' => $data[0]['id'],
'type' => $row['type'],
'name' => $row['name'],
);
$eam->add($ins);
}
}
}
}