Quantcast
Channel: EC-CUBE アーカイブ - あずみ.net
Viewing all 271 articles
Browse latest View live

【EC-CUBE4】ゲスト購入後に会員登録できるフォームを用意する

$
0
0

EC-CUBE4でゲスト購入したユーザーに会員登録を促すフォームを用意する方法です。

今回のコードはサンプルです。本番で使用する際は自己責任でお願いします。

ゲスト購入後に会員登録するFormTypeを用意

ゲスト購入完了ページにはパスワードを除く必要な会員情報が受注データに保持されているので、今回はパスワードを登録してもらうフォームを用意します。

下記のコードをapp/Customize/Form/Type/NonMemberRegisterType.phpに設置して下さい。

<?php

/*
 * Copyright (C) 2019 Akira Kurozumi <info@a-zumi.net>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

namespace Customize\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Eccube\Form\Type\RepeatedPasswordType;
use Eccube\Entity\Order;

/**
 * @author Akira Kurozumi <info@a-zumi.net>
 */
class NonMemberRegisterType extends AbstractType{

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $Order = $options["Order"];
        
        $builder
                ->add('password', RepeatedPasswordType::class)
                ->add('order_id', HiddenType::class, [
                   "data" => $Order->getId() 
                ])
                ->add('button', SubmitType::class, [
                    "label" => "会員登録する"
                ])
        ;
    }
    
    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        // Orderエンティティをオプションに追加するのを必須にする
        $resolver
                ->setRequired("Order")
                ->setAllowedTypes("Order", Order::class);
    }
}

会員登録テンプレートを用意

購入完了画面にパスワード入力フォームを追加するテンプレートを用意します。

下記のテンプレートをapp/template/default/Shopping/complte.register.twigに設置して下さい。

{#
This file is part of EC-CUBE

Copyright(c) LOCKON CO.,LTD. All Rights Reserved.

http://www.lockon.co.jp/

For the full copyright and license information, please view the LICENSE
file that was distributed with this source code.
#}
{% extends 'default_frame.twig' %}

{% set body_class = 'cart_page' %}

{% block main %}
<div class="ec-role">
    <div class="ec-pageHeader">
        <h1>{{ 'front.shopping.complete_title'|trans }}</h1>
    </div>
</div>

<div class="ec-cartRole">
    <div class="ec-cartRole__progress">
        <ul class="ec-progress">
            {% set step = 1 %}
            <li class="ec-progress__item">
                <div class="ec-progress__number">{{ step }}{% set step = step + 1 %}
                </div>
                <div class="ec-progress__label">{{ 'front.cart.nav__cart_items'|trans }}
                </div>
            </li>
            {% if is_granted('ROLE_USER') == false %}
                <li class="ec-progress__item">
                    <div class="ec-progress__number">{{ step }}{% set step = step + 1 %}
                    </div>
                    <div class="ec-progress__label">{{ 'front.cart.nav__customer_info'|trans }}
                    </div>
                </li>
            {% endif %}
            <li class="ec-progress__item">
                <div class="ec-progress__number">{{ step }}{% set step = step + 1 %}
                </div>
                <div class="ec-progress__label">{{ 'front.cart.nav__order'|trans }}
                </div>
            </li>
            <li class="ec-progress__item">
                <div class="ec-progress__number">{{ step }}{% set step = step + 1 %}
                </div>
                <div class="ec-progress__label">{{ 'front.cart.nav__confirm'|trans }}
                </div>
            </li>
            <li class="ec-progress__item  is-complete">
                <div class="ec-progress__number">{{ step }}{% set step = step + 1 %}
                </div>
                <div class="ec-progress__label">{{ 'front.cart.nav__complete'|trans }}
                </div>
            </li>
        </ul>
    </div>
</div>

<div class="ec-cartCompleteRole">
    <div class="ec-off3Grid ec-text-ac">
        <div class="ec-off3Grid__cell">
            <div class="ec-reportHeading">
                <h2>{{ 'front.shopping.complete_message__title'|trans }}</h2>
            </div>
            <p class="ec-reportDescription">
                {{ 'front.shopping.complete_message__body'|trans|nl2br }}
                {% if Order.id %}
                    <br /><br /><strong>{{ 'front.shopping.order_no'|trans }} : {{ Order.orderNo }}</strong>
                {% endif %}
            </p>

            {% if Order.complete_message is not empty %}
                {{ Order.complete_message|raw }}
            {% endif %}

            <div class="ec-off4Grid">
                {% if hasNextCart %}
                    <div class="ec-off4Grid__cell"><a class="ec-blockBtn--primary" href="{{ url('cart') }}">{{ 'front.shopping.continue'|trans }}</a></div>
                {% else %}
                    <div class="ec-off4Grid__cell"><a class="ec-blockBtn--cancel" href="{{ url('homepage') }}">{{ 'common.go_to_top'|trans }}</a></div>
                {% endif %}
            </div>
        </div>
    </div>
</div>
      
<div class="ec-cartCompleteRole">
    <div class="ec-off3Grid ec-text-ac">
        <div class="ec-off3Grid__cell">
            <div class="ec-reportHeading">
                <h2>会員登録をしませんか?</h2>
            </div>
            <p class="ec-reportDescription">
                只今ご入力いただいた情報をもとに会員登録ができます。<br />
            <br />
            会員登録をすれば、次回以降のお買い物時にフォーム入力を省略できます。<br />
            <br />
            会員登録をご希望の場合は、以下フォームにご希望のパスワードを入力し、<br />
            「会員登録する」ボタンを押してください。<br />
            <br />
            その後ご注文時にご入力いただきましたメールアドレスに<br />
            仮会員登録のメールが送信されますので、<br />
            届いたメールに従って本登録をお願いいたします。</p>
            {{ form_start(form) }}
            <dl>
                <dl>
                    <dt>
                        {{ form_label(form.password, 'common.password', { 'label_attr': {'class': 'ec-label' }}) }}
                    </dt>
                    <dd>
                        <div class="ec-input{{ has_errors(form.password.first) ? ' error' }}">
                            {{ form_widget(form.password.first, {
                                'attr': { 'placeholder': 'common.password_sample'|trans({ '%min%': eccube_config.eccube_password_min_len, '%max%': eccube_config.eccube_password_max_len }) },
                                'type': 'password'
                            }) }}
                            {{ form_errors(form.password.first) }}
                        </div>
                        <div class="ec-input{{ has_errors(form.password.second) ? ' error' }}">
                            {{ form_widget(form.password.second, {
                                'attr': { 'placeholder': 'common.repeated_confirm'|trans },
                                'type': 'password'
                            }) }}
                            {{ form_errors(form.password.second) }}
                        </div>
                    </dd>
                </dl>
            </dl>
            {{ form_end(form) }}
        </div>
    </div>
</div>

{% endblock %}

ゲスト購入完了画面で会員登録できるイベントを用意

最後にゲスト購入完了画面で会員登録できるイベントを用意します。

下記コードをapp/Customize/EventSubscriber/NonMemberRegisterSubscriber.phpに設置して下さい。

<?php

/*
 * Copyright (C) 2019 Akira Kurozumi <info@a-zumi.net>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

namespace Customize\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Eccube\Event\TemplateEvent;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Customize\Form\Type\NonMemberRegisterType;
use Eccube\Repository\CustomerRepository;
use Eccube\Service\CartService;
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
use Eccube\Service\MailService;
use Eccube\Repository\BaseInfoRepository;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use Doctrine\ORM\EntityManagerInterface;
use Eccube\Service\OrderHelper;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;

/**
 * @author Akira Kurozumi <info@a-zumi.net>
 */
class NonMemberRegisterSubscriber implements EventSubscriberInterface
{
    use ControllerTrait;
    
    /**
     * @var ContainerInterface
     */
    private $container;
    
    /**
     * @var CustomerRepository 
     */
    private $customerRepository;
    
    /**
     * @var CartService
     */
    private $cartService;
    
    /**
     * @var MailService
     */
    private $mailService;

    /**
     * @var BaseInfo
     */
    private $BaseInfo;
    
    /**
     * @var EncoderFactoryInterface
     */
    private $encoderFactory;
    
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;
    
    /**
     * @var OrderHelper
     */
    private $orderHelper;
    
    public function __construct(
            ContainerInterface $container, 
            CustomerRepository $customerRepository, 
            CartService $cartService,
            MailService $mailService,
            BaseInfoRepository $baseInfoRepository,
            EncoderFactoryInterface $encoderFactory,
            EntityManagerInterface $entityManager,
            OrderHelper $orderHelper
    ) {
        $this->container = $container;
        $this->customerRepository = $customerRepository;
        $this->cartService = $cartService;
        $this->mailService = $mailService;
        $this->BaseInfo = $baseInfoRepository->get();
        $this->encoderFactory = $encoderFactory;
        $this->entityManager = $entityManager;
        $this->orderHelper = $orderHelper;
    }
    
    public function onFrontShoppingCompleteInitialize(EventArgs $event)
    {
        $Order = $event->getArgument("Order");
        
        $Customer = $this->customerRepository->findOneBy(["email" => $Order->getEmail()]);
        
        // メールアドレスが登録されていたら停止
        if($Customer) {
            return;
        }
        
        $request = $event->getRequest();
        
        $builder = $this->container->get('form.factory')->createBuilder(NonMemberRegisterType::class, [], ["Order" => $Order]);
        $form = $builder->getForm();
        
        $form->handleRequest($request);
        
        if ($form->isSubmitted() && $form->isValid()) {
            log_info('会員登録確認開始');
            
            $password = $form->get("password")->getData();
            
            $Customer = $this->customerRepository->newCustomer();
            
            $Customer
                ->setName01($Order->getName01())
                ->setName02($Order->getName02())
                ->setKana01($Order->getKana01())
                ->setKana02($Order->getKana02())
                ->setCompanyName($Order->getCompanyName())
                ->setEmail($Order->getEmail())
                ->setPhonenumber($Order->getPhoneNumber())
                ->setPostalcode($Order->getPostalCode())
                ->setPref($Order->getPref())
                ->setAddr01($Order->getAddr01())
                ->setAddr02($Order->getAddr02())
                ->setPassword($password);
            
            // パスワードを暗号化
            $encoder = $this->encoderFactory->getEncoder($Customer);
            $salt = $encoder->createSalt();
            $password = $encoder->encodePassword($Customer->getPassword(), $salt);
            $secretKey = $this->customerRepository->getUniqueSecretKey();

            // 暗号化したパスワードをセット
            $Customer
                ->setSalt($salt)
                ->setPassword($password)
                ->setSecretKey($secretKey)
                ->setPoint(0);
            
            $this->entityManager->persist($Customer);
            $this->entityManager->flush();

            log_info('会員登録完了');
            
            log_info('[注文完了] 購入フローのセッションをクリアします. ');
            // 会員登録が完了したらセッション削除
            $this->orderHelper->removeSession();
            
            $activateUrl = $this->generateUrl('entry_activate', ['secret_key' => $Customer->getSecretKey()], UrlGeneratorInterface::ABSOLUTE_URL);

            $activateFlg = $this->BaseInfo->isOptionCustomerActivate();

            // 仮会員設定が有効な場合は、確認メールを送信し完了画面表示.
            if ($activateFlg) {
                // メール送信
                $this->mailService->sendCustomerConfirmMail($Customer, $activateUrl);

                log_info('仮会員登録完了画面へリダイレクト');
                
                $event->setResponse($this->redirectToRoute('entry_complete'));
                                
            // 仮会員設定が無効な場合は認証URLへ遷移させ、会員登録を完了させる.
            } else {
                log_info('本会員登録画面へリダイレクト');
                
                $event->setResponse($this->redirect($activateUrl));
            }
        }else{
            $hasNextCart = !empty($this->cartService->getCarts());

            log_info('[注文完了] 注文完了画面を表示しました. ', [$hasNextCart]);

            $event->setResponse($this->render("Shopping/complete.register.twig", [
                'Order' => $Order,
                'hasNextCart' => $hasNextCart,
                'form' => $form->createView()
            ]));            
        }
    }
    
    public static function getSubscribedEvents()
    {
        return [
            EccubeEvents::FRONT_SHOPPING_COMPLETE_INITIALIZE => 'onFrontShoppingCompleteInitialize',
        ];
    }
}

以上で完成です。

これでゲスト購入完了画面に会員登録を促すパスワード入力フォームが出力されます。

デザイン等は考慮していませんのでご了承下さい。

ちなみに今回のコードは会員登録後に購入フローのセッションを削除するようにしています。

なので会員登録しなかった場合は購入フローのセッションが残っているので想定しない問題が起こるかもしれません。

 

パスワード送信後は別ページで会員登録を行うといった処理にして、必ず購入フローのセッションを削除するようにしたほうが良いのかなあと思いますが、今回はサンプルとして公開していますのでご利用は自己責任でお願い致します。

投稿 【EC-CUBE4】ゲスト購入後に会員登録できるフォームを用意するあずみ.net に最初に表示されました。


【EC-CUBE4】商品一覧ページのテンプレートを差し替えるプラグインを作る方法

$
0
0

EC-CUBE4の商品一覧のテンプレートをプラグインで用意したテンプレートに差し替える方法です。

まずは以下のコマンドでプラグインの雛形を作ります。

プラグインコードはなんでも良いのですが、今回は「ChangeTemplate」にしてください。

bin/console eccube:plugin:generate

テンプレートを用意

自動生成されたプラグイン一式内のResource/templateディレクトリ内にdefault/Product/list.twigファイルを設置してください。

商品一覧ページ

上記で作成したTwigファイルに差し替えるイベントを用意

Event.phpに以下のコードを追記してください。

<?php

namespace Plugin\ChangeTemplate;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Eccube\Event\TemplateEvent;

class Event implements EventSubscriberInterface
{
    public function __construct(\Twig_Environment $twig)
    {
        $this->twig = $twig;
    }
    
    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            'Product/list.twig' => ['onTemplateRroductList', 999999]
        ];
    }
    
    public function onTemplateRroductList(TemplateEvent $event)
    {
        $source = $this->twig->getLoader()
            ->getSourceContext("@ChangeTemplate/default/Product/list.twig")
            ->getCode();
                
        $event->setSource($source);
    }
}

以上で完成です。

プラグインのインストールと有効化

プラグインのインストールと有効化を行うと適用されます。

投稿 【EC-CUBE4】商品一覧ページのテンプレートを差し替えるプラグインを作る方法あずみ.net に最初に表示されました。

【EC-CUBE4】お問い合わせ内容をデータベースに保存する方法

$
0
0

EC-CUBE4でお問い合わせ内容をデータベースに保存する方法です。

※下記ソースコードはPHP7.1以上を対象としています。

Contactエンティティを用意

まずお問い合わせ内容を保存するため、Cusotomize/Enitiyディレクトリ内にContact.phpというファイル名で下記のソースコードを設置してください。

<?php

namespace Customize\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass="Customize\Repository\ContactRepository")
 */
class Contact extends \Eccube\Entity\AbstractEntity
{
    /**
     * @ORM\Id()
     * @ORM\GeneratedValue()
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name01;
    
    /**
     * @ORM\Column(type="string", length=255)
     */
    private $name02;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $kana01;
    
    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $kana02;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $postal_code;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $pref;
    
    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $addr01;
    
    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $addr02;

    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $phone_number;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $email;

    /**
     * @ORM\Column(type="text")
     */
    private $contents;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getName01(): ?string
    {
        return $this->name01;
    }

    public function setName01(string $name01): self
    {
        $this->name01 = $name01;

        return $this;
    }
    
    public function getName02(): ?string
    {
        return $this->name02;
    }

    public function setName02(string $name02): self
    {
        $this->name02 = $name02;

        return $this;
    }

    public function getKana01(): ?string
    {
        return $this->kana01;
    }

    public function setKana01(?string $kana01): self
    {
        $this->kana01 = $kana01;

        return $this;
    }
    
    public function getKana02(): ?string
    {
        return $this->kana02;
    }

    public function setKana02(?string $kana02): self
    {
        $this->kana02 = $kana02;

        return $this;
    }

    public function getPostalCode(): ?string
    {
        return $this->postal_code;
    }

    public function setPostalCode(?string $postal_code): self
    {
        $this->postal_code = $postal_code;

        return $this;
    }

    public function getPref(): ?string
    {
        return $this->pref;
    }

    public function setPref(?string $pref): self
    {
        $this->pref = $pref;

        return $this;
    }
    
    public function getAddr01(): ?string
    {
        return $this->addr01;
    }

    public function setAddr01(?string $addr01): self
    {
        $this->addr01 = $addr01;

        return $this;
    }
    
    public function getAddr02(): ?string
    {
        return $this->addr02;
    }

    public function setAddr02(?string $addr02): self
    {
        $this->addr02 = $addr02;

        return $this;
    }

    public function getPhoneNumber(): ?string
    {
        return $this->phone_number;
    }

    public function setPhoneNumber(?string $phone_number): self
    {
        $this->phone_number = $phone_number;

        return $this;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): self
    {
        $this->email = $email;

        return $this;
    }

    public function getContents(): ?string
    {
        return $this->contents;
    }

    public function setContents(string $contents): self
    {
        $this->contents = $contents;

        return $this;
    }
}

データベースにお問い合わせ内容を保存するテーブルを作成

下記のコマンドを実行すると、上記で作成したエンティティを元にお問い合わせ内容を保存するテーブルが自動で生成されます。

bin/console eccube:schema:update --force

お問い合わせ完了時にお問い合わせ内容を保存するイベント処理を用意

お問い合わせ送信後にお問い合わせ内容を保存する処理を追加するため、CusotomizeディレクトリにEvent.phpというファイル名で下記のソースコードを設置してください。

<?php

namespace Customize;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Eccube\Event\EccubeEvents;
use Eccube\Event\EventArgs;
use Customize\Entity\Contact;
use Doctrine\ORM\EntityManagerInterface;

class Event implements EventSubscriberInterface
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;
    
    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }
    
    /**
     * @return array
     */
    public static function getSubscribedEvents()
    {
        return [
            EccubeEvents::FRONT_CONTACT_INDEX_COMPLETE => 'onFrontContactIndexComplete'
        ];
    }
    
    public function onFrontContactIndexComplete(EventArgs $event)
    {
        $data = $event->getArgument("data");
        
        $Contact = new Contact();
        $Contact->setName01($data["name01"]);
        $Contact->setName02($data["name02"]);
        $Contact->setKana01($data["kana01"]);
        $Contact->setKana02($data["kana02"]);
        $Contact->setPostalCode($data["postal_code"]);
        $Contact->setPref($data["pref"]);
        $Contact->setAddr01($data["addr01"]);
        $Contact->setAddr02($data["addr02"]);
        $Contact->setPhoneNumber($data["phone_number"]);
        $Contact->setEmail($data["email"]);
        $Contact->setContents($data["contents"]);
        
        $this->entityManager->persist($Contact);
        $this->entityManager->flush();
    }
}

以上で完成です。

投稿 【EC-CUBE4】お問い合わせ内容をデータベースに保存する方法あずみ.net に最初に表示されました。

【EC-CUBE4】商品一覧ページのキャッシュ時間を変更する方法

$
0
0

EC-CUBE4の商品一覧ページではDoctrineで取得した商品データを10秒間キャッシュしています。そのキャッシュ時間を変更する方法です。

設定は以下のファイルで行います。

app/config/eccube/packages/eccube.yaml

上記ファイルの以下の部分の値を変更すると商品一覧ページなどのキャッシュ時間が変更されます。

eccube_result_cache_lifetime_short: 10  # doctrineのresult cacheのlifetime. 商品一覧画面など長期間キャッシュできない箇所で使用する.

 

投稿 【EC-CUBE4】商品一覧ページのキャッシュ時間を変更する方法あずみ.net に最初に表示されました。

EC-CUBE4で特定の都道府県のみ送料無料条件の適用を外す方法

$
0
0

EC-CUBE4で特定の都道府県のみ送料無料条件の適用を外す方法です。

以下のDeliveryFeeFreeByShippingPreprocessor.phpをCustomize/Service/PurchaseFlow/Processorディレクトリに設定して下さい。

<?php

/*
 * Copyright (C) 2019 Akira Kurozumi <info@a-zumi.net>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

namespace Customize\Service\PurchaseFlow\Processor;

use Eccube\Annotation\ShoppingFlow;
use Eccube\Entity\BaseInfo;
use Eccube\Entity\ItemHolderInterface;
use Eccube\Entity\Order;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Repository\Master\PrefRepository;
use Eccube\Service\PurchaseFlow\ItemHolderPreprocessor;
use Eccube\Service\PurchaseFlow\Processor\DeliveryFeePreprocessor;
use Eccube\Service\PurchaseFlow\PurchaseContext;

/**
 * 送料無料条件を適用する.
 * お届け先ごとに条件判定を行う.
 *
 * @ShoppingFlow()
 */
class DeliveryFeeFreeByShippingPreprocessor implements ItemHolderPreprocessor
{
    /**
     * @var BaseInfo
     */
    protected $BaseInfo;

    /**
     * @var PrefRepository
     */
    private $prefRepository;

    public function __construct(
        BaseInfoRepository $baseInfoRepository,
        PrefRepository $prefRepository
    ) {
        $this->BaseInfo = $baseInfoRepository->get();
        $this->prefRepository = $prefRepository;
    }

    /**
     * @param ItemHolderInterface $itemHolder
     * @param PurchaseContext $context
     */
    public function process(ItemHolderInterface $itemHolder, PurchaseContext $context)
    {
        if (!($this->BaseInfo->getDeliveryFreeAmount() || $this->BaseInfo->getDeliveryFreeQuantity())) {
            return;
        }

        // Orderの場合はお届け先ごとに判定する.
        if ($itemHolder instanceof Order) {
            // 送料無料条件適用を除外する都道府県を指定
            $noFreePref = '沖縄県';

            /** @var Order $Order */
            $Order = $itemHolder;
            foreach ($Order->getShippings() as $Shipping) {
                $isFree = false;
                $total = 0;
                $quantity = 0;
                foreach ($Shipping->getProductOrderItems() as $Item) {
                    $total += $Item->getPriceIncTax() * $Item->getQuantity();
                    $quantity += $Item->getQuantity();
                }
                // 送料無料(金額)を超えている
                if ($this->BaseInfo->getDeliveryFreeAmount()) {
                    if ($total >= $this->BaseInfo->getDeliveryFreeAmount()) {
                        $isFree = true;
                    }
                }
                // 送料無料(個数)を超えている
                if ($this->BaseInfo->getDeliveryFreeQuantity()) {
                    if ($quantity >= $this->BaseInfo->getDeliveryFreeQuantity()) {
                        $isFree = true;
                    }
                }
                if ($isFree) {
                    foreach ($Shipping->getOrderItems() as $Item) {
                        if ($Item->getProcessorName() == DeliveryFeePreprocessor::class) {
                            // 送料無料条件適用を除外する都道府県とマッチしたら送料明細の数量を1とする
                            if ($Shipping->getPref() == $this->prefRepository->findOneBy(['name' => $noFreePref])) {
                                $Item->setQuantity(1);
                                // 都道府県別送料設定で設定した送料と別の送料にしたい場合はこちらを追加
                                // $Item->getPrice(500);
                            }
                        }
                    }
                }
            }
        }
    }
}

 

投稿 EC-CUBE4で特定の都道府県のみ送料無料条件の適用を外す方法あずみ.net に最初に表示されました。

【EC-CUBE4】メーカーで絞り込みをする方法

$
0
0

EC-CUBE4にメーカープラグインを入れて、商品検索機能でメーカーで絞り込みをする方法です。

メーカーIDで絞り込めるようにSearchProductTypeを拡張

メーカーIDを商品絞り込み対象にするためSearchProductTypeを以下のように拡張します。

<?php

/*
 * Copyright (C) 2019 Akira Kurozumi <info@a-zumi.net>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

namespace Customize\Form\Extension;

use Eccube\Form\Type\SearchProductType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;

class SearchProductTypeExtension extends AbstractTypeExtension
{
    /**
     * Returns the name of the type being extended.
     *
     * @return string The name of the type being extended
     */
    public function getExtendedType()
    {
        return SearchProductType::class;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('maker_id', HiddenType::class, []);
    }
}

WhereCustomizerを使って商品検索用QueryBuilderを拡張

以下のようにWhereCustomizerを使って商品検索用QueryBuilderを拡張します。

<?php

/*
 * Copyright (C) 2019 Akira Kurozumi <info@a-zumi.net>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

namespace Customize\Repository;

use Eccube\Doctrine\Query\WhereClause;
use Eccube\Doctrine\Query\WhereCustomizer;
use Eccube\Repository\QueryKey;

class MakerWhereCustomizer extends WhereCustomizer
{
    /**
     * カスタマイズ対象のキーを返します。
     *
     * @return string
     */
    public function getQueryKey()
    {
        return QueryKey::PRODUCT_SEARCH;
    }

    /**
     * @param array $params
     * @param $queryKey
     *
     * @return WhereClause[]
     */
    protected function createStatements($params, $queryKey)
    {
        if($params['maker_id']) {
            return [WhereClause::eq("p.Maker", ':Maker', $params['maker_id'])];
        }

        return [];
    }
}

 

投稿 【EC-CUBE4】メーカーで絞り込みをする方法あずみ.net に最初に表示されました。

【EC-CUBE4】カートへ入れるボタンの文言を管理画面のCSS管理で変更する方法

$
0
0

【EC-CUBE4】カートへ入れるボタンの文言をCSSで変更する方法です。

管理画面のCSS管理に以下のコードを追加して下さい。

/* カスタマイズ用CSS */
button.ec-blockBtn--action {
    font-size:0;
}
button.ec-blockBtn--action:before {
    font-size:14px;
    content: "カートへどうぞ!";
}

 

投稿 【EC-CUBE4】カートへ入れるボタンの文言を管理画面のCSS管理で変更する方法あずみ.net に最初に表示されました。

【EC-CUBE4】商品CSV登録(CSVインポーター)で商品別税率も登録できるよう対応する方法

$
0
0

【EC-CUBE4】商品CSV登録で商品別税率も登録できるよう対応する方法です。

Customizeディレクトリに以下のファイルを設置してください。

細かい動作チェックはしていませんのでバグや実装漏れ等がありましたらコメントにてお知らせ頂けたら対応します。

