Memo

メモ > 技術 > CMS: ECCube > カスタマイズ: 商品情報に項目を追加(既存のテーブルを変更しない方法)

カスタマイズ: 商品情報に項目を追加(既存のテーブルを変更しない方法)
前述の「商品情報に項目を追加」は既存のテーブルに項目が追加されるが、これは既存のテーブルを変更しない方法 具体的には「dtb_product_expand」というテーブルを追加し、「dtb_product」とOneToOneで関連付けてデータを保存する ただしテーブルを変更しない代わりに、プログラムをあちこち変更する必要があるので一長一短ではある ※「expand」という接尾語は、他に適切なものがあるか引き続き考えたい ・「expand」は「面積を広げる」「家を広げる」「ビジネスを広げる」のニュアンスみたい。それなら「データ領域を広げる」ということで変では無いか ・意味的には「extend」の方がより良いかもしれないが、それはclassのextendと被って可読性が下がりそう。コードの検索もしづらい また今回は「継承して作る」ではなく「付属データを置く」なので、exptendでもちょっと違うかもしれない ・「custom」はいいかと思ったが、コードの検索時に「customize」や「customer」がヒットするので微妙 ・「カスタマイズ」の意味で「cst」という文字にしてもいいかもしれない ECCubeデフォルトのテーブルには「dtb_」「mtb_」「plg_」の接頭語が付けられているので、それに合わせるという意味でも ただし「ProductCst」というクラスを作るなら「ProductCstType」というクラスも作ることになり、省略文字が名前の末尾ではなく中ほどに入ることになるのは微妙 また「cst」を付けるなら接尾語ではなく接頭語の方が、ECCubeのテーブルのネーミングルールには合う ・「item」はいいかと思ったが、すでに「dtb_cart_item」「dtb_order_item」などがあるので独自カスタマイズ用のテーブルか否かの判断がしづらくなりそう ・単純な項目追加なので、「addition」はアリかもしれない ・ひとまずは「expand」のままにしておく ■Entityを作成 app\Customize\Entity\ProductExpand.php を作成
<?php namespace Customize\Entity; use Doctrine\ORM\Mapping as ORM; /** * ProductExpand * * @ORM\Table(name="dtb_product_expand") * @ORM\HasLifecycleCallbacks() * @ORM\Entity */ class ProductExpand extends \Eccube\Entity\AbstractEntity { /** * @var int * * @ORM\Column(name="product_id", type="integer", nullable=false, options={"unsigned":true, "comment"="商品ID"}) * @ORM\Id * @ORM\GeneratedValue(strategy="IDENTITY") */ private $product_id; /** * @var string|null * * @ORM\Column(name="recommend_text", type="text", nullable=true, options={"comment"="おすすめポイント"}) */ private $recommend_text; /** * @var \Eccube\Entity\Product * * @ORM\OneToOne(targetEntity="Eccube\Entity\Product", inversedBy="ProductExpand") * @ORM\JoinColumns({ * @ORM\JoinColumn(name="product_id", referencedColumnName="id") * }) */ private $Product; /** * Get the value of product_id. * * @return int */ public function getProductId() { return $this->product_id; } /** * Get the value of recommend_text. * * @return string|null */ public function getRecommendText() { return $this->recommend_text; } /** * Set the value of recommend_text. * * @param string|null $recommend_text */ public function setRecommendText($recommend_text) { $this->recommend_text = $recommend_text; } /** * Get the value of Product. * * @return \Eccube\Entity\Product */ public function getProduct() { return $this->Product; } /** * Set the value of Product. * * @param \Eccube\Entity\Product $product */ public function setProduct(\Eccube\Entity\Product $product) { $this->Product = $product; } }
app\Customize\Entity\ProductTrait.php を作成
<?php namespace Customize\Entity; use Doctrine\ORM\Mapping as ORM; use Eccube\Annotation\EntityExtension; /** * @EntityExtension("Eccube\Entity\Product") */ trait ProductTrait { /** * @var \Customize\Entity\ProductExpand * * @ORM\OneToOne(targetEntity="Customize\Entity\ProductExpand", mappedBy="Product", cascade={"persist","remove"}) */ private $ProductExpand; /** * Get ProductExpand. * * @return \Customize\Entity\ProductExpand|null */ public function getProductExpand() { return $this->ProductExpand; } /** * Set ProductExpand. * * @param \Customize\Entity\ProductExpand|null $productExpand * * @return ProductTrait */ public function setProductExpand(\Customize\Entity\ProductExpand $productExpand) { $this->ProductExpand = $productExpand; return $this; } /** * Has ProductExpand. * * @return bool */ public function hasProductExpand() { if (null === $this->ProductExpand) { return false; } return true; } }
作業したらキャッシュを削除する
$ php bin/console cache:clear --no-warmup
プロキシファイルを作成する
$ php bin/console eccube:generate:proxies
以下に、トレイトを認識させるためのプロキシファイルが作成される app\proxy\entity\src\Eccube\Entity\Product.php ■テーブル定義を変更
$ php bin/console cache:clear --no-warmup $ php bin/console doctrine:migrations:diff Generated new migration class to "/var/www/html/app/DoctrineMigrations/Version20240904031302.php" from schema differences.
以下のファイルが作成される app\DoctrineMigrations\Version20240904031302.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 Version20240904031302 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_product_expand (product_id INT UNSIGNED AUTO_INCREMENT NOT NULL COMMENT \'商品ID\', recommend_text LONGTEXT DEFAULT NULL COMMENT \'おすすめポイント\', PRIMARY KEY(product_id)) DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ENGINE = InnoDB'); $this->addSql('ALTER TABLE dtb_product_expand ADD CONSTRAINT FK_F821F8284584665A FOREIGN KEY (product_id) REFERENCES dtb_product (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_product_expand'); } }
マイグレーションを実行すると、テーブルが作成される
$ php bin/console doctrine:migrations:status $ php bin/console doctrine:migrations:migrate
■フォームを作成 以下のファイルを作成する app\Customize\Form\Type\Admin\ProductExpandType.php
<?php namespace Customize\Form\Type\Admin; use Customize\Entity\ProductExpand; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\TextareaType; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints as Assert; class ProductExpandType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('product_id', HiddenType::class, [ 'mapped' => false, ]) ->add('recommend_text', TextareaType::class, [ 'required' => false, 'constraints' => [ new Assert\Length(['max' => 100]), ], ]); } public function configureOptions(OptionsResolver $resolver) { $resolver->setDefaults([ 'data_class' => ProductExpand::class, ]); } public function getBlockPrefix() { return 'admin_product_expand'; } }
app\Customize\Form\Extension\ProductExtension.php
<?php namespace Customize\Form\Extension; use Customize\Form\Type\Admin\ProductExpandType; use Eccube\Form\Type\Admin\ProductType; use Symfony\Component\Form\AbstractTypeExtension; use Symfony\Component\Form\FormBuilderInterface; class ProductExtension extends AbstractTypeExtension { public function buildForm(FormBuilderInterface $builder, array $options) { $builder ->add('productExpand', ProductExpandType::class, [ 'mapped' => true, ]) ; } public function getExtendedType() { return ProductType::class; } }
■コントローラーを調整 src\Eccube\Controller\Admin\Product\ProductController.php を複製して app\Customize\Controller\Admin\Product\ProductController.php を作成し、以下を調整する
// namespaceを修正 namespace Eccube\Controller\Admin\Product; ↓ namespace Customize\Controller\Admin\Product; // use部分に追加 use Customize\Entity\ProductExpand; // editメソッド内を修正 if (is_null($id)) { $Product = new Product(); $ProductClass = new ProductClass(); $ProductStatus = $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE); $Product ->addProductClass($ProductClass) ->setStatus; $ProductClass ↓ if (is_null($id)) { $Product = new Product(); $ProductClass = new ProductClass(); $ProductExpand = new ProductExpand(); $ProductStatus = $this->productStatusRepository->find(ProductStatus::DISPLAY_HIDE); $Product ->addProductClass($ProductClass) ->setStatus($ProductStatus) ->setProductExpand($ProductExpand); $ProductExpand->setProduct($Product); $ProductClass // editメソッド内を修正 $ProductStock = $ProductClass->getProductStock(); } $ProductExpand = $Product->getProductExpand(); } ↓ $ProductStock = $ProductClass->getProductStock(); } } // editメソッド内を修正 if (!$has_class) { $ProductClass = $form['class']->getData(); ↓ if (!$has_class) { $ProductClass = $form['class']->getData(); $ProductExpand = $form['productExpand']->getData();
■画面表示を調整 src\Eccube\Resource\template\admin\Product\product.twig を複製して app\template\admin\Product\product.twig を作成し、以下を追加する
<div class="collapse show ec-cardCollapse" id="recommendText"> <div class="card-body"> <div class="row"> <div class="col-3"> <span>{{ 'admin.common.recommend_text'|trans }}</span> </div> <div class="col-9"> <div> {{ form_widget(form.productExpand.recommend_text, {id: 'wysiwyg-area', attr : { rows : "8"} }) }} {{ form_errors(form.productExpand.recommend_text) }} </div> </div> </div> </div> </div>
src\Eccube\Resource\template\default\Product\detail.twig を複製して app\template\default\Product\detail.twig を作成し、以下を追加する
{% if Product.ProductExpand.recommendText %} <div class="ec-productRole__recommendText"> {{ include(template_from_string(Product.ProductExpand.recommendText)) }} </div> {% endif %}
■リソースファイルを調整 ラベルを正しく表示させるに、以下のリソースファイルに文言を追加する app\Customize\Resource\locale\messages.ja.yaml
common.recommend_text: おすすめポイント admin.common.recommend_text: おすすめポイント
app\Customize\Resource\locale\messages.en.yaml
common.recommend_text: Recommendation Points admin.common.recommend_text: Recommendation Points
■キャッシュをクリア 最後に、キャッシュをクリアしておく
$ php bin/console cache:clear --no-warmup
これで商品におすすめポイント欄が追加される ■メモ FormTypeについては以下が参考になりそう FormTypeのカスタマイズ - < for EC-CUBE 4 Developers /> https://doc4.ec-cube.net/customize_formtype 商品コントローラーにおいて、引き続き「商品のコピー」「商品CSV出力」にも処理の追加が必要そう プログラムをあちこち変更しているので、それ以外にも悪影響が無いかは要確認

Advertisement