0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

EC-CUBE 4.1系で商品の販売期間を限定するカスタム

Posted at

販売期間を限定するカスタム

dtb_productに販売開始日時と販売終了日時を追加し、商品一覧ページと商品詳細ページにてカートボタンの表示を分岐させることで販売期間中のみに購入が可能になるカスタマイズを実装したい。なお、価格表示もその期間中とする。
カスタマイズを実装するにあたり、コミュニティにてアドバイスをいただくことができました。
ありがとうございます。

当初は商品の公開/非公開自体の予約機能を実装しようと考えていたため、その分は別記事を投稿する。

環境

  • Docker Compose version v2.0.0-rc.1
  • PHP 7.4
  • mysql 5.7
  • EC-CUBE 4.1.1

参考にしたドキュメント

Entityカスタマイズ

dtb_productに開始日時と終了日時のカラムを追加する。
Entity からフォームを自動生成したいため、@EntityExtension アノテーションで拡張したフィールドに @FormAppend アノテーションを追加する。
今回は時間まで指定したいためDateTimeTypeを使用し"with_seconds": trueとする。
常時公開する商品もあるため、"required":falseにしておく。

Customize\Entity\ProductTrait.php
<?php

namespace Customize\Entity;

use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation as Eccube;
use Eccube\Annotation\EntityExtension;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;

/**
 * @Eccube\EntityExtension("Eccube\Entity\Product")
 */
trait ProductTrait
{
  /**
   * @var \DateTime
   * @ORM\Column(name="publish_date", type="datetimetz", nullable=true)
   * @Eccube\FormAppend(
   *  auto_render=true,
   *  type="Symfony\Component\Form\Extension\Core\Type\DateTimeType",
   *  options={
   *    "required":false,
   *    "label":"公開予定日時",
   *    "input": "datetime",
   *    "placeholder": {"year":"----", "month": "--", "day": "--", "hour": "--", "minute": "--", "second": "--"},
   *    "with_seconds": true
   *  }
   * )
   */
  private $publish_date;

  /**
   * @var \DateTime
   * @ORM\Column(name="private_date", type="datetimetz", nullable=true)
   * @Eccube\FormAppend(
   *  auto_render=true,
   *  type="Symfony\Component\Form\Extension\Core\Type\DateTimeType",
   *  options={
   *    "required":false,
   *    "label":"非公開予定日時",
   *    "input": "datetime",
   *    "placeholder": {"year":"----", "month": "--", "day": "--", "hour": "--", "minute": "--", "second": "--"},
   *    "with_seconds": true
   *  }
   * )
   */
  private $private_date;

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

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

    return $this;
  }

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

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

    return $this;
  }
}

Proxyクラスを生成

bin/console eccube:generate:proxies

キャッシュ削除

bin/console cache:clear --no-warmup

実行するSQLを確認

bin/console doctrine:schema:update --dump-sql

スキーマを更新

bin/console doctrine:schema:update --dump-sql --force

商品一覧ページ

Twigのdateフィルタで現時刻と販売期間を比較し、期間内であれば従来のカートボタンを、販売期間外の場合は販売期間の日時を表示しbuttonはdisabledにする。

app/template/default/Product/list.twig
<p class="price02-default">
    {% if date(Product.publish_date) < date() and date(Product.private_date) > date()%}
        {% if Product.hasProductClass %}
            {% if Product.getPrice02Min == Product.getPrice02Max %}
                {{ Product.getPrice02IncTaxMin|price }}
            {% else %}
                {{ Product.getPrice02IncTaxMin|price }}{{ Product.getPrice02IncTaxMax|price }}
            {% endif %}
        {% else %}
            {{ Product.getPrice02IncTaxMin|price }}
        {% endif %}
    {% endif %}
</p>

~~~

{% if Product.stock_find  and Product.publish_date < date() < Product.private_date %}
{% set form = forms[Product.id] %}
<form name="form{{ Product.id }}" id="productForm{{ Product.id }}" action="{{ url('product_add_cart', {id:Product.id}) }}" method="post">
    <div class="ec-productRole__actions">
        {% if form.classcategory_id1 is defined %}
            <div class="ec-select">
                {{ form_widget(form.classcategory_id1) }}
                {{ form_errors(form.classcategory_id1) }}
            </div>
            {% if form.classcategory_id2 is defined %}
                <div class="ec-select">
                    {{ form_widget(form.classcategory_id2) }}
                    {{ form_errors(form.classcategory_id2) }}
                </div>
            {% endif %}
        {% endif %}
        <div class="ec-numberInput"><span>{{ 'common.quantity'|trans }}</span>
            {{ form_widget(form.quantity, {'attr': {'class': 'quantity'}}) }}
            {{ form_errors(form.quantity) }}
        </div>
    </div>
    {{ form_rest(form) }}
</form>
<div class="ec-productRole__btn">
    <button type="submit" class="ec-blockBtn--action add-cart" data-cartid="{{ Product.id }}" form="productForm{{ Product.id }}">
        {{ 'front.product.add_cart'|trans }}
    </button>