<?php

/*
 * Copyright (C) 2019 Akira Kurozumi <info@a-zumi.net>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301  USA
 */

namespace Customize\Controller\Admin\Product;

use Doctrine\DBAL\Exception\ForeignKeyConstraintViolationException;
use Eccube\Common\Constant;
use Eccube\Controller\Admin\AbstractCsvImportController;
use Eccube\Entity\BaseInfo;
use Eccube\Entity\Category;
use Eccube\Entity\Product;
use Eccube\Entity\ProductCategory;
use Eccube\Entity\ProductClass;
use Eccube\Entity\ProductImage;
use Eccube\Entity\ProductStock;
use Eccube\Entity\ProductTag;
use Eccube\Entity\TaxRule;
use Eccube\Form\Type\Admin\CsvImportType;
use Eccube\Repository\BaseInfoRepository;
use Eccube\Repository\CategoryRepository;
use Eccube\Repository\ClassCategoryRepository;
use Eccube\Repository\DeliveryDurationRepository;
use Eccube\Repository\Master\ProductStatusRepository;
use Eccube\Repository\Master\SaleTypeRepository;
use Eccube\Repository\ProductRepository;
use Eccube\Repository\TagRepository;
use Eccube\Repository\TaxRuleRepository;
use Eccube\Service\CsvImportService;
use Eccube\Util\CacheUtil;
use Eccube\Util\StringUtil;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\Filesystem\Filesystem;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Constraints\GreaterThanOrEqual;
use Symfony\Component\Validator\Constraints\Range;
use Symfony\Component\Validator\Validator\ValidatorInterface;

class CsvImportController extends AbstractCsvImportController
{
    /**
     * @var DeliveryDurationRepository
     */
    protected $deliveryDurationRepository;

    /**
     * @var SaleTypeRepository
     */
    protected $saleTypeRepository;

    /**
     * @var TagRepository
     */
    protected $tagRepository;

    /**
     * @var CategoryRepository
     */
    protected $categoryRepository;

    /**
     * @var ClassCategoryRepository
     */
    protected $classCategoryRepository;

    /**
     * @var ProductStatusRepository
     */
    protected $productStatusRepository;

    /**
     * @var ProductRepository
     */
    protected $productRepository;

    /**
     * @var BaseInfo
     */
    protected $BaseInfo;

    /**
     * @var ValidatorInterface
     */
    protected $validator;

    private $errors = [];

    /**
     * @var BaseInfoRepository
     */
    private $baseInfoRepository;

    /**
     * @var TaxRuleRepository
     */
    private $taxRuleRepository;

    /**
     * CsvImportController constructor.
     *
     * @param DeliveryDurationRepository $deliveryDurationRepository
     * @param SaleTypeRepository $saleTypeRepository
     * @param TagRepository $tagRepository
     * @param CategoryRepository $categoryRepository
     * @param ClassCategoryRepository $classCategoryRepository
     * @param ProductStatusRepository $productStatusRepository
     * @param ProductRepository $productRepository
     * @param BaseInfoRepository $baseInfoRepository
     * @param ValidatorInterface $validator
     *
     * @throws \Doctrine\ORM\NoResultException
     * @throws \Doctrine\ORM\NonUniqueResultException
     */
    public function __construct(
        DeliveryDurationRepository $deliveryDurationRepository,
        SaleTypeRepository $saleTypeRepository,
        TagRepository $tagRepository,
        CategoryRepository $categoryRepository,
        ClassCategoryRepository $classCategoryRepository,
        ProductStatusRepository $productStatusRepository,
        ProductRepository $productRepository,
        BaseInfoRepository $baseInfoRepository,
        ValidatorInterface $validator,
        TaxRuleRepository $taxRuleRepository
    )
    {
        $this->deliveryDurationRepository = $deliveryDurationRepository;
        $this->saleTypeRepository = $saleTypeRepository;
        $this->tagRepository = $tagRepository;
        $this->categoryRepository = $categoryRepository;
        $this->classCategoryRepository = $classCategoryRepository;
        $this->productStatusRepository = $productStatusRepository;
        $this->productRepository = $productRepository;
        $this->BaseInfo = $baseInfoRepository->get();
        $this->validator = $validator;
        $this->baseInfoRepository = $baseInfoRepository;
        $this->taxRuleRepository = $taxRuleRepository;
    }

