tobento/app-event

App event support.

1.0.2 2024-02-20 18:01 UTC

This package is auto-updated.

Last update: 2024-12-20 20:01:33 UTC


README

Event support for the app using the Event Service.

Table of Contents

Getting Started

Add the latest version of the app event project running this command.

composer require tobento/app-event

Requirements

  • PHP 8.0 or greater

Documentation

App

Check out the App Skeleton if you are using the skeleton.

You may also check out the App to learn more about the app in general.

Event Boot

The event boot does the following:

  • installs and loads event config file
  • implements event interfaces
  • adds event listeners from config file
use Tobento\App\AppFactory;

// Create the app
$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor');
    
// Adding boots
$app->boot(\Tobento\App\Event\Boot\Event::class);

// Run the app
$app->run();

Event Config

The configuration for the event is located in the app/config/event.php file at the default App Skeleton config location.

Available Event Interfaces

The following interfaces are available after booting:

use Tobento\App\AppFactory;
use Tobento\Service\Event\ListenersInterface;
use Tobento\Service\Event\EventsFactoryInterface;
use Tobento\Service\Event\EventsInterface;
use Psr\EventDispatcher\EventDispatcherInterface;

// Create the app
$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor');
    
// Adding boots
$app->boot(\Tobento\App\Event\Boot\Event::class);
$app->booting();

$listeners = $app->get(ListenersInterface::class);
// is declared as prototype, meaning returning always a new instance!

$eventsFactory = $app->get(EventsFactoryInterface::class);
$events = $app->get(EventsInterface::class);
$eventDispatcher = $app->get(EventDispatcherInterface::class);

// var_dump($events === $eventDispatcher);
// bool(true)

// Run the app
$app->run();

Check out the Event Service to learn more about the interfaces.

Default Events

You can access the default events by using the Tobento\Service\Event\EventsInterface::class from within the app or by autowiring.

Furthermore, the events are used as the default Psr\EventDispatcher\EventDispatcherInterface::class implementation.

Create Event

namespace App\Event;

use App\Entity\User;

final class UserRegistered
{
    public function __construct(
        public readonly User $user
    ) {}
}

Create Listener

namespace App\Listener;

class SendWelcomeMail
{
    public function __invoke(UserRegistered $event): void
    {
        // send welcome mail.
    }
}

Check out the Defining Listeners section to learn more about it.

Add Listeners

You can add listeners by the following ways:

Using event config

You may define the listener in the app/config/event.php file:

use Tobento\App\Event\ListenerRegistry;
use App\Event;
use App\Listener;

return [

    /*
    |--------------------------------------------------------------------------
    | Default Event Listeners
    |--------------------------------------------------------------------------
    |
    | Define the event listeners for the default events.
    |
    | As the events class uses reflection to scan listeners for its events named $event,
    | there is no need to define its event(s) for a listener.
    | But you might do so if you have multiple events in your listener and
    | want only to listen for the specific events or just because of better overview.
    |
    */
    
    'listeners' => [
        // Specify events to listeners:
        Event\UserRegistered::class => [
            Listener\SendWelcomeMail::class,
            
            // with build-in parameters:
            [Listener::class, ['number' => 5]],
            
            // with specific priority:
            new ListenerRegistry(
                listener: Listener::class,
                priority: 1,
            ),
        ],
        
        // Specify listeners without event:
        'auto' => [
            Listener\SendWelcomeMail::class,
            
            // with build-in parameters:
            [Listener::class, ['number' => 5]],
            
            // with specific priority:
            new ListenerRegistry(
                listener: Listener::class,
                priority: 1,
            ),
        ],
    ],
    
    // ...
];

Manually using events

You can add listeners manually by using the EventsInterface::class.

use Tobento\App\AppFactory;
use Tobento\Service\Event\EventsInterface;

// Create the app
$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor');
    
// Adding boots
$app->boot(\Tobento\App\Event\Boot\Event::class);
$app->booting();

$events = $app->get(EventsInterface::class);

// Add listeners
$events->listen(FooListener::class);

$events->listen(AnyListener::class)
       ->event(FooEvent::class)
       ->priority(2000);

// Run the app
$app->run();

Check out the Add Listeners to learn more about adding listeners.

Dispatch Event

You can dispatch events by the following ways:

Using the event dispatcher

The EventDispatcherInterface::class uses the default events as dispatcher implementation.

namespace App\Service;

use Psr\EventDispatcher\EventDispatcherInterface;
use App\Event\UserRegistered;
use App\Entity\User;

final class UserService
{
    public function __construct(
        private readonly EventDispatcherInterface $dispatcher
    ) {}

