ECCUBE4

EC-CUBE4 プラグイン作成手順「商品購入の年齢制限プラグイン」

こんにちはジャムです!

最近はもっぱら、EC-CUBE4を使ってWebアプリケーションの開発を行っています。

その中でプラグインの作成を行ったので、備忘録としてプラグインの開発手順を残しておこうと思います。

今回作るもの

今回は「年齢制限」プラグインを作成してみたいと思います。

機能としては、商品ごとに購入可能な年齢を設けます、そして、管理画面での会員登録時に生年月日を必須入力とします。

本当は商品購入時に年齢制限以下の人は買えないとか、ユーザーの会員登録時に誕生日を入力必須にするとか必要ですが、本記事では割愛します。
本記事の内容が理解できれば自力で作れると思いますので、本記事を作成してみて余裕がある方は作成してみてはどうでしょうか!!

でわ、どーぞ。

認証キーの登録

まずは管理画面を開きましょう。

①「オーナーズストア」→ ②「認証キー設定」をクリックしましょう。

③「認証キーの新規発行」をクリック

④ランダムな文字列を入力し、⑤「認証キーの発行」をクリック

⑥「登録」ボタンを押して完了

プラグインの生成

それでは、EC-CUBE4のプラグイン生成機能を使い、ベースとなるプラグインを生成します。

生成されたファイル群はプラグインの箱みたいなもので、これをアレンジしていく事になります。

$ bin/console e:p:g

入力するとコマンドラインが実行されます。

まず、プラグインの名前を入力します。
「EC-CUBE agelimit Plugin」と入力します。

次に、プラグインコードを入力します。
「AgeLimit」と入力します。

最後にバージョンを入力します。
何も入力せずに「Enter」を押します。
何も入力しないと1.0.0となります。

EC-CUBE Plugin Generator Interactive Wizard
===========================================

 name [EC-CUBE Sample Plugin]:
 > EC-CUBE agelimit Plugin

 code [Sample]:
 > AgeLimit

 ver [1.0.0]:
 > 

プラグインのインストールと有効化

プラグインのインストール

次のコマンドでプラグインをインストールします。

[OK] Installed.が出ればOKです。

$ bin/console e:p:i --code=AgeLimit

 // Clearing the cache for the dev environment with debug                       
 // true                                                                        

                                                                                
 [OK] Cache for the "dev" environment (debug=true) was successfully cleared.    
                                                                                
                                                                                
 [OK] Installed.             

bin/console e:p:iのeはeccube、pはplugin、iはinstallです。なのでbin/console eccube:plugin:installでも可能です。

プラグインの有効化

次のコマンドでプラグインを有効化します。

$ bin/console e:p:e --code=AgeLimit                                                                 

 // Clearing the cache for the dev environment with debug true                                                          

                                                                                                                        
 [OK] Cache for the "dev" environment (debug=true) was successfully cleared.                                            
                                                                                                                        

                                                                                                                        
 [OK] Plugin Enabled.  

参考:プラグインの無効化

$ bin/console e:p:d --code=AgeLimit                                                      

 // Clearing the cache for the dev environment with debug true                                                          

                                                                                                                        
 [OK] Cache for the "dev" environment (debug=true) was successfully cleared.                                            
                                                                                                                        

                                                                                                                        
 [OK] Plugin Disabled.                                                                                                  
                           

商品テーブルに年齢制限用のカラムを追加

トレイトファイルの作成

「app/Plugin/AgeLimit/Entity」フォルダに移動します。
「AgeLimitProductTrait.php」ファイルを作成します。

$ cd app/Plugin/AgeLimit/Entity
$ touch AgeLimitProductTrait.php

コマンドラインを記載していますが、GUI操作でも問題ありません。

<?php

namespace Plugin\AgeLimit\Entity;

use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation\EntityExtension;

/**
 * @EntityExtension("Eccube\Entity\Product")
 */
trait AgeLimitProductTrait
{
    /**
     * @ORM\Column(type="integer", nullable=true)
     */
    private $age_limit;

    /**
     * @return int
     */
    public function getAgeLimit()
    {
        return $this->age_limit;
    }

    /**
     * @param int $age_limit
     *
     * @return $this;
     */
    public function setAgeLimit($age_limit)
    {
        $this->age_limit = $age_limit;

        return $this;
    }
}