    /**
     * 商品登録CSVアップロード
     *
     * @Route("/%eccube_admin_route%/product/product_csv_upload", name="admin_product_csv_import")
     * @Template("@admin/Product/csv_product.twig")
     */
    public function csvProduct(Request $request, CacheUtil $cacheUtil)
    {
        $form = $this->formFactory->createBuilder(CsvImportType::class)->getForm();
        $headers = $this->getProductCsvHeader();
        if ('POST' === $request->getMethod()) {
            $form->handleRequest($request);
            if ($form->isValid()) {
                $formFile = $form['import_file']->getData();
                if (!empty($formFile)) {
                    log_info('商品CSV登録開始');
                    $data = $this->getImportData($formFile);
                    if ($data === false) {
                        $this->addErrors(trans('admin.common.csv_invalid_format'));

                        return $this->renderWithError($form, $headers, false);
                    }
                    $getId = function ($item) {
                        return $item['id'];
                    };
                    $requireHeader = array_keys(array_map($getId, array_filter($headers, function ($value) {
                        return $value['required'];
                    })));

                    $columnHeaders = $data->getColumnHeaders();

                    if (count(array_diff($requireHeader, $columnHeaders)) > 0) {
                        $this->addErrors(trans('admin.common.csv_invalid_format'));

                        return $this->renderWithError($form, $headers, false);
                    }

                    $size = count($data);

                    if ($size < 1) {
                        $this->addErrors(trans('admin.common.csv_invalid_no_data'));

                        return $this->renderWithError($form, $headers, false);
                    }

                    $headerSize = count($columnHeaders);
                    $headerByKey = array_flip(array_map($getId, $headers));
                    $deleteImages = [];

                    $this->entityManager->getConfiguration()->setSQLLogger(null);
                    $this->entityManager->getConnection()->beginTransaction();
                    // CSVファイルの登録処理
                    foreach ($data as $row) {
                        $line = $data->key() + 1;
                        if ($headerSize != count($row)) {
                            $message = trans('admin.common.csv_invalid_format_line', ['%line%' => $line]);
                            $this->addErrors($message);

                            return $this->renderWithError($form, $headers);
                        }

                        if (!isset($row[$headerByKey['id']]) || StringUtil::isBlank($row[$headerByKey['id']])) {
                            $Product = new Product();
                            $this->entityManager->persist($Product);
                        } else {
                            if (preg_match('/^\d+$/', $row[$headerByKey['id']])) {
                                $Product = $this->productRepository->find($row[$headerByKey['id']]);
                                if (!$Product) {
                                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['id']]);
                                    $this->addErrors($message);

                                    return $this->renderWithError($form, $headers);
                                }
                            } else {
                                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['id']]);
                                $this->addErrors($message);

                                return $this->renderWithError($form, $headers);
                            }

                            if (isset($row[$headerByKey['product_del_flg']])) {
                                if (StringUtil::isNotBlank($row[$headerByKey['product_del_flg']]) && $row[$headerByKey['product_del_flg']] == (string)Constant::ENABLED) {
                                    // 商品を物理削除
                                    $deleteImages[] = $Product->getProductImage();

                                    try {
                                        $this->productRepository->delete($Product);
                                        $this->entityManager->flush();

                                        continue;
                                    } catch (ForeignKeyConstraintViolationException $e) {
                                        $message = trans('admin.common.csv_invalid_foreign_key', ['%line%' => $line, '%name%' => $Product->getName()]);
                                        $this->addErrors($message);

                                        return $this->renderWithError($form, $headers);
                                    }
                                }
                            }
                        }

                        if (StringUtil::isBlank($row[$headerByKey['status']])) {
                            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['status']]);
                            $this->addErrors($message);
                        } else {
                            if (preg_match('/^\d+$/', $row[$headerByKey['status']])) {
                                $ProductStatus = $this->productStatusRepository->find($row[$headerByKey['status']]);
                                if (!$ProductStatus) {
                                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['status']]);
                                    $this->addErrors($message);
                                } else {
                                    $Product->setStatus($ProductStatus);
                                }
                            } else {
                                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['status']]);
                                $this->addErrors($message);
                            }
                        }

                        if (StringUtil::isBlank($row[$headerByKey['name']])) {
                            $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['name']]);
                            $this->addErrors($message);

                            return $this->renderWithError($form, $headers);
                        } else {
                            $Product->setName(StringUtil::trimAll($row[$headerByKey['name']]));
                        }

                        if (isset($row[$headerByKey['note']]) && StringUtil::isNotBlank($row[$headerByKey['note']])) {
                            $Product->setNote(StringUtil::trimAll($row[$headerByKey['note']]));
                        } else {
                            $Product->setNote(null);
                        }

                        if (isset($row[$headerByKey['description_list']]) && StringUtil::isNotBlank($row[$headerByKey['description_list']])) {
                            $Product->setDescriptionList(StringUtil::trimAll($row[$headerByKey['description_list']]));
                        } else {
                            $Product->setDescriptionList(null);
                        }

                        if (isset($row[$headerByKey['description_detail']]) && StringUtil::isNotBlank($row[$headerByKey['description_detail']])) {
                            $Product->setDescriptionDetail(StringUtil::trimAll($row[$headerByKey['description_detail']]));
                        } else {
                            $Product->setDescriptionDetail(null);
                        }

                        if (isset($row[$headerByKey['search_word']]) && StringUtil::isNotBlank($row[$headerByKey['search_word']])) {
                            $Product->setSearchWord(StringUtil::trimAll($row[$headerByKey['search_word']]));
                        } else {
                            $Product->setSearchWord(null);
                        }

                        if (isset($row[$headerByKey['free_area']]) && StringUtil::isNotBlank($row[$headerByKey['free_area']])) {
                            $Product->setFreeArea(StringUtil::trimAll($row[$headerByKey['free_area']]));
                        } else {
                            $Product->setFreeArea(null);
                        }

                        // 商品画像登録
                        $this->createProductImage($row, $Product, $data, $headerByKey);

                        $this->entityManager->flush();

                        // 商品カテゴリ登録
                        $this->createProductCategory($row, $Product, $data, $headerByKey);

                        //タグ登録
                        $this->createProductTag($row, $Product, $data, $headerByKey);

                        // 商品規格が存在しなければ新規登録
                        /** @var ProductClass[] $ProductClasses */
                        $ProductClasses = $Product->getProductClasses();
                        if ($ProductClasses->count() < 1) {
                            // 規格分類1(ID)がセットされていると規格なし商品、規格あり商品を作成
                            $ProductClassOrg = $this->createProductClass($row, $Product, $data, $headerByKey);
                            if ($this->BaseInfo->isOptionProductDeliveryFee()) {
                                if (isset($row[$headerByKey['delivery_fee']]) && StringUtil::isNotBlank($row[$headerByKey['delivery_fee']])) {
                                    $deliveryFee = str_replace(',', '', $row[$headerByKey['delivery_fee']]);
                                    $errors = $this->validator->validate($deliveryFee, new GreaterThanOrEqual(['value' => 0]));
                                    if ($errors->count() === 0) {
                                        $ProductClassOrg->setDeliveryFee($deliveryFee);
                                    } else {
                                        $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['delivery_fee']]);
                                        $this->addErrors($message);
                                    }
                                }
                            }

                            if (isset($row[$headerByKey['class_category1']]) && StringUtil::isNotBlank($row[$headerByKey['class_category1']])) {
                                if (isset($row[$headerByKey['class_category2']]) && $row[$headerByKey['class_category1']] == $row[$headerByKey['class_category2']]) {
                                    $message = trans('admin.common.csv_invalid_not_same', [
                                        '%line%' => $line,
                                        '%name1%' => $headerByKey['class_category1'],
                                        '%name2%' => $headerByKey['class_category2'],
                                    ]);
                                    $this->addErrors($message);
                                } else {
                                    // 商品規格あり
                                    // 規格分類あり商品を作成
                                    $ProductClass = clone $ProductClassOrg;
                                    $ProductStock = clone $ProductClassOrg->getProductStock();

                                    // 規格分類1、規格分類2がnullであるデータを非表示
                                    $ProductClassOrg->setVisible(false);

                                    // 規格分類1、2をそれぞれセットし作成
                                    $ClassCategory1 = null;
                                    if (preg_match('/^\d+$/', $row[$headerByKey['class_category1']])) {
                                        $ClassCategory1 = $this->classCategoryRepository->find($row[$headerByKey['class_category1']]);
                                        if (!$ClassCategory1) {
                                            $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category1']]);
                                            $this->addErrors($message);
                                        } else {
                                            $ProductClass->setClassCategory1($ClassCategory1);
                                        }
                                    } else {
                                        $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category1']]);
                                        $this->addErrors($message);
                                    }

                                    if (isset($row[$headerByKey['class_category2']]) && StringUtil::isNotBlank($row[$headerByKey['class_category2']])) {
                                        if (preg_match('/^\d+$/', $row[$headerByKey['class_category2']])) {
                                            $ClassCategory2 = $this->classCategoryRepository->find($row[$headerByKey['class_category2']]);
                                            if (!$ClassCategory2) {
                                                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                                                $this->addErrors($message);
                                            } else {
                                                if ($ClassCategory1 &&
                                                    ($ClassCategory1->getClassName()->getId() == $ClassCategory2->getClassName()->getId())
                                                ) {
                                                    $message = trans('admin.common.csv_invalid_not_same', ['%line%' => $line, '%name1%' => $headerByKey['class_category1'], '%name2%' => $headerByKey['class_category2']]);
                                                    $this->addErrors($message);
                                                } else {
                                                    $ProductClass->setClassCategory2($ClassCategory2);
                                                }
                                            }
                                        } else {
                                            $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                                            $this->addErrors($message);
                                        }
                                    }
                                    $ProductClass->setProductStock($ProductStock);
                                    $ProductStock->setProductClass($ProductClass);

                                    $this->entityManager->persist($ProductClass);
                                    $this->entityManager->persist($ProductStock);
                                }
                            } else {
                                if (isset($row[$headerByKey['class_category2']]) && StringUtil::isNotBlank($row[$headerByKey['class_category2']])) {
                                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                                    $this->addErrors($message);
                                }
                            }
                        } else {
                            // 商品規格の更新
                            $flag = false;
                            $classCategoryId1 = StringUtil::isBlank($row[$headerByKey['class_category1']]) ? null : $row[$headerByKey['class_category1']];
                            $classCategoryId2 = StringUtil::isBlank($row[$headerByKey['class_category2']]) ? null : $row[$headerByKey['class_category2']];

                            foreach ($ProductClasses as $pc) {
                                $classCategory1 = is_null($pc->getClassCategory1()) ? null : $pc->getClassCategory1()->getId();
                                $classCategory2 = is_null($pc->getClassCategory2()) ? null : $pc->getClassCategory2()->getId();

                                // 登録されている商品規格を更新
                                if ($classCategory1 == $classCategoryId1 &&
                                    $classCategory2 == $classCategoryId2
                                ) {
                                    $this->updateProductClass($row, $Product, $pc, $data, $headerByKey);

                                    if ($this->BaseInfo->isOptionProductDeliveryFee()) {
                                        if (isset($row[$headerByKey['delivery_fee']]) && StringUtil::isNotBlank($row[$headerByKey['delivery_fee']])) {
                                            $deliveryFee = str_replace(',', '', $row[$headerByKey['delivery_fee']]);
                                            $errors = $this->validator->validate($deliveryFee, new GreaterThanOrEqual(['value' => 0]));
                                            if ($errors->count() === 0) {
                                                $pc->setDeliveryFee($deliveryFee);
                                            } else {
                                                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['delivery_fee']]);
                                                $this->addErrors($message);
                                            }
                                        }
                                    }

                                    if ($this->BaseInfo->isOptionProductTaxRule()) {
                                        if (isset($row[$headerByKey['tax_rate']])) {
                                            $TaxRule = $pc->getTaxRule();
                                            if (StringUtil::isNotBlank($row[$headerByKey['tax_rate']])) {
                                                $errors = $this->validator->validate($row[$headerByKey['tax_rate']], new Range(['min' => 0, 'max' => 100]));
                                                if ($errors->count() === 0) {
                                                    $rate = $row[$headerByKey['tax_rate']];
                                                    if ($TaxRule) {
                                                        $TaxRule->setTaxRate($rate);
                                                    } else {
                                                        // 初期税率設定の計算方法を設定する
                                                        $RoundingType = $this->taxRuleRepository->find(TaxRule::DEFAULT_TAX_RULE_ID)
                                                            ->getRoundingType();

                                                        $TaxRule = new TaxRule();
                                                        $TaxRule->setProduct($Product);
                                                        $TaxRule->setProductClass($pc);
                                                        $TaxRule->setTaxRate($rate);
                                                        $TaxRule->setRoundingType($RoundingType);
                                                        $TaxRule->setTaxAdjust(0);
                                                        $TaxRule->setApplyDate(new \DateTime());
                                                        $this->entityManager->persist($TaxRule);
                                                    }
                                                } else {
                                                    $message = sprintf('%line%行目の%name%は0以上の数値を設定してください。', $line, $headerByKey['tax_rate']);
                                                    $this->addErrors($message);
                                                }
                                            } else {
                                                if ($TaxRule) {
                                                    $this->taxRuleRepository->delete($TaxRule);
                                                    $pc->setTaxRule(null);
                                                }
                                            }
                                        }
                                    }

                                    $flag = true;
                                    break;
                                }
                            }

                            // 商品規格を登録
                            if (!$flag) {
                                $pc = $ProductClasses[0];
                                if ($pc->getClassCategory1() == null &&
                                    $pc->getClassCategory2() == null
                                ) {
                                    // 規格分類1、規格分類2がnullであるデータを非表示
                                    $pc->setVisible(false);
                                }

                                if (isset($row[$headerByKey['class_category1']]) && isset($row[$headerByKey['class_category2']])
                                    && $row[$headerByKey['class_category1']] == $row[$headerByKey['class_category2']]) {
                                    $message = trans('admin.common.csv_invalid_not_same', [
                                        '%line%' => $line,
                                        '%name1%' => $headerByKey['class_category1'],
                                        '%name2%' => $headerByKey['class_category2'],
                                    ]);
                                    $this->addErrors($message);
                                } else {
                                    // 必ず規格分類1がセットされている
                                    // 規格分類1、2をそれぞれセットし作成
                                    $ClassCategory1 = null;
                                    if (preg_match('/^\d+$/', $classCategoryId1)) {
                                        $ClassCategory1 = $this->classCategoryRepository->find($classCategoryId1);
                                        if (!$ClassCategory1) {
                                            $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category1']]);
                                            $this->addErrors($message);
                                        }
                                    } else {
                                        $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category1']]);
                                        $this->addErrors($message);
                                    }

                                    $ClassCategory2 = null;
                                    if (isset($row[$headerByKey['class_category2']]) && StringUtil::isNotBlank($row[$headerByKey['class_category2']])) {
                                        if ($pc->getClassCategory1() != null && $pc->getClassCategory2() == null) {
                                            $message = trans('admin.common.csv_invalid_can_not', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                                            $this->addErrors($message);
                                        } else {
                                            if (preg_match('/^\d+$/', $classCategoryId2)) {
                                                $ClassCategory2 = $this->classCategoryRepository->find($classCategoryId2);
                                                if (!$ClassCategory2) {
                                                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                                                    $this->addErrors($message);
                                                } else {
                                                    if ($ClassCategory1 &&
                                                        ($ClassCategory1->getClassName()->getId() == $ClassCategory2->getClassName()->getId())
                                                    ) {
                                                        $message = trans('admin.common.csv_invalid_not_same', [
                                                            '%line%' => $line,
                                                            '%name1%' => $headerByKey['class_category1'],
                                                            '%name2%' => $headerByKey['class_category2'],
                                                        ]);
                                                        $this->addErrors($message);
                                                    }
                                                }
                                            } else {
                                                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                                                $this->addErrors($message);
                                            }
                                        }
                                    } else {
                                        if ($pc->getClassCategory1() != null && $pc->getClassCategory2() != null) {
                                            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                                            $this->addErrors($message);
                                        }
                                    }
                                    $ProductClass = $this->createProductClass($row, $Product, $data, $headerByKey, $ClassCategory1, $ClassCategory2);

                                    if ($this->BaseInfo->isOptionProductDeliveryFee()) {
                                        if (isset($row[$headerByKey['delivery_fee']]) && StringUtil::isNotBlank($row[$headerByKey['delivery_fee']])) {
                                            $deliveryFee = str_replace(',', '', $row[$headerByKey['delivery_fee']]);
                                            $errors = $this->validator->validate($deliveryFee, new GreaterThanOrEqual(['value' => 0]));
                                            if ($errors->count() === 0) {
                                                $ProductClass->setDeliveryFee($deliveryFee);
                                            } else {
                                                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['delivery_fee']]);
                                                $this->addErrors($message);
                                            }
                                        }
                                    }
                                    $Product->addProductClass($ProductClass);
                                }
                            }
                        }
                        if ($this->hasErrors()) {
                            return $this->renderWithError($form, $headers);
                        }
                        $this->entityManager->persist($Product);
                    }
                    $this->entityManager->flush();
                    $this->entityManager->getConnection()->commit();

                    // 画像ファイルの削除(commit後に削除させる)
                    foreach ($deleteImages as $images) {
                        foreach ($images as $image) {
                            try {
                                $fs = new Filesystem();
                                $fs->remove($this->eccubeConfig['eccube_save_image_dir'] . '/' . $image);
                            } catch (\Exception $e) {
                                // エラーが発生しても無視する
                            }
                        }
                    }

                    log_info('商品CSV登録完了');
                    $message = 'admin.common.csv_upload_complete';
                    $this->session->getFlashBag()->add('eccube.admin.success', $message);

                    $cacheUtil->clearDoctrineCache();
                }
            }
        }

        return $this->renderWithError($form, $headers);
    }

    /**
     * カテゴリ登録CSVアップロード
     *
     * @Route("/%eccube_admin_route%/product/category_csv_upload", name="admin_product_category_csv_import")
     * @Template("@admin/Product/csv_category.twig")
     */
    public function csvCategory(Request $request, CacheUtil $cacheUtil)
    {
        $form = $this->formFactory->createBuilder(CsvImportType::class)->getForm();

        $headers = $this->getCategoryCsvHeader();
        if ('POST' === $request->getMethod()) {
            $form->handleRequest($request);
            if ($form->isValid()) {
                $formFile = $form['import_file']->getData();
                if (!empty($formFile)) {
                    log_info('カテゴリCSV登録開始');
                    $data = $this->getImportData($formFile);
                    if ($data === false) {
                        $this->addErrors(trans('admin.common.csv_invalid_format'));

                        return $this->renderWithError($form, $headers, false);
                    }

                    $getId = function ($item) {
                        return $item['id'];
                    };
                    $requireHeader = array_keys(array_map($getId, array_filter($headers, function ($value) {
                        return $value['required'];
                    })));

                    $headerByKey = array_flip(array_map($getId, $headers));

                    $columnHeaders = $data->getColumnHeaders();
                    if (count(array_diff($requireHeader, $columnHeaders)) > 0) {
                        $this->addErrors(trans('admin.common.csv_invalid_format'));

                        return $this->renderWithError($form, $headers, false);
                    }

                    $size = count($data);
                    if ($size < 1) {
                        $this->addErrors(trans('admin.common.csv_invalid_no_data'));

                        return $this->renderWithError($form, $headers, false);
                    }
                    $this->entityManager->getConfiguration()->setSQLLogger(null);
                    $this->entityManager->getConnection()->beginTransaction();
                    // CSVファイルの登録処理
                    foreach ($data as $row) {
                        /** @var $Category Category */
                        $Category = new Category();
                        if (isset($row[$headerByKey['id']]) && strlen($row[$headerByKey['id']]) > 0) {
                            if (!preg_match('/^\d+$/', $row[$headerByKey['id']])) {
                                $this->addErrors(($data->key() + 1) . '行目のカテゴリIDが存在しません。');

                                return $this->renderWithError($form, $headers);
                            }
                            $Category = $this->categoryRepository->find($row[$headerByKey['id']]);
                            if (!$Category) {
                                $this->addErrors(($data->key() + 1) . '行目のカテゴリIDが存在しません。');

                                return $this->renderWithError($form, $headers);
                            }
                            if ($row[$headerByKey['id']] == $row[$headerByKey['parent_category_id']]) {
                                $this->addErrors(($data->key() + 1) . '行目のカテゴリIDと親カテゴリIDが同じです。');

                                return $this->renderWithError($form, $headers);
                            }
                        }

                        if (isset($row[$headerByKey['category_del_flg']]) && StringUtil::isNotBlank($row[$headerByKey['category_del_flg']])) {
                            if (StringUtil::trimAll($row[$headerByKey['category_del_flg']]) == 1) {
                                if ($Category->getId()) {
                                    log_info('カテゴリ削除開始', [$Category->getId()]);
                                    try {
                                        $this->categoryRepository->delete($Category);
                                        log_info('カテゴリ削除完了', [$Category->getId()]);
                                    } catch (ForeignKeyConstraintViolationException $e) {
                                        log_info('カテゴリ削除エラー', [$Category->getId(), $e]);
                                        $message = trans('admin.common.delete_error_foreign_key', ['%name%' => $Category->getName()]);
                                        $this->addError($message, 'admin');

                                        return $this->renderWithError($form, $headers);
                                    }
                                }

                                continue;
                            }
                        }

                        if (!isset($row[$headerByKey['category_name']]) || StringUtil::isBlank($row[$headerByKey['category_name']])) {
                            $this->addErrors(($data->key() + 1) . '行目のカテゴリ名が設定されていません。');

                            return $this->renderWithError($form, $headers);
                        } else {
                            $Category->setName(StringUtil::trimAll($row[$headerByKey['category_name']]));
                        }

                        $ParentCategory = null;
                        if (isset($row[$headerByKey['parent_category_id']]) && StringUtil::isNotBlank($row[$headerByKey['parent_category_id']])) {
                            if (!preg_match('/^\d+$/', $row[$headerByKey['parent_category_id']])) {
                                $this->addErrors(($data->key() + 1) . '行目の親カテゴリIDが存在しません。');

                                return $this->renderWithError($form, $headers);
                            }

                            /** @var $ParentCategory Category */
                            $ParentCategory = $this->categoryRepository->find($row[$headerByKey['parent_category_id']]);
                            if (!$ParentCategory) {
                                $this->addErrors(($data->key() + 1) . '行目の親カテゴリIDが存在しません。');

                                return $this->renderWithError($form, $headers);
                            }
                        }
                        $Category->setParent($ParentCategory);

                        // Level
                        if (isset($row['階層']) && StringUtil::isNotBlank($row['階層'])) {
                            if ($ParentCategory == null && $row['階層'] != 1) {
                                $this->addErrors(($data->key() + 1) . '行目の親カテゴリIDが存在しません。');

                                return $this->renderWithError($form, $headers);
                            }
                            $level = StringUtil::trimAll($row['階層']);
                        } else {
                            $level = 1;
                            if ($ParentCategory) {
                                $level = $ParentCategory->getHierarchy() + 1;
                            }
                        }

                        $Category->setHierarchy($level);

                        if ($this->eccubeConfig['eccube_category_nest_level'] < $Category->getHierarchy()) {
                            $this->addErrors(($data->key() + 1) . '行目のカテゴリが最大レベルを超えているため設定できません。');

                            return $this->renderWithError($form, $headers);
                        }

                        if ($this->hasErrors()) {
                            return $this->renderWithError($form, $headers);
                        }
                        $this->entityManager->persist($Category);
                        $this->categoryRepository->save($Category);
                    }

                    $this->entityManager->getConnection()->commit();
                    log_info('カテゴリCSV登録完了');
                    $message = 'admin.common.csv_upload_complete';
                    $this->session->getFlashBag()->add('eccube.admin.success', $message);

                    $cacheUtil->clearDoctrineCache();
                }
            }
        }

        return $this->renderWithError($form, $headers);
    }

    /**
     * アップロード用CSV雛形ファイルダウンロード
     *
     * @Route("/%eccube_admin_route%/product/csv_template/{type}", requirements={"type" = "\w+"}, name="admin_product_csv_template")
     */
    public function csvTemplate(Request $request, $type)
    {
        if ($type == 'product') {
            $headers = $this->getProductCsvHeader();
            $filename = 'product.csv';
        } elseif ($type == 'category') {
            $headers = $this->getCategoryCsvHeader();
            $filename = 'category.csv';
        } else {
            throw new NotFoundHttpException();
        }

        return $this->sendTemplateResponse($request, array_keys($headers), $filename);
    }

    /**
     * 登録、更新時のエラー画面表示
     *
     * @param FormInterface $form
     * @param array $headers
     * @param bool $rollback
     *
     * @return array
     *
     * @throws \Doctrine\DBAL\ConnectionException
     */
    protected function renderWithError($form, $headers, $rollback = true)
    {
        if ($this->hasErrors()) {
            if ($rollback) {
                $this->entityManager->getConnection()->rollback();
            }
        }

        $this->removeUploadedFile();

        return [
            'form' => $form->createView(),
            'headers' => $headers,
            'errors' => $this->errors,
        ];
    }

    /**
     * 商品画像の削除、登録
     *
     * @param $row
     * @param Product $Product
     * @param CsvImportService $data
     */
    protected function createProductImage($row, Product $Product, $data, $headerByKey)
    {
        if (isset($row[$headerByKey['product_image']]) && StringUtil::isNotBlank($row[$headerByKey['product_image']])) {
            // 画像の削除
            $ProductImages = $Product->getProductImage();
            foreach ($ProductImages as $ProductImage) {
                $Product->removeProductImage($ProductImage);
                $this->entityManager->remove($ProductImage);
            }

            // 画像の登録
            $images = explode(',', $row[$headerByKey['product_image']]);

            $sortNo = 1;

            $pattern = "/\\$|^.*.\.\\\.*|\/$|^.*.\.\/\.*/";
            foreach ($images as $image) {
                $fileName = StringUtil::trimAll($image);

                // 商品画像名のフォーマットチェック
                if (strlen($fileName) > 0 && preg_match($pattern, $fileName)) {
                    $message = trans('admin.common.csv_invalid_image', ['%line%' => $data->key() + 1, '%name%' => $headerByKey['product_image']]);
                    $this->addErrors($message);
                } else {
                    // 空文字は登録対象外
                    if (!empty($fileName)) {
                        $ProductImage = new ProductImage();
                        $ProductImage->setFileName($fileName);
                        $ProductImage->setProduct($Product);
                        $ProductImage->setSortNo($sortNo);

                        $Product->addProductImage($ProductImage);
                        $sortNo++;
                        $this->entityManager->persist($ProductImage);
                    }
                }
            }
        }
    }

    /**
     * 商品カテゴリの削除、登録
     *
     * @param $row
     * @param Product $Product
     * @param CsvImportService $data
     * @param $headerByKey
     */
    protected function createProductCategory($row, Product $Product, $data, $headerByKey)
    {
        // カテゴリの削除
        $ProductCategories = $Product->getProductCategories();
        foreach ($ProductCategories as $ProductCategory) {
            $Product->removeProductCategory($ProductCategory);
            $this->entityManager->remove($ProductCategory);
            $this->entityManager->flush();
        }

        if (isset($row[$headerByKey['product_category']]) && StringUtil::isNotBlank($row[$headerByKey['product_category']])) {
            // カテゴリの登録
            $categories = explode(',', $row[$headerByKey['product_category']]);
            $sortNo = 1;
            $categoriesIdList = [];
            foreach ($categories as $category) {
                $line = $data->key() + 1;
                if (preg_match('/^\d+$/', $category)) {
                    $Category = $this->categoryRepository->find($category);
                    if (!$Category) {
                        $message = trans('admin.common.csv_invalid_not_found_target', [
                            '%line%' => $line,
                            '%name%' => $headerByKey['product_category'],
                            '%target_name%' => $category,
                        ]);
                        $this->addErrors($message);
                    } else {
                        foreach ($Category->getPath() as $ParentCategory) {
                            if (!isset($categoriesIdList[$ParentCategory->getId()])) {
                                $ProductCategory = $this->makeProductCategory($Product, $ParentCategory, $sortNo);
                                $this->entityManager->persist($ProductCategory);
                                $sortNo++;

                                $Product->addProductCategory($ProductCategory);
                                $categoriesIdList[$ParentCategory->getId()] = true;
                            }
                        }
                        if (!isset($categoriesIdList[$Category->getId()])) {
                            $ProductCategory = $this->makeProductCategory($Product, $Category, $sortNo);
                            $sortNo++;
                            $this->entityManager->persist($ProductCategory);
                            $Product->addProductCategory($ProductCategory);
                            $categoriesIdList[$Category->getId()] = true;
                        }
                    }
                } else {
                    $message = trans('admin.common.csv_invalid_not_found_target', [
                        '%line%' => $line,
                        '%name%' => $headerByKey['product_category'],
                        '%target_name%' => $category,
                    ]);
                    $this->addErrors($message);
                }
            }
        }
    }

    /**
     * タグの登録
     *
     * @param array $row
     * @param Product $Product
     * @param CsvImportService $data
     */
    protected function createProductTag($row, Product $Product, $data, $headerByKey)
    {
        // タグの削除
        $ProductTags = $Product->getProductTag();
        foreach ($ProductTags as $ProductTag) {
            $Product->removeProductTag($ProductTag);
            $this->entityManager->remove($ProductTag);
        }

        if (isset($row[$headerByKey['product_tag']]) && StringUtil::isNotBlank($row[$headerByKey['product_tag']])) {
            // タグの登録
            $tags = explode(',', $row[$headerByKey['product_tag']]);
            foreach ($tags as $tag_id) {
                $Tag = null;
                if (preg_match('/^\d+$/', $tag_id)) {
                    $Tag = $this->tagRepository->find($tag_id);

                    if ($Tag) {
                        $ProductTags = new ProductTag();
                        $ProductTags
                            ->setProduct($Product)
                            ->setTag($Tag);

                        $Product->addProductTag($ProductTags);

                        $this->entityManager->persist($ProductTags);
                    }
                }
                if (!$Tag) {
                    $message = trans('admin.common.csv_invalid_not_found_target', [
                        '%line%' => $data->key() + 1,
                        '%name%' => $headerByKey['product_tag'],
                        '%target_name%' => $tag_id,
                    ]);
                    $this->addErrors($message);
                }
            }
        }
    }

    /**
     * 商品規格分類1、商品規格分類2がnullとなる商品規格情報を作成
     *
     * @param $row
     * @param Product $Product
     * @param CsvImportService $data
     * @param $headerByKey
     * @param null $ClassCategory1
     * @param null $ClassCategory2
     *
     * @return ProductClass
     */
    protected function createProductClass($row, Product $Product, $data, $headerByKey, $ClassCategory1 = null, $ClassCategory2 = null)
    {
        // 規格分類1、規格分類2がnullとなる商品を作成
        $ProductClass = new ProductClass();
        $ProductClass->setProduct($Product);
        $ProductClass->setVisible(true);

        $line = $data->key() + 1;
        if (isset($row[$headerByKey['sale_type']]) && StringUtil::isNotBlank($row[$headerByKey['sale_type']])) {
            if (preg_match('/^\d+$/', $row[$headerByKey['sale_type']])) {
                $SaleType = $this->saleTypeRepository->find($row[$headerByKey['sale_type']]);
                if (!$SaleType) {
                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['sale_type']]);
                    $this->addErrors($message);
                } else {
                    $ProductClass->setSaleType($SaleType);
                }
            } else {
                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['sale_type']]);
                $this->addErrors($message);
            }
        } else {
            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['sale_type']]);
            $this->addErrors($message);
        }

        $ProductClass->setClassCategory1($ClassCategory1);
        $ProductClass->setClassCategory2($ClassCategory2);

        if (isset($row[$headerByKey['delivery_date']]) && StringUtil::isNotBlank($row[$headerByKey['delivery_date']])) {
            if (preg_match('/^\d+$/', $row[$headerByKey['delivery_date']])) {
                $DeliveryDuration = $this->deliveryDurationRepository->find($row[$headerByKey['delivery_date']]);
                if (!$DeliveryDuration) {
                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['delivery_date']]);
                    $this->addErrors($message);
                } else {
                    $ProductClass->setDeliveryDuration($DeliveryDuration);
                }
            } else {
                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['delivery_date']]);
                $this->addErrors($message);
            }
        }

        if (isset($row[$headerByKey['product_code']]) && StringUtil::isNotBlank($row[$headerByKey['product_code']])) {
            $ProductClass->setCode(StringUtil::trimAll($row[$headerByKey['product_code']]));
        } else {
            $ProductClass->setCode(null);
        }

        if (!isset($row[$headerByKey['stock_unlimited']])
            || StringUtil::isBlank($row[$headerByKey['stock_unlimited']])
            || $row[$headerByKey['stock_unlimited']] == (string)Constant::DISABLED
        ) {
            $ProductClass->setStockUnlimited(false);
            // 在庫数が設定されていなければエラー
            if (isset($row[$headerByKey['stock']]) && StringUtil::isNotBlank($row[$headerByKey['stock']])) {
                $stock = str_replace(',', '', $row[$headerByKey['stock']]);
                if (preg_match('/^\d+$/', $stock) && $stock >= 0) {
                    $ProductClass->setStock($stock);
                } else {
                    $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['stock']]);
                    $this->addErrors($message);
                }
            } else {
                $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['stock']]);
                $this->addErrors($message);
            }
        } elseif ($row[$headerByKey['stock_unlimited']] == (string)Constant::ENABLED) {
            $ProductClass->setStockUnlimited(true);
            $ProductClass->setStock(null);
        } else {
            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['stock_unlimited']]);
            $this->addErrors($message);
        }

        if (isset($row[$headerByKey['sale_limit']]) && StringUtil::isNotBlank($row[$headerByKey['sale_limit']])) {
            $saleLimit = str_replace(',', '', $row[$headerByKey['sale_limit']]);
            if (preg_match('/^\d+$/', $saleLimit) && $saleLimit >= 0) {
                $ProductClass->setSaleLimit($saleLimit);
            } else {
                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['sale_limit']]);
                $this->addErrors($message);
            }
        }

        if (isset($row[$headerByKey['price01']]) && StringUtil::isNotBlank($row[$headerByKey['price01']])) {
            $price01 = str_replace(',', '', $row[$headerByKey['price01']]);
            $errors = $this->validator->validate($price01, new GreaterThanOrEqual(['value' => 0]));
            if ($errors->count() === 0) {
                $ProductClass->setPrice01($price01);
            } else {
                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['price01']]);
                $this->addErrors($message);
            }
        }

        if (isset($row[$headerByKey['price02']]) && StringUtil::isNotBlank($row[$headerByKey['price02']])) {
            $price02 = str_replace(',', '', $row[$headerByKey['price02']]);
            $errors = $this->validator->validate($price02, new GreaterThanOrEqual(['value' => 0]));
            if ($errors->count() === 0) {
                $ProductClass->setPrice02($price02);
            } else {
                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['price02']]);
                $this->addErrors($message);
            }
        } else {
            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['price02']]);
            $this->addErrors($message);
        }

        if ($this->BaseInfo->isOptionProductDeliveryFee()) {
            if (isset($row[$headerByKey['delivery_fee']]) && StringUtil::isNotBlank($row[$headerByKey['delivery_fee']])) {
                $delivery_fee = str_replace(',', '', $row[$headerByKey['delivery_fee']]);
                $errors = $this->validator->validate($delivery_fee, new GreaterThanOrEqual(['value' => 0]));
                if ($errors->count() === 0) {
                    $ProductClass->setDeliveryFee($delivery_fee);
                } else {
                    $message = trans('admin.common.csv_invalid_greater_than_zero',
                        ['%line%' => $line, '%name%' => $headerByKey['delivery_fee']]);
                    $this->addErrors($message);
                }
            }
        }

        if ($this->BaseInfo->isOptionProductTaxRule()) {
            if (isset($row[$headerByKey['tax_rate']])) {
                $TaxRule = $ProductClass->getTaxRule();
                if (StringUtil::isNotBlank($row[$headerByKey['tax_rate']])) {
                    $errors = $this->validator->validate($row[$headerByKey['tax_rate']], new Range(['min' => 0, 'max' => 100]));
                    if ($errors->count() === 0) {
                        $rate = $row[$headerByKey['tax_rate']];
                        if ($TaxRule) {
                            $TaxRule->setTaxRate($rate);
                        } else {
                            // 初期税率設定の計算方法を設定する
                            $RoundingType = $this->taxRuleRepository->find(TaxRule::DEFAULT_TAX_RULE_ID)
                                ->getRoundingType();

                            $TaxRule = new TaxRule();
                            $TaxRule->setProduct($Product);
                            $TaxRule->setProductClass($pc);
                            $TaxRule->setTaxRate($rate);
                            $TaxRule->setRoundingType($RoundingType);
                            $TaxRule->setTaxAdjust(0);
                            $TaxRule->setApplyDate(new \DateTime());
                            $this->entityManager->persist($TaxRule);
                        }
                    } else {
                        $message = sprintf('%line%行目の%name%は0以上の数値を設定してください。', $line, $headerByKey['tax_rate']);
                        $this->addErrors($message);
                    }
                } else {
                    if ($TaxRule) {
                        $this->taxRuleRepository->delete($TaxRule);
                        $ProductClass->setTaxRule(null);
                    }
                }
            }
        }

        $Product->addProductClass($ProductClass);
        $ProductStock = new ProductStock();
        $ProductClass->setProductStock($ProductStock);
        $ProductStock->setProductClass($ProductClass);

        if (!$ProductClass->isStockUnlimited()) {
            $ProductStock->setStock($ProductClass->getStock());
        } else {
            // 在庫無制限時はnullを設定
            $ProductStock->setStock(null);
        }

        $this->entityManager->persist($ProductClass);
        $this->entityManager->persist($ProductStock);

        return $ProductClass;
    }

    /**
     * 商品規格情報を更新
     *
     * @param $row
     * @param Product $Product
     * @param ProductClass $ProductClass
     * @param CsvImportService $data
     *
     * @return ProductClass
     */
    protected function updateProductClass($row, Product $Product, ProductClass $ProductClass, $data, $headerByKey)
    {
        $ProductClass->setProduct($Product);

        $line = $data->key() + 1;
        if ($row[$headerByKey['sale_type']] == '') {
            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['sale_type']]);
            $this->addErrors($message);
        } else {
            if (preg_match('/^\d+$/', $row[$headerByKey['sale_type']])) {
                $SaleType = $this->saleTypeRepository->find($row[$headerByKey['sale_type']]);
                if (!$SaleType) {
                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['sale_type']]);
                    $this->addErrors($message);
                } else {
                    $ProductClass->setSaleType($SaleType);
                }
            } else {
                $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['sale_type']]);
                $this->addErrors($message);
            }
        }

        // 規格分類1、2をそれぞれセットし作成
        if ($row[$headerByKey['class_category1']] != '') {
            if (preg_match('/^\d+$/', $row[$headerByKey['class_category1']])) {
                $ClassCategory = $this->classCategoryRepository->find($row[$headerByKey['class_category1']]);
                if (!$ClassCategory) {
                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category1']]);
                    $this->addErrors($message);
                } else {
                    $ProductClass->setClassCategory1($ClassCategory);
                }
            } else {
                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category1']]);
                $this->addErrors($message);
            }
        }

        if ($row[$headerByKey['class_category2']] != '') {
            if (preg_match('/^\d+$/', $row[$headerByKey['class_category2']])) {
                $ClassCategory = $this->classCategoryRepository->find($row[$headerByKey['class_category2']]);
                if (!$ClassCategory) {
                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                    $this->addErrors($message);
                } else {
                    $ProductClass->setClassCategory2($ClassCategory);
                }
            } else {
                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['class_category2']]);
                $this->addErrors($message);
            }
        }

        if ($row[$headerByKey['delivery_date']] != '') {
            if (preg_match('/^\d+$/', $row[$headerByKey['delivery_date']])) {
                $DeliveryDuration = $this->deliveryDurationRepository->find($row[$headerByKey['delivery_date']]);
                if (!$DeliveryDuration) {
                    $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['delivery_date']]);
                    $this->addErrors($message);
                } else {
                    $ProductClass->setDeliveryDuration($DeliveryDuration);
                }
            } else {
                $message = trans('admin.common.csv_invalid_not_found', ['%line%' => $line, '%name%' => $headerByKey['delivery_date']]);
                $this->addErrors($message);
            }
        }

        if (StringUtil::isNotBlank($row[$headerByKey['product_code']])) {
            $ProductClass->setCode(StringUtil::trimAll($row[$headerByKey['product_code']]));
        } else {
            $ProductClass->setCode(null);
        }

        if (!isset($row[$headerByKey['stock_unlimited']])
            || StringUtil::isBlank($row[$headerByKey['stock_unlimited']])
            || $row[$headerByKey['stock_unlimited']] == (string)Constant::DISABLED
        ) {
            $ProductClass->setStockUnlimited(false);
            // 在庫数が設定されていなければエラー
            if ($row[$headerByKey['stock']] == '') {
                $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['stock']]);
                $this->addErrors($message);
            } else {
                $stock = str_replace(',', '', $row[$headerByKey['stock']]);
                if (preg_match('/^\d+$/', $stock) && $stock >= 0) {
                    $ProductClass->setStock($row[$headerByKey['stock']]);
                } else {
                    $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['stock']]);
                    $this->addErrors($message);
                }
            }
        } elseif ($row[$headerByKey['stock_unlimited']] == (string)Constant::ENABLED) {
            $ProductClass->setStockUnlimited(true);
            $ProductClass->setStock(null);
        } else {
            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['stock_unlimited']]);
            $this->addErrors($message);
        }

        if ($row[$headerByKey['sale_limit']] != '') {
            $saleLimit = str_replace(',', '', $row[$headerByKey['sale_limit']]);
            if (preg_match('/^\d+$/', $saleLimit) && $saleLimit >= 0) {
                $ProductClass->setSaleLimit($saleLimit);
            } else {
                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['sale_limit']]);
                $this->addErrors($message);
            }
        }

        if ($row[$headerByKey['price01']] != '') {
            $price01 = str_replace(',', '', $row[$headerByKey['price01']]);
            $errors = $this->validator->validate($price01, new GreaterThanOrEqual(['value' => 0]));
            if ($errors->count() === 0) {
                $ProductClass->setPrice01($price01);
            } else {
                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['price01']]);
                $this->addErrors($message);
            }
        }

        if ($row[$headerByKey['price02']] == '') {
            $message = trans('admin.common.csv_invalid_required', ['%line%' => $line, '%name%' => $headerByKey['price02']]);
            $this->addErrors($message);
        } else {
            $price02 = str_replace(',', '', $row[$headerByKey['price02']]);
            $errors = $this->validator->validate($price02, new GreaterThanOrEqual(['value' => 0]));
            if ($errors->count() === 0) {
                $ProductClass->setPrice02($price02);
            } else {
                $message = trans('admin.common.csv_invalid_greater_than_zero', ['%line%' => $line, '%name%' => $headerByKey['price02']]);
                $this->addErrors($message);
            }
        }

        $ProductStock = $ProductClass->getProductStock();

        if (!$ProductClass->isStockUnlimited()) {
            $ProductStock->setStock($ProductClass->getStock());
        } else {
            // 在庫無制限時はnullを設定
            $ProductStock->setStock(null);
        }

        return $ProductClass;
    }

    /**
     * 登録、更新時のエラー画面表示
     */
    protected function addErrors($message)
    {
        $this->errors[] = $message;
    }

    /**
     * @return array
     */
    protected function getErrors()
    {
        return $this->errors;
    }

    /**
     * @return boolean
     */
    protected function hasErrors()
    {
        return count($this->getErrors()) > 0;
    }

    /**
     * 商品登録CSVヘッダー定義
     *
     * @return array
     */
    protected function getProductCsvHeader()
    {
        return [
            trans('admin.product.product_csv.product_id_col') => [
                'id' => 'id',
                'description' => 'admin.product.product_csv.product_id_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.display_status_col') => [
                'id' => 'status',
                'description' => 'admin.product.product_csv.display_status_description',
                'required' => true,
            ],
            trans('admin.product.product_csv.product_name_col') => [
                'id' => 'name',
                'description' => 'admin.product.product_csv.product_name_description',
                'required' => true,
            ],
            trans('admin.product.product_csv.shop_memo_col') => [
                'id' => 'note',
                'description' => 'admin.product.product_csv.shop_memo_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.description_list_col') => [
                'id' => 'description_list',
                'description' => 'admin.product.product_csv.description_list_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.description_detail_col') => [
                'id' => 'description_detail',
                'description' => 'admin.product.product_csv.description_detail_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.keyword_col') => [
                'id' => 'search_word',
                'description' => 'admin.product.product_csv.keyword_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.free_area_col') => [
                'id' => 'free_area',
                'description' => 'admin.product.product_csv.free_area_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.delete_flag_col') => [
                'id' => 'product_del_flg',
                'description' => 'admin.product.product_csv.delete_flag_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.product_image_col') => [
                'id' => 'product_image',
                'description' => 'admin.product.product_csv.product_image_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.category_col') => [
                'id' => 'product_category',
                'description' => 'admin.product.product_csv.category_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.tag_col') => [
                'id' => 'product_tag',
                'description' => 'admin.product.product_csv.tag_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.sale_type_col') => [
                'id' => 'sale_type',
                'description' => 'admin.product.product_csv.sale_type_description',
                'required' => true,
            ],
            trans('admin.product.product_csv.class_category1_col') => [
                'id' => 'class_category1',
                'description' => 'admin.product.product_csv.class_category1_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.class_category2_col') => [
                'id' => 'class_category2',
                'description' => 'admin.product.product_csv.class_category2_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.delivery_duration_col') => [
                'id' => 'delivery_date',
                'description' => 'admin.product.product_csv.delivery_duration_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.product_code_col') => [
                'id' => 'product_code',
                'description' => 'admin.product.product_csv.product_code_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.stock_col') => [
                'id' => 'stock',
                'description' => 'admin.product.product_csv.stock_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.stock_unlimited_col') => [
                'id' => 'stock_unlimited',
                'description' => 'admin.product.product_csv.stock_unlimited_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.sale_limit_col') => [
                'id' => 'sale_limit',
                'description' => 'admin.product.product_csv.sale_limit_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.normal_price_col') => [
                'id' => 'price01',
                'description' => 'admin.product.product_csv.normal_price_description',
                'required' => false,
            ],
            trans('admin.product.product_csv.sale_price_col') => [
                'id' => 'price02',
                'description' => 'admin.product.product_csv.sale_price_description',
                'required' => true,
            ],
            trans('admin.product.product_csv.delivery_fee_col') => [
                'id' => 'delivery_fee',
                'description' => 'admin.product.product_csv.delivery_fee_description',
                'required' => false,
            ],
            trans('税率') => [
                'id' => 'tax_rate',
                'description' => '税率設定',
                'required' => false,
            ],
        ];
    }

    /**
     * カテゴリCSVヘッダー定義
     */
    protected function getCategoryCsvHeader()
    {
        return [
            trans('admin.product.category_csv.category_id_col') => [
                'id' => 'id',
                'description' => 'admin.product.category_csv.category_id_description',
                'required' => false,
            ],
            trans('admin.product.category_csv.category_name_col') => [
                'id' => 'category_name',
                'description' => 'admin.product.category_csv.category_name_description',
                'required' => true,
            ],
            trans('admin.product.category_csv.parent_category_id_col') => [
                'id' => 'parent_category_id',
                'description' => 'admin.product.category_csv.parent_category_id_description',
                'required' => false,
            ],
            trans('admin.product.category_csv.delete_flag_col') => [
                'id' => 'category_del_flg',
                'description' => 'admin.product.category_csv.delete_flag_description',
                'required' => false,
            ],
        ];
    }

    /**
     * ProductCategory作成
     *
     * @param \Eccube\Entity\Product $Product
     * @param \Eccube\Entity\Category $Category
     * @param int $sortNo
     *
     * @return ProductCategory
     */
    private function makeProductCategory($Product, $Category, $sortNo)
    {
        $ProductCategory = new ProductCategory();
        $ProductCategory->setProduct($Product);
        $ProductCategory->setProductId($Product->getId());
        $ProductCategory->setCategory($Category);
        $ProductCategory->setCategoryId($Category->getId());

        return $ProductCategory;
    }
}

 

