EC-CUBE４系のプラグインを開発してみた

プライベートで**EC-CUBE４系のプラグイン**を作成しました。ネット上に４系プラグインの開発情報があまりなかったので、開発手順を書いておきます。

> 
[残り在庫数表示プラグイン | EC-CUBEオーナーズストア](https://www.ec-cube.net/products/detail.php?product_id=1936)
プラグイン設定で登録した在庫数以下になったとき、「残り◯点です　ご注文はお早めに‼︎」と表示されます。
**ソースコード**：[ec-cube4_stock-show-plugin | GitHub]
(https://github.com/yoshiharu-semachi/ec-cube4_stock-show-plugin)
![07231640_5d36b9d39b6ab.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287365/a0cb4931-db6b-765d-3daf-3233bd30415c.jpeg)
![07231639_5d36b9aa339a9.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287365/c7f319a6-84da-e42e-ca35-5313b03557a9.jpeg)


#プラグインジェネレータ
1. プラグインジェネレータコマンドを実行する。
`bin/console eccube:plugin:generate`

2. 以下の項目を入力していく。
`name [EC-CUBE Sample Plugin]: `StockShow4
`code [Sample]: `StockShow4
`ver [1.0.0]: `1.0.0

EC-CUBE本体のapp/Plugin配下にプラグインの雛形が作成される。
```[OK] Plugin was successfully created:
```


## プラグインジェネレータ実行後のディレクトリ構成

```
プラグインのルートディレクトリ
｜ーController/
｜ーEntity/
｜ーForm/
｜ーRepository/
｜ーResource/
｜
｜composer.json（プラグインの情報が記述されている）
｜Event.php（イベントクラス）
｜Nav.php（管理画面のサイドメニューに項目を追加する）
｜TwigBlock.php（UI要素の追加）
```


## composer.jsonの中身
プラグインジェネレータを使いプラグインを作成すると、プラグインディレクトリ内に**composer.json**が作成されています。
このファイルには**プラグインの情報**が記述されています。

内容は以下の通りです。

```composer.json
{
  "name": "パッケージ名（ec-cube/プラグインコードにあたります。）",
  "version": "プラグインのバージョン番号です。",
  "description": "プラグイン名称",
  "type": "パッケージタイプ（eccube-pluginになっています。）",
  "require": {
    "ec-cube/plugin-installer": "~0.0.７は常に記述してください。"
  },
  "extra": {
    "code": "プラグインコードが記述されています。"
  }
}
```

【参考】
[プラグイン仕様｜EC-CUBE4.0開発ドキュメント]
(http://doc4.ec-cube.net/plugin_spec#composerjson)


# Entity/Config
DBとデータをやり取りするために**DoctrineというORM**を使用します。ORMとはMySQLなどのRDBとオブジェクト指向プログラミング言語の間の非互換なデータをマッピングするためのプログラミング技法のことです。
Symfony3.4をベースに開発されているEC-CUBE４系では、**Entity**というクラス（オブジェクト）をDBのテーブルにマッピングします。

今回開発したプラグインは、管理画面のプラグイン設定で「在庫数」を登録し、「商品の在庫数が登録した在庫数を下回った時、その商品の残り在庫数を表示する」というものになります。
ですので、プラグイン設定のEntityをDBのテーブルにマッピングし「在庫数」を登録できるようにします。

プラグインジェネレータで作成された**Entity/Config.php**を編集します。なお、ファイル名は”StockShowConfig.php”に変更しています。

なお、プロパティを**private**でGetterおよびSetterメソッドを**public**で定義しカプセル化しています。

【参考】
[Databases and the Doctrine ORM | Symfony 3.4 Docs]
(https://symfony.com/doc/3.4/doctrine.html#add-mapping-information)
[超入門 Symfony3 : (6) Doctrine ORM【前編】 | シムノート]
(http://symnote.kwalk.jp/blog/2015-12-12/%E8%B6%85%E5%85%A5%E9%96%80_symfony3_doctrine_orm_%E5%89%8D%E7%AF%87)
[Annotations Reference | Doctrine Object Relational Mapper (ORM)]
(https://www.doctrine-project.org/projects/doctrine-orm/en/2.6/reference/annotations-reference.html)
[PHPのオブジェクト指向入門 | オブジェクト指向PHP.NET]
(http://www.objective-php.net/basic/capsule)

``` StockShowConfig.php
<?php
namespace Plugin\StockShow4\Entity;

// Doctrine\ORM\Mappingクラスをインポートし、ORMという別名をつける。
use Doctrine\ORM\Mapping as ORM;

// @ORM\TableでDBのテーブルにマッピングしています。
// @ORM\Entityでこのクラスに対するRepositoryを定義しています。
/**
 * Config
 *
 * @ORM\Table(name="plg_stock_show4_config")
 * @ORM\Entity(repositoryClass="Plugin\StockShow4\Repository\StockShowConfigRepository")
 */
class StockShowConfig
{
    // privateでプロパティ$idを定義します。
    // @ORM\Columnでプロパティ$idをDBテーブルのカラムにマッピングしています。
    // @ORM\Idで$idがプライマリーキーであることを定義しています。
    // @ORM\GeneratedValueで"IDENTITY"を指定し、idを自動採番しています。
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer", options={"unsigned":true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;

    // privateでプロパティ$stock_qty_showを定義します。
    // @ORM\Columnでプロパティ$stock_qty_showをDBテーブルのカラムにマッピングしています。
    /**
     * @var int
     *
     * @ORM\Column(name="stock_qty_show", type="smallint", nullable=true, options={"unsigned":true, "default":5})
     */
    private $stock_qty_show;

    // プロパティ$idのGetterメソッドを定義します。
    /**
     * Get id.
     * 
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    // プロパティ$stock_qty_showのGetterメソッドを定義します。
    /**
     * Get StockQtyShow
     * 
     * @return int
     */
    public function getStockQtyShow()
    {
        return $this->stock_qty_show;
    }

    // プロパティ$stock_qty_showのSetterメソッドを定義します。
    /**
     * Set $qty
     * 
     * @param int $qty
     *
     * @return $this;
     */
    public function setStockQtyShow($qty)
    {
        $this->stock_qty_show = $qty;
        return $this;
    }
}
```


# ConfigRepository
RepositoryとはEntityのfetch(読み込み)を補助するためのクラスです。

プラグインジェネレータで作成されたRepository/**ConfigRepository.php**は特に編集していません。ただし、ファイル名は”**StockShowConfigRepository.php**”に変更しています。

【参考】
[Databases and the Doctrine ORM | Symfony Docs]
(https://symfony.com/doc/current/doctrine.html#querying-for-objects-the-repository)
[【Symfony】【Doctrine】getRepository() の使い方 | SatelliteSeven]
(http://satellite7.hatenablog.com/entry/2013/07/26/183230)

``` StockShowConfigRepository.php
<?php
namespace Plugin\StockShow4\Repository;

use Eccube\Repository\AbstractRepository;
use Plugin\StockShow4\Entity\StockShowConfig;
use Symfony\Bridge\Doctrine\RegistryInterface;

// EC-CUBE本体のAbstractRepositoryを継承しています。
// AbstractRepositoryはServiceEntityRepositoryを継承しています。
class StockShowConfigRepository extends AbstractRepository
{
    /**
     * ConfigRepository constructor.
     *
     * @param RegistryInterface $registry
     */
    public function __construct(RegistryInterface $registry)
    {
        parent::__construct($registry, StockShowConfig::class);
    }

    // エンティティのidを指定し、一致するレコードをオブジェクトで返します。
    /**
     * @param int $id
     *
     * @return null|StockShowConfig
     */
    public function get($id = 1)
    {
        return $this->find($id);
    }
}
```


# ConfigType
プラグインジェネレータで作成されたForm/Type/Admin/ConfigType.phpを編集し、プラグイン設定の**Form**を作成します。
今回はファイル名を”**StockShowConfigType.php**”に変更しています。

【参考】
[Forms | Symfony 3.4 Docs]
(https://symfony.com/doc/3.4/forms.html#creating-form-classes)


``` ConfigType.php
<?php
namespace Plugin\StockShow4\Form\Type\Admin;

use Plugin\StockShow4\Entity\StockShowConfig;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class StockShowConfigType extends AbstractType
{
    // フォーム項目を生成します。
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('stock_qty_show', IntegerType::class);
    }

    // data_classオプションでエンティティのクラス名を指定しています。
    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => StockShowConfig::class,
        ]);
    }
}
```


# ConfigController
プラグイン設定画面から在庫数を登録するため、プラグインジェネレータで作成されたController/Admin/ConfigController.phpを編集します。

【参考】
[Controller | Symfony 3.4 Docs]
(https://symfony.com/doc/3.4/controller.html)
[フォーム | SymDoc - PHP フレームワーク Symfony3 日本語ドキュメント Wiki]
(http://symdoc.kwalk.jp/doc/book/forms)
[How to Use the submit() Function to Handle Form Submissions | Symfony 3.4 Docs]
(https://symfony.com/doc/3.4/form/direct_submit.html)
[Symfony2で利用されているDoctrineに入門する(後編) | OTOBANK Engineering Blog]
(https://engineering.otobank.co.jp/entry/2017/05/09/190056)

``` ConfigController.php
<?php

namespace Plugin\StockShow4\Controller\Admin;

use Plugin\StockShow4\Form\Type\Admin\StockShowConfigType;
use Plugin\StockShow4\Repository\StockShowConfigRepository;
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;

// EC-CUBE本体のAbstractControllerを継承しています。
/**
 * Class ConfigController.
 */
class ConfigController extends \Eccube\Controller\AbstractController
{
    // @RouteでURLをマッピングしています。
    // @TemplateでTwigのテンプレートファイルを指定しています。
    /**
     * @Route("/%eccube_admin_route%/stock_show4/config", name="stock_show4_admin_config")
     * @Template("@StockShow4/admin/config.twig")
     * 
     * @param Request $request
     * @param StockShowConfigRepository $configRepository
     * 
     * @return array
     */
    public function index(Request $request, StockShowConfigRepository $configRepository)
    {
        $Config = $configRepository->get();
        // フォームを構築します。
        $form = $this->createForm(StockShowConfigType::class, $Config);
        // ユーザーから送信されたリクエストをフォームオブジェクト内に書き込みます。
        $form->handleRequest($request);

        // フォームチェックはSymfonyのisSubmitted()とisValid()を使用します。
        if ($form->isSubmitted() && $form->isValid()) {
            // $formの値を取得します。
            $Config = $form->getData();
            // $Configを永続化するエンティティとして管理します。
            $this->entityManager->persist($Config);
            // DBに永続化します。
            $this->entityManager->flush($Config);
            log_info('Stock show config', ['status' => 'Success']);
            $this->addSuccess('登録しました。', 'admin');
            return $this->redirectToRoute('stock_show4_admin_config');
        }

        return [
            'form' => $form->createView(),
        ];
    }
}
```


# Event
プラグインジェネレータによって作成されたEvent.phpの中身を編集します。
このファイルには、**イベント名**と**呼び出すメソッド名**を定義します。

【参考】
[イベント｜EC-CUBE 開発ドキュメント]
(https://doc.ec-cube.net/plugin_bp_event)
[EC-CUBE/sample-payment-plugin/SamplePaymentEvent.php | GitHub](https://github.com/EC-CUBE/sample-payment-plugin/blob/4.0/SamplePaymentEvent.php)
[EC-CUBE/ProductReview-plugin/ProductReviewEvent.php | GitHub](https://github.com/EC-CUBE/ProductReview-plugin/blob/4.0/ProductReviewEvent.php)

ちなみに今回はファイル名を”**StockShowEvent.php**”に変更しています。

```StockShowEvent.php
<?php
namespace Plugin\StockShow4;

use Eccube\Event\TemplateEvent;
use Plugin\StockShow4\Repository\StockShowConfigRepository;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class StockShowEvent implements EventSubscriberInterface
{
    /**
     * @var StockShowConfigRepository
     */
    protected $ConfigRepository;

    /**
     * ProductReview constructor.
     * 
     * @param StockShowConfigRepository $ConfigRepository
     */
    public function __construct(StockShowConfigRepository $ConfigRepository)
    {
        $this->ConfigRepository = $ConfigRepository;
    }

    /**
     * 配列のキーはイベント名、値は呼び出すメソッド名です。
     *
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            'Product/detail.twig' => 'StockShowTwig',
        ];
    }

    /**
     * @param TemplateEvent $event
     */
    public function StockShowTwig(TemplateEvent $event)
    {
        $twig = '@StockShow4/default/Product/stock_show.twig';
        // addSnippet()関数で指定したテンプレートを<body>タグの下部に追加できます。
        $event->addSnippet($twig);
        $Config = $this->ConfigRepository->get();
        $parameters = $event->getParameters();
        $parameters['StockQtyShow'] = $Config->getStockQtyShow();
        $event->setParameters($parameters);
    }
}
```


# Resource/~/Twigの作成
## stock_show.twig
"Resource/template/default/Product/**stock_show.twig**"を作成します。
ここに記述された内容はProduct/**detail.twig**（商品詳細画面）に挿入されます。

【参考】
[EC-CUBE/ProductReview-plugin/~/review.twig | GitHub]
(https://github.com/EC-CUBE/ProductReview-plugin/blob/4.0/Resource/template/default/review.twig)
[EC-CUBE/related-product-plugin/~/related_product.twig | GitHub](https://github.com/EC-CUBE/related-product-plugin/blob/4.0/Resource/template/front/related_product.twig)
[jQuery 要素を移動・追加する方法 | プラカンブログ]
(https://www.plusdesign.co.jp/blog/?p=8494)

```stock_show.twig
{# Style #}
<style type="text/css">
    #stock_show_area{
        padding: 0 0 14px 0;
        border-bottom: 1px dotted #ccc;
    }
    #stock_show_area p{
        font-size: 18px;
    }
    #stock_show_area strong{
        font-size: 35px;
    }
    #stock_show_area .order_soon{
        font-size: 20px;
    }