    public function register(User $user): void
    {
        // ...
        
        $this->dispatcher->dispatch(new UserRegistered($user));
    }
}

Using the events

namespace App\Service;

use Tobento\Service\Event\EventsInterface;
use App\Event\UserRegistered;
use App\Entity\User;

final class UserService
{
    public function __construct(
        private readonly EventsInterface $dispatcher
    ) {}

    public function register(User $user): void
    {
        // ...
        
        $this->dispatcher->dispatch(new UserRegistered($user));
    }
}

Specific Events

You may create specific events for certain services, components or bundles.

Create Events

To create specific events simply extend the Events::class:

use Tobento\Service\Event\Events;

final class ShopEvents extends Events
{
    //
}

The ShopEvents::class will be autowired while using the app!

Add Specific Listeners

You can add listeners for your specific events by the following ways:

Using event config

You may define the listener in the app/config/event.php file:

use Tobento\App\Event\ListenerRegistry;

return [
    // ...
    
    /*
    |--------------------------------------------------------------------------
    | Specific Events Listeners
    |--------------------------------------------------------------------------
    |
    | Define the event listeners for the specific events.
    |
    | As the events class uses reflection to scan listeners for its events named $event,
    | there is no need to define its event(s) for a listener.
    | But you might do so if you have multiple events in your listener and
    | want only to listen for the specific events or just because of better overview.
    |
    */
    
    'events' => [
        ShopEvents::class => [
            // Specify events to listeners:
            SomeEvent::class => [
                Listener::class,

                // with build-in parameters:
                [Listener::class, ['number' => 5]],
            ],

            // Specify listeners without event:
            'auto' => [
                Listener::class,

                // with build-in parameters:
                [Listener::class, ['number' => 5]],

                // to define specific priority:
                new ListenerRegistry(
                    listener: Listener::class,
                    priority: 1,
                ),
            ],
        ],
    ],
    
    // ...
];

Manually using events

You can add listeners manually by using the ShopEvents::class.

use Tobento\App\AppFactory;

// Create the app
$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor');
    
// Adding boots
$app->boot(\Tobento\App\Event\Boot\Event::class);
$app->booting();

$events = $app->get(ShopEvents::class);

// Add listeners
$events->listen(FooListener::class);

$events->listen(AnyListener::class)
       ->event(FooEvent::class)
       ->priority(2000);

// Run the app
$app->run();

Check out the Add Listeners to learn more about adding listeners.

Using the app on method

You can add listeners by using the app on method.

use Tobento\App\AppFactory;
use Tobento\App\Event\ConfigEventsRegistry;

// Create the app
$app = (new AppFactory())->createApp();

// Add directories:
$app->dirs()
    ->dir(realpath(__DIR__.'/../'), 'root')
    ->dir(realpath(__DIR__.'/../app/'), 'app')
    ->dir($app->dir('app').'config', 'config', group: 'config')
    ->dir($app->dir('root').'vendor', 'vendor');
    
// Adding boots
$app->boot(\Tobento\App\Event\Boot\Event::class);
$app->booting();

$app->on(ShopEvents::class, function(ShopEvents $shopEvents) {
    // Add listeners
    $shopEvents->listen(FooListener::class);

    // Or add listeners from config:
    (new ConfigEventsRegistry(priority: 1000))->addListenersFromArray(
        events: $shopEvents,
        
        // using same definition as config listeners.
        eventListeners: [
            SomeEvent::class => [
                Listener::class,

                // with build-in parameters:
                [Listener::class, ['number' => 5]],
                
                // ...
            ],
        ],
    );
});
        
// Run the app
$app->run();

Check out the Add Listeners to learn more about adding listeners.

Use Events

After setting up your specific events, you can use it in your service.

namespace App\Service;

use Psr\EventDispatcher\EventDispatcherInterface;

final class ShopService
{
    public function __construct(
        private readonly EventDispatcherInterface $dispatcher
    ) {}
}

// or:
final class AnotherShopService
{
    public function __construct(
        private readonly ShopEvents $dispatcher
    ) {}
}

There are several ways to inject your events into your ShopService::class:

// using a closure:
$app->set(ShopService::class, function() {
    return new ShopService(
        dispatcher: $app->get(ShopEvents::class),
    );
});

// using the construct method:
$app->set(ShopService::class)->construct($app->get(ShopEvents::class));

// using the on method:
$app->on(ShopService::class, ['dispatcher' => ShopEvents::class]);

The AnotherShopService::class requires no action for injection as ShopEvents::class is defined as dispatcher which gets autowired.

You may check out the App Definitions and the App On method for more information.

Queue Listeners

In progress ...

Credits