投稿 【EC-CUBE4】商品CSV登録(CSVインポーター)で商品別税率も登録できるよう対応する方法あずみ.net に最初に表示されました。


【EC-CUBE4】Customize領域に翻訳ファイルを設置して本体やプラグインの翻訳を上書きする方法

$
0
0

EC-CUBE4のCustomize領域に翻訳ファイルを設置して本体やプラグインの翻訳を上書きする方法です。

translation.yamlにパスを追加

以下にあるtranslation.yamlに以下のようにパスを追加して下さい。

app/config/eccube/packages/translation.yaml

framework:
    default_locale: '%locale%'
    translator:
        paths:
            - '%kernel.project_dir%/src/Eccube/Resource/locale/'
            - '%kernel.project_dir%/app/Customize/Resource/locale/'
        fallbacks:
            - '%locale%'

これで完了です。

以下のようにmessages.ja.yamlを設置すると本体やプラグインの翻訳を上書きできます。

app/Customize/Resource/local/messages.ja.yaml

common.select__pref: 都道府県を選択して下さい!!!!!

プラグインを作りました

本体をアップデートすると上記の設定が更新されてしまうのでプラグインを作ってGitHubに公開しました。

設定ファイルを編集しないでプラグインを利用すれば、本体をアップデートしてもCustomize領域の翻訳ファイルが反映されるようになります。


