PHPackages                             jahudka/component-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. jahudka/component-events

ActiveLibrary

jahudka/component-events
========================

Relay events to lazily created components

v2.0.2(5y ago)2312MITPHPPHP &gt;=7.4CI failing

Since May 4Pushed 5y ago1 watchersCompare

[ Source](https://github.com/jahudka/component-events)[ Packagist](https://packagist.org/packages/jahudka/component-events)[ Docs](https://github.com/jahudka/component-events)[ RSS](/packages/jahudka-component-events/feed)WikiDiscussions master Synced 2d ago

READMEChangelogDependencies (8)Versions (5)Used By (0)

ComponentEvents
===============

[](#componentevents)

[![Latest Stable Version](https://camo.githubusercontent.com/2957f6a00347a1ee134819c2b34f6508018b94f0aa295b290ee0ae780561fe98/68747470733a2f2f706f7365722e707567782e6f72672f6a616875646b612f636f6d706f6e656e742d6576656e74732f76)](https://packagist.org/packages/jahudka/component-events)[![License](https://camo.githubusercontent.com/1a7095d3a866b3fa15f18a8b5376e18ec69696a877879e65fb6d4ffeaf7c44bb/68747470733a2f2f706f7365722e707567782e6f72672f6a616875646b612f636f6d706f6e656e742d6576656e74732f6c6963656e7365)](https://packagist.org/packages/jahudka/component-events)[![Tests](https://github.com/jahudka/component-events/workflows/Tests/badge.svg)](https://github.com/jahudka/component-events/workflows/Tests/badge.svg)

This package provides a lazy bridge between various event dispatchers and the Nette Component model. It integrates out of the box with `symfony/event-dispatcher`, `doctrine/event-manager` and `contributte/nextras-orm-events`, but other projects can be easily added. The primary use case of components reacting to events is, of course, redrawing snippets.

Why is this useful? Well, if you're developing an AJAX-first site and make heavy use of components you'll run into situations where some business logic triggered from within one component should be noticed by another, which might decide it needs to be redrawn. For example a shopping cart: if you have a product list with an "Add to Cart" button then the product list is probably going to be composed of several components; the "Add to Cart" button might be one of them. If the user hits the button, the button component handles that directly and so it can redraw itself as needed, but how will the Cart component sitting in the top right of the page know that it should be redrawn as well? When the signal from the button press is being handled by the Button component the Cart component might not even be created yet. Enter ComponentEvents: make the Cart component an event subscriber and have your business logic emit an event when the cart contents are changed. ComponentEvents will register a relay listener for the event in the event dispatcher and when the event is emitted it will relay it to the component, which will get lazily created at that moment if it wasn't created yet.

The package works by statically analysing all services extending the `Nette\Application\UI\Presenter`class when the DI container is being compiled. Since components are created using statically defined `createComponent()` methods it is easy to traverse the component tree and check each component for the relevant interfaces, provided the factory methods have appropriate return type hints.

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

[](#installation)

You can install ComponentEvents using Composer:

```
composer require jahudka/component-events
```

Then you need to register the `Jahudka\ComponentEvents\ComponentEventsExtension` in your config.

Configuration
-------------

[](#configuration)

ComponentEvents is designed to be as automatic as possible - every integration detects automatically whether it's supposed to be enabled. That said, you may need to override that magic sometimes. This is how you do it:

```
componentEvents:
    # string means class name of the integration's Jahudka\ComponentEvents\IBridge implementation
    # (this is how custom integrations are registered, see below); bool means enabled
    # and null means detect automatically (which is the default)
    : string|bool|null
```

Integrations
------------

[](#integrations)

### Symfony Event Dispatcher

[](#symfony-event-dispatcher)

**config key:** `symfony`

Integration with `symfony/event-dispatcher` and its Nette wrapper `contributte/event-dispatcher`works out of the box and you don't need to do anything special to use them. Simply implement the `Symfony\Component\EventDispatcher\EventSubscriberInterface` in any component you wish and prosper.

The only caveat with the Symfony EventDispatcher comes from the Contributte wrapper which automatically subscribes *all* services implementing the appropriate interface and there is no way around it. This means that it will also autosubscribe all presenters which implement the interface - and that in turn means that all presenters will be automatically created when the EventDispatcher service is accessed and they'll receive events even when they're not the current presenter. The `lazy` option of the Contributte wrapper doesn't help this, enabling it only postpones the moment the presenters are created. Unless something changes in Contributte's code the only way you can have presenters listen to events is to inject the EventDispatcher service into the presenter and have the presenter subscribe to the relevant events in the `startup()` method and then unsubscribe again in `shutdown()`.

Or you can simply opt out of using the Contributte wrapper and use the `symfony/event-dispatcher`package directly - all you need to do is register the `EventDispatcher` class as a service in your DIC config. The `contributte/event-dispatcher-extra` package which bridges several commonly used built-in Nette events into the Symfony EventDispatcher doesn't depend on the Contributte wrapper, so you can still use it even if you ditch the wrapper.

### Doctrine Event Manager

[](#doctrine-event-manager)

**config key:** `doctrine`

Integration with `doctrine/event-manager`, as well as any wrapper which registers an instance of the `Doctrine\Common\EventManager` class or its descendant in the DIC, should work *almost* as well as the Symfony integration. The only difference here is that unlike the EventSubscriber interface in Symfony, the `getSubscribedEvents()` method in the `Doctrine\Common\EventSubscriber` interface is not `static`, which means we can't *call* it statically when we're analysing a class which implements it during container rebuild. The ComponentEvents Doctrine bridge works around this by creating an instance of the implementing class *without* calling the constructor and then calling the `getSubscribedEvents()` method of the instance - but this means that if the method tries to e.g. access a dependency that should've been set in the constructor the call will fail. The Doctrine bridge will simply ignore the component in that case.

### Nextras ORM Events

[](#nextras-orm-events)

**config key:** `nextras-orm`

The `contributte/nextras-orm-events` package is by far the most obnoxious to use because, unlike the previous two, this one *requires* you to code something differently than you're used to. Specifically, with traditional Nextras ORM Events you must specify *listeners* using annotations on the *entity class*, but with ComponentEvents you need to specify *entities* using annotations on the *listener class*. For example: let's say you have a Book entity and an Author entity. The Book entity could have a traditional service attached as a listener like this:

```
// Book.php

/**
 * @AfterInsert(App\Listener\NewBookListener)
 */
class Book extends Entity {}

// NewBookListener.php

class NewBookListener implements AfterInsertListener {
    public function onAfterInsert(IEntity $entity) : void { /* ... */ }
}
```

But the implementation of Nextras ORM Events means that the `NewBookListener` class *must* be registered as a service in the DIC and it will be created every time the repository class configured for the Book entity is created. Contrast this with a component configured as a listener for the same event using ComponentEvents:

```
// Book.php

class Book extends Entity {}

// AuthorBookCountControl.php

/**
 * @AfterInsert(App\Entity\Book)
 */
class AuthorBookCountControl extends Control implements AfterInsertListener {
    public function onAfterInsert(IEntity $entity) : void {
        $this->redrawControl();
    }
}
```

It's about the same amount of code, only written elsewhere. Note that you can specify multiple entity classes in the annotation as a comma-separated list, and namespace resolution works exactly the same as native PHP namespaces, so `use` statements are taken into consideration and specifying `App\Entity\Book`in an annotation on a class that is itself in the `App\Components` namespace will resolve to `App\Components\App\Entity\Book`, which is the **only acceptable way it should ever be resolved** - if you want it to resolve to `App\Entity\Book`, either `use` it and specify just `Book` in the annotation, or prefix it with a backslash - just like you would in PHP code.

Custom integrations
-------------------

[](#custom-integrations)

You can of course create a custom integration for any EventDispatcher you wish! An integration consists of three classes:

### Bridge

[](#bridge)

The integration's Bridge class must implement the `Jahudka\ComponentEvents\IBridge`interface. This class is used during DIC rebuild to detect the presence of the event dispatcher the integration is bridging to and to customise how the other two classes of the integration will be created.

### Analyser

[](#analyser)

This class is responsible for analysing a presenter or a component and extract all events the target wants to subscribe to. It must implement the `Jahudka\ComponentEvents\IAnalyser` interface.

### Relay

[](#relay)

This class is the only one used at runtime. Its responsibility is to subscribe to all the relevant events for the current presenter and then to relay the events to their proper destination when they're fired. It must implement the `Jahudka\ComponentEvents\IBridge` interface.

###  Health Score

29

—

LowBetter than 60% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity15

Limited adoption so far

Community7

Small or concentrated contributor base

Maturity62

Established project with proven stability

 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

Every ~212 days

Total

4

Last Release

1930d ago

Major Versions

v1.0.0 → v2.0.02021-01-23

PHP version history (2 changes)v1.0.0PHP &gt;=7.1

v2.0.0PHP &gt;=7.4

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/330635?v=4)[jahudka](/maintainers/jahudka)[@jahudka](https://github.com/jahudka)

---

Top Contributors

[![jahudka](https://avatars.githubusercontent.com/u/330635?v=4)](https://github.com/jahudka "jahudka (9 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/jahudka-component-events/health.svg)

```
[![Health](https://phpackages.com/badges/jahudka-component-events/health.svg)](https://phpackages.com/packages/jahudka-component-events)
```

###  Alternatives

[kdyby/autowired

Syntax sugar for working with services in Nette Framework

30885.7k9](/packages/kdyby-autowired)

PHPackages © 2026

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