ちょっとEC-CUBEを久しぶりに触ったので軽く解説。
ベースに会員ランクが仮会員、本会員、退会とあるのだが、これを仮会員(承認するまでログインできない)、本会員(通常に買い物できる)、優良会員(本会員価格の10%割引)、特別会員(本会員価格の30%割引)と退会に拡張する方法を解説する。
まず、準備は終わっていてEC-CUBE4のセットアップが終わっていると定義します。
まず、管理画面にログインしてマスターデータを編集する。
マスターデータはEC-CUBEのCONST(定数)になっているのでここを編集したままにすると影響がでるのでコードの編集を前提に行うこと。
mtb_customer_statusをこのように書き替える。保存したら\src\Eccube\Entity\Master\CustomerStatus.phpをいま変更したマスターデータと同じく書き換える。
※定数を書き換えるのはCustomizeディレクトリを使わずに直接ソースをコピーしてバックアップしてから編集したほうがいい。
/** * 仮会員. * * @deprecated */ const NONACTIVE = 1; /** * 本会員. * * @deprecated */ const ACTIVE = 2; /** * 優良会員. * * @deprecated */ const GOOACTIVE = 3; /** * 特別会員. * * @deprecated */ const BIGACTIVE = 4; /** * 仮会員. */ const PROVISIONAL = 1; /** * 本会員 */ const REGULAR = 2; /** * 優良会員 */ const GOOMEMBER = 3; /** * 特別会員 */ const BIGMEMBER = 4; /** * 退会 */ const WITHDRAWING = 5;
で、続いて\src\Eccube\Form\Type\Admin\SearchCustomerType.phpを書き換える。このモジュールは管理画面の会員管理で使われるモジュールだ。
->add('customer_status', CustomerStatusType::class, [
'label' => 'admin.customer.customer_status',
'required' => false,
'expanded' => true,
'multiple' => true,
'placeholder' => false,
'data' => $this->customerStatusRepository->findBy([
'id' => [
CustomerStatus::PROVISIONAL,
CustomerStatus::REGULAR,
CustomerStatus::GOOMEMBER,//こいつも検索されるように追加する
CustomerStatus::BIGMEMBER,//こいつも
],
]),
])
そしたら、顧客がフロントページでログインする際にログインできる会員ランクを追加しておく。
\src\Eccube\Security\Core\User\CustomerProvider.phpに以下を追加する

/*$Customer = $this->customerRepository->findOneBy([
'email' => $username,
'Status' => CustomerStatus::REGULAR,
]);*///こっちは元のコード
// 本会員がログインしてきたら本会員を$Customerに入れる。優良会員、特別会員も同様。それ以外はnull
if($this->customerRepository->findOneBy(['email' => $username,'Status' => CustomerStatus::REGULAR,])){
$Customer = $this->customerRepository->findOneBy(['email' => $username,'Status' => CustomerStatus::REGULAR,]);
// あとで比較するのに使うのでログインしている会員ランクをセッションに入れとく
$_SESSION['user_status'] = 2;
}elseif($this->customerRepository->findOneBy(['email' => $username,'Status' => CustomerStatus::GOOMEMBER,])){
$Customer = $this->customerRepository->findOneBy(['email' => $username,'Status' => CustomerStatus::GOOMEMBER,]);
// SESSION in the GOOMEMBER
$_SESSION['user_status'] = 3;
}elseif($this->customerRepository->findOneBy(['email' => $username,'Status' => CustomerStatus::BIGMEMBER,])){
$Customer = $this->customerRepository->findOneBy(['email' => $username,'Status' => CustomerStatus::BIGMEMBER,]);
// SESSION in the BIGMEMBER
$_SESSION['user_status'] = 4;
}
で、ここからはTWIGの修正。まずはヘッダーの修正。\app\template\default\Block\header.twigにコピーしてきて修正する。

<!-- ADD CUSTOMER STSTUS //-->
{% if app.user != null %}
<div class="ec-headerNaviRole__left" style="margin-left: 10px;">
ようこそ{{ app.user.status }}:{{ app.user.name01 }}様
{% if app.user.status == '優良会員' %}
<p style="color:red;font-size:12px;">{{'10%OFF'}}</p>
{% elseif app.user.status == "特別会員" %}
<p style="color:red;font-size:12px;font-weight:600;">{{'30%OFF'}}</p>
{% endif %}
</div>
{% endif %}
<!-- END ADD //-->
で、会員のマイページの会員情報のTWIG。\app\template\default\Mypage\change.twigにコピーしてきて修正する。