https://github.com/kurozumi/eccube4-CustomizeTranslator4
0 forks.
0 stars.
0 open issues.
Recent commits:

投稿 【EC-CUBE4】Customize領域に翻訳ファイルを設置して本体やプラグインの翻訳を上書きする方法あずみ.net に最初に表示されました。

【EC-CUBE4】WordPressのContact Form 7とVue.jsを使ってお問合せフォームを作る方法

$
0
0

WordPressのContact Form 7とVue.jsを使ってEC-CUBE4でお問合せフォームを作る方法です。

WordPressにContact Form 7をインストールしてお問合せフォームを作成

WordPressにContact Form 7というプラグインをインストールして、コンタクトフォームを新規作成して下さい。

コンタクトフォームを作成すると以下のようなショートコードが発行されます。

[contact-form-7 id="4167" title="サンプル"]

EC-CUBE4でお問合せフォームを作るときにショートコードのidを使用しますのでメモしておいて下さい。

EC-CUBE4のページ管理でお問い合わせページを作成

フォーム送信はContact Form 7のREST APIを使って行います。

EC-CUBE4のページ管理でお問合せフォーム用のページを作成して、以下のコードを貼り付けて下さい。

{% extends 'default_frame.twig' %}

{% block main %}
<div class="ec-layoutRole__contents">
    <div class="ec-layoutRole__main">
        <div class="ec-contactRole">
            <div class="ec-pageHeader">
                <h1>お問い合わせ</h1>
            </div>
        </div>
        <div class="ec-off1Grid">
            <div class="ec-off1Grid__cell">
        <form id="vueform" v-on:submit.prevent="onSubmit" ref="form" novalidate>
            <p>
                <label for="name">
                    お名前<br>
                    <input type="text" id="name" name="your-name" /><br />
                    <span class="wpcf7-form-control-wrap your-name"></span>
                </label>
            </p>
            <p>
                <label for="email">
                    メールアドレス<br>
                    <input type="text" id="email" name="your-email" /><br />
                    <span class="wpcf7-form-control-wrap your-email"></span>
                </label>
            </p>
            <p>
                <label for="subject">
                    題名<br>
                    <input type="text" id="subject" name="your-subject" /><br />
                    <span class="wpcf7-form-control-wrap your-subject"></span>
                </label>
            </p>
            <p>
                <label for="message">
                    メッセージ本文<br>
                    <textarea id="message" name="your-message"></textarea><br />
                    <span class="wpcf7-form-control-wrap your-message"></span>
                </label>
            </p>
            <input type="submit" value="送信" />
            <input type="hidden" name="_wpcf7" v-model="formId" />
            <input type="hidden" name="_wpcf7_unit_tag" v-model="unitTag" />
            <input type="hidden" name="_wpcf7_locale" value="ja" />
        </form>
            </div>
        </div>
    </div>
