【Eccube4】お客様情報や非会員情報欄に項目を追加する方法

updated 2022-5-9

Eccube4

Eccube4はSymfonyというフレームワークの上に作られていて、ベースはSymfonyなので、Eccubeの解決方法で出てこない場合にはSymfonyを探してみると解決策が見つかる可能性があります

編集前に

基本的にPlugin内に新たにNewPluginというプラグインを作成して編集します
Twigファイルのみ使用しているテンプレートを書き換えないといけないです

公式のアップデートで修正するまでの一時処理になりそうな部分もあります

環境

Eccube 4.1.2

お客様情報に項目を追加する

編集する必要があるファイルは以下になります

app/Plugin/NewPlugin/Entity/CustomerTrait.php
app/template/<TemplateName>/admin/Customer/edit.twig

<TemplateName>は使用しているテンプレートの名前です
変更していない場合にはdefaultになります

CustomerTrait.php

<?php

namespace Plugin\NewPlugin\Entity;

use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation as Eccube;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Eccube\EntityExtension("Eccube\Entity\Customer")
 */
trait CustomerTrait {

    /**
     * @ORM\Column(name="added_property", type="string", length=255, nullable=true)
     * @Eccube\FormAppend(
     *  auto_render=true,
     *  type="\Symfony\Component\Form\Extension\Core\Type\TextType",
     *  options={
     *    "required": true,
     *    "label": "追加する項目のラベル",
     *    "attr": {"placeholder": "例:SAMPLE"}
     *  }
     * )
     * @Assert\NotBlank(message="追加する項目のラベルを入力してください")
     */
    private $added_property;

    public function getAddedProperty() {
        return $this->added_property;
    }

    public function setAddedProperty($added_property) {
        $this->added_property = $added_property;

        return $this;
    }
}

edit.twig

これは必須の場合にラベルをつけるためのプロパティが用意されていないので、必須項目ラベルを付け足すための編集です
CustomerTraitauto_rendererfalseにして、src/Eccube/Resource/template/Entry内のTwigを上書きするのでも構わないと思います

sec/Eccube/Resource/template/admin/Customer/edit.twigをコピーして編集します

...
<div class="row mb-2">
    <div class="col-3">
        <span>{{ 'admin.common.added_property'|trans }}</span>
        <span class="badge badge-primary ml-1">{{ 'admin.common.required'|trans }}</span>
    </div>
    <div class="col">
        {{ form_widget(form.added_property) }}
        {{ form_errors(form.added_property) }}
    </div>