</style>

<script>
    {# JQueryのinsertAfterメソッドを使いProduct/detail.twigの<div class="ec-productRole__tags">の直後に挿入#}
    $(function () {
        $('#stock_show_area').insertAfter($('.ec-productRole__tags'));
    });
</script>

{# 規格あり商品の場合 #}
{% if Product.hasProductClass %}

{# 規格なし商品の場合 #}
{% else %}
    {% if Product.getStockMin > 0 and Product.getStockMin <= StockQtyShow %}
        <div id="stock_show_area">
            <p>残り <strong>{{ Product.getStockMin }}</strong> 点です</p>
            <p class="order_soon">ご注文はお早めに!!</p>
        </div>
    {% endif %}
{% endif %}
```

## Config.twig
"Resource/template/admin/**config.twig**"（プラグイン設定画面）を作成します。

【参考】
[How to Customize Form Rendering | Symfony 3.4 Docs](https://symfony.com/doc/3.4/form/form_customization.html#form-rendering-basics)

``` Config.twig
{% extends '@admin/default_frame.twig' %}

{% set menus = ['store', 'plugin', 'plugin_list'] %}

{% block title %}StockShow4{% endblock %}
{% block sub_title %}プラグイン一覧{% endblock %}

{% form_theme form '@admin/Form/bootstrap_4_horizontal_layout.html.twig' %}

{% block main %}
    <form method="post" action="{{ url('stock_show4_admin_config') }}">

        {{ form_widget(form._token) }}

        <div class="c-contentsArea__cols">
            <div class="c-contentsArea__primaryCol">
                <div class="c-primaryCol">
                    <div class="card rounded border-0 mb-4">
                        <div class="card-header"><span>設定</span></div>
                        <div class="card-body">
                            <div class="row">
                                <div class="col-3">
                                    <span>
                                        指定した在庫数<b>以下</b>になったときに<br>
                                        商品の<b>残り在庫数</b>を表示します
                                    </span>
                                </div>
                                <div class="col mb-2">
                                    {{ form_widget(form.stock_qty_show) }}
                                    {{ form_errors(form.stock_qty_show) }}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <div class="c-conversionArea">
            <div class="c-conversionArea__container">
                <div class="row justify-content-between align-items-center">
                    <div class="col-6">
                        <div class="c-conversionArea__leftBlockItem">
                            <a class="c-baseLink" href="{{ url('admin_store_plugin') }}">
                                <i class="fa fa-backward" aria-hidden="true"></i>
                                <span>プラグイン一覧</span>
                            </a>
                        </div>
                    </div>
                    <div class="col-6">
                        <div class="row align-items-center justify-content-end">
                            <div class="col-auto">
                                <button class="btn btn-ec-conversion px-5" type="submit">登録</button>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </form>
{% endblock %}
```


# PluginManager
プラグインルートディレクトリ内に**PluginManager.php**を作成します。
PluginManager.phpには、プラグインの**インストール**・**アンインストール**・**有効**・**無効**・**アップデート**を行うときに必ず呼び出されるクラスを記述します。

【参考】
[プラグインマネージャー｜EC-CUBE 開発ドキュメント]
(https://doc.ec-cube.net/plugin_bp_pluginmanager)
[EC-CUBE/ProductReview-plugin/PluginManager.php | GitHub]
(https://github.com/EC-CUBE/ProductReview-plugin/blob/4.0/PluginManager.php)

``` PluginManager.php
<?php
namespace Plugin\StockShow4;

use Doctrine\ORM\EntityManagerInterface;
use Eccube\Plugin\AbstractPluginManager;
use Plugin\StockShow4\Entity\StockShowConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;

class PluginManager extends AbstractPluginManager
{
    /**
     * プラグイン有効時の処理
     *
     * @param $meta
     * @param ContainerInterface $container
     */
    public function enable(array $meta, ContainerInterface $container)
    {
        $em = $container->get('doctrine.orm.entity_manager');
        $Config = $this->createConfig($em);
    }

    /**
     * プラグイン設定を追加
     * 
     * @param EntityManagerInterface $em
     */
    protected function createConfig(EntityManagerInterface $em)
    {
        $Config = $em->find(StockShowConfig::class, 1);
        if ($Config) {
            return $Config;
        }
        $Config = new StockShowConfig();
        $Config->setStockQtyShow(5);
        $em->persist($Config);
        $em->flush($Config);
        return $Config;
    }
}
```


# Tests

## UnitTestの作成
**StockShowConfigController.php**を検証するTests/Web/**StockShowConfigControllerTest.php**を作成します。

【参考】
[ProductReview-plugin/Tests/Web/ProductReviewConfigControllerTest.php | GitHub]
(https://github.com/EC-CUBE/ProductReview-plugin/blob/4.0/Tests/Web/ProductReviewConfigControllerTest.php)

``` StockShowConfigControllerTest.php
<?php
namespace Plugin\StockShow4\Tests\Web;

use Faker\Generator;
use Symfony\Component\HttpKernel\Client;
use Symfony\Component\DomCrawler\Crawler;
use Eccube\Tests\Web\Admin\AbstractAdminWebTestCase;

/**
 * Class StockShowConfigControllerTest.
 */
class StockShowConfigControllerTest extends AbstractAdminWebTestCase
{
    /**
     * @var Generator
     */
    protected $faker;
    /**
     * セットアップ
     */
    public function setUp(){
        parent::setUp();
        $this->faker = $this->getFaker();
    }
    /**
     * プラグイン設定のtwig表示テスト
     */
    public function testRouting()
    {
        /**
         * @var Client
         */
        $client = $this->client;
        /**
         * @var Crawler
         */
        $crawler = $this->client->request('GET', $this->generateUrl('stock_show4_admin_config'));
        $this->assertTrue($client->getResponse()->isSuccessful());
        $this->assertContains('指定した在庫数<b>以下</b>になったときに<br>',$crawler->html());
    }
    /**
     * プラグイン設定のテスト
     */
    public function testSuccess()
    {
        /**
        * @var Client
        */
       $client = $this->client;
       /**
        * @var Crawler
        */
       $crawler = $this->client->request('GET', $this->generateUrl('stock_show4_admin_config'));
       $this->assertTrue($client->getResponse()->isSuccessful());
       $form = $crawler->selectButton('登録')->form();
       $form['stock_show_config[stock_qty_show]'] = $this->faker->randomNumber(1);
       $crawler = $client->submit($form);
       $this->assertTrue($client->getResponse()->isRedirection($this->generateUrl('stock_show4_admin_config')));
       $crawler = $client->followRedirect();
       $this->assertContains('登録しました。', $crawler->html());
    }
}
```

## phpunit.xml.dist
``` phpunit.xml.dist
<?xml version="1.0" encoding="UTF-8"?>


<phpunit 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/6.5/phpunit.xsd"
    backupGlobals="false"
    colors="true"
    bootstrap="Tests/bootstrap.php"
>
    <php>
        <ini name="error_reporting" value="-1" />
        <env name="KERNEL_CLASS" value="Eccube\Kernel" />
        <env name="APP_ENV" value="test" />
        <env name="APP_DEBUG" value="1" />
        <env name="SHELL_VERBOSITY" value="-1" />
        <env name="SYMFONY_DEPRECATIONS_HELPER" value="weak" />
        <!-- define your env variables for the test env here -->
    </php>

    <!-- テストの場所 -->
    <testsuites>
        <testsuite name="Plugin Test Suite">
            <directory>./Tests</directory>
        </testsuite>
    </testsuites>

    <!-- カバーレージのターゲット -->
    <filter>
        <whitelist processUncoveredFilesFromWhitelist="true">
            <directory suffix=".php">./</directory>
            <exclude>
                <directory suffix=".php">./Tests</directory>
                <directory suffix=".php">./Resource</directory>
                <file>./PluginManager.php</file>
            </exclude>
        </whitelist>
    </filter>

    <listeners>
        <listener class="Symfony\Bridge\PhpUnit\SymfonyTestsListener" />
        <listener class="\DAMA\DoctrineTestBundle\PHPUnit\PHPUnitListener" />
    </listeners>
</phpunit>
```

## ローカル環境でUnitTestを実行する
以下のコマンドでUnitTsetを実行することができます。

 `vendor/bin/phpunit ./app/Plugin/[自身で作成したプラグインのフォルダ名]`

## Travis CI
Travis CIでテストを行うため、"**.travis.yml**"をプラグインディレクトリ直下に作成します。

【参考】
[CIを利用したプラグインのテスト｜EC-CUBE 開発ドキュメント]
(https://doc.ec-cube.net/plugin_test#%E7%B6%99%E7%B6%9A%E7%9A%84%E3%82%A4%E3%83%B3%E3%83%86%E3%82%B0%E3%83%AC%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E8%A4%87%E6%95%B0%E7%92%B0%E5%A2%83%E3%81%A7%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88)
[ProductReview-plugin/.travis.yml | GitHub]
(https://github.com/EC-CUBE/ProductReview-plugin/blob/4.0/.travis.yml)
[coupon-plugin | GitHub](https://github.com/EC-CUBE/coupon-plugin/blob/4.0/.travis.yml)


```.travis.yml
language: php

sudo: required

cache:
  directories:
    - vendor
    - $HOME/.composer/cache
    - bin/.phpunit

php:
  - 7.1
  - 7.2

env:
  # 環境変数を定義する
  global:
    PLUGIN_CODE=StockShow4
  # テストする環境を定義する
  matrix:
    - ECCUBE_VERSION=4.0.0 DATABASE_URL=mysql://root:@localhost/cube4_dev DATABASE_SERVER_VERSION=5
    - ECCUBE_VERSION=4.0.0 DATABASE_URL=postgres://postgres:password@localhost/cube4_dev DATABASE_SERVER_VERSION=9
    - ECCUBE_VERSION=4.0.1 DATABASE_URL=mysql://root:@localhost/cube4_dev DATABASE_SERVER_VERSION=5
    - ECCUBE_VERSION=4.0.1 DATABASE_URL=postgres://postgres:password@localhost/cube4_dev DATABASE_SERVER_VERSION=9
    - ECCUBE_VERSION=4.0.2 DATABASE_URL=mysql://root:@localhost/cube4_dev DATABASE_SERVER_VERSION=5
    - ECCUBE_VERSION=4.0.2 DATABASE_URL=postgres://postgres:password@localhost/cube4_dev DATABASE_SERVER_VERSION=9

before_install: &php_setup |
  phpenv config-rm xdebug.ini || true
  echo "opcache.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
  echo "apc.enable_cli=1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
  echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini

install_eccube: &install_eccube |
  tar cvzf ${HOME}/${PLUGIN_CODE}.tar.gz ./*
  git clone https://github.com/EC-CUBE/ec-cube.git
  cd ec-cube
  sh -c "if [ '${ECCUBE_VERSION}' = '4.0' ];   then git checkout origin/${ECCUBE_VERSION}; fi"
  sh -c "if [ ! '${ECCUBE_VERSION}' = '4.0' ]; then git checkout refs/tags/${ECCUBE_VERSION}; fi"
  composer selfupdate
  composer install --dev --no-interaction -o --apcu-autoloader

eccube_setup: &eccube_setup |
  echo "APP_ENV=test" > .env
  bin/console doctrine:database:create
  bin/console doctrine:schema:create
  bin/console eccube:fixtures:load
  bin/console eccube:plugin:install --path=${HOME}/${PLUGIN_CODE}.tar.gz
  bin/console eccube:plugin:enable --code=${PLUGIN_CODE}

install:
  - *install_eccube
  - *eccube_setup

script:
  # UnitTsetを実行する
  -  ./vendor/bin/phpunit app/Plugin/${PLUGIN_CODE}/Tests;

after_script:
  # プラグインを無効化する
  - bin/console eccube:plugin:disable --code=${PLUGIN_CODE}
  # プラグインをアンインストールする
  - bin/console eccube:plugin:uninstall --code=${PLUGIN_CODE}
  # プラグインを再インストールする
  - bin/console eccube:plugin:install --code=${PLUGIN_CODE}
  # プラグインを有効化する
  - bin/console eccube:plugin:enable --code=${PLUGIN_CODE}

jobs:
  fast_finish: true
  include:
    - stage: Inspection
      php: 7.1
      env: DATABASE_URL=postgres://postgres:password@localhost/cube4_dev DATABASE_SERVER_VERSION=9
      install:
        - *install_eccube
        - *eccube_setup
      script: vendor/bin/php-cs-fixer fix --config=.php_cs.dist --dry-run app/Plugin/${PLUGIN_CODE}
    - &coverage
      stage: Code Coverage
      env: DATABASE_URL=postgres://postgres:password@localhost/cube4_dev DATABASE_SERVER_VERSION=9 COVERAGE=1
      before_install:
        - *php_setup
        - gem install mime-types -v 2.99.1
        - gem install mailcatcher
      install:
        - *install_eccube
        - *eccube_setup
        # mail catcher
        - mailcatcher
      script:
        - phpdbg -qrr ./vendor/bin/phpunit -c app/Plugin/${PLUGIN_CODE}/phpunit.xml.dist --coverage-clover=coverage.clover
      after_success:
        - php vendor/bin/php-coveralls -v -x coverage.clover
```

# 今回作成したプラグインのファイル構成

```
プラグインディレクトリ
｜ーController/Admin/
｜　　｜ConfigController.php
｜
｜ーEntity/
｜　　｜ーStockShowConfig.php
｜
｜ーForm/Type/Admin/
｜　　｜ーStockShowConfigType.php
｜
｜ーRepository/
｜　　｜ーStockShowConfigRepository.php
｜
｜ーResource/template/
｜　　｜ーadmin/
｜　　｜　　｜ーconfig.twig
｜　　｜ーdefault/Product/
｜　　　　　｜ーstock_show.twig
｜
｜.travis.yml
｜PluginManager.php
｜StockShowEvent.php
｜composer.json
```

プラグインジェネレータ実行後に作成された以下のファイルは削除しました。
- Nav.php
- TwigBlock.php