</div>
{% endblock %}

{% block javascript %}
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script>
var vm = new Vue({
    el: "#vueform",
    data: {
        formId: 4167 // 作成したContact Form 7のフォームIDを設定
    },
    computed: {
        unitTag() {
            return "wpcf7-f"+this.formId+"-o1";
        }
    },
    methods: {
        onSubmit() {
            $(".text-danger").empty();
            $(".alert").remove();
            
            const formData = new FormData(this.$refs.form);
            
            // 所有しているドメインに書き換えて下さい
            axios.post(`https://mydomain.com/wp-json/contact-form-7/v1/contact-forms/${this.formId}/feedback`, formData)
            .then(function(response) {
                switch(response.data.status) {
                    case "validation_failed":
                        for(i in response.data.invalidFields) {
                            $(response.data.invalidFields[i].into).text(response.data.invalidFields[i].message).addClass("text-danger")
                        }
                        break;
                    case "mail_sent":
                        $("form#vueform").append($("<p />").text(response.data.message).addClass("alert alert-success"));
                        $("form#vueform p input, form#vueform p textarea")val("");
                        break;
                    default:
                        $("form#vueform").append($("<p />").text(response.data.message).addClass("alert alert-danger"));
                }
            })
            .catch(function(error){
                $("form#vueform").append($("<p />").text("エラーが発生しました。").addClass("alert alert-danger"));
            })
            .finally(function(){
                $('.bg-load-overlay').remove();
            });
        },
    }
})
</script>
{% endblock %}

formIdにはContact Form 7で発行したショートコードのidを設定して下さい。

POST送信するときのドメインをコンタクトフォームを作成したWordPressのドメインに変更して下さい。

送信後やエラー時のメッセージの編集

送信後やエラー時のメッセージ編集はContact Form 7の編集画面で行えば反映されます。

注意

Contact Form 7のインテグレーションでreCAPTCHAを設定した場合、送信時にspam判定されてしまい送信出来ませんので、reCAPTCHAを設定している場合は解除する必要があります。

投稿 【EC-CUBE4】WordPressのContact Form 7とVue.jsを使ってお問合せフォームを作る方法あずみ.net に最初に表示されました。

[EC-CUBE4]ブロックでWordPressの記事一覧を表示する方法

$
0
0

EC-CUBE4のブロックでWordPressの記事一覧を表示する方法です。

Twig関数を用意

まずはWordPressのREST APIを取得するTwig関数を用意します。

<?php

namespace Customize\Twig\Extension;

use GuzzleHttp\Client;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class WordPressExtension extends AbstractExtension
{
    public function getFunctions(): array
    {
        return [
            new TwigFunction('wordpress', function($url){
                $client = new Client();
                $response = $client->request("GET", $url);
                if($response->getStatusCode() === 200) {
                    return json_decode($response->getBody()->getContents(), true);
                }
            }, ['pre_escape' => 'html', 'is_safe' => ['html']]),
        ];
    }
}

 

ブロック作成

次に管理画面にて適当にブロックを作成して以下を貼り付けて下さい。

一行目のwordpress関数のURLは任意のものを指定してください。

{% set posts = wordpress('https://a-zumi.net/wp-json/wp/v2/posts') %}

{% block javascript %}
<script>
$(function() {
    $('.ec-newsRole__newsHeading').on('click', function() {
        $newsItem = $(this).parent('.ec-newsRole__newsItem');
        $newsDescription = $newsItem.children('.ec-newsRole__newsDescription');
        if ($newsDescription.css('display') == 'none') {
            $newsItem.addClass('is_active');
            $newsDescription.slideDown(300);
        } else {
            $newsItem.removeClass('is_active');
            $newsDescription.slideUp(300);
        }
        return false;
    });
});
</script>
{% endblock %}
<div class="ec-role">
    <div class="ec-newsRole">
        <div class="ec-secHeading">
            <span class="ec-secHeading__en">WordPress</span>
            <span class="ec-secHeading__line"></span>
            <span class="ec-secHeading__ja">ワードプレス</span>
        </div>
        <div class="ec-newsRole__news">
            {% for post in posts %}
                <div class="ec-newsRole__newsItem">
                    <div class="ec-newsRole__newsHeading">
                        <div class="ec-newsRole__newsDate">
                            {{ post.date|date_day }}
                        </div>
                        <div class="ec-newsRole__newsColumn">
                            <div class="ec-newsRole__newsTitle">
                                {{ post.title.rendered }}
                            </div>
                            <div class="ec-newsRole__newsClose">
                                <a class="ec-newsRole__newsCloseBtn">
                                    <i class="fas fa-angle-down"></i>
                                </a>
                            </div>
                        </div>
                    </div>
                    <div class="ec-newsRole__newsDescription">
                        {{ post.excerpt.rendered|raw|nl2br }}
                        {% if post.link %}
                            <a href="{{ post.link }}" target="_blank">{{ 'front.block.news.see_details'|trans }}</a>
                        {% endif %}
                    </div>
                </div>
            {% endfor %}
        </div>
    </div>
</div>

 

レイアウト管理でブロック設置

レイアウト管理で任意の場所に上記のブロックを設置してください。

 

以上で完成です。

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

 

ちなみにこの実装方法だとアクセスするたびにワードプレスの記事を取得するので表示が遅くなります。

なので、取得データを一時的にキャッシュするか、PHPでデータを取得せずJavaScriptの非同期処理で取得することをおすすめします。

投稿 [EC-CUBE4]ブロックでWordPressの記事一覧を表示する方法あずみ.net に最初に表示されました。

【EC-CUBE4】コントローラーで{{ asset(‘file.jpg’, ‘save_image’) }}みたいにassetsの画像パスを取得する方法

$
0
0

コントローラーで使うことはほとんどないかもしれませんが、フォームなどで画像パスを設定したいときに使えるかもしれません。

取得方法は以下のとおりです。

$this->container->get("assets.packages")->getPackage('save_image')->getUrl("file.jpg")

 

投稿 【EC-CUBE4】コントローラーで{{ asset(‘file.jpg’, ‘save_image’) }}みたいにassetsの画像パスを取得する方法あずみ.net に最初に表示されました。

【EC-CUBE4】商品に商品規格を新規で設定するページの商品規格名に管理名を追加する方法

$
0
0

EC-CUBE4の商品に商品規格を新規で設定するページの商品規格名に管理名を追加する方法です。

以下のページに、

以下のように商品規格管理名を表示させます。

 

Customizeディレクトリに以下のProductClassMatrixTypeExtension.phpを設置してください。

<?php

namespace Customize\Form\Extension;

use Eccube\Form\Type\Admin\ProductClassMatrixType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\FormBuilderInterface;

class ProductClassMatrixTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $options = $builder->get('class_name1')->getOptions();
        $options["choice_label"] = function($class_name) {
            return $class_name->getName()."[".trans('admin.product.class_name__backend_name').trans('admin.common.separator__colon').$class_name->getBackendName()."]";
        };
        $builder->add('class_name1', EntityType::class, $options);

        $options = $builder->get('class_name2')->getOptions();
        $options["choice_label"] = function($class_name) {
            return $class_name->getName()."[".trans('admin.product.class_name__backend_name').trans('admin.common.separator__colon').$class_name->getBackendName()."]";
        };
        $builder->add('class_name2', EntityType::class, $options);
    }

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

 

以上で完成です。

投稿 【EC-CUBE4】商品に商品規格を新規で設定するページの商品規格名に管理名を追加する方法あずみ.net に最初に表示されました。

【EC-CUBE4】販売価格が変更されたときに何かする方法

$
0
0

EC-CUBE4で販売価格が変更されたときに何かする方法です。

Doctrineのイベントを使えばデータ変更の検知などいろんなことが出来ます。

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

<?php


namespace Customize\Doctrine\EventSubscriber;


use Doctrine\Common\EventSubscriber;
use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use Doctrine\ORM\Events;
use Eccube\Entity\ProductClass;

class PriceSubscriber implements EventSubscriber
{
    private $prevPrice02 = [];

    /**
     * @inheritDoc
     */
    public function getSubscribedEvents()
    {
        return [
            Events::postLoad,
            Events::postUpdate
        ];
    }

    /**
     * データ取得時に元データをメモリに保持
     *
     * @param LifecycleEventArgs $args
     */
    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if($entity instanceof ProductClass) {
            $this->prevPrice02[$entity->getId()] = $entity->getPrice02();
        }
    }

    /**
     * データ更新時に元データと更新データを比較
     *
     * @param LifecycleEventArgs $args
     */
    public function postUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof ProductClass) {
            // 販売価格が変更されたかチェック
            if ($this->prevPrice02[$entity->getId()] != $entity->getPrice02()) {
                // 何かする
            }
        }
    }
}

 

投稿 【EC-CUBE4】販売価格が変更されたときに何かする方法あずみ.net に最初に表示されました。

