【Laminas】blogのWriteControllerのファクトリーとルートを作る!

Laminas

どーも!marusukeです!

前回の記事で、WriteControllerの雛形を作成したので、このコントローラーをインスタンス化するためのファクトリー(WriteControllerFactory.php)と、ルートを作成していきます!

作成の流れとしては、

  • Formクラスを作成する。
  • コントローラーを作る。
  • コントローラーのインスタンス化のためのFactoryとルートを作る。<= 現在ここです!
  • モデル(repositoryクラス)を作成する。
  • view側を作る。

WriteControllerFactoryを作成する!

WriteControllerクラスをインスタンス化するための、WriteControllerFactoryを作成します。

以下のようになります。

module/Blog/src/Factory/WriteControllerFactory.php

<?php

// ① 
namespace Blog\Factory;

// ②
use Blog\Controller\WriteController;
use Blog\Form\PostForm;
use Blog\Model\PostCommandInterface;
use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\Factory\FactoryInterface;

// ③
class WriteControllerFactory implements FactoryInterface
{
    /**
     * @param ContainerInterface $container
     * @param string $requestedName
     * @param null|array $options
     * @return WriteController
     */

    // ④
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $formManager = $container->get('FormElementManager');
        return new WriteController(
            $container->get(PostCommandInterface::class),
            $formManager->get(PostForm::class)
        );
    }
}

上から簡単に説明していきます。

// ①「namespace Blog\Factory;」について

namespaceをBlogディレクトリ内のFactoryディレクトリを指定しています。今後Factoryクラスを追加する場合は、このFactoryディレクトリ内に追加していきます。

// ② 各use文について

use Blog\Controller\WriteController;
use Blog\Form\PostForm;
use Blog\Model\PostCommandInterface;
use Interop\Container\ContainerInterface;
use Laminas\ServiceManager\Factory\FactoryInterface;

ここにこのWriteControllerFactoryクラスで使用するその他のクラスを読み込んでいきます。

// ③「class WriteControllerFactory implements FactoryInterface」について

FactoryInterfaceをimplementsし、WriteControllerFactoryクラスを定義しています。

ちなみにFactoryInterface.phpは、

vendor/laminas/laminas-servicemanager/src/Factory/FactoryInterface.phpにあり、

以下のような内容になっています。

<?php

declare(strict_types=1);

namespace Laminas\ServiceManager\Factory;

use Interop\Container\ContainerInterface;
use Interop\Container\Exception\ContainerException;
use Laminas\ServiceManager\Exception\ServiceNotCreatedException;
use Laminas\ServiceManager\Exception\ServiceNotFoundException;

/**
 * Interface for a factory
 *
 * A factory is an callable object that is able to create an object. It is
 * given the instance of the service locator, the requested name of the class
 * you want to create, and any additional options that could be used to
 * configure the instance state.
 */
interface FactoryInterface
{
    /**
     * Create an object
     *
     * @param  string             $requestedName
     * @param  null|array<mixed>  $options
     * @return object
     * @throws ServiceNotFoundException If unable to resolve the service.
     * @throws ServiceNotCreatedException If an exception is raised when creating a service.
     * @throws ContainerException If any other error occurs.
     */
    public function __invoke(ContainerInterface $container, $requestedName, ?array $options = null);
}

// ④ __invokeメソッドについて

public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
        $formManager = $container->get('FormElementManager');
        return new WriteController(
            $container->get(PostCommandInterface::class),
            $formManager->get(PostForm::class)
        );
}

この__invokeメソッドを簡単に解説していきます。

__invokeメソッドは、マジックメソッドと呼ばれるもので、既にPHPによって動作が決まっているメソッドです。詳細はこちらです。PHPマニュアル マジックメソッド

以下の部分で、__invokeメソッドを定義しています。これはこれによって、このFactoryクラスは、クラスをインスタンス化し、メソッドを呼び出すことなく、インスタンスの呼び出しのみで__invokeメソッド内の処理を実行できます。

引数について

public function __invoke(ContainerInterface $container, $requestedName, array $options = null)

第1引数の「ContainerInterface $container」は、アプリケーションのコンテナです。PSR-11に準拠しています。詳しくはこちらです。PHP-FIG PSR-11: Container interface

第2引数の「$requestedName」は、FactoryInterfaceを自作した場合、そのインスタンスを呼び出す時の名前を設定します。

第3引数の「array $options = null」は、コントローラーマネージャーがインスタンス作成する時の任意のオプションです。これは使用しないので、このままで良いです。

次に__invokeメソッド内の実装について簡単に説明していきます!

__invokeメソッド内の実装について

$formManager = $container->get('FormElementManager');
return new WriteController(
       $container->get(PostCommandInterface::class),
       $formManager->get(PostForm::class)
);

上から1行ずつ簡単に説明していきます。

$container->get(‘FormElementManager’);について

