もがき系プログラマの日常

もがき系エンジニアの勉強したこと、日常のこと、気になっている技術、備忘録などを紹介するブログです。

cakephp3でeventを使用する

はじめに

こんばんは。

今回 CakephpでEventを使用する機会があったので、やり方を備忘録として残しておきます。

book.cakephp.org

もともとCakephp3 には 以下のイベントが用意されています。

MVCにそって開発していくとよく使うのはモデルイベントかもしれません。

今回は独自イベントの設定をやってみました。

本題

まず、EventをDispatchするクラスを作成します。

EventDispatcher.php

<?php
namespace App;

use Cake\Event\Event;
use Cake\Event\EventManager;
use Kojirock5260\Event\EventDispatcherInterface;
use Kojirock5260\Event\EventInterface;

class EventDispatcher implements EventDispatcherInterface
{
    /** @var EventManager  */
    private $eventManager;

    /**
     * EventDispatcher constructor.
     * @param EventManager $eventManager
     */
    public function __construct(EventManager $eventManager)
    {
        $this->eventManager = $eventManager;
    }

    /**
     * @param EventInterface $event
     */
    public function dispatch(EventInterface $event): void
    {
        $this->eventManager->dispatch(new Event($event->eventName(), null, ['data' => $event->eventData()]));
    }
}

また、それぞれ使用するInterfaceを作成します。

EventDispatcherInterface.php

<?php
namespace Kojirock5260\Event;

interface EventDispatcherInterface
{
    /**
     * @param EventInterface $event
     */
    public function dispatch(EventInterface $event): void;
}

EventInterface.php

<?php
namespace Kojirock5260\Event;

interface EventInterface
{
    /**
     * @return string
     */
    public function eventName(): string;

    /**
     * @return array
     */
    public function eventData(): array;
}

次に、EventListenerを作成します。

NotificationEventListener.php

<?php
namespace App\EventListener;

use Cake\Event\EventListenerInterface;

class NotificationEventListener implements EventListenerInterface
{
    /**
     * @return array|string[]
     */
    public function implementedEvents(): array
    {
        return [
            'Notification.Slack' => 'notificationSlack',
        ];
    }

    /**
     * @param $event
     * @param array $data
     */
    public function notificationSlack($event, array $data): void
    {
        // Slackにデータを送信する
    }
}

次に、例えばのビジネスロジックとして、ユーザー登録用のユースケースを作成します。

RegisterUser.php

<?php
namespace Kojirock5260\User\UseCase;

use Kojirock5260\User\Domain\Repository\UserRepositoryInterface;
use Kojirock5260\User\Domain\Event\RegisterUserEvent;
use Kojirock5260\Event\EventDispatcherInterface;

class RegisterUser
{
    /** @var UserRepositoryInterface  */
    private $userRepository;

    /** @var EventDispatcherInterface  */
    private $eventDispatcher;

    /**
     * RegisterUser constructor.
     * @param UserRepositoryInterface $userRepository
     * @param EventDispatcherInterface $eventDispatcher
     */
    public function __construct(UserRepositoryInterface $userRepository, EventDispatcherInterface $eventDispatcher)
    {
        $this->userRepository   = $userRepository;
        $this->eventDispatcher = $eventDispatcher;
    }

    /**
     * @param  array $registerData
     * @throws \Exception
     * @return int
     */
    public function __invoke(array $registerData): int
    {
        $user = $this-userRepository->register($registerData);
        $this->eventDispatcher->dispatch(new RegisterUserEvent($user));

        return $user['id'];
    }
}

ユーザーが新規作成された際に実行したいイベントクラスを作成します。

RegisterUserEvent.php

<?php
namespace Kojirock5260\User\Domain\Event;

use Kojirock5260\Event\EventInterface;

class RegisterUserEvent implements EventInterface
{
    /** @var array  */
    private $user;

    /**
     * RegisterUserEvent constructor.
     * @param array $user
     */
    public function __construct(array $user)
    {
        $this->user = $user;
    }

    /**
     * @return string
     */
    public function eventName(): string
    {
        return 'Notification.Slack';
    }

    /**
     * @return array
     */
    public function eventData(): array
    {
        return $this->user;
    }
}

最後に、ContorllerからEventをセットしつつビジネスロジックを実行します。

ExampleController.php

<?php
namespace App\Controller;

use App\Repository\UserRepository;
use App\EventListener\NotificationEventListener;
use App\EventDispatcher;
use Kojirock5260\User\UseCase\RegisterUser;

class ExampleController extends AppController
{
    public function indexAction()
    {
        $this->getEventManager()->on(new NotificationEventListener());
        $userId = (new RegisterUser(
            new UserRepository($this->UsersTable),
            new EventDispatcher($this->getEventManager())
        ))($this->request->getData('userData'));
    }
}

これで、実際にEventが実行されるようになると思います。

Cakephpのsrc内ですべて完了させるのであれば、こんなに複雑にはしなくていいですが、FWとビジネスロジックを切り離したい場合はこの形が良いかと思いました。

終わりに

愚痴になりますが、今やってる仕事で開発の人が来なくなり自分がすべてを開発することになりました。。。

今年は1人アドベントカレンダーは無理そうです。。。

現場からは以上です。