Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EventedMapper as a proxy #74 #3

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
136 changes: 136 additions & 0 deletions library/Respect/Data/Event/EventManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php

namespace Respect\Data\Event;

use Respect\Data\Event\Interfaces\EventManager as EventManagerInterface;
use Respect\Data\Event\Interfaces\Listener;

class EventManager implements EventManagerInterface
{
/*
* This is the events array
* @var array
* @example
* array(
* "EventName1" => 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);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this switch?

Can we use if instead?

    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;

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably for future implementations or checks. I don't see any trouble by changing it.
@Rafael-BP could you?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, no problem.


// 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);
}
}

}

112 changes: 112 additions & 0 deletions library/Respect/Data/Event/EventedMapper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

namespace Respect\Data\Event;

use Respect\Data\AbstractMapper;

class EventedMapper
{

protected $mapper;

protected $eventManager;

public function __construct(AbstractMapper $mapper)
{
$this->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);
}
}
42 changes: 42 additions & 0 deletions library/Respect/Data/Event/Interfaces/EventManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace Respect\Data\Event\Interfaces;

interface EventManager
{
/**
* Do the dispatch of a specific event
* @param string $eventName
* @param array $args
*
* @return void
*/
public function dispatch($eventName, $args = array());

/**
* Add action (Listener or callable) for a event
* @param string $eventName
* @param \Respect\Data\Event\Interfaces\Listener|\callable $action
*/
public function on($eventName, $action);

/**
* Remove a Event from the events array
* @param string $eventName
*/
public function removeEvent($eventName);

/**
* Return the events array
* @return array
*/
public function getEvents();

/**
* Search and return event
* @param string $eventName
* @return array
*/
public function getEvent($eventName);
}

16 changes: 16 additions & 0 deletions library/Respect/Data/Event/Interfaces/Listener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Respect\Data\Event\Interfaces;

interface Listener
{

/**
* Update listener, can recevei optional args array
* @param array $args
* @return void
*/
function update($args = array());

}