トレイトファイルとはEC-CUBE4がデフォルトで持っている、商品を管理するProductクラスやユーザーを管理するCustomerクラスなどを拡張するための機能です。

プロクシー(proxy)ファイルを生成します。

$ bin/console eccube:generate:proxies

gen -> /ec-cube/app/proxy/entity/src/Eccube/Entity/Product.php

proxyファイルとは、先ほど作成したトレイトファイルを拡張するための実体です。中身を見てみましょう

31行目の「use \Plugin\AgeLimit\Entity\AgeLimitProductTrait;」を記載する事で、先ほど作成したトレイトファイルを読み込む事で、拡張させています。

<?php

/*
 * 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.
 */

namespace Eccube\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;


    /**
     * Product
     *
     * @ORM\Table(name="dtb_product")
     * @ORM\InheritanceType("SINGLE_TABLE")
     * @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
     * @ORM\HasLifecycleCallbacks()
     * @ORM\Entity(repositoryClass="Eccube\Repository\ProductRepository")
     */
    class Product extends \Eccube\Entity\AbstractEntity
    {
    use \Plugin\AgeLimit\Entity\AgeLimitProductTrait;

        private $_calc = false;
        private $stockFinds = [];
・・・・・

データベースへの反映

「bin/console doctrine:schema:update」のコマンドでトレイトファイルに記載した「age_limit」カラムを追加できます。

「–dump-sql」をつける事で、どのようなSQLが実行されるのか確認できます。

今回はdtb_productテーブルにage_limitというカラムを追加。
age_limitカラムはint型で、デフォルトはnull

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

gen -> /var/folders/g6/dgt2m9dn2mx_9pxqzx_rg4140000gn/T/proxy_Vj4QiLLOnQJa/src/Eccube/Entity/Product.php

 The following SQL statements will be executed:

     ALTER TABLE dtb_product ADD age_limit INT DEFAULT NULL;

でわ、実際にテーブルに反映しましょう。今度は「–force」をつけます。

$ bin/console doctrine:schema:update --dump-sql --force                                  2538ms  金  9/ 4 23:18:46 2020
gen -> /var/folders/g6/dgt2m9dn2mx_9pxqzx_rg4140000gn/T/proxy_4XQaUNtaWOpD/src/Eccube/Entity/Product.php

 The following SQL statements will be executed:

     ALTER TABLE dtb_product ADD age_limit INT DEFAULT NULL;

 Updating database schema...

     1 query was executed

                                                                                                                        
 [OK] Database schema updated successfully!                                                                             
                                                                                                                        

OKが出ればテーブルに反映されています。

商品登録ページに年齢制限の登録機能を追加

フォームを拡張するFormExtensionファイルの作成

$ cd app/Plugin/AgeLimit/Form/Extension
$ touch AgeLimitProductExtension.php

次のように記載しましょう

<?php

/*
 * 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.
 */

namespace Plugin\AgeLimit\Form\Extension;

use Eccube\Entity\Product;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Eccube\Form\Type\Admin\ProductType;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;

class AgeLimitProductExtension extends AbstractTypeExtension
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $age_limit = 1;

        $Product = $builder->getData();
        if ($Product->getId()) {
            $age_limit = $Product->getAgeLimit();
        }

        $builder
            ->add('age_limit', IntegerType::class, [
                'mapped' => true,
                'attr' => [
                    'min' => 1,
                    'value' => $age_limit,
                ],
                'eccube_form_options' => [
                    'auto_render' => true,
                    'form_theme' => '@AgeLimit/admin/age_limit_product_edit.twig',
                ],
            ]);

        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
            /** @var Product $Product */
            $Product = $event->getData();
            if ($Product->getAgeLimit() == null) {
                $Product->setAgeLimit(1);
            }
        });
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getExtendedType()
    {
        return ProductType::class;
    }
}
AgeLimitProductExtension.phpの解説

31〜36行目:年齢制限の入力フォームが新規登録時は1で編集等で既に値を持っていたら、その値を$age_limit変数に代入する

39行目:formのname属性の値を「age_limit」としてIntegerTypeのフォーム使用

40行目:mappedをtureにする事で、dtb_productのカラム名と照らし合わせて、一致してるか確認します。

46行目:auto_renderをtureにする事で、商品登録・編集ページで自動にレンダリングします。

47行目:form_themeを指定する事で、独自のフォームを使用できます。