</div>
...
{# エンティティ拡張の自動出力 #}
{% for f in form|filter(f => f.vars.eccube_form_options.auto_%}
    {% if f.vars.eccube_form_options.form_theme %}
        {% form_theme f f.vars.eccube_form_options.form_theme %}
        {{ form_row(f) }}
    {% if f.vars.id == "admin_customer_added_property" %}
    {% else %}
        <div class="row mb-2">
            <div class="col-3">
                <span>{{ f.vars.label|trans }}</span>
            </div>
            <div class="col">
                {{ form_widget(f) }}
                {{ form_errors(f) }}
            </div>
        </div>
    {% endif %}
{% endfor %}
...

足したのは{% if f.vars.id == "admin_customer_added_property" %}の箇所です
追加したプロパティは自動で出力せず、上の既存のものに追加しています

公式でプロパティを増やしてくれれば...例えばrequiredをauto_rendererと並列に置くとか...

ゲスト購入時に項目を追加する

// form table
app/Plugin/NewPlugin/Entity/OrderTrait.php
app/Plugin/NewPlugin/Entity/ShoppingTrait.php
app/Plugin/NewPlugin/Form/Extension/NonMemberExtension.php
// display
app/Plugin/NewPlugin/Controller/ShoppingController.php
app/Plugin/NewPlugin/Controller/NonMemberShoppingController.php
app/Plugin/NewPlugin/Service/OrderHelperService.php
app/Plugin/NewPlugin/Repository/OrderRepository.php
app/config/eccube/services.yaml
app/template/<TemplateName>/Shopping/index.twig
app/template/<TemplateName>/Shopping/confirm.twig

/shoppingを操作しているのはsrc/Eccube/Controller/ShoppingController.phpなので困ったらそのファイルを見ると良いです

OrderTrait.php

Entity

<?php

namespace Plugin\NewPlugin\Entity;

use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation as Eccube;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @Eccube\EntityExtension("Eccube\Entity\Order")
 */
trait OrderTrait {

    /**
     * @ORM\Column(name="added_property", type="string", length=255, nullable=true)
     * @Eccube\FormAppend(
     *  auto_render=true,
     *  type="\Symfony\Component\Form\Extension\Core\Type\TextType",
     *  options={
     *    "required": true,
     *    "label": "追加する項目のラベル",
     *    "attr": {"placeholder": "例:SAMPLE"}
     *  }
     * )
     * @Assert\NotBlank(message="追加する項目のラベルを入力してください")
     */
    private $added_property;

    public function getAddedProperty() {
        return $this->added_property;
    }

    public function setAddedProperty($added_property) {
        $this->added_property = $added_property;

        return $this;
    }
}

プラグインの編集が終わったら
データベースを更新する
データベースのスキーマを更新する
を行います

ShoppingTrait.php

上記で作成したOrderTrait.phpをコピーして、
ファイル名とtraitの名前をShoppingTraitにしてください

ShoppingController

4.0.2からある公式のバグ?のようです OrderTraitで追加した項目をhandleRequestに渡すと追加したプロパティが消えるので修正します

$form->handleRequest($request);

上記のコードを全て下のコードに置き換えます

$form->submit($request->get($form->getName()), false);

SymfonyのFormのsubmitにfalseを第2項目で追加すると追加でプロパティを増やせるのですが、handleRequestでラップされているので、消えます

NonMemberExtension.php

FormTypeのカスタマイズです
Entityからカスタマイズする方が簡単ですが、NonMemberにはEntityがないのでFormTypeから変更します

src/Eccube/Form/Type/Front/NonMemberType.phpを上書きするためのものです
ファイル名ではなく、getExtendedTypegetExtendedTypesで返す型を上書きできます

<?php

namespace Plugin\NewPlugin\Form\Extension;

use Eccube\Form\Type\Front\NonMemberType;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Form\FormView;
use Symfony\Component\Form\FormInterface;

class NonMemberExtension extends AbstractTypeExtension
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('added_property', TextType::class, [
            'required' => true,
            'constraints' => [
                new NotBlank(),
            ],
            'attr' => [
                'placeholder' => '例:SAMPLE',
            ]
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function buildView(FormView $view, FormInterface $form, array $options): void
    {
        $builder = $form->getConfig();
        $view->vars['added_property'] = $builder->getAttribute('added_property');
    }

    /**
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        return NonMemberType::class;
    }
    
    /**
     * {@inheritdoc}
     */
    public static function getExtendedTypes(): iterable
    {
        yield NonMemberType::class;
    }
}

NonMemberShoppingController.php

/shopping/nonmemberページのコントローラーです

sec/Eccube/Controller/NonMemberShoppingController.phpをコピーしてきます
以下の部分に新しいプロパティを追加します

<?php
...
class NonMemberShoppingController extends AbstractShoppingController
{
    ...
    public function customer(Request $request)
    {
        ...
        $Order
            ...
            ->setEmail($data['customer_email'])
            ->setAddedProperty($data['customer_added_property']);

        $this->session->set(OrderHelper::SESSION_NON_MEMBER, [
                ...,
                'email' => $data['customer_email'],
                'added_property' => $data['customer_added_property'],
            ]);

OrderHelperService.php

src/Eccube/Service/OrderHelper.phpをコピペして作成します
追加した項目をshoppingのお客様情報部分に渡すためのコードです
後でservices.yamlに上書きすると記載します

<?php
...
namespace Plugin\NewPlugin\Service;
...
class OrderHelper
{
    ...
    public function getNonMember($session_key = self::SESSION_NON_MEMBER)
    {
        ...
        $Customer
            ...
            ->setAddr02($data['addr02'])
+           ->setAddedProperty($data['added_property']);

OrderRepository.php

src/Eccube/Repository/OrderRepository.phpgetQueryBuilderBySearchDataForAdmin関数をそのままコピーして来て追加項目の分も追加します

namespace Plugin\NewPlugin\Repository;
use Eccube\Repository\OrderRepository as BaseOrderRepository;
use Doctrine\ORM\QueryBuilder;
use Eccube\Entity\Master\OrderStatus;
use Eccube\Entity\Shipping;
use Eccube\Util\StringUtil;
use Eccube\Repository\QueryKey;

class OrderRepository extends BaseOrderRepository
{
    public function getQueryBuilderBySearchDataForAdmin($searchData)
    {
        ...
        // added_property
        if (isset($searchData['added_property']) && StringUtil::isNotBlank($searchData['added_property'])) {
            $qb
                ->andWhere('o.added_property LIKE :added_property')
                ->setParameter('added_property', '%'.$searchData['added_property'].'%');
        }
        ...
    }
}

services.yaml

一番下に下記を追加します

Plugin\NewPlugin\Service\OrderHelperService:
        decorates: Eccube\Service\OrderHelper

Plugin\NewPlugin\Repository\OrderRepository:
        decorates: Eccube\Repository\OrderRepository

これで上書きできました

index.twig

あとはデータがあるので見た目に追加してあげます

...
    <dl>
        <dt>
            <label class="ec-label required">{{ '追加する項目のラベル'|trans }}</label>
            <span class="ec-required">{{ '必須'|trans }}</span>
        </dt>
        <dd>
            <div class="ec-input">
                <span class="customer-form customer-added_property"></span>
            </div>
        </dd>
    </dl>
</div>
...
<input type="hidden" id="customer-added_property" class="customer-in" name="customer_added_property" value="{{ Order.added_property }}">
...

ここでadded_propertyが追加する項目のidになります

confirm.twig

<div class="ec-orderAccount__account">
    ...
    <p class="ec-input">{{ Order.added_property }}</p>
</div>

困った時のコマンド

キャッシュを消す

bin/console cache:clear --no-warmup

プラグインを読み込む

bin/console eccube:plugin:install --code=PluginName

PluginNameにプラグインの名前を入れてください

プラグインを削除する

bin/console eccube:plugin:uninstall --code=PluginName

PluginNameにプラグインの名前を入れてください

プラグインを有効化する

bin/console eccube:plugin:enable --code=PluginName

PluginNameにプラグインの名前を入れてください

プラグインを無効化する

bin/console eccube:plugin:disable --code=PluginName

PluginNameにプラグインの名前を入れてください 強制的に消す場合には--uninstall-force=trueオプションもつけられます

データベースを更新する

bin/console make:migration

データベースのスキーマを更新する

bin/console doctrine:schema:update --dump-sql

参考

Symfonyで $form->isValid() のエラー内容を表示したい
プラグインのインストール
データ構造の説明
How to Decorate Services
FormTypeのカスタマイズ