この部分で、新たにFormElementManagerというものが現れました。これはフォーム専用のプラグインマネージャーです。便利な機能があります。

  • コンテナインスタンスのgetメソッドで、作成したフォームクラスやフィールドセットクラスを取得すると、initメソッドを実行し、作成したフォームやフィールドセットを呼び出してくれます(全ての依存関係が注入された後にinitメソッドが実行されます)。
  • フォームへの入力内容の検証に関するプラグインマネージャーなどもインスタンスと共有されるようになります。

ここまででFactoryが完成しました!

次にこのFactoryをmodule.config.phpに追記し、WriteControllerのルートを作成します。

WriteControllerのルートを作成する!

まずは、WriteControllerのインスタンス化するFactoryを指定するために、module.config.phpに以下を追記します。ルートについては公式ドキュメントはこちらです。Components laminas-router

module/Blog/config/module.config.php内のcontrollers構成部分内に追記します。

<?php

namespace Blog;

use Laminas\Router\Http\Literal;
use Laminas\Router\Http\Segment;
use Laminas\ServiceManager\Factory\InvokableFactory;

return [
 'controllers' => [
  'factories' => [
 Controller\ListController::class => Factory\ListControllerFactory::class,
 // 以下の部分を追加します
Controller\WriteController::class => Factory\WriteControllerFactory::class,
  ],
 ]
 'service_manager' => [/* ... */]
 'router' => [/* ... */]
 'view_manager' => [/* ... */]
];

これで、WriteControllerをインスタンス化するFactoryをModuleManagerに知らせることが出来ます。

次にWriteControllerのルートを作成します。同様にmodule.config.phpのrouter構成部分に、以下の内容を追記します。

module/Blog/config/module.config.php

router構成部分に新たに「add」というルートを追加します。

<?php

namespace Blog;

use Laminas\Router\Http\Literal;
use Laminas\Router\Http\Segment;
use Laminas\ServiceManager\Factory\InvokableFactory;

return [
    'service_manager' => [ /* ... */ ],
    'controllers'     => [ /* ... */ ],
    'router'          => [
        'routes' => [
            'blog' => [
                'type' => Literal::class,
                'options' => [
                    'route'    => '/blog',
                    'defaults' => [
                        'controller' => Controller\ListController::class,
                        'action'     => 'index',
                    ],
                ],
                'may_terminate' => true,
                'child_routes'  => [
                    'detail' => [
                        'type' => Segment::class,
                        'options' => [
                            'route'    => '/:id',
                            'defaults' => [
                                'action' => 'detail',
                            ],
                            'constraints' => [
                                'id' => '\d+',
                            ],
                        ],
                    ],

                    // 追記箇所です。
                    'add' => [
                        'type' => Literal::class,
                        'options' => [
                            'route'    => '/add',
                            'defaults' => [
                                'controller' => Controller\WriteController::class,
                                'action'     => 'add',
                            ],
                        ],
                    ],
                ],
            ],
        ],
    ],
    'view_manager'    => [ /* ... */ ],
];

追記箇所のみを抜き出して簡単に説明していきます。

'add' => [
     'type' => Literal::class,
     'options' => [
          'route'    => '/add',
          'defaults' => [
              'controller' => Controller\WriteController::class,
              'action'     => 'add',
          ],
     ],
],

このルートは、

「http://ドメイン/blog/add」をブラウザからリクエストすると、WriteControllerのaddActionメソッドが呼び出され、そのメソッドの処理結果が、addビュースクリプトを経由してレスポンスとして返ってくる

という設定です。

ルートの記述内容について簡単に説明します。

‘add’について

このルートは、/blogの子ルートです。

ブラウザで「http://ドメイン/blog/add」にリクエストすると、WriteControllerのaddActionメソッドが実行され、そのメソッドによって処理されたデータが、add.phtmlというviewscriptに渡され、ブラウザ上に表示されます。

つまり「http://ドメイン/add」でリクエストしても、そのようなルートは見つからないというエラーが返ってきます。

‘type’について

このルートのタイプは、Literal(リテラル)タイプで、‘options’内の‘route’で指定したものに完全一致した場合、リクエストが成功するタイプです。

つまり今回は、‘route’ => ‘/add’なので、/addに完全一致する場合がリクエスト成功となります。

‘options’について

‘options’には、‘route’‘defaults’を含み、以下のような意味を持っています。

  • ‘route’ はリクエストが成功するときのURI(サブディレクトリ部分)
  • ‘defaults’ はリクエストが来たときに実行するコントローラーとそのアクションメソッドを指定します。

今回の場合は、

  • ‘route’ は /addを指定します。(サブディレクトリ部分)
  • ‘defaults’ は、WriteControllerのaddActionメソッドを指定します。

ここまでで、ルートの作成が完了しました!

お疲れ様でした!

次は、PostCommandリポジトリの雛形の作成をします!

コメント

タイトルとURLをコピーしました