<!-- ADD CUSTOMER STATUS //-->
<dl>
<dt>
{{ form_label(form.name, '会員レベル', { 'label_attr': { 'class': 'ec-label' }}) }}
</dt>
<dd>
<div>
{{ app.user.status }}
</div>
</dd>
</dl>
\app\template\admin\Customer\index.twigをこちらもコピーして修正する。:会員一覧に会員ランクを追加します

こんな感じになるよう335行目と349行目に追加
<!-- ADD customer_rank //-->
<th class="border-top-0 pt-2 pb-3">{{ 'admin.common.customer_rank'|trans }}</th>
<!-- add end /-->
<th class="border-top-0 pt-2 pb-3">{{ 'admin.common.name'|trans }}<a href="#" class="js-listSort" data-sortkey="name"><i class="fa fa-arrow-up" aria-hidden="true"></i></a></th>
<th class="border-top-0 pt-2 pb-3">{{ 'admin.common.phone_number'|trans }}</th>
<th class="border-top-0 pt-2 pb-3">{{ 'admin.common.mail_address'|trans }}</th>
<th class="border-top-0 pt-2 pb-3"> </th>
<th class="border-top-0 pt-2 pb-3 pr-3"> </th>
</tr>
</thead>
<tbody>
{% for Customer in pagination %}
<tr id="ex-customer-{{ Customer.id }}">
<td class="align-middle pl-3">{{ Customer.id }}</td>
<!-- ADD customer_rank //-->
<td class="align-middle">{{ Customer.status }}</td>
<!-- ADD END //-->
最後に\app\template\admin\Customer\edit.twigをこちらもコピーして修正する。

<!-- ADD CUSTOMER STSTUS //-->
<div class="row mb-2">
<div class="col-3">
<span>{{ 'admin.common.customer_rank'|trans }}</span>
<span class="badge badge-primary ml-1">{{ 'admin.common.required'|trans }}</span>
</div>
<div class="col">
<div class="row">
<div class="col">
{{ form_widget(form.status) }}
</div>
</div>
</div>
</div>
<!-- END ADD //-->
これはadmin.common.customer_rankのラベル。\src\Eccube\Resource\locale\messages.ja.yaml

これに以下を追加しておく。
common.select__customer_rank: 会員ランク admin.common.customer_rank: 会員ランク admin.common.default_rank: 本会員 tooltip.product.sales_rank: 本会員を指定するとすべての会員が購入できます。優良会員を指定すると本会員は購入できません。特別会員を指定すると特別会員のみ購入できます。 tooltip.product.sales_rankfront: | 本会員商品は全ての会員様がご購入できます。 優良会員商品は本会員様はご購入できません。 特j別会員商品は特別会員様のみがご購入できます。 admin.product.sales_rank: 販売会員
dtb_productにsales_rankカラムを追加する
リレーションビューでmtb_customer_statusを割り当てる
D:\xampp\htdocs\ec-cube\app\Customize\Entity\ProductTrait.phpを作成する
<?php
namespace Customize\Entity;
use Doctrine\ORM\Mapping as ORM;
use Eccube\Annotation as Eccube;
use Eccube\Annotation\EntityExtension;
use Eccube\Entity\Product;
/**
* @EntityExtension("Eccube\Entity\Product")
*/
trait ProductTrait{
/**
* @var \Eccube\Entity\Master\CustomerStatus
* @ORM\ManyToOne(targetEntity="Eccube\Entity\Master\CustomerStatus")
* @ORM\JoinColumns({@ORM\JoinColumn(name="sales_rank",referencedColumnName="id")})
*/
private $sales_rank;
/**
* Get sales_rank
* @return \Eccube\Entity\Master\CustomerStatus|null
*/
public function getSalesRank(){
return $this->sales_rank;
}
/**
* Set sales_rank
* @param \Eccube\Entity\Master\CustomerStatus|null $sales_rank
* @return ProductTrait
*/
public function setSalesRank(\Eccube\Entity\Master\CustomerStatus $sales_rank = null){
$this->sales_rank = $sales_rank;
return $this;
}
}
で、ProductTraitを使えるようにする。
cd c:\xampp\htdoc\ec-cube⏎ php bin\console eccube:generate:proxies⏎ php bin\console chash:clear --no-warmup⏎
これでD:\xampp\htdocs\ec-cube\app\proxy\entity\src\Eccube\Entity\Product.phpができていればOK
続いてD:\xampp\htdocs\ec-cube\app\Customize\Form\Extension\ProductTypeExtension.phpを作成します。
<?php
namespace Customize\Form\Extension;
use Eccube\Form\Type\Master\CustomerStatusType;
use Eccube\Form\Type\Admin\ProductType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Validator\Constraints as Assert;
class ProductTypeExtension extends AbstractTypeExtension{
/**
* {@inheritdoc}
*/
public function getExtendedType(){
return ProductType::class;
}
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options){
$builder ->add('sales_rank',CustomerStatusType::class,[
'multiple' => false,
'expanded' => false,
'constraints' => [ new Assert\NotBlank(),],
]);
}
/**
* {@inheritdoc}
*/
public static function getExtendedTypes(): iterable{
yield ProductType::class;
}
}
これは先ほど作成したTraitをシステムに追加するものです。
まず、D:\xampp\htdocs\ec-cube\app\template\admin\Product\index.twigを修正します。
<!-- ADD sales_rank //-->
<th class="border-top-0 pt-2 pb-2">{{ 'admin.product.sales_rank'|trans }}</th>
<!-- ADD END //-->
392行目あたりに追加
td class="align-middle">
{{ Product.sales_rank }} 用商品
</td>
<!-- ADD END //-->

