【EC-CUBE4】商品に販売期間を設定する方法

EC-CUBE4で商品に販売期間を設定する方法です。

販売期間を設定していて販売期間内の商品のみ商品一覧と商品詳細ページに表示させるようにします。

dtb_productテーブルに販売開始日時と販売終了日時カラムを追加

dtb_productテーブルに販売開始日時と販売終了日時カラムを追加します。

サンプルコードは以下のとおりです。

<?php

namespace Customize\Entity;

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

/**
 * dtb_productテーブルに販売開始日時と販売位終了日時カラム追加
 *
 * @EntityExtension("Eccube\Entity\Product")
 */
trait ProductTrait
{
    /**
     * @ORM\Column(type="datetimetz", nullable=true)
     */
    private $sale_start;

    /**
     * @ORM\Column(type="datetimetz", nullable=true)
     */
    private $sale_end;

    public function getSaleStart(): ?\DateTimeInterface
    {
        return $this->sale_start;
    }

    public function setSaleStart(?\DateTimeInterface $sale_start): self
    {
        $this->sale_start = $sale_start;

        return $this;
    }

    public function getSaleEnd(): ?\DateTimeInterface
    {
        return $this->sale_end;
    }

    public function setSaleEnd(?\DateTimeInterface $sale_end): self
    {
        $this->sale_end = $sale_end;

        return $this;
    }
}

 

ProductTraitを作ったら以下のコマンドを実行してください。

bin/console eccube:generate:proxies
bin/console doctrine:schema:update --force

 

商品登録フォームに販売開始日時と販売位終了日時項目を追加

商品登録フォームに販売開始日時と販売位終了日時項目を追加します。

サンプルコードは以下のとおりです。

<?php

namespace Customize\Form\Extension;

use Eccube\Form\Type\Admin\ProductType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

/**
 * 商品登録フォームに販売開始日時と販売位終了日時項目を追加
 *
 * Class ProductTypeExtension
 * @package Customize\Form\Extension
 */
class ProductTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('sale_start', DateTimeType::class, [
                'label' => '販売開始日時',
                'required' => false,
                'years' => range(date('Y'), date('Y') + 10),
                'placeholder' => [
                    'year' => '----', 'month' => '--', 'day' => '--', 'hour' => '--', 'minute' => '--'
                ],
                'eccube_form_options' => [
                    'auto_render' => true
                ]
            ])
            ->add('sale_end', DateTimeType::class, [
                'label' => '販売終了日時',
                'required' => false,
                'years' => range(date('Y'), date('Y') + 10),
                'placeholder' => [
                    'year' => '----', 'month' => '--', 'day' => '--', 'hour' => '--', 'minute' => '--'
                ],
                'eccube_form_options' => [
                    'auto_render' => true
                ]
            ])
        ;

        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
            $form = $event->getForm();

            $start = $form['sale_start']->getData();
            $end = $form['sale_end']->getData();

            // 販売開始日時が販売終了日時より大きいときエラー
            if ($start && $end && ($end < $start)) {
                $form['sale_start']->addError(new FormError("販売開始日時がおかしいです。"));
            }
            // 販売開始日時が空で販売終了日時が空じゃないときエラー
            if(empty($start) && $end) {
                $form['sale_start']->addError(new FormError("販売開始日時を設定してください。"));
            }
            // 販売終了日時が空で販売開始日時が空じゃないときエラー
            if($start && empty($end)) {
                $form['sale_end']->addError(new FormError("販売終了日時を設定してください。"));
            }
        });

    }

    /**
     * {@inheritdoc}
     */
    public function getExtendedType()
    {
        return ProductType::class;
    }
}

 

こんな感じで表示されます。

 

販売期間外の商品は商品一覧から非表示設定

販売期間外の商品は商品一覧から非表示にするよう設定します。

販売期間を設定していない商品も非表示にしているので、販売期間設定をしていない商品は表示させたい場合は絞り込み条件を調整してください。

サンプルコードは以下のとおりです。

<?php


namespace Customize\Repository\QueryCustomizer;


use Doctrine\ORM\QueryBuilder;
use Eccube\Doctrine\Query\QueryCustomizer;
use Eccube\Repository\QueryKey;

/**
 * 販売期間外の商品は商品一覧から非表示
 *
 * Class NowOnSaleCustomizer
 * @package Customize\Repository\QueryCustomizer
 */
class NowOnSaleCustomizer implements QueryCustomizer
{

    /**
     * @inheritDoc
     */
    public function customize(QueryBuilder $builder, $params, $queryKey)
    {
        // これは商品詳細ページで利用します
        if(isset($params["id"]) && !empty($params["id"])) {
            $builder
                ->andWhere("p.id = :id")
                ->setParameter("id", $params["id"]);
        }

        // 販売期間中の商品のみに絞り込み
        $builder
            ->andWhere("p.sale_start <= :now")
            ->andWhere("p.sale_end >= :now")
            ->setParameter("now", new \DateTime());
    }

    /**
     * @inheritDoc
     */
    public function getQueryKey()
    {
        return QueryKey::PRODUCT_SEARCH;
    }
}

 

販売期間外の商品はNot Foundにする

販売期間外の商品は商品詳細ページをNot Foundにします。

サンプルコードは以下のとおりです。

<?php

namespace Customize\EventSubscriber;

use Eccube\Event\EventArgs;
use Eccube\Repository\ProductRepository;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * 商品が販売期間中かチェック
 *
 * Class NowOnSaleSubscriber
 * @package Customize\EventSubscriber
 */
class NowOnSaleSubscriber implements EventSubscriberInterface
{
    /**
     * @var ProductRepository
     */
    private $productRepository;

    public function __construct(
        ProductRepository $productRepository
    ) {
        $this->productRepository = $productRepository;
    }

    public function onFrontProductDetailInitialize(EventArgs $event)
    {
        $Product = $event->getArgument("Product");

        // 販売期間中商品かチェック
        $qb = $this->productRepository->getQueryBuilderBySearchData([
            "id" => $Product->getId()
        ]);
        $result = $qb->getQuery()->getResult();

        // 販売期間中じゃなければNot Fount
        if(!$result) {
            throw new NotFoundHttpException();
        }
    }

    public static function getSubscribedEvents()
    {
        return [
           'front.product.detail.initialize' => 'onFrontProductDetailInitialize',
        ];
    }
}

 

以上で完成です。

お気軽にコメントをどうぞ

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください