BeginnerEngineerBlog
中の人
中の人

【ECCUBE4系 symfony4系】terminateイベントにはphp-fpmが必要です。あと、commitされません。

公開: 2022-08-09 18:40
更新: 2023-04-05 23:24
676
symfony4.x ECCUBE4.1 eventListener terminate doctrine
terminateイベントを初めて利用したら、何だかかなりつまづいたので、メモとして紹介します。

こんにちは。

中の人です。

もーsymfonyもdoctorineも本当にもーということで、タイトルの通りECCUBE4系でeventListenerでterminateイベントを利用したときに、つまづいた点をメモとして紹介します。

ちなみに、あまりコアの部分は調べてないので、ECCUBE4系のみの挙動なのかよく分かってません。
多分symfonyの挙動だと思ってますが、まぁ、日本でsymfonyのフレームワークを使っているのはECCUBE4系だけな気がするので(偏見)、純粋なsymfonyフレームワークを利用している方は参考にしてください。

ということで紹介していきますぅぅぅよ!おらぁぁぁ!

EventListener terminateイベントとは


おそらくこの記事に辿り着いた方はほとんど理解されているかと思いますが、簡単に説明します。

EventListener


Symfony アプリケーションの実行中に、多くのイベント通知がトリガーされます。アプリケーションはこれらの通知をリッスンし、任意のコードを実行して応答できます。
symfony は、HTTP リクエストの処理中にカーネルに関連するいくつかのイベントをトリガーします。サードパーティのバンドルもイベントをディスパッチする場合があり、独自のコードからカスタム イベントをディスパッチすることもできます。

日本語訳でよくわからないかと思いますしそもそもよくわからないと思いますが、つまり、「コード中にこのフラグあったらこの処理してね」「データベースに値を保存することあったら、この処理してね」みたいな感じで、特定の処理に追加で処理を行うことができる仕組みです。
ECCUBE4を触っていると、コントローラのコードの中に

📁 root/src/Eccube/Controller/ProductController.php
        ~~ 省略
        $event = new EventArgs(
            [
                'builder' => $builder,
            ],
            $request
        );
        $this->eventDispatcher->dispatch(EccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE, $event);

こんなようなコードがいろんな箇所にありますが、このコードが実行される際に、追加で処理したい内容があったら、

EccubeEvents::FRONT_PRODUCT_INDEX_INITIALIZE

のイベントで処理してね。みたいなことです。


terminateイベント


terminateイベントは、簡単に言うと(簡単にしか理解してない)、クライアントにレスポンスを返した後に実行されるイベントです。

📁 root/index.php

~~ 省略
$kernel = new Kernel($env, $debug);
$response = $kernel->handle($request);
$response->send();
$kernel->terminate($request, $response); //👈 こいつの処理の中でイベントが発火される

$response->send();

レスポンスを送信後に、なんか重たい処理を実行したい時などに利用するイベントらしいです。


terminateイベントにはphp-fpmの環境が必要です


へーめちゃ便利じゃーんと実際にコードを動かしてみたところ、terminateイベントが終了(フレームワークの処理が全て完了)してからじゃないとレスポンスが返らなかったんですよね。
おーいフ◯ックだぜまじでと思ってめちゃ調べたら、terminateイベントてphp-fpmじゃないとちゃんと動作しないみたいなんですよね。

というのも、terminateイベントはphp-fpmの

fastcgi_finish_request()

という関数ありきの処理みたいなので、Apacheだと期待する動きにならないんですよね。


開発環境がApacheだったので、開発環境をphp-fpmに変えて試したら、ちゃんとレスポンスが返ったあとにterminateイベントの処理を行うことができました。
へー(゚∀゚ ) < ソンナンシランワ

(こちらにはApacheでも動作させることできるっぽいこと書かれてますが、どうなんでしょう?)

まぁいいや。

