BeginnerEngineerBlog
中の人
中の人

【symfony Eccube4 validator atPath】Validatorでなんの値をバリデートしたかセットしたい

公開: 2023-10-04 00:27
更新: 2023-10-04 00:28
65
symfony5.x validator callback validate atPath
バリデートでなんの値をバリデートしたかセットするやり方です


こんにちは!

中の人です。

以前【php symfony validation ConstraintViolationList】バリデートしたエラーを合体させたいでsymfonyのvalidatorについて記事を書きましたが、検証結果を合体させるのとは別に、一体なんの値をバリデートしたかの値をConstraintViolationListにセットするやり方を紹介します!

Callback


callbackは、イメージとしてはlaravelのafterのように自由にロジックを書きたい時に便利です。

code


※ Eccube4を利用しています。

Template


📁 root/ec-cube/app/template/default/Hoge/index.twig

{% extends 'default_frame.twig' %}

{% block main %}
    <div class="ec-layoutRole__main">
        <form method="POST", action="{{url('hoge')}}">
            <input type="text" name="name"/>
            {% for error in errors %}
                {% if error.propertyPath is same as ('name') %}{# 👈 errorsの中にpropertyPathが'name'の奴の場合表示する処理 #}
                    <div>{{error.message}}</div>
                {% endif %}
            {% endfor %}
            <input type="submit" value="送信"/>
        </form>
    </div>
{% endblock %}


Controller


📁 root/ec-cube/app/Customize/Controller/HogeController.php

<?php

namespace Customize\Controller;

use Eccube\Controller\AbstractController;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

class HogeController extends AbstractController
{
    private $validator;

    public function __construct(
        ValidatorInterface $validator
    )
    {
        $this->validator = $validator;
    }

    /**
     * @Route("/hoge", name="hoge", methods={"GET", "POST"})
     * @Template("Hoge/index.twig")
     */
    public function index(Request $request)
    {
        if ($request->getMethod() === 'POST') {
            $name = $request->get('name');

            $errors = $this->validator->validate(
                $name,
                [
                    new Assert\Callback(function ($name, ExecutionContextInterface $context) {
                        if ($name === null || $name === '') {
                            $context->buildViolation('空やんねこれ')
                                ->atPath('name')// 👈 なんの値を検証したかセットできる
                                ->addViolation();
                        }
                    }),
                ]
            );

        }
        return [
            'errors' => $errors ?? [],
        ];
    } 
}

挙動



空で送信


デバッグ結果ですが、violationsの中の一つに「propertyPath」というプロパティに「name」という値がセットされてますね!


Templateでのif文で、propertyPathがnameの場合に表示するエラーがちゃんと機能してますね!

普通のvalidator


いやー全部Callbackてのも。。て感じますよね

普通の検証結果にもpropertyPathを設定できます

code


Controller


<?php

namespace Customize\Controller;

use Eccube\Controller\AbstractController;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

class HogeController extends AbstractController
{
    private $validator;

    public function __construct(
        ValidatorInterface $validator
    )
    {
        $this->validator = $validator;
    }

    /**
     * @Route("/hoge", name="hoge", methods={"GET", "POST"})
     * @Template("Hoge/index.twig")
     */
    public function index(Request $request)
    {
        if ($request->getMethod() === 'POST') {
            $name = $request->get('name');

                                        // 👇 こんな感じでpropertyPathをセットできる
            $errors = $this->validator->startContext()->atPath('name')->validate(
                $name,
                [
                    new Assert\NotBlank(),
                ]
            )->getViolations();// 👈 これ書かないと別のインスタンスが取得されるので書いとく
        }
        return [
            'errors' => $errors ?? [],
        ];
    } 
}


挙動


同じく空で送信



こんな感じでセットすることができます


ちなみに


->getViolations();

が無い状態で結果を取得すると

Symfony\Component\Validator\ConstraintViolationList

のインスタンスではなく、

Symfony\Component\Validator\Validator\RecursiveContextualValidator

のインスタンスを取得します。

その場合Templateで

            {% for error in errors.violations %}{# 👈 こう書けば以下の処理が正常に作動する #}
                {% if error.propertyPath is same as ('name') %}
                    <div>{{error.message}}</div>
                {% endif %}
            {% endfor %}

このように書けば同じように動作しますが、Templateでわざわざインスタンスを判定するのはナンセンスなのでControllerでやっちゃった方がいいですね。

さらにちなみに


<?php

namespace Customize\Controller;

use Eccube\Controller\AbstractController;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ConstraintViolationList;
use Symfony\Component\Validator\Context\ExecutionContextInterface;

class HogeController extends AbstractController
{
    private $validator;

    public function __construct(
        ValidatorInterface $validator
    )
    {
        $this->validator = $validator;
    }

    /**
     * @Route("/hoge", name="hoge", methods={"GET", "POST"})
     * @Template("Hoge/index.twig")
     */
    public function index(Request $request)
    {
        if ($request->getMethod() === 'POST') {
            $name = $request->get('name');
            
                                                               // 👇 'name'をセット
            $errors = $this->validator->startContext()->atPath('name')->validate(
                $name,
                [
                    new Assert\Callback(function ($name, ExecutionContextInterface $context) {
                        if ($name === null || $name === '') {
                            $context->buildViolation('空やんねこれ')
                                ->atPath('name_osu')// 👈 こっちにもセットしてみる
                                ->addViolation();
                        }
                    }),
                ]
            )->getViolations();
        }
        return [
            'errors' => $errors ?? [],
        ];
    } 
}

親にも子にも(という表現で合ってるかわかりませんが)atPathでpropertyPathをセットしてみます。

これの実行結果は


name.name_osuというpropertyPathがセットされます

これはこれで便利ですね!

終わりに



こちらに最初記載してた内容に色をつけて転載しました。

上記の記事の趣旨と違うなーと感じたので切り出しました。

誰かの参考になれば幸いです!

お疲れ様でした。
0
0
0
0
通信エラーが発生しました。
似たような記事