51〜57行目:商品登録・編集ページ、登録ボタンを押し、POSTした際に動作を加えます。内容はフォームの値がnull(空入力)だった場合に1にするです。

65〜68行目:どのフォームタイプを拡張するか指定します。

商品登録ページに表示するビューを作成

$ cd app/Plugin/AgeLimit/Resource/template/admin
$ touch age_limit_product_edit.twig
{#
 This file is part of the Coupon plugin

 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 '@admin/Form/bootstrap_4_horizontal_layout.html.twig' -%}

{%- block _admin_product_age_limit_row -%}

<div class="row">
    <div class="col-3">
        <div class="d-inline-block" data-tooltip="true" data-placement="top"
            title="{{ 'tooltip.product.age_limit'|trans }}">
            <span>{{ 'admin.product.age_limit'|trans }}</span>
            <i class="fa fa-question-circle fa-lg ml-1"></i>
            <span class="badge badge-primary ml-1">
                {{ 'admin.common.required'|trans }}
            </span>
        </div>
    </div>
    <div class="col-2 mb-2">
        <div>
            {{- form_widget(form) -}}
            {{- form_errors(form) -}}
        </div>
    </div>
</div>

{%- endblock _admin_product_age_limit_row -%}

日本語化ファイルの編集

tooltip.product.age_limit: 商品購入にあたって、年齢制限を設ける事ができます。
admin.product.age_limit: 年齢制限

動作確認をしましょう

管理ページへ移動しましょう。

①商品管理 → ②商品登録をクリック

③年齢制限の入力欄が増えてればOKです
(下の方にスクロールして下さい)

登録・編集等の作業を繰り返してみてエラーがでないか確認してください。

管理画面で会員登録・編集時に生年月日を必須に変更

Extensionファイルの作成

$ cd app/Plugin/AgeLimit/Form/Extension
$ touch AgeLimitCustomerExtension.php

下記のように記入しましょう。

<?php

/*
 * 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.
 */

namespace Plugin\AgeLimit\Form\Extension\Admin;

use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Eccube\Form\Type\Admin\CustomerType;
use Symfony\Component\Form\Extension\Core\Type\BirthdayType;
use Symfony\Component\Validator\Constraints as Assert;

class AgeLimitCustomerExtension extends AbstractTypeExtension
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $options = $builder->get('birth')->getOptions();

        $options['required'] = true;
        $options['constraints'] = [
            new Assert\NotBlank(),
            new Assert\LessThanOrEqual([
                'value' => date('Y-m-d', strtotime('-1 day')),
                'message' => 'form_error.select_is_future_or_now_date',
            ]),
        ];
        

        $builder->add('birth', BirthdayType::class, $options);
    }

    /**
     * {@inheritdoc}
     *
     * @return string
     */
    public function getExtendedType()
    {
        return CustomerType::class;
    }
}
AgeLimitCustomerExtensionの解説

29行目src/Eccube/Form/Type/Admin/CustomerType.phpに記載されいる’birth’のoptionデータを取得しています。

31〜38行目:入力必須と空白設定をしています。new Assert\LessThanOrEqualを記載しているのは、optionの’constraints’にblankだけ記載すると上書きされてしまうっためです。

必須のラベルを作成

Event.phpを編集

<?php

namespace Plugin\AgeLimit;

use Eccube\Event\TemplateEvent;
use Eccube\Twig\Template;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class Event implements EventSubscriberInterface
{
    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            '@admin/Customer/edit.twig' => 'customer_edit',
        ];
    }

    /**
     *  @param TemplateEvent $event
     */
    public function customer_edit(TemplateEvent $event)
    {
        $event->addSnippet('@AgeLimit/admin/customer_edit.twig');
    }
}
Event.phpの解説

17行目:管理画面の会員情報編集のページが開かれたら、’customer_edit’関数を実行してね。

26行目:17行目の会員情報編集ページに’@AgeLimit/admin/customer_edit.twig’のファイルを追加してね

AgeLimit/admin/customer_edit.twigの作成

<script>
    $(function () {
        $(document).find('input[id="admin_customer_birth"]').parent().parent().find('.col-3').append($('#label_birth'));
    });
</script>

<span id="label_birth" class="badge badge-primary ml-1">{{ 'admin.common.required'|trans }}</span>

動作確認

誕生日の横に必須ラベル、空で入力した際にエラー文がでていれば問題なく動作しています。

以上です!お疲れ様でした!(自分に)