バージョン
3.0.16
動機
画像アップロードのプラグインについての記事がなかったのでまとめようと思いました。
職場の上司からはプラグイン作成は簡単だよと言われたんですが、かなり難しかった(フレームワークの概念を認識するまで)ので備忘録で。
参考
画像アップロードプラグイン作成
上記のプラグインチュートリアルでステップバイステップで、カテゴリーにコメントを追加できるプラグインが作成でき、画像アップロード機能はこれに追加。
まず上記チュートリアルを実施してください。
チュートリアル後のファイル構成
プラグインルートフォルダ
├── CategoryContentEvent.php
├── config.yml
├── Entity
│ └── CategoryContent.php
├── event.yml
├── PluginManager.php
├── Repository
│ └── CategoryContentRepository.php
├── Resource
│ └── doctrine
│ ├── migration
│ │ └── Version20160218160500.php
│ └── Plugin.CategoryContent.Entity.CategoryContent.dcm.yml
│
└── ServiceProvider
└── CategoryContentServiceProvider.php
config.yml
config.yml
name: カテゴリコンテンツプラグイン
code: CategoryContent
version: 1.0.0
orm.path:
- /Resource/doctrine
event: CategoryContentEvent
service:
- CategoryContentServiceProvider
event.yml
event.yml
# カテゴリコントローラのフックポイト
admin.product.category.index.initialize:
- [onAdminProductCategoryIndexInit, NORMAL]
# フォーム登録のために利用するフックポイント
admin.product.category.index.complete:
- [onAdminProductCategoryIndexComplete, NORMAL]
# 商品一覧ページ表示のフックポイント
Product/list.twig:
- [onRenderProductList, NORMAL]
PluginManager.php
PluginManager.php
<?php
namespace Plugin\CategoryContent;
use Eccube\Plugin\AbstractPluginManager;
class PluginManager extends AbstractPluginManager
{
// インストール時に、指定の処理を実行できます。(今回はなし)
public function install($config, $app)
{
}
// アンインストール時にマイグレーションの「down」メソッドを実行します
public function uninstall($config, $app)
{
$this->migrationSchema($app, __DIR__.'/Resource/doctrine/migration', $config['code'], 0);
}
// プラグイン有効時に、マイグレーションの「up」メソッドを実行します
public function enable($config, $app)
{
$this->migrationSchema($app, __DIR__.'/Resource/doctrine/migration', $config['code']);
}
// プラグイン無効時に、指定の処理 ( ファイルの削除など ) を実行できます。(今回はなし)
public function disable($config, $app)
{
}
// プラグインアップデート時に、指定の処理を実行できます。(今回はなし)
public function update($config, $app)
{
}
}
CategoryContentEvent.php
CategoryContentEvent.php
<?php
namespace Plugin\CategoryContent;
use Eccube\Application;
use Eccube\Common\Constant;
use Eccube\Entity\Category;
use Eccube\Event\EventArgs;
use Eccube\Event\TemplateEvent;
use Plugin\CategoryContent\Entity\CategoryContent;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
class CategoryContentEvent
{
/**
* プラグインが追加するフォーム名
*/
const CATEGORY_CONTENT_TEXTAREA_NAME = 'plg_category_content';
/** @var \Eccube\Application $app */
private $app;
public function __construct($app)
{
$this->app = $app;
}
public function onAdminProductCategoryIndexInit(EventArgs $event)
{
/** @var Category $target_category */
$TargetCategory = $event->getArgument('TargetCategory');
$id = $TargetCategory->getId();
$CategoryContent = null;
// IDの有無で登録か編集かを判断
if ($id) {
// カテゴリ編集時は初期値を取得
$CategoryContent = $this->app['category_content.repository.category_content']->find($id);
}
// カテゴリ新規登録またはコンテンツが未登録の場合
if (is_null($CategoryContent)) {
$CategoryContent = new CategoryContent();
}
// フォームの追加
/** @var FormInterface $builder */
// FormBuilderの取得
$builder = $event->getArgument('builder');
// 項目の追加
$builder->add(
self::CATEGORY_CONTENT_TEXTAREA_NAME,
'textarea',
array(
'required' => false,
'label' => false,
'mapped' => false,
'attr' => array(
'placeholder' => 'コンテンツを入力してください(HTMLタグ使用可)',
),
)
);
// 初期値を設定
$builder->get(self::CATEGORY_CONTENT_TEXTAREA_NAME)->setData($CategoryContent->getContent());
}
/**
* 管理画面:カテゴリ登録画面で、登録処理を行う.
*
* @param EventArgs $event
*/
public function onAdminProductCategoryIndexComplete(EventArgs $event)
{
/** @var Application $app */
$app = $this->app;
/** @var Category $target_category */
$TargetCategory = $event->getArgument('TargetCategory');
/** @var FormInterface $form */
$form = $event->getArgument('form');
// 現在のエンティティを取得
$id = $TargetCategory->getId();
// フォーム値のIDをもとに登録カテゴリ情報を取得
$CategoryContent = $app['category_content.repository.category_content']->find($id);
if (is_null($CategoryContent)) {
$CategoryContent = new CategoryContent();
}
// エンティティを更新
$CategoryContent
->setId($id)
->setContent($form[self::CATEGORY_CONTENT_TEXTAREA_NAME]->getData());
// DB更新
$app['orm.em']->persist($CategoryContent);
$app['orm.em']->flush($CategoryContent);
}
/**
* 商品一覧画面にカテゴリコンテンツを表示する.
*
* @param TemplateEvent $event
*/
public function onRenderProductList(TemplateEvent $event)
{
$parameters = $event->getParameters();
// カテゴリIDがない場合、レンダリングしない
if (is_null($parameters['Category'])) {
return;
}
// 登録がない、もしくは空で登録されている場合、レンダリングをしない
$Category = $parameters['Category'];
$CategoryContent = $this->app['category_content.repository.category_content']
->find($Category->getId());
if (is_null($CategoryContent) || $CategoryContent->getContent() == '') {
return;
}
// twigコードにカテゴリコンテンツを挿入
$snipet = '<div class="row">{{ CategoryContent.content | raw }}</div>';
$search = '<div id="result_info_box"';
$replace = $snipet.$search;
$source = str_replace($search, $replace, $event->getSource());
$event->setSource($source);
// twigパラメータにカテゴリコンテンツを追加
$parameters['CategoryContent'] = $CategoryContent;
$event->setParameters($parameters);
}
}
Entity/CategoryContent.php
Entity/CategoryContent.php
<?php
namespace Plugin\CategoryContent\Entity;
class CategoryContent extends \Eccube\Entity\AbstractEntity
{
private $id;
private $content;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function getContent()
{
return $this->content;
}
public function setContent($content)
{
$this->content = $content;
return $this;
}
}
ServiceProvider/CategoryContentServiceProvider.php
ServiceProvider/CategoryContentServiceProvider.php
<?php
namespace Plugin\CategoryContent\ServiceProvider;
use Eccube\Application;
use Silex\Application as BaseApplication;
use Silex\ServiceProviderInterface;
class CategoryContentServiceProvider implements ServiceProviderInterface
{
public function register(BaseApplication $app)
{
// Repository
$app['category_content.repository.category_content'] = $app->share(function () use ($app) {
return $app['orm.em']->getRepository('Plugin\CategoryContent\Entity\CategoryContent');
});
}
public function boot(BaseApplication $app)
{
}
}
Repository/CategoryContentRepository.php
<?php
namespace Plugin\CategoryContent\Repository;
use Doctrine\ORM\EntityRepository;
class CategoryContentRepository extends EntityRepository
{
}
Resource/doctrine/migration/Version20160218160500.php
Resource/doctrine/migration/Version20160218160500.php
<?php
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Tools\SchemaTool;
use Eccube\Common\Constant;
class Version20160218160500 extends AbstractMigration
{
// 対象のエンティティを指定
protected $entities = array(
'Plugin\CategoryContent\Entity\CategoryContent',
);
public function up(Schema $schema)
{
// テーブルの生成
$app = \Eccube\Application::getInstance();
$meta = $this->getMetadata($app['orm.em']);
$tool = new SchemaTool($app['orm.em']);
$tool->createSchema($meta);
}
public function down(Schema $schema)
{
$app = \Eccube\Application::getInstance();
$meta = $this->getMetadata($app['orm.em']);
$tool = new SchemaTool($app['orm.em']);
$schemaFromMetadata = $tool->getSchemaFromMetadata($meta);
// テーブル削除
foreach ($schemaFromMetadata->getTables() as $table) {
if ($schema->hasTable($table->getName())) {
$schema->dropTable($table->getName());
}
}
// シーケンス削除
foreach ($schemaFromMetadata->getSequences() as $sequence) {
if ($schema->hasSequence($sequence->getName())) {
$schema->dropSequence($sequence->getName());
}
}
}
protected function getMetadata(EntityManager $em)
{
$meta = array();
foreach ($this->entities as $entity) {
$meta[] = $em->getMetadataFactory()->getMetadataFor($entity);
}
return $meta;
}
}
Resource/doctrine/Plugin.CategoryContent.Entity.CategoryContent.dcm.yml
Resource/doctrine/Plugin.CategoryContent.Entity.CategoryContent.dcm.yml
Plugin\CategoryContent\Entity\CategoryContent:
type: entity
table: plg_category_content
repositoryClass: Plugin\CategoryContent\Repository\CategoryContentRepository
id:
id:
type: integer
nullable: false
options:
unsigned: false
id: true
column: category_id
generator:
strategy: NONE
fields:
content:
type: text
nullable: true
lifecycleCallbacks: { }
チュートリアル後の画面
画像アップロード機能の追加
- user_data内に保存
- html/user_data/category_image/{id}/カテゴリー画像
html/user_data/
└── category_image
├── 1
│ └── category01.png
├── 2
│ └── category01.png
├── 3
│ └── category01.png
└── 4
└── category01.png
- 後々管理画面の、コンテンツ管理>ファイル管理からも編集可能
画像ファイルのパスを保存するカラムを追加する
- チュートリアルで作ったplg_category_contentテーブルにimageカラムを追加
- Resource/doctrine/Plugin.CategoryContent.Entity.CategoryContent.dcm.ymlを編集
- CategoryContent.phpを編集
- CategoryContentプラグインを一度アンインストールし、再度インストールして有効化する
Resource/doctrine/Plugin.CategoryContent.Entity.CategoryContent.dcm.ymlを編集
Resource/doctrine/Plugin.CategoryContent.Entity.CategoryContent.dcm.yml
Plugin\CategoryContent\Entity\CategoryContent:
type: entity
table: plg_category_content
repositoryClass: Plugin\CategoryContent\Repository\CategoryContentRepository
id:
id:
type: integer
nullable: false
options:
unsigned: false
id: true
column: category_id
generator:
strategy: NONE
fields:
content:
type: text
nullable: true
#下記に追加
image:
type: text
nullable: true
lifecycleCallbacks: { }
CategoryContent.phpを編集
CategoryContent.php
<?php
namespace Plugin\CategoryContent\Entity;
class CategoryContent extends \Eccube\Entity\AbstractEntity
{
private $id;
private $content;
public function getId()
{
return $this->id;
}
public function setId($id)
{
$this->id = $id;
return $this;
}
public function getContent()
{
return $this->content;
}
public function setContent($content)
{
$this->content = $content;
return $this;
}
#下記に追加
private $image;
public function getImage()
{
return $this->image;
}
public function setImage($image)
{
$this->image = $image;
return $this;
}
}
CategoryContentプラグインを一度アンインストールし、再度インストールして有効化する
$ php app/console plugin:develop uninstall --code CategoryContent
success
$ php app/console plugin:develop install --code CategoryContent
success
$ php app/console plugin:develop enable --code CategoryContent
success
フォームを追加
- CategoryContentEvent.php
- 関数 onAdminProductCategoryIndexInit を編集
CategoryContentEvent.php
public function onAdminProductCategoryIndexInit(EventArgs $event)
{
/** @var Category $target_category */
$TargetCategory = $event->getArgument('TargetCategory');
$id = $TargetCategory->getId();
$CategoryContent = null;
// IDの有無で登録か編集かを判断
if ($id) {
// カテゴリ編集時は初期値を取得
$CategoryContent = $this->app['category_content.repository.category_content']->find($id);
}
// カテゴリ新規登録またはコンテンツが未登録の場合
if (is_null($CategoryContent)) {
$CategoryContent = new CategoryContent();
}
// フォームの追加
/** @var FormInterface $builder */
// FormBuilderの取得
$builder
->add(
self::CATEGORY_CONTENT_TEXTAREA_NAME,
'textarea',
array(
'required' => false,
'label' => false,
'mapped' => false,
'attr' => array(
'placeholder' => 'コンテンツを入力してください(HTMLタグ使用可)',
),
))
->add(
'plg_category_image',
'file',
array(
'label' => 'カテゴリー画像',
'required' => false,
'mapped' => false,
))
;
// 初期値を設定
$builder->get(self::CATEGORY_CONTENT_TEXTAREA_NAME)->setData($CategoryContent->getContent());
}
上記から・・・・・
カテゴリー画像のファイル選択が追加された。
データベースに登録できるようにする
- CategoryContentEvent.php
- 関数 onAdminProductCategoryIndexComplete を編集
CategoryContentEvent.php
public function onAdminProductCategoryIndexComplete(EventArgs $event)
{
/** @var Application $app */
$app = $this->app;
/** @var Category $target_category */
$TargetCategory = $event->getArgument('TargetCategory');
/** @var FormInterface $form */
$form = $event->getArgument('form');
// 現在のエンティティを取得
$id = $TargetCategory->getId();
// フォーム値のIDをもとに登録カテゴリ情報を取得
$CategoryContent = $app['category_content.repository.category_content']->find($id);
if (is_null($CategoryContent)) {
$CategoryContent = new CategoryContent();
}
// エンティティを更新
if ($form['plg_category_image']->getData() === null) {
$CategoryContent
->setId($id)
->setContent($form[self::CATEGORY_CONTENT_TEXTAREA_NAME]->getData());
} else {
$CategoryContent
->setId($id)
->setContent($form[self::CATEGORY_CONTENT_TEXTAREA_NAME]->getData())
->setImage("/category_image/" . $form->getData()->getId() . '/' . $form['plg_category_image']->getData()->getClientOriginalName());
}
// DB更新
$app['orm.em']->persist($CategoryContent);
$app['orm.em']->flush($CategoryContent);
}
これで、DBへ保存されるようになった。
DBのimageカラムには"/category_image/{id}/{画像名}"で保存される。
画像ファイルをuser_data内に保存できるようにする。
- user_data直下にcategory_imageフォルダを追加しておく
/src/Eccube/Controller/Admin/Product/CategoryController.php
106行目辺りに追加 if ($status) 内
// CategoryContentX pluginにて画像アップロードの為の設定
$file = $form['plg_category_image']->getData();
$upload_path = __DIR__.'/../../../../../html/user_data/category_image/' . $event->getArgument('TargetCategory')->getId();
if ($file) {
if (!file_exists($upload_path)) {
mkdir($upload_path, 0755);
}
$file->move($upload_path , $file->getClientOriginalName());
}
// CategoryContentX pluginにて画像アップロードの為の設定
これで、user_data内にファイル保存されるようになった。
user_data/category_image/{id}/{画像名}"で保存される。
編集画面で画像ファイルを表示させる
- イベント追加
event.yml
Admin/Product/category.twig:
- [onadmin_product_category_twig, NORMAL]
- CategoryContentEvent.php
- 関数 onadmin_product_category_twig を追加
CategoryContentEvent.php
public function onadmin_product_category_twig(TemplateEvent $event)
{
$parameters = $event->getParameters();
$a = explode("/", $_SERVER["REQUEST_URI"]);
if (end($a) === "edit") {
//カテゴリIDがない場合、レンダリングしない
if (is_null($parameters['TargetCategory'])) {
return;
}
//登録がない、もしくは空で登録されている場合、レンダリングをしない
$Category = $parameters['TargetCategory'];
$CategoryContent = $this->app['category_content.repository.category_content']->find($Category->getId());
if (is_null($CategoryContent) || $CategoryContent->getImage() == '') {
return;
}
// twigコードにカテゴリコンテンツを挿入
$snipet = '<div class="col-md-12 form-group"><p>現在適用されている画像</p><div><img src="{{app.config.user_data_urlpath ~ CategoryContent.image}}" width="300px"></div></div>';
$search = '</form>';
$replace = $snipet . $search;
$source = str_replace($search, $replace, $event->getSource());
$event->setSource($source);
// twigパラメータにカテゴリコンテンツを追加
$parameters['CategoryContent'] = $CategoryContent;
$event->setParameters($parameters);
}
$event->setParameters($parameters);
}
編集画面にて、登録されていれば画像が表示されるようになった。
商品一覧での画像の取得
event.yml
Product/list.twig:
- [onRenderProductList, NORMAL]
CategoryContentEvent.php
class CategoryContentEvent
{
.
.
.
.
.
.
/**
* 商品一覧画面にカテゴリコンテンツを表示する.
*/
public function onRenderProductList(TemplateEvent $event)
{
$parameters = $event->getParameters();
// カテゴリIDがない場合、レンダリングしない
if (is_null($parameters['Category'])) {
return;
}
$Category = $parameters['Category'];
$CategoryContent = $this->app['category_content.repository.category_content']->find($Category->getId());
// twigパラメータにカテゴリコンテンツを追加
$parameters['CategoryContent'] = $CategoryContent;
$event->setParameters($parameters);
}
}
app/template/default/Product/list.twig
{# カテゴリー画像を表示 #}
<img src="{{app.config.user_data_urlpath ~ CategoryContent.image}}">
今後の課題
- 一部src配下のコアコードを触っているので、これもプラグインとして完結させたい
- 画像の削除機能が無いため残ったままになる。
所感
Symfony2を使ったことがなかったので、世界観を掴むまでに時間がかかった。
これからEC-CUBE3の開発をされる方は、まずSymfony2を理解してからの状態で臨むのがおすすめです。