販売期間を限定するカスタム
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
にしておく。
<?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にする。
<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を編集することなく条件分岐させる。
{% 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 %}
あとがき
初心者ながら調べつつコミュニティの参加者さんにも助けられながらカスタマイズすることができました。
もし、もっとスマートな方法をご存知の方がいらっしゃいましたらぜひご教授いただけますと幸いです。
ブログ機能も開発したいところですが、コストが大変そうなので検討中。