diff --git a/library/Respect/Data/Event/EventManager.php b/library/Respect/Data/Event/EventManager.php new file mode 100644 index 0000000..c9feaec --- /dev/null +++ b/library/Respect/Data/Event/EventManager.php @@ -0,0 +1,136 @@ + array( + * 0 => callable, + * 1 => concreteListener + * ... + * ) + * ... + * ) + */ + private $events; + + /** + * Construct + */ + public function __construct() + { + $this->events = array(); + } + + /** + * Add a callable or listener to a new or existent event + * @param string $eventName The event to attach the listener + * @param \Respect\Data\Event\Interfaces\Listener|\callable $action The event listener callback + * @return void + */ + public function on($eventName, $action) + { + if (!(is_callable($action)) || !($action instanceof Listener)) { + $message = 'Invalid type of action provided for event manager'; + throw new \InvalidArgumentException($message, 400); + } + + // The block below you can use for control or remove it + $events = $this->getEvents(); + if (array_key_exists("attach", $events)) { + $this->dispatch("attach", array($eventName, $action)); + } + + $this->events[$eventName][] = $action; + } + + /** + * Dispatch event + * + * If your event have more than one action, unless all the actions doesn't have + * any args, the args array need to be a array of arrays, each index of the + * args array needs to correspond to the index of the actions in the event + * array to work properly. + * + * @param string $eventName The event to be dispatched + * @param array $args Optional args for event actions + * + * @return void + */ + public function dispatch($eventName, $args = array()) + { + $events = $this->getEvents(); + if (array_key_exists($eventName, $events)) { + $argsIndex = 0; + foreach ($events[$eventName] as $action) { + switch (true) { + case is_callable($action): + if ( (count($events[$eventName]) > 1) && !empty($args) ) { + call_user_func_array($action, $args[$argsIndex]); + } else { + call_user_func_array($action, $args); + } + break; + case $action instanceof Listener: + if ( (count($events[$eventName]) > 1) && !empty($args) ) { + $action->update($args[$argsIndex]); + } else { + $action->update($args); + } + break; + default: + $message = 'Invalid type of action provided on event manager'; + throw new \InvalidArgumentException($message, 400); + } + $argsIndex++; + } + } + + } + + /** + * Return events array + * @return array + */ + public function getEvents() + { + return $this->events; + } + + /** + * Search and return event from the events array + * @param string $eventName the event name + * @return array + */ + public function getEvent($eventName) + { + $events = $this->getEvents(); + if (array_key_exists($eventName, $events)) { + return $events[$eventName]; + } + return null; + } + + /** + * Remove event from events array + * @param string $eventName The event to be removed + * @return void + */ + public function removeEvent($eventName) + { + $events = $this->getEvents(); + if (array_key_exists($eventName, $events)) { + $this->events = array_splice($events, $eventName, 1); + } + } + +} + diff --git a/library/Respect/Data/Event/EventedMapper.php b/library/Respect/Data/Event/EventedMapper.php new file mode 100644 index 0000000..8d60973 --- /dev/null +++ b/library/Respect/Data/Event/EventedMapper.php @@ -0,0 +1,112 @@ +mapper = $mapper; + $this->eventManager = new EventManager(); + } + + public function __get($name) + { + return $this->mapper->__get($name); + } + + public function __set($alias, $collection) + { + return $this->mapper->__set($alias, $collection); + } + + public function __call($name, $children) + { + return $this->mapper->__call($name, $children); + } + + public function __isset($alias) + { + return $this->mapper->__isset($alias); + } + + public function on($event, $callback) + { + return $this->eventManager->on($event, $callback); + } + + public function removeEvent($eventName) + { + return $this->eventManager->removeEvent($eventName); + } + + public function flush() + { + $trackedQueue = $this->getTrackedQueue(); + $trackedEntities = $this->getTrackedEntities(); + + $this->processFlushQueue($trackedQueue, $trackedEntities, 'pre'); + $flushResult = $this->mapper->flush(); + $this->processFlushQueue($trackedQueue, $trackedEntities, 'post'); + + return $flushResult; + } + + protected function processFlushQueue($queue, $trackedEntities, $eventSuffix) + { + $em = $this->eventManager; + + foreach ($queue as $eventType => $objects) { + foreach ($objects as $entity) { + $collection = $trackedEntities[$entity]; + + $em->dispatch( + "{$collection->getName()}:{$eventType}:{$eventSuffix}", + array($entity, $collection) + ); + } + } + } + + protected function getTrackedQueue() + { + $m = $this->mapper; + $inserts = $this->getObjectPropertyFromReflection($m, 'new'); + $updates = $this->getObjectPropertyFromReflection($m, 'changed'); + $deletes = $this->getObjectPropertyFromReflection($m, 'removed'); + + return array( + 'insert' => $inserts, + 'update' => $updates, + 'delete' => $deletes + ); + } + + protected function getTrackedEntities() + { + return $this->getObjectPropertyFromReflection( + $this->mapper, + 'tracked' + ); + } + + private function getObjectPropertyFromReflection( + $object, + $property + ) { + $ref = new \ReflectionObject($object); + $refProp = $ref->getProperty($property); + if ($refProp->isPrivate() || $refProp->isProtected()) { + $refProp->setAccessible(true); + } + + return $refProp->getValue($object); + } +} diff --git a/library/Respect/Data/Event/Interfaces/EventManager.php b/library/Respect/Data/Event/Interfaces/EventManager.php new file mode 100644 index 0000000..cb0ac80 --- /dev/null +++ b/library/Respect/Data/Event/Interfaces/EventManager.php @@ -0,0 +1,42 @@ +