</div>
{% elseif Product.publish_date > date() or Product.private_date < date() %}
<div class="ec-productRole__btn">
    <button type="button" class="ec-blockBtn--action" disabled="disabled">
        <p>
            {{ Product.publish_date|date('m/d h:m') }} ~ {{ Product.private_date|date('m/d h:m') }}
        </p>
    </button>
</div>
{% else %}
<div class="ec-productRole__btn">
    <button type="button" class="ec-blockBtn--action" disabled="disabled">
        {{ 'front.product.out_of_stock'|trans }}
    </button>
</div>
{% endif %}

商品詳細ページ

商品詳細ページのボタンは{{ form_rest(form) }}で生成されているため、formごとifで囲うことでFormTypeを編集することなく条件分岐させる。

app/template/default/Product/detail.twig
{% if Product.private_date > date() >Product.publish_date %}
{# 通常価格 #}
{% if Product.hasProductClass -%}
    <div class="ec-productRole__priceRegular">
        {% if Product.getPrice01Min is not null and Product.getPrice01IncTaxMin == Product.getPrice01IncTaxMax %}
            <span class="ec-productRole__priceRegularPrice">{{ 'front.product.normal_price'|trans }}<span class="price01-default">{{ Product.getPrice01IncTaxMin|price }}</span></span>
            <span class="ec-productRole__priceRegularTax">{{ 'common.tax_include'|trans }}</span>
        {% elseif Product.getPrice01Min is not null and Product.getPrice01Max is not null %}
            <span class="ec-productRole__priceRegularPrice">{{ 'front.product.normal_price'|trans }}<span class="price01-default">{{ Product.getPrice01IncTaxMin|price }}{{ Product.getPrice01IncTaxMax|price }}</span></span>
            <span class="ec-productRole__priceRegularTax">{{ 'common.tax_include'|trans }}</span>
        {% endif %}
    </div>
{% else %}
    {% if Product.getPrice01Max is not null %}
        <span class="ec-productRole__priceRegularPrice">{{ 'front.product.normal_price'|trans }}{{ Product.getPrice01IncTaxMin|price }}</span>
        <span class="ec-productRole__priceRegularTax">{{ 'common.tax_include'|trans }}</span>
    {% endif %}
{% endif %}
{# 販売価格 #}
<div class="ec-productRole__price">
    {% if Product.hasProductClass -%}
        {% if Product.getPrice02IncTaxMin == Product.getPrice02IncTaxMax %}
            <div class="ec-price">
                <span class="ec-price__price price02-default">{{ Product.getPrice02IncTaxMin|price }}</span>
                <span class="ec-price__tax">{{ 'common.tax_include'|trans }}</span>
            </div>
        {% else %}
            <div class="ec-price">
                <span class="ec-price__price price02-default">{{ Product.getPrice02IncTaxMin|price }}{{ Product.getPrice02IncTaxMax|price }}</span>
                <span class="ec-price__tax">{{ 'common.tax_include'|trans }}</span>
            </div>
        {% endif %}
    {% else %}
        <div class="ec-price">
            <span class="ec-price__price">{{ Product.getPrice02IncTaxMin|price }}</span>
            <span class="ec-price__tax">{{ 'common.tax_include'|trans }}</span>
        </div>
    {% endif %}
</div>
{% endif %}

~~~

{% if date( Product.publish_date ) < date() < date( Product.private_date ) %}
<form action="{{ url('product_add_cart', {id:Product.id}) }}" method="post" id="form1" name="form1">
    {% if Product.stock_find %}
        <div class="ec-productRole__actions">
            {% if form.classcategory_id1 is defined %}
                <div class="ec-select">
                    {{ form_widget(form.classcategory_id1) }}
                    {{ form_errors(form.classcategory_id1) }}
                </div>
                {% if form.classcategory_id2 is defined %}
                    <div class="ec-select">
                        {{ form_widget(form.classcategory_id2) }}
                        {{ form_errors(form.classcategory_id2) }}
                    </div>
                {% endif %}
            {% endif %}
            <div class="ec-numberInput"><span>{{ 'common.quantity'|trans }}</span>
                {{ form_widget(form.quantity) }}
                {{ form_errors(form.quantity) }}
            </div>
        </div>
        <div class="ec-productRole__btn">
            <button type="submit" class="ec-blockBtn--action add-cart">
                {{ 'front.product.add_cart'|trans }}
            </button>
        </div>
    {% else %}
        <div class="ec-productRole__btn">
            <button type="button" class="ec-blockBtn--action" disabled="disabled">
                {{ 'front.product.out_of_stock'|trans }}
            </button>
        </div>
    {% endif %}
        {{ form_rest(form) }}
</form>
{% else %}
        <div class="ec-productRole__btn">
        <button type="button" class="ec-blockBtn--action" disabled="disabled">
            {{ Product.publish_date|date('Y/m/d h:m') }} ~ {{ Product.private_date|date('Y/m/d h:m') }}
        </button>
    </div>
{% endif %}

あとがき

初心者ながら調べつつコミュニティの参加者さんにも助けられながらカスタマイズすることができました。
もし、もっとスマートな方法をご存知の方がいらっしゃいましたらぜひご教授いただけますと幸いです。

ブログ機能も開発したいところですが、コストが大変そうなので検討中。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?