2017 © Pedro Peláez
 

library deferred-kdyby-events

deferred kdyby events helpers for doctrine2 and nette

image

pcz/deferred-kdyby-events

deferred kdyby events helpers for doctrine2 and nette

  • Thursday, March 31, 2016
  • by petrofcz
  • Repository
  • 1 Watchers
  • 0 Stars
  • 14 Installations
  • PHP
  • 0 Dependents
  • 0 Suggesters
  • 0 Forks
  • 0 Open issues
  • 2 Versions
  • 0 % Grown

The README.md

pcz/deferred-kdyby-events

deferred events extension for Nette framework (using kdyby/events & kdyby/doctrine) aneb ~~kdyby~~ až bude Bavorov..., (*1)

This extension of kdyby/events event system provides support of deferred events in your appliaction. Two following basic types of deferred events are implemented:, (*2)

  • simple deferred event - the event is persisted & set up to be fired at some particular time in future
  • grouped deferred event - each event has a groupID property. When a new event is persisted, all waiting events with its groupID are cancelled. You can use them for example for sending notifications - when user receives 5 messages in 10 minutes, only 1 notification will be sent using this type of event. Also, there's a class annotation @Keeping, that changes the behaviour of grouping strategy - the nearest event will be kept, and all the following will be cancelled, so the final waiting period will not be extended.

Requirements

Installation

$ composer require pcz/deferred-kdyby-events
  • enable the extension in your config neon file
extensions:
    # add this line
    deferredEvents: pcz\DeferredKdybyEvents\DI\DeferredEventsExtension 
  • configuration of other extesions is also required, see documentation of following dependencies: Kdyby/Doctrine, Kdyby/Events and Kdyby/Console., (*3)

  • update the database schema after enabling the extension in config (be sure to clear cache and run the orm:schema-tool:update command), (*4)

  • almost there, let's setup a cron job:, (*5)

*/5 * * * * /path/to/php /path/to/your/app/www/index.php deferredEvents:fire

Introduction

Each event is represented by its event class. It must extend one of the following base classes: pcz\DeferredKdybyEvents\DeferredEvent or pcz\DeferredKdybyEvents\GroupedDeferredEvent. In fact, each event class is an ORM Entity, so don't forget to use ORM Annotations on class attributes. Usage of MTI (multi-table-inheritance) inheritance type causes that each event is represented by 1 table in database schema. It's also necessary to update database schema after adding new event class., (*6)

Examples

Let's start with the simple deferred event type, the following example shows implementation of planned change of product price:, (*7)

The event class, (*8)

use Doctrine\ORM\Mapping as ORM;

/** @ORM\Entity */
class ChangeProductPriceEvent extends \pcz\DeferredKdybyEvents\DeferredEvent {

    const EVENT_NAME = 'ChangeProductPrice';

    /**
     * @var Product
     * @ORM\ManyToOne(targetEntity="Product")
     */
    protected $product;

    /**
     * @var integer
     * @ORM\Column(type="integer")
     */
    protected $new_price;

    public function __construct(\DateTime $execution_date, Product $product, $new_price) {
        parent::__construct($execution_date);
        $this->product = $product;
        $this->new_price = $new_price;
    }

    public function getEventName() {
        return self::EVENT_NAME;
    }

    public function getProduct() { return $this->product; }

    public function getNewPrice() { return $this->new_price; }

}

Event creation, (*9)

/** @var $em Kdyby\Doctrine\EntityManager */

/** @var $product Product */

$event = new ChangeProductPriceEvent(
    new \DateTime('2016-04-01 00:00:00'),
    $product,
    10
);

$em->persist($event);
$em->flush();

Event handling, (*10)

class ChangeProductEventListener implements Kdyby\Events\Subscriber {

    // this listener must be registered in config, with 'kdyby.subscriber' tag of course

    /** @var Kdyby\Doctrine\EntityManager */
    protected $em;

    public function getSubscribedEvents() {
        return [ChangeProductPriceEvent::EVENT_NAME     =>  'changePrice'];
    }

    public function changePrice(ChangeProductPriceEvent $e) {
        // don't use this in production, some locking should be implemented
        $product = $e->getProduct();
        $product->setPrice($e->getNewPrice());
        $this->em->persist($product)->flush();
    }

}

.. that's it:-) Now, let's see some more interesting scenario. An internal message system is part of your awesome app and you want to send a notification when user receives a message. But you don't want to spam user's mailbox, so the notification will be delayed. If another message is received during the delay period, user will still receive the single notification., (*11)

/** @pcz\DeferredKdybyEvents\Annotations\Keeping */
class NewMessageNotificationEvent extends pcz\DeferredKdybyEvents\GroupedDeferredEvent {
    // ...

    protected $user;

    public function __construct(\DateTime $execution_date, User $user) {
        parent::__construct($execution_date);
        $this->user = $user;
    }

    public function getGroupKey() {
        return $this->user->getId();
    }

    // ...
}

Note the getGroupKey() method, it must be implemented when overriding the GroupedDeferredEvent class. It's necessary because it defines the grouping key. Also the @Keeping annotation is important, it will be discussed later. Another example is removing inactive chat member from a chat room. Notice the lack of the @Keeping annotation and the getGroupKey() method., (*12)

class RemoveUserFromRoomEvent extends pcz\DeferredKdybyEvents\GroupedDeferredEvent {
    // ...

    public function getGroupKey() {
        return $this->user->getId() . '_' . $this->room->getId();
    }

    // ...
}

New event then would be created every time user sends a chat message. If the delay period would be for example 48 hours (eg. call like $event = new RemoveUserFromRoomEvent((new \DateTime())->add(new \DateInterval('PT48H')), ...) would be used), user would be removed from a chat room that hasn't participated to for 48 hours. When new event is persisted, all other waiting events with the same groupKey will be suspended., (*13)

But it might not be the proper behavior for the first case (notification example). If someone send me a message every 5 minutes, i'll never receive a notification, because the created event will always "overwrite" the last waiting one. In such cases, use the @Keeping annotation. It changes the strategy of grouping - the closest event will be kept and all succeeding events will be suspended., (*14)

Thanks & enjoy & a big round of applause for Kdyby extensions, (*15)

The Versions

31/03 2016

dev-master

9999999-dev

deferred kdyby events helpers for doctrine2 and nette

  Sources   Download

The Requires

 

by Avatar petrofcz

31/03 2016

v0.1

0.1.0.0

deferred kdyby events helpers for doctrine2 and nette

  Sources   Download

The Requires

 

by Avatar petrofcz