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

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

以上で完成です。

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

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

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

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

 

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

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

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