メモ > 技術 > CMS: ECCube > カスタマイズ: 新着情報管理をもとに独自の記事管理を作成
カスタマイズ: 新着情報管理をもとに独自の記事管理を作成
既存機能の「新着情報管理」をもとに、独自の記事管理を作成してみる
独自に作成したテーブルには専用のプレフィックスを付けておく方が管理しやすいか。contactも含めて考える
■調査メモ
@ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
EC-CUBE4のDBのそれぞれのテーブルにあるdiscriminator_typeとは何なのか - Qiita
https://qiita.com/okazy/items/974d8e07b0dbd50b9c6a
Entityを継承して拡張するときに利用されるらしい
ECCubeでは以下のアノテーションを指定すると、discriminator_type列にクラス名が小文字になった文字列が入る
現状それ以上は気にしなくていいか
@ORM\InheritanceType("SINGLE_TABLE")
Doctrine2のInheritanceMapping | QUARTETCOM TECH BLOG
https://tech.quartetcom.co.jp/2015/08/24/doctorine2-inheritance-mapping/
Single Table Inheritance は、全ての階層構造を一つのテーブルで表現したもの
Class Table Inheritance は、複数テーブルで継承関係を表現したもの
ECCubeではすべてのテーブルで SINGLE_TABLE が指定されいているみたい
@ORM\HasLifecycleCallbacks()
データーベースと Doctrine | SymDoc - PHP フレームワーク Symfony3 日本語ドキュメント Wiki
http://symdoc.kwalk.jp/doc/book/doctrine
エンティティが INSERT や UPDATE、DELETE される直前または、直後に、アクションを実行することができる
例えば「データを登録する際に、自動で特定のメソッドが実行されるようにする」のような処理ができる
ECCubeではすべてのテーブルに指定されいているみたい
■Entityとテーブルの作成
src\Eccube\Entity\News.php をもとに
app\Customize\Entity\Article.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 Customize\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Article
*
* @ORM\Table(name="dtb_article")
* @ORM\InheritanceType("SINGLE_TABLE")
* @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
* @ORM\HasLifecycleCallbacks()
* @ORM\Entity(repositoryClass="Customize\Repository\ArticleRepository")
* @ORM\Cache(usage="NONSTRICT_READ_WRITE")
*/
class Article extends \Eccube\Entity\AbstractEntity
{
/**
* @return string
*/
public function __toString()
{
return (string) $this->getTitle();
}
/**
* @var int
*
* @ORM\Column(name="id", type="integer", options={"unsigned":true})
* @ORM\Id
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @var \DateTime|null
*
* @ORM\Column(name="publish_date", type="datetimetz", nullable=true)
*/
private $publish_date;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* @var string|null
*
* @ORM\Column(name="description", type="text", nullable=true)
*/
private $description;
/**
* @var string|null
*
* @ORM\Column(name="url", type="string", length=4000, nullable=true)
*/
private $url;
/**
* @var boolean
*
* @ORM\Column(name="link_method", type="boolean", options={"default":false})
*/
private $link_method = false;
/**
* @var \DateTime
*
* @ORM\Column(name="create_date", type="datetimetz")
*/
private $create_date;
/**
* @var \DateTime
*
* @ORM\Column(name="update_date", type="datetimetz")
*/
private $update_date;
/**
* @var boolean
*
* @ORM\Column(name="visible", type="boolean", options={"default":true})
*/
private $visible;
/**
* @var \Eccube\Entity\Member
*
* @ORM\ManyToOne(targetEntity="Eccube\Entity\Member")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="creator_id", referencedColumnName="id")
* })
*/
private $Creator;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set publishDate.
*
* @param \DateTime|null $publishDate
*
* @return Article
*/
public function setPublishDate($publishDate = null)
{
$this->publish_date = $publishDate;
return $this;
}
/**
* Get publishDate.
*
* @return \DateTime|null
*/
public function getPublishDate()
{
return $this->publish_date;
}
/**
* Set title.
*
* @param string $title
*
* @return Article
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title.
*
* @return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set description.
*
* @param string|null $description
*
* @return Article
*/
public function setDescription($description = null)
{
$this->description = $description;
return $this;
}
/**
* Get description.
*
* @return string|null
*/
public function getDescription()
{
return $this->description;
}
/**
* Set url.
*
* @param string|null $url
*
* @return Article
*/
public function setUrl($url = null)
{
$this->url = $url;
return $this;
}
/**
* Get url.
*
* @return string|null
*/
public function getUrl()
{
return $this->url;
}
/**
* Set linkMethod.
*
* @param boolean $linkMethod
*
* @return Article
*/
public function setLinkMethod($linkMethod)
{
$this->link_method = $linkMethod;
return $this;
}
/**
* Get linkMethod.
*
* @return boolean
*/
public function isLinkMethod()
{
return $this->link_method;
}
/**
* Set createDate.
*
* @param \DateTime $createDate
*
* @return Article
*/
public function setCreateDate($createDate)
{
$this->create_date = $createDate;
return $this;
}
/**
* Get createDate.
*
* @return \DateTime
*/
public function getCreateDate()
{
return $this->create_date;
}
/**
* Set updateDate.
*
* @param \DateTime $updateDate
*
* @return Article
*/
public function setUpdateDate($updateDate)
{
$this->update_date = $updateDate;
return $this;
}
/**
* Get updateDate.
*
* @return \DateTime
*/
public function getUpdateDate()
{
return $this->update_date;
}
/**
* @return integer
*/
public function isVisible()
{
return $this->visible;
}
/**
* @param boolean $visible
*
* @return Article
*/
public function setVisible($visible)
{
$this->visible = $visible;
return $this;
}
/**
* Set creator.
*
* @param \Eccube\Entity\Member|null $creator
*
* @return Article
*/
public function setCreator(\Eccube\Entity\Member $creator = null)
{
$this->Creator = $creator;
return $this;
}
/**
* Get creator.
*
* @return \Eccube\Entity\Member|null
*/
public function getCreator()
{
return $this->Creator;
}
}
以下でマイグレーションファイルを作成
$ cd /var/www/html
$ php bin/console cache:clear --no-warmup
$ php bin/console doctrine:migrations:diff
Generated new migration class to "/var/www/html/app/DoctrineMigrations/Version20200820110258.php" from schema differences.
app\DoctrineMigrations\Version20200820110258.php を修正(自動作成されたマイグレーションファイル)
<?php declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20200820110258 extends AbstractMigration
{
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('CREATE TABLE dtb_article (id INT UNSIGNED AUTO_INCREMENT NOT NULL, creator_id INT UNSIGNED DEFAULT NULL, publish_date DATETIME DEFAULT NULL COMMENT \'(DC2Type:datetimetz)\', title VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, url VARCHAR(4000) DEFAULT NULL, link_method TINYINT(1) DEFAULT \'0\' NOT NULL, create_date DATETIME NOT NULL COMMENT \'(DC2Type:datetimetz)\', update_date DATETIME NOT NULL COMMENT \'(DC2Type:datetimetz)\', visible TINYINT(1) DEFAULT \'1\' NOT NULL, discriminator_type VARCHAR(255) NOT NULL, INDEX IDX_23A0E6661220EA6 (creator_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB');
$this->addSql('ALTER TABLE dtb_article ADD CONSTRAINT FK_23A0E6661220EA6 FOREIGN KEY (creator_id) REFERENCES dtb_member (id)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('DROP TABLE dtb_article');
}
}
以下でマイグレーションを実行
$ php bin/console doctrine:migrations:status
== Configuration
>> Name: Application Migrations
>> Database Driver: pdo_mysql
>> Database Name: eccubedb
>> Configuration Source: manually configured
>> Version Table Name: migration_versions
>> Version Column Name: version
>> Migrations Namespace: DoctrineMigrations
>> Migrations Directory: /var/www/html/app/DoctrineMigrations
>> Previous Version: 2020-08-17 05:51:48 (20200817055148)
>> Current Version: 2020-08-20 03:20:03 (20200820032003)
>> Next Version: 2020-08-20 11:02:58 (20200820110258)
>> Latest Version: 2020-08-20 11:02:58 (20200820110258)
>> Executed Migrations: 6
>> Executed Unavailable Migrations: 0
>> Available Migrations: 7
>> New Migrations: 1
$ php bin/console doctrine:migrations:migrate
Application Migrations
WARNING! You are about to execute a database migration that could result in schema changes and data loss. Are you sure you wish to continue? (y/n)y
Migrating up to 20200820110258 from 20200820032003
++ migrating 20200820110258
-> CREATE TABLE article (id INT UNSIGNED AUTO_INCREMENT NOT NULL, creator_id INT UNSIGNED DEFAULT NULL, publish_date DATETIME DEFAULT NULL COMMENT '(DC2Type:datetimetz)', title VARCHAR(255) NOT NULL, description LONGTEXT DEFAULT NULL, url VARCHAR(4000) DEFAULT NULL, link_method TINYINT(1) DEFAULT '0' NOT NULL, create_date DATETIME NOT NULL COMMENT '(DC2Type:datetimetz)', update_date DATETIME NOT NULL COMMENT '(DC2Type:datetimetz)', visible TINYINT(1) DEFAULT '1' NOT NULL, discriminator_type VARCHAR(255) NOT NULL, INDEX IDX_23A0E6661220EA6 (creator_id), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB
-> ALTER TABLE article ADD CONSTRAINT FK_23A0E6661220EA6 FOREIGN KEY (creator_id) REFERENCES dtb_member (id)
++ migrated (0.28s)
------------------------
++ finished in 0.28s
++ 1 migrations executed
++ 2 sql queries
実行できたら、テーブルが作成されていることを確認する
■プログラムの作成
src\Eccube\Repository\NewsRepository.php をもとに
app\Customize\Repository\ArticleRepository.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 Customize\Repository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Criteria;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Symfony\Bridge\Doctrine\RegistryInterface;
use Customize\Entity\Article;
/**
* ArticleRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class ArticleRepository extends \Eccube\Repository\AbstractRepository
{
public function __construct(RegistryInterface $registry)
{
parent::__construct($registry, Article::class);
}
/**
* 記事を登録します.
*
* @param $Article
*/
public function save($Article)
{
$em = $this->getEntityManager();
$em->persist($Article);
$em->flush($Article);
}
/**
* 記事を削除します.
*
* @param Article $Article
*
* @throws ForeignKeyConstraintViolationException 外部キー制約違反の場合
* @throws DriverException SQLiteの場合, 外部キー制約違反が発生すると, DriverExceptionをthrowします.
*/
public function delete($Article)
{
$em = $this->getEntityManager();
$em->remove($Article);
$em->flush($Article);
}
/**
* @return \Doctrine\ORM\QueryBuilder
*/
public function getQueryBuilderAll()
{
$qb = $this->createQueryBuilder('n');
$qb->orderBy('n.publish_date', 'DESC')
->addOrderBy('n.id', 'DESC');
return $qb;
}
/**
* @return Article[]|ArrayCollection
*/
public function getList()
{
// second level cacheを効かせるためfindByで取得
$Results = $this->findBy(['visible' => true], ['publish_date' => 'DESC', 'id' => 'DESC']);
// 公開日時前のArticleをフィルター
$criteria = Criteria::create();
$criteria->where(Criteria::expr()->lte('publish_date', new \DateTime()));
$Article = new ArrayCollection($Results);
return $Article->matching($criteria);
}
}
src\Eccube\Controller\Admin\Content\NewsController.php をもとに
app\Customize\Controller\Admin\Content\ArticleController.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 Customize\Controller\Admin\Content;
use Eccube\Controller\AbstractController;
//use Eccube\Event\EccubeEvents;
//use Eccube\Event\EventArgs;
use Eccube\Util\CacheUtil;
use Knp\Component\Pager\Paginator;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Customize\Entity\Article;
use Customize\Form\Type\Admin\ArticleType;
use Customize\Repository\ArticleRepository;
class ArticleController extends AbstractController
{
/**
* @var ArticleRepository
*/
protected $articleRepository;
/**
* ArticleController constructor.
*
* @param ArticleRepository $articleRepository
*/
public function __construct(ArticleRepository $articleRepository)
{
$this->articleRepository = $articleRepository;
}
/**
* 記事一覧を表示する。
*
* @Route("/%eccube_admin_route%/content/article", name="admin_content_article")
* @Route("/%eccube_admin_route%/content/article/page/{page_no}", requirements={"page_no" = "\d+"}, name="admin_content_article_page")
* @Template("@admin/Content/article.twig")
*
* @param Request $request
* @param int $page_no
* @param Paginator $paginator
*
* @return array
*/
public function index(Request $request, $page_no = 1, Paginator $paginator)
{
$qb = $this->articleRepository->getQueryBuilderAll();
/*
$event = new EventArgs(
[
'qb' => $qb,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_INDEX_INITIALIZE, $event);
*/
$pagination = $paginator->paginate(
$qb,
$page_no,
$this->eccubeConfig->get('eccube_default_page_count')
);
return [
'pagination' => $pagination,
];
}
/**
* 記事を登録・編集する。
*
* @Route("/%eccube_admin_route%/content/article/new", name="admin_content_article_new")
* @Route("/%eccube_admin_route%/content/article/{id}/edit", requirements={"id" = "\d+"}, name="admin_content_article_edit")
* @Template("@admin/Content/article_edit.twig")
*
* @param Request $request
* @param null $id
*
* @return array|\Symfony\Component\HttpFoundation\RedirectResponse
*/
public function edit(Request $request, $id = null, CacheUtil $cacheUtil)
{
if ($id) {
$Article = $this->articleRepository->find($id);
if (!$Article) {
throw new NotFoundHttpException();
}
} else {
$Article = new \Customize\Entity\Article();
$Article->setPublishDate(new \DateTime());
}
$builder = $this->formFactory
->createBuilder(ArticleType::class, $Article);
/*
$event = new EventArgs(
[
'builder' => $builder,
'Article' => $Article,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_EDIT_INITIALIZE, $event);
*/
$form = $builder->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
if (!$Article->getUrl()) {
$Article->setLinkMethod(false);
}
$this->articleRepository->save($Article);
/*
$event = new EventArgs(
[
'form' => $form,
'Article' => $Article,
],
$request
);
$this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_EDIT_COMPLETE, $event);
*/
$this->addSuccess('admin.common.save_complete', 'admin');
// キャッシュの削除
$cacheUtil->clearDoctrineCache();
return $this->redirectToRoute('admin_content_article_edit', ['id' => $Article->getId()]);
}
return [
'form' => $form->createView(),
'Article' => $Article,
];
}
/**
* 指定した記事を削除する。
*
* @Route("/%eccube_admin_route%/content/article/{id}/delete", requirements={"id" = "\d+"}, name="admin_content_article_delete", methods={"DELETE"})
*
* @param Request $request
* @param Article $Article
*
* @return \Symfony\Component\HttpFoundation\RedirectResponse
*/
public function delete(Request $request, Article $Article, CacheUtil $cacheUtil)
{
$this->isTokenValid();
log_info('記事削除開始', [$Article->getId()]);
try {
$this->articleRepository->delete($Article);
/*
$event = new EventArgs(['Article' => $Article], $request);
$this->eventDispatcher->dispatch(EccubeEvents::ADMIN_CONTENT_ARTICLE_DELETE_COMPLETE, $event);
*/
$this->addSuccess('admin.common.delete_complete', 'admin');
log_info('記事削除完了', [$Article->getId()]);
// キャッシュの削除
$cacheUtil->clearDoctrineCache();
} catch (\Exception $e) {
$message = trans('admin.common.delete_error_foreign_key', ['%name%' => $Article->getTitle()]);
$this->addError($message, 'admin');
log_error('記事削除エラー', [$Article->getId(), $e]);
}
return $this->redirectToRoute('admin_content_article');
}
}
src\Eccube\Form\Type\Admin\NewsType.php をもとに
app\Customize\Form\Type\Admin\ArticleType.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 Customize\Form\Type\Admin;
use Eccube\Common\EccubeConfig;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Assert;
use Customize\Entity\Article;
class ArticleType extends AbstractType
{
/**
* @var EccubeConfig
*/
protected $eccubeConfig;
public function __construct(EccubeConfig $eccubeConfig)
{
$this->eccubeConfig = $eccubeConfig;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('publish_date', DateTimeType::class, [
'date_widget' => 'choice',
'input' => 'datetime',
'format' => 'yyyy-MM-dd hh:mm',
'years' => range($this->eccubeConfig['eccube_article_start_year'], date('Y') + 3),
'constraints' => [
new Assert\NotBlank(),
],
])
->add('title', TextType::class, [
'required' => true,
'constraints' => [
new Assert\NotBlank(),
new Assert\Length(['max' => $this->eccubeConfig['eccube_mtext_len']]),
],
])
->add('url', TextType::class, [
'required' => false,
'constraints' => [
new Assert\Url(),
new Assert\Length(['max' => $this->eccubeConfig['eccube_mtext_len']]),
],
])
->add('link_method', CheckboxType::class, [
'required' => false,
'label' => 'admin.content.article.new_window',
'value' => '1',
])
->add('description', TextareaType::class, [
'required' => false,
'attr' => [
'rows' => 8,
],
'constraints' => [
new Assert\Length(['max' => $this->eccubeConfig['eccube_ltext_len']]),
],
])
->add('visible', ChoiceType::class, [
'label' => false,
'choices' => ['admin.content.article.display_status__show' => true, 'admin.content.article.display_status__hide' => false],
'required' => true,
'expanded' => false,
]);
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Article::class,
]);
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'admin_article';
}
}
■テンプレートの作成
src\Eccube\Resource\template\admin\Content\news.twig をもとに
app\template\admin\Content\article.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 '@admin/default_frame.twig' %}
{% set menus = ['content', 'article'] %}
{% block title %}{{ 'admin.content.article_management'|trans }}{% endblock %}
{% block sub_title %}{{ 'admin.content.contents_management'|trans }}{% endblock %}
{% block stylesheet %}
<style type="text/css">
li.list-group-item {
z-index: inherit !important;
}
</style>
{% endblock %}
{% block main %}
<div class="c-contentsArea__cols">
<div class="c-contentsArea__primaryCol">
<div class="c-primaryCol">
<div class="d-block mb-3">
<a id="addNew" class="btn btn-ec-regular"
href="{{ url('admin_content_article_new') }}">{{ 'admin.common.create__new'|trans }}</a>
</div>
<div class="card rounded border-0 mb-4">
<div class="card-body p-0">
<ul class="list-group list-group-flush mb-4 sortable-container">
<li class="list-group-item">
<div class="row justify-content-around">
<div class="col-2"><strong>{{ 'admin.content.article.publish_date'|trans }}</strong>
</div>
<div class="col-1"><strong>{{ 'admin.content.article.display_status'|trans }}</strong>
</div>
<div class="col"><strong>{{ 'admin.content.article.title'|trans }}</strong></div>
</div>
</li>
{% for Article in pagination %}
<li class="list-group-item sortable-item" data-id="{{ Article.id }}">
<div class="row justify-content-around">
<div class="col-2 d-flex align-items-center">
<span>{{ Article.publishDate|date_min }}</span></div>
<div class="col-1 d-flex align-items-center">{{ Article.visible ? 'admin.content.article.display_status__show'|trans : 'admin.content.article.display_status__hide'|trans }}</div>
<div class="col d-flex align-items-center"><a
href="{{ url('admin_content_article_edit', {id: Article.id}) }}">{{ Article.title }}</a>
</div>
<div class="col-2">
<div class="row">
<div class="col px-0 text-center">
<a class="btn btn-ec-actionIcon"
href="{{ url('admin_content_article_edit', {id: Article.id}) }}"
data-toggle="tooltip" data-placement="top"
title="{{ 'admin.common.edit'|trans }}">
<i class="fa fa-pencil fa-lg text-secondary"></i>
</a>
</div>
<div class="col pl-0 text-center" data-toggle="tooltip"
data-placement="top" title="{{ 'admin.common.delete'|trans }}">
<a class="btn btn-ec-actionIcon" data-toggle="modal"
data-target="#delete_{{ Article.id }}">
<i class="fa fa-close fa-lg text-secondary"
aria-hidden="true"></i>
</a>
<div class="modal fade" id="delete_{{ Article.id }}" tabindex="-1"
role="dialog"
aria-labelledby="delete_{{ Article.id }}" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title font-weight-bold">{{ 'admin.common.delete_modal__title'|trans }}</h5>
<button class="close" type="button"
data-dismiss="modal"
aria-label="Close"><span
aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body text-left">
<p class="text-left">{{ 'admin.common.delete_modal__message'|trans({'%name%': Article.title}) }}</p>
</div>
<div class="modal-footer">
<button class="btn btn-ec-sub" type="button"
data-dismiss="modal">{{ 'admin.common.cancel'|trans }}</button>
<a class="btn btn-ec-delete"
href="{{ url('admin_content_article_delete', {id: Article.id}) }}" {{ csrf_token_for_anchor() }}
data-method="delete"
data-confirm="false">{{ 'admin.common.delete'|trans }}</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</li>
{% endfor %}
</ul>
{% if pagination.paginationData.pageCount > 1 %}
<div class="row justify-content-md-center mb-4">
{% include "@admin/pager.twig" with { 'pages' : pagination.paginationData, 'routes' : 'admin_content_article_page' } %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
src\Eccube\Resource\template\admin\Content\news_edit.twig をもとに
app\template\admin\Content\article_edit.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 '@admin/default_frame.twig' %}
{% set menus = ['content', 'article'] %}
{% block title %}{{ 'admin.content.article_management'|trans }}{% endblock %}
{% block sub_title %}{{ 'admin.content.contents_management'|trans }}{% endblock %}
{% form_theme form '@admin/Form/bootstrap_4_horizontal_layout.html.twig' %}
{% block main %}
<form role="form" class="form-horizontal" name="form1" id="form1" method="post" action="?">
{{ 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">
<div class="row">
<div class="col-8">
<span class="card-title">{{ 'admin.content.article.article_registration'|trans }}</span></div>
<div class="col-4 text-right">
<a data-toggle="collapse" href="#articleForm"
aria-expanded="true" aria-controls="articleForm"><i class="fa fa-lg fa-angle-up"></i></a></div>
</div>
</div>
<div class="ec-cardCollapse collapse show" id="articleForm" style="">
<div class="card-body">
<div class="row">
<div class="col-3"><span>{{ 'admin.content.article.publish_date'|trans }}</span></div>
<div class="col mb-2">
{{ form_widget(form.publish_date) }}
{{ form_errors(form.publish_date) }}
</div>
</div>
<div class="row">
<div class="col-3"><span>{{ 'admin.content.article.title'|trans }}</span></div>
<div class="col mb-2">
{{ form_widget(form.title) }}
{{ form_errors(form.title) }}
</div>
</div>
<div class="row">
<div class="col-3">
<div class="d-inline-block" data-tooltip="true" data-placement="top" title="{{ 'tooltip.content.article.url'|trans }}">
<span>{{ 'admin.content.article.url'|trans }}</span><i class="fa fa-question-circle fa-lg ml-1"></i>
</div>
</div>
<div class="col mb-2">
{{ form_widget(form.url) }}
{{ form_errors(form.url) }}
</div>
</div>
<div class="row">
<div class="col-3"> </div>
<div class="col mb-2">
{{ form_widget(form.link_method) }}
{{ form_errors(form.link_method) }}
</div>
</div>
<div class="row">
<div class="col-3">
<div class="d-inline-block" data-tooltip="true" data-placement="top" title="{{ 'tooltip.content.article.body'|trans }}">
<span>{{ 'admin.content.article.body'|trans }}</span><i class="fa fa-question-circle fa-lg ml-1"></i>
</div>
</div>
<div class="col mb-2">
{{ form_widget(form.description) }}
{{ form_errors(form.description) }}
</div>
</div>
{# エンティティ拡張の自動出力 #}
{% for f in form if f.vars.eccube_form_options.auto_render %}
{% if f.vars.eccube_form_options.form_theme %}
{% form_theme f f.vars.eccube_form_options.form_theme %}
{{ form_row(f) }}
{% 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 %}
</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_content_article') }}">
<i class="fa fa-backward" aria-hidden="true"></i>
<span>{{ 'admin.content.article_management'|trans }}</span></a>
</div>
</div>
<div class="col-6">
<div id="ex-conversion-action" class="row align-items-center justify-content-end">
<div class="col-auto">
{{ form_widget(form.visible) }}
{{ form_errors(form.visible) }}
</div>
<div class="col-auto">
<button class="btn btn-ec-conversion px-5" type="submit">{{ 'admin.common.registration'|trans }}</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
{% endblock %}
■文字列定義の作成
app\config\eccube\packages\eccube_nav.yaml に追記(news の直下)
article:
name: admin.content.article_management
url : admin_content_article
src\Eccube\Resource\locale\messages.en.yaml をもとに
app\Customize\Resource\locale\messages.en.yaml を作成
front.block.article.title__en: ARTICLE
front.block.article.title__ja: Article
front.block.article.read_more: More
front.block.article.see_details: Read details
admin.home.article_title: Article
admin.content.article_management: Article
admin.content.article.publish_date: Published on
admin.content.article.display_status: Display Status
admin.content.article.display_status__show: Displayed
admin.content.article.display_status__hide: Hidden
admin.content.article.title: Title
admin.content.article.url: URL
admin.content.article.body: Body
admin.content.article.article_registration: Create News
admin.content.article.new_window: Open in a New Window
tooltip.content.article.url: If there is a page which provides the details of this article, enter its URL. You can also enter external URLs.
tooltip.content.article.body: You can use HTML tags.
src\Eccube\Resource\locale\messages.ja.yaml をもとに
app\Customize\Resource\locale\messages.ja.yaml を作成
front.block.article.title__en: Article
front.block.article.title__ja: 記事
front.block.article.read_more: more
front.block.article.see_details: 詳しくはこちら
admin.home.article_title: 記事
admin.content.article_management: 記事管理
admin.content.article.publish_date: 公開日時
admin.content.article.display_status: 公開状態
admin.content.article.display_status__show: 公開
admin.content.article.display_status__hide: 非公開
admin.content.article.title: タイトル
admin.content.article.url: URL
admin.content.article.body: 本文
admin.content.article.article_registration: 記事登録
admin.content.article.new_window: 別ウィンドウで開く
tooltip.content.article.url: この記事の詳細な内容を記したウェブページある場合、URLを入力します。外部サイトのURLなどを利用することもできます。
tooltip.content.article.body: HTMLタグが利用可能です。
app\config\eccube\packages\eccube.yaml に追記(news の直下)
eccube_article_start_year: 2000
■動作確認
管理画面の「コンテンツ管理 → 記事管理」から、データの登録編集削除を行える
挙動は基本的に「新着情報管理」と同じ