そしたらTWIGを修正します。\app\template\admin\Product\product.twig
<!-- ADD sales_rank //-->
<div class="row">
<div class="col-3">
<div class="d-inli>ne-block" data-tooltip="true" data-placement="top" title="{{ 'tooltip.product.sales_rank'|trans }}">
<span>{{ 'admin.product.sales_rank'|trans }}</span>
<i class="fa fa-question-circle fa-lg ml-1"></i>
<span class="badge badge-primary ml-1">
{{ 'admin.common.required'|trans }}
</span>
</div>
</div>
<div class="col mb-2">
{{ form_widget(form.sales_rank) }}
</div>
</div>
↑を325行付近に追加
最後に肝心の割引率を設定。D:\xampp\htdocs\ec-cube\src\Eccube\Entity\ProductClass.phpを編集します。489行目を以下のように書き換えます。
public function getPrice02()
{
if(isset($_SESSION['user_status'])){//ログインしてたら
switch($_SESSION['user_status']){
case 3: $salesp = 0.9; break;
case 4: $salesp = 0.7; break;
default: $salesp = 1.0;
}
}else{
$salesp = 1.0;//未ログインなら等価
}
return $this->price02 * $salesp;
}
D:\xampp\htdocs\ec-cube\src\Eccube\Resource\template\default\Product\list.twigを編集します。153行目以降に以下を追加
<!-- ADD sales_rank //-->
{% set break = false %}
{% set pro_id = 1 %}
{% set cus_id = 1 %}
{% if app.user != null %}
{% if Product.sales_rank == "優良会員" %}
{% set pro_id = 2 %}
{% elseif Product.sales_rank == "特別会員" %}
{% set pro_id = 3 %}
{% endif %}
{% if app.user.status == "優良会員" %}
{% set cus_id = 2 %}
{% elseif app.user.status == "特別会員" %}
{% set cus_id = 3 %}
{% endif %}
{% if pro_id <= cus_id %}
{% set break = true %}
{% endif %}
{% else %}
{% set cus_id = 0 %}
{% endif %}
<!-- ADD END //-->
183行目付近に以下を追加
<!-- ADD sales_rank //-->
{{ Product.sales_rank }}用商品
<!-- ADD END //-->
195行目付近を書き換え
{% if Product.hasProductClass %}
{% if Product.getPrice02Min == Product.getPrice02Max %}
<!-- ADD sales_rank //-->
{{ Product.getPrice01IncTaxMin|price }}
{% if cus_id >= pro_id %}
<div style="font-size: 20px; color: red; font-weight: bold;">{{ Product.getPrice02IncTaxMin|price }}</div>
{% endif %}
<!-- ADD END //-->
{#{ Product.getPrice02IncTaxMin|price }#}
{% else %}
<!-- ADD sales_rank //-->
{{ Product.getPrice01IncTaxMin|price }} ~ {{ Product.getPrice01IncTaxMax|price }}
{% if cus_id >= pro_id %}
<div style="font-size: 20px; color: red; font-weight: bold;">{{ Product.getPrice02IncTaxMin|price }} ~ {{ Product.getPrice02IncTaxMax|price }}</div>
{% endif %}
<!-- ADD END //-->
{#{ Product.getPrice02IncTaxMin|price }} ~ {{ Product.getPrice02IncTaxMax|price }#}
{% endif %}
{% else %}
<!-- ADD sales_rank //-->
{{ Product.getPrice01IncTaxMin|price }}
{% if cus_id >= pro_id %}
<div style="font-size: 20px; color: red; font-weight: bold;">{{ Product.getPrice02IncTaxMin|price }}</div>
{% endif %}
<!-- ADD END //-->
{#{ Product.getPrice02IncTaxMin|price }#}
{% endif %}
228行目付近にif分岐を追加
<!-- ADD sales_rank //-->
{% if break %}
<!-- ADD END //-->
<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>{{ '数量'|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 }}">
{{ 'カートに入れる'|trans }}
</button>
</div>
<!-- ADD sales_rank //-->
{% endif %}
<!-- ADD END //-->
D:\xampp\htdocs\ec-cube\src\Eccube\Resource\template\default\Product\detail.twigを修正。245行目以降に追加
<!-- ADD sales_rank //-->
{% set break = false %}
{% set pro_id = 1 %}
{% set cus_id = 1 %}
{% if app.user != null %}
{% if Product.sales_rank == "優良会員" %}
{% set pro_id = 2 %}
{% elseif Product.sales_rank == "特別会員" %}
{% set pro_id = 3 %}
{% endif %}
{% if app.user.status == "優良会員" %}
{% set cus_id = 2 %}
{% elseif app.user.status == "特別会員" %}
{% set cus_id = 3 %}
{% endif %}
{% if pro_id <= cus_id %}
{% set break = true %}
{% endif %}
{% else %}
{% set cus_id = 0 %}
{% endif %}
<!-- ADD END //-->
321行目を修正
<!-- ADD sales_rank //-->
{% if cus_id >= pro_id %}
<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">{{ '税込'|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">{{ '税込'|trans }}</span>
</div>
{% endif %}
{% else %}
<div class="ec-price">
<span class="ec-price__price">{{ Product.getPrice02IncTaxMin|price }}</span>
<span class="ec-price__tax">{{ '税込'|trans }}</span>
</div>
{% endif %}
</div>
{% endif %}
<!-- ADD END //-->
370行目に追加
<!-- ADD sales_rank //-->
<div class="col-3">
<div class="d-inline-block" data-tooltip="true" data-pricement="top" title="{{'tooltip.product.sales_rankfront'|trans}}">
{{ Product.sales_rank }} 用商品
<span style="color:white;text-align:senter;" class="circle">?</span>
</div>
</div>
<!-- ADD END //-->
その下にif分岐を追加
{% if break %}
<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>{{ '数量'|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">
{{ 'カートに入れる'|trans }}
</button>
</div>
{% else %}
<div class="ec-productRole__btn">
<button type="button" class="ec-blockBtn--action" disabled="disabled">
{{ 'ただいま品切れ中です。'|trans }}
</button>
</div>
{% endif %}
{{ form_rest(form) }}
</form>
{% endif %}
最後に判定を追加D:\xampp\htdocs\ec-cube\src\Eccube\Controller\AbstractShoppingController.phpに判定をいれます:これにより優良会員、特別会員による価格の割引は警告ではなく、それ以外の警告は発信され、購入できないようになります。
if ($flowResult->hasWarning()) {
if( (!isset($_SESSION['user_status']) || $_SESSION['user_status'] == 2) && strpos(serialize($flowResult->getWarning()), '販売価格が変更されました')){
log_info('Warningが発生したため注文手続き画面へ遷移します.', [$flowResult->getWarning()]);
return $this->redirectToRoute('shopping');
}elseif(strpos(serialize($flowResult->getWarning()), '販売価格が変更されました')){
log_info('Warningが発生しましたが会員ランクによる売価の変更です.', [$flowResult->getWarning()]);
}else{
log_info('Warningが発生したため注文手続き画面へ遷移します.', [$flowResult->getWarning()]);
return $this->redirectToRoute('shopping');
}
}













