【Laminas】addActionメソッドにlaminas-hydratorを組み込む

Laminas

どーも!marusukeです!

前回の記事で、blog追加ページが完成しましたが、laminas-hydratorコンポーネントを用いることでaddActionメソッドを少し改善できるので、修正していきます!

修正する箇所

修正部分は、WriteControllerのaddActionメソッドの以下の部分になります。

<?php
// namespaceやuse文は割愛します。

class WriteController extends AbstractActionController
{
    private $command;

    private $form;

    public function __construct(PostCommandInterface $command, PostForm $form)
    {
        $this->command = $command;
        $this->form = $form;
    }

    public function addAction()
    {
        $request   = $this->getRequest();
        $viewModel = new ViewModel(['form' => $this->form]);

        if (! $request->isPost()) {
            return $viewModel;
        }

        $this->form->setData($request->getPost());

        if (! $this->form->isValid()) {
            return $viewModel;
        }
        
        // 以下の2行を修正します。
        $data = $this->form->getData()['post'];
        $post = new Post($data['title'], $data['text']);

        try {
            $post = $this->command->insertPost($post);
        } catch (\Exception $ex) {
            // An exception occurred; we may want to log this later and/or
            // report it to the user. For now, we'll just re-throw.
            throw $ex;
        }

        return $this->redirect()->toRoute(
            'blog/detail',
            ['id' => $post->getId()]
        );
    }
}

今のaddActionメソッドの処理の流れを簡単に説明すると、

  1. GET通信かPOST通信かを判別する
  2. GET通信の場合、追加ページのviewModelをview側に渡す
  3. POST通信の場合は、フォームから入力されたデータを取得する
  4. 3のデータを検証し、問題があれば、エラーメッセージと追加ページのviewModelをview側に渡す
  5. 4の検証で問題がなければ、データを生成したPostオブジェクトに渡し、DBに保存する

こちらの処理の流れの詳細は、以下の記事で説明しています。

【laminas】blogのview側を作成する!:addActionメソッドの処理ロジックについて

修正後の完成形

現状は、以下のようになっています。

//addActionメソッド内
...
// 以下の2行を修正します。
$data = $this->form->getData()['post'];
$post = new Post($data['title'], $data['text']);
...

修正後は、以下の形になります。

//addActionメソッド内
...
//修正後
$post = $this->form->getData();
...

この1行に修正するためにlaminasのコンポーネントを使用し、PostFieldsetクラスとPostFormクラスを修正します。(後ほど説明します)

なぜ修正するのか?

理由は、FormクラスとControllerクラスの分離です。簡単に説明します。

$data = $this->form->getData()['post'];
$post = new Post($data['title'], $data['text'])

この2行は、今のところ以下の流れになっています。

  1. 検証済みデータをPostFormオブジェクトから取得する
  2. Postオブジェクトを生成する
  3. その検証済みデータからtitleとtextを取り出し、Postオブジェクトに渡す

上記の中で、2つの問題があります。

  • getDataメソッドで「post」を指定しているため、フォームの名前(PostFormクラス)が変更になるとエラーとなる
  • date[‘title’]、date[‘text’]という部分で、データ内にtitleとtextがない場合エラーとなる

したがって、PostFormクラスが変更になった場合、上記のWriteControllerクラス内も変更しなければならず、とても手間です。

以下のように実装して上記2つの問題を解決することが今回の目的です。(ちなみに下記の変数$postに格納されるのは、検証済みデータの入ったPostオブジェクトです。)

$post = $this->form->getData();

上記の1行を実現するためにlaminas-hydratorコンポーネントを使って修正します。

どのように修正するのか?

修正箇所はPostFieldsetクラスとPostFormクラスを修正します。

PostFieldsetクラスの修正

以下の内容を追記します。

module/Blog/src/Form/PostFieldset.php

<?php
namespace Blog\Form;

use Laminas\Form\Fieldset;

// ① 2つのuse文を追加します
use Blog\Model\Post;
use Laminas\Hydrator\ReflectionHydrator;

class PostFieldset extends Fieldset
{
    public function init()
    {
        // ② 以下の2行を追加します
        $this->setHydrator(new ReflectionHydrator());
        $this->setObject(new Post('', ''));

        $this->add([
            'type' => 'hidden',
            'name' => 'id',
        ]);

        $this->add([
            'type' => 'text',
            'name' => 'title',
            'options' => [
                'label' => 'Post Title',
            ],
        ]);

        $this->add([
            'type' => 'textarea',
            'name' => 'text',
            'options' => [
                'label' => 'Post Text',
            ],
        ]);
    }
}

追加部分の①と②を簡単に説明します。

① 追加した2つのuse文について

use Blog\Model\Post;
use Laminas\Hydrator\ReflectionHydrator;

このPostFieldsetクラスで、使用するPostクラスとReflectionHydratorクラスを読み込みます。

ちなみに今回使用するReflectionHydratorとは、

ReflectionHydratorはObjectPropertyHydratorに似ていますが、 PHPのReflection APIを使用して、任意の可視性のプロパティをハイドレートまたは抽出します。既存のプロパティにマッチする任意のデータキーがハイドレートされます。既存のプロパティは、データを抽出するために使用されます。

Components laminas-hydrator : ReflectionHydrator

ReflectionHydratorができることを簡単に説明すると、

  • ハイドレート(hydrateメソッド) = 空のエンティティ(今回はPostクラス)に、データを入れ込むこと
  • 抽出(extractメソッド) = データが入ったエンティティからデータを出すこと

です。

次の部分で、このReflectionHydratorクラスとPostエンティティクラスをPostFieldsetに入れ込みます。

② initメソッド内の追加した2行について

public function init()
{
     // ② 以下の2行を追加します
     $this->setHydrator(new ReflectionHydrator());
     $this->setObject(new Post('', ''));

     ...

上記の2行を簡単に説明すると、

  • setHydratorメソッドで、ReflectHydratorインスタンスを設定しています。
  • setObjectメソッドで、Postオブジェクトを設定しています。

この上記2つを設定することで、ブラウザの入力フォームから入力されたデータは、自動でPostオブジェクトに渡される準備ができました。

次はPostFormクラスを修正していきます。

PostFormクラスの修正

以下の内容を追記します。

<?php
namespace Blog\Form;

use Laminas\Form\Form;

class PostForm extends Form
{
    public function init()
    {
        $this->add([
            'name' => 'post',
            'type' => PostFieldset::class,

            // 以下のoptionsを追加する
            'options' => [
                'use_as_base_fieldset' => true,
            ],
        ]);

        $this->add([
            'type' => 'submit',
            'name' => 'submit',
            'attributes' => [
                'value' => 'Insert new Post',
            ],
        ]);
    }
}

追記した’options’部分について

$this->add([
    'name' => 'post',
    'type' => PostFieldset::class,

    // 以下のoptionsを追加する
    'options' => [
        'use_as_base_fieldset' => true,
    ],
]);

上記の’options’に’use_as_base_fieldset’をtrueで設定することで、このPostFieldsetをデフォルトで使用するベースフィールドセットとして設定します。

最後にWriteControllerのaddActionメソッドの修正

WriteControllerのaddActionメソッド内の以下の2行を

//WriteControllerのaddActionメソッド内
...
// 以下の2行を修正します。
$data = $this->form->getData()['post'];
$post = new Post($data['title'], $data['text']);
...

下記の1行に変更します。

$post = $this->form->getData();

これで、addActionメソッドの修正が完了しました!

お疲れ様でした!

次は、blog記事を編集する機能(コントローラー・ファクトリー・ルート・リポジトリ・画面)を作成していきます!

コメント

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