2022-10-29追記
php-fpmの環境構築はこちらに記事にしました。(バージョンはECCUBE4.2です)


エンティティが保存されねーぞおい


ひとまずEventListenerを作成


📁 root/app/Customize/EventListener/TerminateListener

<?php

namespace Customize\EventListener;

use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class TerminateListener implements EventSubscriberInterface
{
    public function __construct()
    {
    }


    public function onTerminate(PostResponseEvent $event)
    {
    }


    public static function getSubscribedEvents()
    {
        return array(KernelEvents::TERMINATE => 'onTerminate');
    }
}

一旦こんな形で作成します。

で、例えばですが、

📁 root/app/Customize/EventListener/TerminateListener

<?php

namespace Customize\EventListener\Admin;

use Eccube\Entity\Order;
use Eccube\Entity\Master\OrderStatus;
use Eccube\Repository\OrderRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Eccube\Repository\Master\OrderStatusRepository;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;


class TerminateListener implements EventSubscriberInterface
{
    private $orderRepository;

    private $entityManager;

    private $orderStatusRepository;

    public function __construct(
        OrderRepository $orderRepository,
        EntityManagerInterface $entityManager,
        OrderStatusRepository $orderStatusRepository
    )
    {
        $this->orderRepository = $orderRepository;
        $this->entityManager = $entityManager;
        $this->orderStatusRepository = $orderStatusRepository;
    }


    public function onTerminate(PostResponseEvent $event)
    {
        $request = $event->getRequest();
        $order_id = $request->get('order_id');
        $order = $this->orderRepository->find($order_id);
        $order->setOrderStatus($this->orderStatusRepository->find(OrderStatus::CANCEL));
        $this->entityManager->persist($order);
        $this->entityManager->flush();
    }


    public static function getSubscribedEvents()
    {
        return array(KernelEvents::TERMINATE => 'onTerminate');
    }
}


こんな処理を追加したとします。

これだと、レコード更新されないんですよね。


保存される書き方


なんてことないですが、

📁 root/app/Customize/EventListener/TerminateListener

<?php

namespace Customize\EventListener\Admin;

use Eccube\Entity\Order;
use Eccube\Entity\Master\OrderStatus;
use Eccube\Repository\OrderRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpKernel\KernelEvents;
use Eccube\Repository\Master\OrderStatusRepository;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class TerminateListener implements EventSubscriberInterface
{
    private $orderRepository;

    private $entityManager;

    private $orderStatusRepository;

    public function __construct(
        OrderRepository $orderRepository,
        EntityManagerInterface $entityManager,
        OrderStatusRepository $orderStatusRepository
    )
    {
        $this->orderRepository = $orderRepository;
        $this->entityManager = $entityManager;
        $this->orderStatusRepository = $orderStatusRepository;
    }

    public function onTerminate(PostResponseEvent $event)
    {
        $request = $event->getRequest();
        $order_id = $request->get('order_id');
        $order = $this->orderRepository->find($order_id);
        $order->setOrderStatus($this->orderStatusRepository->find(OrderStatus::CANCEL));
        $this->entityManager->persist($order);
        $this->entityManager->flush();
        // 👇 こいつを追加
        $this->entityManager->commit();
    }

    public static function getSubscribedEvents()
    {
        return array(KernelEvents::TERMINATE => 'onTerminate');
    }
}

$this->entityManager->commit();

これ追加したら保存されました。

どっかでtransactionがスタートしてたみたいです。

スタートしたらcommitまでしてくれやぁもーって思いましたが、うーん自分の書き方がおかしいんですかね。わからん。あと、ECCUBEだから?なのかもよくわかりません。
だからと言ってコアの処理まで追いたくないよー。

終わりに


やっぱ初めて利用する機能とかって色々と罠がありますねーと感じました改めて。

ということで、同じような現象に悩まされている方の助けになったら幸いです。

お疲れした!
0
0
0
0
通信エラーが発生しました。
【広告】
似たような記事