PHPackages                             pcz/deferred-kdyby-events - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. pcz/deferred-kdyby-events

ActiveLibrary

pcz/deferred-kdyby-events
=========================

deferred kdyby events helpers for doctrine2 and nette

v0.1(10y ago)023PHP

Since Mar 31Pushed 10y ago1 watchersCompare

[ Source](https://github.com/petrofcz/deferred-kdyby-events)[ Packagist](https://packagist.org/packages/pcz/deferred-kdyby-events)[ RSS](/packages/pcz-deferred-kdyby-events/feed)WikiDiscussions master Synced 2mo ago

READMEChangelogDependencies (3)Versions (2)Used By (0)

pcz/deferred-kdyby-events
=========================

[](#pczdeferred-kdyby-events)

deferred events extension for Nette framework (using [kdyby/events](https://packagist.org/packages/kdyby/events) &amp; [kdyby/doctrine](https://packagist.org/packages/kdyby/doctrine)) *aneb kdyby až bude Bavorov...*

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

- simple deferred event - the event is persisted &amp; 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
------------

[](#requirements)

- PHP 5.4 or higher.
- [Nette Framework](https://github.com/nette/nette)
- [kdyby/doctrine](https://packagist.org/packages/kdyby/doctrine)
- [kdyby/events](https://packagist.org/packages/kdyby/events)
- [kdyby/console](https://packagist.org/packages/kdyby/console)

Installation
------------

[](#installation)

- install this extension using [Composer](http://getcomposer.org/):

```
$ 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](https://github.com/Kdyby/Doctrine/blob/master/docs/en/index.md), [Kdyby/Events](https://github.com/Kdyby/Events/blob/master/docs/en/index.md) and [Kdyby/Console](https://github.com/Kdyby/Console/blob/master/docs/en/index.md).
- **update the database schema** after enabling the extension in config (be sure to clear cache and run the `orm:schema-tool:update` command)
- almost there, let's setup a cron job:

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

```

Introduction
------------

[](#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**.

Examples
--------

[](#examples)

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

**The event class**

```
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**

```
/** @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**

```
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.

```
/** @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.

```
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.

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.

Thanks &amp; enjoy **&amp; a big round of applause for [Kdyby extensions](https://github.com/Kdyby/)**

###  Health Score

24

—

LowBetter than 32% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity6

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

3694d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/9c21b4a9defb8773955aa8358fe0582da5e41f53d891697feee0d22b7c1b3612?d=identicon)[petrofcz](/maintainers/petrofcz)

---

Top Contributors

[![petrofcz](https://avatars.githubusercontent.com/u/3965506?v=4)](https://github.com/petrofcz "petrofcz (8 commits)")

### Embed Badge

![Health badge](/badges/pcz-deferred-kdyby-events/health.svg)

```
[![Health](https://phpackages.com/badges/pcz-deferred-kdyby-events/health.svg)](https://phpackages.com/packages/pcz-deferred-kdyby-events)
```

###  Alternatives

[apigen/apigen

PHP source code API generator.

2.2k627.9k225](/packages/apigen-apigen)[kdyby/doctrine

Doctrine integration into Nette Framework

1091.0M86](/packages/kdyby-doctrine)[rixxi/gedmo

Gedmo Doctrine Extensions integration into Nette Framework using Kdyby/Doctrine

1368.2k1](/packages/rixxi-gedmo)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