【EC-CUBE4】商品ごとに送料無料設定して購入手続き中に送料無料商品が含まれていたら送料無料にする方法

$
0
0

EC-CUBE4で商品ごとに送料無料設定して購入手続き中に送料無料商品が含まれていたら送料無料にする方法です。

dtb_productテーブルに送料無料フラグカラムを追加

送料無料商品があるか判定するために、dtb_productテーブルに送料無料フラグカラムを追加します。

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

<?php

namespace Customize\Entity;

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

/**
 * @EntityExtension("Eccube\Entity\Product")
 */
trait ProductTrait
{
    /**
     * @ORM\Column(type="boolean", nullable=true)
     * @FormAppend(
     *     auto_render=true,
     *     type="\Eccube\Form\Type\ToggleSwitchType",
     *     options={
     *          "label": "送料無料設定"
     *     }
     * )
     */
    private $delivery_free;

    public function isDeliveryFree(): ?bool
    {
        return $this->delivery_free;
    }

    public function setDeliveryFree(?bool $delivery_free): self
    {
        $this->delivery_free = $delivery_free;

        return $this;
    }
}

以下のように表示されます。

 

送料無料設定を有効化したらその商品は送料無料商品となります。

 

購入手続き中に送料無料設定商品があったら送料無料にする

EC-CUBE4で実装されたPurchaseFlowを使って購入手続き中に送料無料設定商品があったら送料無料にします。

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

<?php


namespace Customize\Service\PurchaceFlow\Validator;


use Eccube\Annotation\ShoppingFlow;
use Eccube\Entity\ItemHolderInterface;
use Eccube\Service\PurchaseFlow\ItemHolderPreprocessor;
use Eccube\Service\PurchaseFlow\PurchaseContext;

/**
 * 送料無料設定商品があったら送料無料にする
 *
 * Class DeliveryFreeValidator
 * @package Customize\Service\PurchaceFlow\Validator
 *
 * @ShoppingFlow()
 */
class DeliveryFreeValidator implements ItemHolderPreprocessor
{

    /**
     * @inheritDoc
     */
    public function process(ItemHolderInterface $itemHolder, PurchaseContext $context)
    {
        // 送料無料設定商品がないか探す
        $isDeliveryFree = false;
        foreach($itemHolder->getItems() as $item) {
            if($item->isProduct()) {
                if($item->getProductClass()->getProduct()->isDeliveryFree()) {
                    $isDeliveryFree = true;
                    break;
                }
            }
        }

        // 送料無料設定商品があったら送料明細の数量を0にする
        if($isDeliveryFree) {
            foreach($itemHolder->getItems() as $item) {
                if($item->isDeliveryFee()) {
                    $item->setQuantity(0);
                }
            }
        }
    }
}

 

注文明細一覧から送料無料商品を探して、送料無料商品が見つかったら送料明細の数量を0としています。

送料明細を0とすることで送料無料となります。

以上で完成です。

投稿 【EC-CUBE4】商品ごとに送料無料設定して購入手続き中に送料無料商品が含まれていたら送料無料にする方法あずみ.net に最初に表示されました。


【EC-CUBE4】新着商品ブロックの商品一覧を動的に取得する方法

$
0
0

EC-CUBE4の新着商品ブロックの商品一覧を動的に取得する方法です。

デフォルトの新着商品ブロックの商品一覧は静的なので動的に出力するようにして自動で新着商品を表示するようにします。

新着商品を取得するTwig関数を作成

新着商品を取得するTwig関数を作成します。

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

<?php

namespace Customize\Twig\Extension;

use Doctrine\Common\Collections\Criteria;
use Eccube\Repository\ProductRepository;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

class NewProductExtension extends AbstractExtension
{
    /**
     * @var ProductRepository
     */
    private $productRepository;

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

    public function getFunctions(): array
    {
        return [
            new TwigFunction('newProducts', function($limit){
                $qb = $this->productRepository->getQueryBuilderBySearchData([]);
                $qb
                    ->orderBy('p.create_date', Criteria::DESC)
                    ->setMaxResults($limit);

                return $qb->getQuery()->getResult();
            }, ['pre_escape' => 'html', 'is_safe' => ['html']]),
        ];
    }
}

 

ProductRepositoryのgetQueryBuilderBySearchDataメソッドからデータを取得することでCustomizeやプラグインでQueryCustomizerでカスタマイズされたクエリを取得できるようにしています。

これ重要です。

 

新着情報ブロックを更新

新着情報ブロックの内容を以下のように更新してください。

{% set products = newProducts(5) %}

<div class="ec-role">
    <div class="ec-newItemRole">
        <div class="ec-newItemRole__list">
            <div class="ec-newItemRole__listItem">
                <div class="ec-newItemRole__listItemHeading ec-secHeading--tandem">
                    <span class="ec-secHeading__en">{{ 'front.block.new_item.title__en'|trans }}</span>
                    <span class="ec-secHeading__line"></span>
                    <span class="ec-secHeading__ja">{{ 'front.block.new_item.title__ja'|trans }}</span>
                    <a class="ec-inlineBtn--top" href="{{ url('product_list') }}">{{ 'front.block.new_item.more'|trans }}</a>
                </div>
            </div>
            {% for p in products %}
            <div class="ec-newItemRole__listItem">
                <a href="{{ url('product_detail', {'id': p.id}) }}">
                    <img src="{{ asset(p.main_list_image|no_image_product, 'save_image') }}">
                    <p class="ec-newItemRole__listItemTitle">{{ p.name }}</p>
                    <p class="ec-newItemRole__listItemPrice">
                        {% if p.hasProductClass %}
                            {% if p.getPrice02Min == p.getPrice02Max %}
                                {{ p.getPrice02IncTaxMin|price }}
                            {% else %}
                                {{ p.getPrice02IncTaxMin|price }} ~ {{ p.getPrice02IncTaxMax|price }}
                            {% endif %}
                        {% else %}
                            {{ p.getPrice02IncTaxMin|price }}
                        {% endif %}
                    </p>
                </a>
            </div>
            {% endfor %}
        </div>
    </div>
</div>

 

以上で完成です。

newProductsの数値を変更することで取得する商品数を指定することができます。

投稿 【EC-CUBE4】新着商品ブロックの商品一覧を動的に取得する方法あずみ.net に最初に表示されました。

【EC-CUBE4】会員登録完了時にポイントを付与する方法

$
0
0

EC-CUBE4で会員登録完了時にポイントを付与する方法です。

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

会員登録完了時に1000ポイント付与しています。

<?php

namespace Customize\EventSubscriber;

use Doctrine\ORM\EntityManagerInterface;
use Eccube\Entity\Customer;
use Eccube\Event\EventArgs;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class EntryPointSubscriber implements EventSubscriberInterface
{
    /**
     * @var EntityManagerInterface
     */
    private $entityManager;

    public function __construct(EntityManagerInterface $entityManager)
    {
        $this->entityManager = $entityManager;
    }

    public function onFrontEntryActivateComplete(EventArgs $event)
    {
        $Customer = $event->getArgument("Customer");

        if($Customer instanceof Customer) {
            // 会員登録完了時に1000ポイント付与
            $Customer->setPoint(1000);
            $this->entityManager->persist($Customer);
            $this->entityManager->flush();
        }
    }

    public static function getSubscribedEvents()
    {
        return [
           'front.entry.activate.complete' => 'onFrontEntryActivateComplete',
        ];
    }
}

 

投稿 【EC-CUBE4】会員登録完了時にポイントを付与する方法あずみ.net に最初に表示されました。

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

$
0
0

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',
        ];
    }
}

 

以上で完成です。

投稿 【EC-CUBE4】商品に販売期間を設定する方法あずみ.net に最初に表示されました。

【EC-CUBE4】在庫が切れた商品をメールで通知する方法

$
0
0

EC-CUBE4で在庫が切れた商品をメールで通知する方法です。

サンプルソースは以下のとおりです。

<?php

namespace Customize\EventSubscriber;

use Eccube\Entity\BaseInfo;
use Eccube\Entity\Order;
use Eccube\Event\EventArgs;
use Eccube\Repository\BaseInfoRepository;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
 * 注文確定後、在庫切れ商品があったらメール通知
 *
 * Class OutOfStockNoticeSubscriber
 * @package Customize\EventSubscriber
 */
class OutOfStockNoticeSubscriber implements EventSubscriberInterface
{
    /**
     * @var \Swift_Mailer
     */
    private $mailer;

    /**
     * @var BaseInfoRepository
     */
    private $baseInfoRepository;

    public function __construct(
        \Swift_Mailer $mailer,
        BaseInfoRepository $baseInfoRepository
    ) {
        $this->mailer = $mailer;
        $this->baseInfoRepository = $baseInfoRepository;
    }

    public function onFrontShoppingCompleteInitialize(EventArgs $event)
    {
        $Order = $event->getArgument("Order");

        if($Order instanceof Order) {
            $productClasses = [];
            foreach($Order->getOrderItems() as $item) {
                // 注文情報から商品明細を探す
                if($item->isProduct()) {
                    // 在庫が切れた商品の商品名を格納
                    if($item->getProductClass()->getStockFind() === false) {
                        $productClasses[] = $item->getProductClass()->formattedProductName();
                    }
                }
            }

            // 在庫なし商品があった場合メール送信
            if($productClasses) {
                $baseInfo = $this->baseInfoRepository->get();
                $subject = '['.$baseInfo->getShopName().']在庫切れ通知';
                $body = "以下の商品の在庫が切れました。".PHP_EOL.PHP_EOL;
                foreach($productClasses as $productClassName) {
                    $body .= $productClassName.PHP_EOL;
                }

                $this->sendMail($baseInfo, $subject, $body);
            }
        }
    }

    public function sendMail(BaseInfo $baseInfo, $subject, $body)
    {
        $message = (new \Swift_Message())
            ->setSubject($subject)
            ->setFrom([$baseInfo->getEmail01() => $baseInfo->getShopName()])
            ->setTo([$baseInfo->getEmail01()])
            ->setReplyTo($baseInfo->getEmail03())
            ->setReturnPath($baseInfo->getEmail04());

        $message->setBody($body);
        $this->mailer->send($message);
    }

    public static function getSubscribedEvents()
    {
        return [
           'front.shopping.complete.initialize' => 'onFrontShoppingCompleteInitialize',
        ];
    }
}

 

投稿 【EC-CUBE4】在庫が切れた商品をメールで通知する方法あずみ.net に最初に表示されました。

【EC-CUBE4】商品ごとに購入年齢制限を設定する方法

$
0
0

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="integer", nullable=true)
     */
    private $age_limit;

    public function getAgeLimit(): ?int
    {
        return $this->age_limit;
    }

    public function setAgeLimit(?int $age_limit): self
    {
        $this->age_limit = $age_limit;

        return $this;
    }
}

 

商品登録フォームに年齢制限項目を追加

商品登録フォームに年齢制限項目を追加します。

<?php

namespace Customize\Form\Extension;

use Eccube\Form\Type\Admin\ProductType;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\FormBuilderInterface;

/**
 * 商品登録フォームに年齢制限を追加
 *
 * Class ProductTypeExtension
 * @package Customize\Form\Extension
 */
class ProductTypeExtension extends AbstractTypeExtension
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('age_limit', NumberType::class, [
                'label' => '年齢制限',
                'required' => false,
                'eccube_form_options' => [
                    'auto_render' => true
                ]
            ])
        ;
    }

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

 

購入手続きで年齢チェックするバリデーションを追加

PurchaseFlowを利用して購入手続きで年齢チェックをするバリデーションを追加します。

<?php


namespace Customize\Service\PurchaceFlow\Validator;


use Eccube\Annotation\ShoppingFlow;
use Eccube\Entity\Customer;
use Eccube\Entity\ItemInterface;
use Eccube\Service\PurchaseFlow\ItemValidator;
use Eccube\Service\PurchaseFlow\PurchaseContext;

/**
 * 商品毎に年齢制限チェック
 *
 * @ShoppingFlow()
 *
 * Class AgeLimitValidator
 * @package Customize\Service\PurchaceFlow\Validator
 */
class AgeLimitValidator extends ItemValidator
{
    /**
     * @inheritDoc
     */
    protected function validate(ItemInterface $item, PurchaseContext $context)
    {
        if(!$item->isProduct()) {
            return;
        }

        $User = $context->getUser();

        if($User instanceof Customer) {
            // 誕生日が登録されている場合
            if($User->getBirth()) {
                // 年齢を計算
                $age = floor(((new \DateTime())->format("Ymd") - $User->getBirth()->format("Ymd")) / 10000);

                $ageLimit = $item->getProductClass()->getProduct()->getAgeLimit();

                // 年齢制限の値と年齢を比較
                if($ageLimit >= $age) {
                    $this->throwInvalidItemException("「%product%」は{$ageLimit}歳以上じゃないと購入できません。", $item->getProductClass());
                }
            }
        }
    }

    protected function handle(ItemInterface $item, PurchaseContext $context)
    {
        $item->setQuantity(0);
    }
}

 

以上で完成です。

 

商品に制限年齢を登録していて顧客が誕生日を登録している場合、以下のように表示されます。

投稿 【EC-CUBE4】商品ごとに購入年齢制限を設定する方法あずみ.net に最初に表示されました。

Viewing all 271 articles
Browse latest View live