BeginnerEngineerBlog
中の人
中の人

【php symfony】FormTypeの同じフォームを複数回表示したい

公開: 2023-10-13 23:31
更新: 2023-10-14 00:00
232
php symfony4.x FormType ECCUBE4.1 form_widget
ごくごくたまーに同じフォームを複数表示したいときがあるんですが、そのやり方です

こんにちは!

中の人です

たまにフォームタイプのフォームを複数回表示したいときってありますよね?

ない?

ないかー(゚∀゚ ) < マーソンナナイヨネ

さぁいつも通り需要があろうがなかろうが紹介しますよ!

動作環境: ECCUBE4.1


どういうことか



ECCUBE4.1のマイページの会員情報編集画面を利用します


これらの入力フォームはtwigのform_widgetという関数で表示されています。

ここから、例えば会社名の入力フォームを2個表示したいとします。

準備


対象のテンプレートをapp以下にコピペします

📁 root/ec-cube/app/template/default/Mypage/change.twig

{# 👇 ソースからコピペ #}
{#
This file is part of EC-CUBE

Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.

http://www.ec-cube.co.jp/

For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
#}
{% extends 'default_frame.twig' %}

{% set body_class = 'mypage' %}

{% set mypageno = 'change' %}

{% form_theme form 'Form/form_div_layout.twig' %}

{% block javascript %}
    <script src="//yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script>
{% endblock javascript %}

{% block main %}
    <div class="ec-layoutRole__main">
        <div class="ec-mypageRole">
            <div class="ec-pageHeader">
                <h1>{{ 'マイページ'|trans }}/{{ '会員情報編集'|trans }}</h1>
            </div>
            {% include 'Mypage/navi.twig' %}
        </div>
        <div class="ec-mypageRole">
            <div class="ec-editRole">
                <div class="ec-off1Grid">
                    <div class="ec-off1Grid__cell">
                        <form method="post" action="{{ url('mypage_change') }}" novalidate class="h-adr">
                            <span class="p-country-name" style="display:none;">Japan</span>
                            {{ form_widget(form._token) }}
                            <div class="ec-borderedDefs">
                                <dl>
                                    <dt>
                                        {{ form_label(form.name, 'お名前', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.name.name01, form.name.name02) ? ' error'}}">
                                            {{ form_widget(form.name.name01, { 'attr': { 'placeholder': '姓' }}) }}
                                            {{ form_widget(form.name.name02, { 'attr': { 'placeholder': '名' }}) }}
                                            {{ form_errors(form.name.name01) }}
                                            {{ form_errors(form.name.name02) }}
                                        </div>
                                    </dd>
                                </dl>
                                <dl>
                                    <dt>
                                        {{ form_label(form.kana, 'お名前(カナ)', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.kana.kana01, form.kana.kana02) ? ' error'}}">
                                            {{ form_widget(form.kana.kana01, { 'attr': { 'placeholder': 'セイ' }}) }}
                                            {{ form_widget(form.kana.kana02, { 'attr': { 'placeholder': 'メイ' }}) }}
                                            {{ form_errors(form.kana.kana01) }}
                                            {{ form_errors(form.kana.kana02) }}
                                        </div>
                                    </dd>
                                </dl>
                                {# 👇 こいつを2回表示したい #}
                                <dl>
                                    <dt>
                                        {{ form_label(form.company_name, '会社名', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.company_name) ? ' error' }}">
                                            {{ form_widget(form.company_name) }}
                                            {{ form_errors(form.company_name) }}
                                        </div>
                                    </dd>
                                </dl>
                                {# 以下省略 #}

ダメなパターン


で、company_nameフォームを複製してみます

📁 root/ec-cube/app/template/default/Mypage/change.twig

{#
This file is part of EC-CUBE

Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.

http://www.ec-cube.co.jp/

For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
#}
{% extends 'default_frame.twig' %}

{% set body_class = 'mypage' %}

{% set mypageno = 'change' %}

{% form_theme form 'Form/form_div_layout.twig' %}

{% block javascript %}
    <script src="//yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script>
{% endblock javascript %}

{% block main %}
    <div class="ec-layoutRole__main">
        <div class="ec-mypageRole">
            <div class="ec-pageHeader">
                <h1>{{ 'マイページ'|trans }}/{{ '会員情報編集'|trans }}</h1>
            </div>
            {% include 'Mypage/navi.twig' %}
        </div>
        <div class="ec-mypageRole">
            <div class="ec-editRole">
                <div class="ec-off1Grid">
                    <div class="ec-off1Grid__cell">
                        <form method="post" action="{{ url('mypage_change') }}" novalidate class="h-adr">
                            <span class="p-country-name" style="display:none;">Japan</span>
                            {{ form_widget(form._token) }}
                            <div class="ec-borderedDefs">
                                <dl>
                                    <dt>
                                        {{ form_label(form.name, 'お名前', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.name.name01, form.name.name02) ? ' error'}}">
                                            {{ form_widget(form.name.name01, { 'attr': { 'placeholder': '姓' }}) }}
                                            {{ form_widget(form.name.name02, { 'attr': { 'placeholder': '名' }}) }}
                                            {{ form_errors(form.name.name01) }}
                                            {{ form_errors(form.name.name02) }}
                                        </div>
                                    </dd>
                                </dl>
                                <dl>
                                    <dt>
                                        {{ form_label(form.kana, 'お名前(カナ)', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.kana.kana01, form.kana.kana02) ? ' error'}}">
                                            {{ form_widget(form.kana.kana01, { 'attr': { 'placeholder': 'セイ' }}) }}
                                            {{ form_widget(form.kana.kana02, { 'attr': { 'placeholder': 'メイ' }}) }}
                                            {{ form_errors(form.kana.kana01) }}
                                            {{ form_errors(form.kana.kana02) }}
                                        </div>
                                    </dd>
                                </dl>
                                <dl>
                                    <dt>
                                        {{ form_label(form.company_name, '会社名', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.company_name) ? ' error' }}">
                                            {{ form_widget(form.company_name) }}
                                            {{ form_errors(form.company_name) }}
                                        </div>
                                    </dd>
                                </dl>
                                {# 👆こいつを👇以下のように複製 #}
                                <dl>
                                    <dt>
                                        {{ form_label(form.company_name, '会社名', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.company_name) ? ' error' }}">
                                            {{ form_widget(form.company_name) }}
                                            {{ form_errors(form.company_name) }}
                                        </div>
                                    </dd>
                                </dl>
                                {# 以下省略 #}

挙動


マイページの会員情報編集画面にアクセスしてみます


会社名が追加されてますが、フォームが作成されていません。

symfonyでは、formTypeでフォームを作成した場合、idの重複があるので複数回のフォーム表示は許可されていないみたいです。
なので、一度表示したフォームは作成されないみたいです。
(わかりやすい記事探したんですが、見つけられませんでした。🙇‍♂️)

id重複してもいいから表示したいぜ

表示できるパターン


📁 root/ec-cube/app/template/default/Mypage/change.twig

{#
This file is part of EC-CUBE

Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.

http://www.ec-cube.co.jp/

For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
#}
{% extends 'default_frame.twig' %}

{% set body_class = 'mypage' %}

{% set mypageno = 'change' %}

{% form_theme form 'Form/form_div_layout.twig' %}

{% block javascript %}
    <script src="//yubinbango.github.io/yubinbango/yubinbango.js" charset="UTF-8"></script>
{% endblock javascript %}

{% block main %}
    <div class="ec-layoutRole__main">
        <div class="ec-mypageRole">
            <div class="ec-pageHeader">
                <h1>{{ 'マイページ'|trans }}/{{ '会員情報編集'|trans }}</h1>
            </div>
            {% include 'Mypage/navi.twig' %}
        </div>
        <div class="ec-mypageRole">
            <div class="ec-editRole">
                <div class="ec-off1Grid">
                    <div class="ec-off1Grid__cell">
                        <form method="post" action="{{ url('mypage_change') }}" novalidate class="h-adr">
                            <span class="p-country-name" style="display:none;">Japan</span>
                            {{ form_widget(form._token) }}
                            <div class="ec-borderedDefs">
                                <dl>
                                    <dt>
                                        {{ form_label(form.name, 'お名前', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.name.name01, form.name.name02) ? ' error'}}">
                                            {{ form_widget(form.name.name01, { 'attr': { 'placeholder': '姓' }}) }}
                                            {{ form_widget(form.name.name02, { 'attr': { 'placeholder': '名' }}) }}
                                            {{ form_errors(form.name.name01) }}
                                            {{ form_errors(form.name.name02) }}
                                        </div>
                                    </dd>
                                </dl>
                                <dl>
                                    <dt>
                                        {{ form_label(form.kana, 'お名前(カナ)', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.kana.kana01, form.kana.kana02) ? ' error'}}">
                                            {{ form_widget(form.kana.kana01, { 'attr': { 'placeholder': 'セイ' }}) }}
                                            {{ form_widget(form.kana.kana02, { 'attr': { 'placeholder': 'メイ' }}) }}
                                            {{ form_errors(form.kana.kana01) }}
                                            {{ form_errors(form.kana.kana02) }}
                                        </div>
                                    </dd>
                                </dl>
                                {# 👇 form_widgetで取得したhtmlを変数にセットしてしまう #}
                                {% set company_name_form = form_widget(form.company_name) %}
                                <dl>
                                    <dt>
                                        {{ form_label(form.company_name, '会社名', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.company_name) ? ' error' }}">
                                            {# 👇 rawフィルターを使ってhtmlを出力する #}
                                            {{company_name_form|raw}}
                                            {{form_errors(form.company_name)}}
                                        </div>
                                    </dd>
                                </dl>
                                <dl>
                                    <dt>
                                        {{ form_label(form.company_name, '会社名', { 'label_attr': { 'class': 'ec-label' }}) }}
                                    </dt>
                                    <dd>
                                        <div class="ec-halfInput{{ has_errors(form.company_name) ? ' error' }}">
                                            {# 👇 上記に同じ #}
                                            {{company_name_form|raw}}
                                            {{form_errors(form.company_name)}}
                                        </div>
                                    </dd>
                                </dl>
                                {# 以下省略 #}

挙動



表示されました

つまりは「form_widget」で呼び出すフォームのhtmlが一度限りなら、その出力されたhtmlはいくら使っても大丈夫ってことですね

ちなみに


デベロッパーツールででhtmlの構造を見てみると


同じhtmlを表示しているので、当然idやnameは重複しています
たぶんですが、htmlのベストプラクティスではないでしょう


こちらのogizanagiさんの回答を参考にしました(thanks!!)


データを登録してみる


そしたら、この状態でどんな挙動になるのか見てみます


二つ目をカタカナで入力して、送信してみます


追加したフォームの方が優先されました。
まぁこの辺はphp?の仕様だと思いますが、後に定義したフォームのvalueが優先されるみたいですね。

特にエラーもないし、多分問題ないはず


ちなみに


form_errorsについてはどうなるか見てみます

company_nameはformTypeでlength 255が設定されているので、255文字以上入力してsubmitしてみます


form_errorsはそのまま表示できるみたいです。

デベロッパーツール


デベロッパーツールでhtmlを見てみます


form_errorsはclassだけ設定されてるからか、複数回表示できるみたいです。


終わりに


私がこの方法知った背景として、レスポンシブでスマホだったらこっちの要素、pcだったらこっちの要素を表示する的なテンプレートで、そこにフォームタイプで表示するフォームがあったんですよね。
レスポンシブで表示非表示が切り替わるだけで、submitした時の挙動は一緒だったので、formType拡張するの面倒くさいなーなんとか同じフォーム表示できないかなーと思っていたら、先人がよき回答をしてくれていてできました。

ただ、まーhtml的には同じidがあるってのは避けないといけないよねってのがあるので文法的にはアウトでしょう
jsの挙動とか、cssとかに影響出るかもなので、利用する際は慎重にお願いします!

ということで

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