EC-CUBE4でメールアドレスではなく任意のユーザー名でログインする方法

EC-CUBE Advent Calendar 2020 17日目の記事です。

時々お客さんからの要望で、メールアドレスだけではなく任意のユーザー名でログインさせたいという要望があります。 もっと詰めていくと、会員登録時には「任意のユーザー名」「メールアドレス」「パスワード」だけで登録させたいというものまであります。

その場合、住所はどのタイミングで入力させるのかというと購入画面で入力させるようにすれば良いということですが、購入画面で購入者情報と配送情報を入力させる方法は別の機会に説明します。

今回は会員登録画面を「ユーザー名」「メールアドレス」「パスワード」だけの項目にし、ユーザー名でもログインできる方法を説明します。

Customerクラスにユーザー名を追加

Customerクラスにユーザー名の項目並びに、重複チェック用の関数を追加します。 全行を載せます。

  • src/Eccube/Entity/Customer.php
<?php

/*
 * This file is part of EC-CUBE
 *
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
 *
 * http://www.ec-cube.co.jp/
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eccube\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Validator\Mapping\ClassMetadata;

if (!class_exists('\Eccube\Entity\Customer')) {
    /**
     * Customer
     *
     * @ORM\Table(name="dtb_customer", uniqueConstraints={@ORM\UniqueConstraint(name="secret_key", columns={"secret_key"})}, indexes={@ORM\Index(name="dtb_customer_buy_times_idx", columns={"buy_times"}), @ORM\Index(name="dtb_customer_buy_total_idx", columns={"buy_total"}), @ORM\Index(name="dtb_customer_create_date_idx", columns={"create_date"}), @ORM\Index(name="dtb_customer_update_date_idx", columns={"update_date"}), @ORM\Index(name="dtb_customer_last_buy_date_idx", columns={"last_buy_date"}), @ORM\Index(name="dtb_customer_email_idx", columns={"email"})})
     * @ORM\InheritanceType("SINGLE_TABLE")
     * @ORM\DiscriminatorColumn(name="discriminator_type", type="string", length=255)
     * @ORM\HasLifecycleCallbacks()
     * @ORM\Entity(repositoryClass="Eccube\Repository\CustomerRepository")
     */
    class Customer extends \Eccube\Entity\AbstractEntity implements UserInterface
    {
        /**
         * @var int
         *
         * @ORM\Column(name="id", type="integer", options={"unsigned":true})
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="IDENTITY")
         */
        private $id;

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

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

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

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

        /**
         * @var string|null
         *
         * @ORM\Column(name="company_name", type="string", length=255, nullable=true)
         */
        private $company_name;

        /**
         * @var string|null
         *
         * @ORM\Column(name="postal_code", type="string", length=8, nullable=true)
         */
        private $postal_code;

        /**
         * @var string|null
         *
         * @ORM\Column(name="addr01", type="string", length=255, nullable=true)
         */
        private $addr01;

        /**
         * @var string|null
         *
         * @ORM\Column(name="addr02", type="string", length=255, nullable=true)
         */
        private $addr02;

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

        /**
         * @var string|null
         *
         * @ORM\Column(name="phone_number", type="string", length=14, nullable=true)
         */
        private $phone_number;

        /**
         * @var \DateTime|null
         *
         * @ORM\Column(name="birth", type="datetimetz", nullable=true)
         */
        private $birth;

        /**
         * @var string|null
         *
         * @ORM\Column(name="password", type="string", length=255)
         */
        private $password;

        /**
         * @var string|null
         *
         * @ORM\Column(name="salt", type="string", length=255, nullable=true)
         */
        private $salt;

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

        /**
         * @var \DateTime|null
         *
         * @ORM\Column(name="first_buy_date", type="datetimetz", nullable=true)
         */
        private $first_buy_date;

        /**
         * @var \DateTime|null
         *
         * @ORM\Column(name="last_buy_date", type="datetimetz", nullable=true)
         */
        private $last_buy_date;

        /**
         * @var string|null
         *
         * @ORM\Column(name="buy_times", type="decimal", precision=10, scale=0, nullable=true, options={"unsigned":true,"default":0})
         */
        private $buy_times = 0;

        /**
         * @var string|null
         *
         * @ORM\Column(name="buy_total", type="decimal", precision=12, scale=2, nullable=true, options={"unsigned":true,"default":0})
         */
        private $buy_total = 0;

        /**
         * @var string|null
         *
         * @ORM\Column(name="note", type="string", length=4000, nullable=true)
         */
        private $note;

        /**
         * @var string|null
         *
         * @ORM\Column(name="reset_key", type="string", length=255, nullable=true)
         */
        private $reset_key;

        /**
         * @var \DateTime|null
         *
         * @ORM\Column(name="reset_expire", type="datetimetz", nullable=true)
         */
        private $reset_expire;

        /**
         * @var string
         *
         * @ORM\Column(name="point", type="decimal", precision=12, scale=0, options={"unsigned":false,"default":0})
         */
        private $point = '0';

        /**
         * @var \DateTime
         *
         * @ORM\Column(name="create_date", type="datetimetz")
         */
        private $create_date;

        /**
         * @var \DateTime
         *
         * @ORM\Column(name="update_date", type="datetimetz")
         */
        private $update_date;

        /**
         * @var \Doctrine\Common\Collections\Collection
         *
         * @ORM\OneToMany(targetEntity="Eccube\Entity\CustomerFavoriteProduct", mappedBy="Customer", cascade={"remove"})
         */
        private $CustomerFavoriteProducts;

        /**
         * @var \Doctrine\Common\Collections\Collection
         *
         * @ORM\OneToMany(targetEntity="Eccube\Entity\CustomerAddress", mappedBy="Customer", cascade={"remove"})
         * @ORM\OrderBy({
         *     "id"="ASC"
         * })
         */
        private $CustomerAddresses;

        /**
         * @var \Doctrine\Common\Collections\Collection
         *
         * @ORM\OneToMany(targetEntity="Eccube\Entity\Order", mappedBy="Customer")
         */
        private $Orders;

        /**
         * @var \Eccube\Entity\Master\CustomerStatus
         *
         * @ORM\ManyToOne(targetEntity="Eccube\Entity\Master\CustomerStatus")
         * @ORM\JoinColumns({
         *   @ORM\JoinColumn(name="customer_status_id", referencedColumnName="id")
         * })
         */
        private $Status;

        /**
         * @var \Eccube\Entity\Master\Sex
         *
         * @ORM\ManyToOne(targetEntity="Eccube\Entity\Master\Sex")
         * @ORM\JoinColumns({
         *   @ORM\JoinColumn(name="sex_id", referencedColumnName="id")
         * })
         */
        private $Sex;

        /**
         * @var \Eccube\Entity\Master\Job
         *
         * @ORM\ManyToOne(targetEntity="Eccube\Entity\Master\Job")
         * @ORM\JoinColumns({
         *   @ORM\JoinColumn(name="job_id", referencedColumnName="id")
         * })
         */
        private $Job;

        /**
         * @var \Eccube\Entity\Master\Country
         *
         * @ORM\ManyToOne(targetEntity="Eccube\Entity\Master\Country")
         * @ORM\JoinColumns({
         *   @ORM\JoinColumn(name="country_id", referencedColumnName="id")
         * })
         */
        private $Country;

        /**
         * @var \Eccube\Entity\Master\Pref
         *
         * @ORM\ManyToOne(targetEntity="Eccube\Entity\Master\Pref")
         * @ORM\JoinColumns({
         *   @ORM\JoinColumn(name="pref_id", referencedColumnName="id")
         * })
         */
        private $Pref;

        /**
         * Constructor
         */
        public function __construct()
        {
            $this->CustomerFavoriteProducts = new \Doctrine\Common\Collections\ArrayCollection();
            $this->CustomerAddresses = new \Doctrine\Common\Collections\ArrayCollection();
            $this->Orders = new \Doctrine\Common\Collections\ArrayCollection();

            $this->setBuyTimes(0);
            $this->setBuyTotal(0);
        }

        /**
         * @return string
         */
        public function __toString()
        {
            return (string) ($this->getName01().' '.$this->getName02());
        }

        /**
         * {@inheritdoc}
         */
        public function getRoles()
        {
            return ['ROLE_USER'];
        }

        /**
         * {@inheritdoc}
         */
        public function getUsername()
        {
            return $this->email;
        }

        /**
         * {@inheritdoc}
         */
        public function eraseCredentials()
        {
        }

        // TODO: できればFormTypeで行いたい
        public static function loadValidatorMetadata(ClassMetadata $metadata)
        {
            $metadata->addConstraint(new UniqueEntity([
                'fields' => 'email',
                'message' => 'form_error.customer_already_exists',
                'repositoryMethod' => 'getNonWithdrawingCustomers',
            ]));

            $metadata->addConstraint(new UniqueEntity([
                'fields' => 'nickname',
                'message' => 'このユーザー名は既に登録されています。',
            ]));
        }

        /**
         * Get id.
         *
         * @return int
         */
        public function getId()
        {
            return $this->id;
        }

        /**
         * Set name01.
         *
         * @param string $name01
         *
         * @return Customer
         */
        public function setName01($name01)
        {
            $this->name01 = $name01;

            return $this;
        }

        /**
         * Get name01.
         *
         * @return string
         */
        public function getName01()
        {
            return $this->name01;
        }

        /**
         * Set name02.
         *
         * @param string $name02
         *
         * @return Customer
         */
        public function setName02($name02)
        {
            $this->name02 = $name02;

            return $this;
        }

        /**
         * Get name02.
         *
         * @return string
         */
        public function getName02()
        {
            return $this->name02;
        }

        /**
         * Set kana01.
         *
         * @param string|null $kana01
         *
         * @return Customer
         */
        public function setKana01($kana01 = null)
        {
            $this->kana01 = $kana01;

            return $this;
        }

        /**
         * Get kana01.
         *
         * @return string|null
         */
        public function getKana01()
        {
            return $this->kana01;
        }

        /**
         * Set kana02.
         *
         * @param string|null $kana02
         *
         * @return Customer
         */
        public function setKana02($kana02 = null)
        {
            $this->kana02 = $kana02;

            return $this;
        }

        /**
         * Get kana02.
         *
         * @return string|null
         */
        public function getKana02()
        {
            return $this->kana02;
        }

        /**
         * Set companyName.
         *
         * @param string|null $companyName
         *
         * @return Customer
         */
        public function setCompanyName($companyName = null)
        {
            $this->company_name = $companyName;

            return $this;
        }

        /**
         * Get companyName.
         *
         * @return string|null
         */
        public function getCompanyName()
        {
            return $this->company_name;
        }

        /**
         * Set postal_code.
         *
         * @param string|null $postal_code
         *
         * @return Customer
         */
        public function setPostalCode($postal_code = null)
        {
            $this->postal_code = $postal_code;

            return $this;
        }

        /**
         * Get postal_code.
         *
         * @return string|null
         */
        public function getPostalCode()
        {
            return $this->postal_code;
        }

        /**
         * Set addr01.
         *
         * @param string|null $addr01
         *
         * @return Customer
         */
        public function setAddr01($addr01 = null)
        {
            $this->addr01 = $addr01;

            return $this;
        }

        /**
         * Get addr01.
         *
         * @return string|null
         */
        public function getAddr01()
        {
            return $this->addr01;
        }

        /**
         * Set addr02.
         *
         * @param string|null $addr02
         *
         * @return Customer
         */
        public function setAddr02($addr02 = null)
        {
            $this->addr02 = $addr02;

            return $this;
        }

        /**
         * Get addr02.
         *
         * @return string|null
         */
        public function getAddr02()
        {
            return $this->addr02;
        }

        /**
         * Set email.
         *
         * @param string $email
         *
         * @return Customer
         */
        public function setEmail($email)
        {
            $this->email = $email;

            return $this;
        }

        /**
         * Get email.
         *
         * @return string
         */
        public function getEmail()
        {
            return $this->email;
        }

        /**
         * Set phone_number.
         *
         * @param string|null $phone_number
         *
         * @return Customer
         */
        public function setPhoneNumber($phone_number = null)
        {
            $this->phone_number = $phone_number;

            return $this;
        }

        /**
         * Get phone_number.
         *
         * @return string|null
         */
        public function getPhoneNumber()
        {
            return $this->phone_number;
        }

        /**
         * Set birth.
         *
         * @param \DateTime|null $birth
         *
         * @return Customer
         */
        public function setBirth($birth = null)
        {
            $this->birth = $birth;

            return $this;
        }

        /**
         * Get birth.
         *
         * @return \DateTime|null
         */
        public function getBirth()
        {
            return $this->birth;
        }

        /**
         * Set password.
         *
         * @param string|null $password
         *
         * @return Customer
         */
        public function setPassword($password = null)
        {
            $this->password = $password;

            return $this;
        }

        /**
         * Get password.
         *
         * @return string|null
         */
        public function getPassword()
        {
            return $this->password;
        }

        /**
         * Set salt.
         *
         * @param string|null $salt
         *
         * @return Customer
         */
        public function setSalt($salt = null)
        {
            $this->salt = $salt;

            return $this;
        }

        /**
         * Get salt.
         *
         * @return string|null
         */
        public function getSalt()
        {
            return $this->salt;
        }

        /**
         * Set secretKey.
         *
         * @param string $secretKey
         *
         * @return Customer
         */
        public function setSecretKey($secretKey)
        {
            $this->secret_key = $secretKey;

            return $this;
        }

        /**
         * Get secretKey.
         *
         * @return string
         */
        public function getSecretKey()
        {
            return $this->secret_key;
        }

        /**
         * Set firstBuyDate.
         *
         * @param \DateTime|null $firstBuyDate
         *
         * @return Customer
         */
        public function setFirstBuyDate($firstBuyDate = null)
        {
            $this->first_buy_date = $firstBuyDate;

            return $this;
        }

        /**
         * Get firstBuyDate.
         *
         * @return \DateTime|null
         */
        public function getFirstBuyDate()
        {
            return $this->first_buy_date;
        }

        /**
         * Set lastBuyDate.
         *
         * @param \DateTime|null $lastBuyDate
         *
         * @return Customer
         */
        public function setLastBuyDate($lastBuyDate = null)
        {
            $this->last_buy_date = $lastBuyDate;

            return $this;
        }

        /**
         * Get lastBuyDate.
         *
         * @return \DateTime|null
         */
        public function getLastBuyDate()
        {
            return $this->last_buy_date;
        }

        /**
         * Set buyTimes.
         *
         * @param string|null $buyTimes
         *
         * @return Customer
         */
        public function setBuyTimes($buyTimes = null)
        {
            $this->buy_times = $buyTimes;

            return $this;
        }

        /**
         * Get buyTimes.
         *
         * @return string|null
         */
        public function getBuyTimes()
        {
            return $this->buy_times;
        }

        /**
         * Set buyTotal.
         *
         * @param string|null $buyTotal
         *
         * @return Customer
         */
        public function setBuyTotal($buyTotal = null)
        {
            $this->buy_total = $buyTotal;

            return $this;
        }

        /**
         * Get buyTotal.
         *
         * @return string|null
         */
        public function getBuyTotal()
        {
            return $this->buy_total;
        }

        /**
         * Set note.
         *
         * @param string|null $note
         *
         * @return Customer
         */
        public function setNote($note = null)
        {
            $this->note = $note;

            return $this;
        }

        /**
         * Get note.
         *
         * @return string|null
         */
        public function getNote()
        {
            return $this->note;
        }

        /**
         * Set resetKey.
         *
         * @param string|null $resetKey
         *
         * @return Customer
         */
        public function setResetKey($resetKey = null)
        {
            $this->reset_key = $resetKey;

            return $this;
        }

        /**
         * Get resetKey.
         *
         * @return string|null
         */
        public function getResetKey()
        {
            return $this->reset_key;
        }

        /**
         * Set resetExpire.
         *
         * @param \DateTime|null $resetExpire
         *
         * @return Customer
         */
        public function setResetExpire($resetExpire = null)
        {
            $this->reset_expire = $resetExpire;

            return $this;
        }

        /**
         * Get resetExpire.
         *
         * @return \DateTime|null
         */
        public function getResetExpire()
        {
            return $this->reset_expire;
        }

        /**
         * Set createDate.
         *
         * @param \DateTime $createDate
         *
         * @return Customer
         */
        public function setCreateDate($createDate)
        {
            $this->create_date = $createDate;

            return $this;
        }

        /**
         * Get createDate.
         *
         * @return \DateTime
         */
        public function getCreateDate()
        {
            return $this->create_date;
        }

        /**
         * Set updateDate.
         *
         * @param \DateTime $updateDate
         *
         * @return Customer
         */
        public function setUpdateDate($updateDate)
        {
            $this->update_date = $updateDate;

            return $this;
        }

        /**
         * Get updateDate.
         *
         * @return \DateTime
         */
        public function getUpdateDate()
        {
            return $this->update_date;
        }

        /**
         * Add customerFavoriteProduct.
         *
         * @param \Eccube\Entity\CustomerFavoriteProduct $customerFavoriteProduct
         *
         * @return Customer
         */
        public function addCustomerFavoriteProduct(\Eccube\Entity\CustomerFavoriteProduct $customerFavoriteProduct)
        {
            $this->CustomerFavoriteProducts[] = $customerFavoriteProduct;

            return $this;
        }

        /**
         * Remove customerFavoriteProduct.
         *
         * @param \Eccube\Entity\CustomerFavoriteProduct $customerFavoriteProduct
         *
         * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
         */
        public function removeCustomerFavoriteProduct(\Eccube\Entity\CustomerFavoriteProduct $customerFavoriteProduct)
        {
            return $this->CustomerFavoriteProducts->removeElement($customerFavoriteProduct);
        }

        /**
         * Get customerFavoriteProducts.
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getCustomerFavoriteProducts()
        {
            return $this->CustomerFavoriteProducts;
        }

        /**
         * Add customerAddress.
         *
         * @param \Eccube\Entity\CustomerAddress $customerAddress
         *
         * @return Customer
         */
        public function addCustomerAddress(\Eccube\Entity\CustomerAddress $customerAddress)
        {
            $this->CustomerAddresses[] = $customerAddress;

            return $this;
        }

        /**
         * Remove customerAddress.
         *
         * @param \Eccube\Entity\CustomerAddress $customerAddress
         *
         * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
         */
        public function removeCustomerAddress(\Eccube\Entity\CustomerAddress $customerAddress)
        {
            return $this->CustomerAddresses->removeElement($customerAddress);
        }

        /**
         * Get customerAddresses.
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getCustomerAddresses()
        {
            return $this->CustomerAddresses;
        }

        /**
         * Add order.
         *
         * @param \Eccube\Entity\Order $order
         *
         * @return Customer
         */
        public function addOrder(\Eccube\Entity\Order $order)
        {
            $this->Orders[] = $order;

            return $this;
        }

        /**
         * Remove order.
         *
         * @param \Eccube\Entity\Order $order
         *
         * @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
         */
        public function removeOrder(\Eccube\Entity\Order $order)
        {
            return $this->Orders->removeElement($order);
        }

        /**
         * Get orders.
         *
         * @return \Doctrine\Common\Collections\Collection
         */
        public function getOrders()
        {
            return $this->Orders;
        }

        /**
         * Set status.
         *
         * @param \Eccube\Entity\Master\CustomerStatus|null $status
         *
         * @return Customer
         */
        public function setStatus(\Eccube\Entity\Master\CustomerStatus $status = null)
        {
            $this->Status = $status;

            return $this;
        }

        /**
         * Get status.
         *
         * @return \Eccube\Entity\Master\CustomerStatus|null
         */
        public function getStatus()
        {
            return $this->Status;
        }

        /**
         * Set sex.
         *
         * @param \Eccube\Entity\Master\Sex|null $sex
         *
         * @return Customer
         */
        public function setSex(\Eccube\Entity\Master\Sex $sex = null)
        {
            $this->Sex = $sex;

            return $this;
        }

        /**
         * Get sex.
         *
         * @return \Eccube\Entity\Master\Sex|null
         */
        public function getSex()
        {
            return $this->Sex;
        }

        /**
         * Set job.
         *
         * @param \Eccube\Entity\Master\Job|null $job
         *
         * @return Customer
         */
        public function setJob(\Eccube\Entity\Master\Job $job = null)
        {
            $this->Job = $job;

            return $this;
        }

        /**
         * Get job.
         *
         * @return \Eccube\Entity\Master\Job|null
         */
        public function getJob()
        {
            return $this->Job;
        }

        /**
         * Set country.
         *
         * @param \Eccube\Entity\Master\Country|null $country
         *
         * @return Customer
         */
        public function setCountry(\Eccube\Entity\Master\Country $country = null)
        {
            $this->Country = $country;

            return $this;
        }

        /**
         * Get country.
         *
         * @return \Eccube\Entity\Master\Country|null
         */
        public function getCountry()
        {
            return $this->Country;
        }

        /**
         * Set pref.
         *
         * @param \Eccube\Entity\Master\Pref|null $pref
         *
         * @return Customer
         */
        public function setPref(\Eccube\Entity\Master\Pref $pref = null)
        {
            $this->Pref = $pref;

            return $this;
        }

        /**
         * Get pref.
         *
         * @return \Eccube\Entity\Master\Pref|null
         */
        public function getPref()
        {
            return $this->Pref;
        }

        /**
         * Set point
         *
         * @param string $point
         *
         * @return Customer
         */
        public function setPoint($point)
        {
            $this->point = $point;

            return $this;
        }

        /**
         * Get point
         *
         * @return string
         */
        public function getPoint()
        {
            return $this->point;
        }

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

        /**
         * Set nickname
         *
         * @param string $nickname
         *
         * @return Customer
         */
        public function setNickname($nickname)
        {
            $this->nickname = $nickname;

            return $this;
        }

        /**
         * Get nickname
         *
         * @return string
         */
        public function getNickname()
        {
            return $this->nickname;
        }
    }
}

nicknameという名前で項目名を追加しています。

また、loadValidatorMetadata関数に

$metadata->addConstraint(new UniqueEntity([
    'fields' => 'nickname',
    'message' => 'このユーザー名は既に登録されています。',
]));

という定義を追加して重複チェックを行っています。

このloadValidatorMetadata関数は重複チェックを行ったりするのに非常に便利なので覚えておいてください。

項目を追加すれば実際にdtb_customerテーブルへ項目を追加する必要がありますが、今回はdoctrineコマンドを利用して作成します。

EC-CUBEディレクトリ直下にコマンドプロンプトやターミナルで移動します。

どのようなSQL文が実行されるかを以下のコマンドで確認します。

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

実際にSQL文をするために、以下のコマンドを実行します。

php bin/console doctrine:schema:update --force

もし、何も実行されないという方は、var/cacheディレクトリを削除して試してください。 それでも何も起きないという方は、以下の順序でコマンドを実行してもらい、再度doctrineコマンドを実行してください。

rm -rf app/proxy/entity/src
composer dump-autoload;
php bin/console eccube:generate:proxies;
rm -rf var/cache

FormTypeの作成、EntryContorllerの修正

会員登録画面で利用するFormTypeですがsrc/Eccube/Form/Type/Front/EntryType.phpとなりますが、これはそのままにしておき、新たにEntryMailTypeクラスを作成します。

また、メールアドレスの入力も確認用のメールアドレスを省いて登録させるようにします。

  • src/Eccube/Form/Type/Front/EntryMailType.php
<?php

namespace Eccube\Form\Type\Front;

use Eccube\Common\EccubeConfig;
use Eccube\Entity\Customer;
use Eccube\Form\Type\RepeatedPasswordType;
use Eccube\Form\Validator\Email;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints as Assert;

class EntryMailType extends AbstractType
{
    /**
     * @var EccubeConfig
     */
    protected $eccubeConfig;

    /**
     * EntryType constructor.
     *
     * @param EccubeConfig $eccubeConfig
     */
    public function __construct(EccubeConfig $eccubeConfig)
    {
        $this->eccubeConfig = $eccubeConfig;
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('nickname', TextType::class, [
                'constraints' => [
                    new Assert\NotBlank(),
                    new Assert\Length([
                        'min' => 3,
                        'max' => 30,
                    ]),
                    new Assert\Regex([
                        'pattern' => '/^[a-zA-Z0-9-_]+$/',
                        'message' => 'form_error.graph_only',
                    ]),
                ],
            ])
            ->add('email', EmailType::class, [
                'constraints' => [
                    new Assert\NotBlank(),
                    new Email(['strict' => $this->eccubeConfig['eccube_rfc_email_check']]),
                ],
            ])
            ->add('password', RepeatedPasswordType::class);

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
            $Customer = $event->getData();
            if ($Customer instanceof Customer && !$Customer->getId()) {
                $form = $event->getForm();

                $form->add('user_policy_check', CheckboxType::class, [
                    'label' => null,
                    'mapped' => false,
                    'constraints' => [
                        new Assert\NotBlank(),
                    ],
                ]);
            }
        }
        );
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => 'Eccube\Entity\Customer',
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'entry_mail';
    }
}

次にEntryMailTypeクラスを利用するためにEntryControllerクラスを修正します。

  • src/Eccube/Controller/EntryController.php
/* @var $builder \Symfony\Component\Form\FormBuilderInterface */
$builder = $this->formFactory->createBuilder(EntryType::class, $Customer);

use Eccube\Form\Type\Front\EntryMailType;
・
・
・
/* @var $builder \Symfony\Component\Form\FormBuilderInterface */
$builder = $this->formFactory->createBuilder(EntryMailType::class, $Customer);

を追加、変更します。

次に182行目付近(flushの手前)で以下の行も追加します。

// null対応
$Customer->setName01('')
    ->setName02('');

上記でユーザー名での登録準備が整いました。次にtwigファイル及びメール本文の修正を行います。

twigファイル、メール本文の修正

会員登録画面で不要な項目を削除し、ユーザー名を設定するための項目を追加します。

先ず、Entry/index.twigファイルの修正を行います。今回は直接本体側のファイルを修正します。

  • src/Eccube/Resource/template/default/Entry/index.twig
{#
This file is part of EC-CUBE

Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.

http://www.ec-cube.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 = 'registration_page' %}

{% form_theme form 'Form/form_div_layout.twig' %}

{% block main %}
    <div class="ec-registerRole">
        <div class="ec-pageHeader">
            <h1>{{ 'front.entry.title'|trans }}</h1>
        </div>
        <div class="ec-off1Grid">
            <div class="ec-off1Grid__cell">
                <form method="post" action="{{ url('entry') }}" novalidate>
                    {{ form_widget(form._token) }}
                    <div class="ec-borderedDefs">
                        <dl>
                            <dt>
                                {{ form_label(form.nickname, 'ユーザー名', { 'label_attr': { 'class': 'ec-label' }}) }}
                            </dt>
                            <dd>
                                <div class="ec-halfInput{{ has_errors(form.nickname) ? ' error' }}">
                                    {{ form_widget(form.nickname) }}
                                    {{ form_errors(form.nickname) }}
                                </div>
                            </dd>
                        </dl>
                        <dl>
                            <dt>
                                {{ form_label(form.email, 'common.mail_address', { 'label_attr': { 'class': 'ec-label' }}) }}
                            </dt>
                            <dd>
                                <div class="ec-input{{ has_errors(form.email.first) ? ' error' }}">
                                    {{ form_widget(form.email.first, { 'attr': { 'placeholder': 'common.mail_address_sample' }}) }}
                                    {{ form_errors(form.email.first) }}
                                </div>
                                <div class="ec-input{{ has_errors(form.email.second) ? ' error' }}">
                                    {{ form_widget(form.email.second, { 'attr': { 'placeholder': 'common.repeated_confirm' }}) }}
                                    {{ form_errors(form.email.second) }}
                                </div>
                            </dd>
                        </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>
                        {# エンティティ拡張の自動出力 #}
                        {% for f in form if f.vars.eccube_form_options.auto_render %}
                            {% if f.vars.eccube_form_options.form_theme %}
                                {% form_theme f f.vars.eccube_form_options.form_theme %}
                                {{ form_row(f) }}
                            {% else %}
                                <dl>
                                    <dt>
                                        {{ form_label(f) }}
                                    </dt>
                                    <dd>
                                        <div class="{{ f.vars.eccube_form_options.style_class }}{{ has_errors(f) ? ' error' }}">
                                            {{ form_widget(f) }}
                                            {{ form_errors(f) }}
                                        </div>
                                    </dd>
                                </dl>
                            {% endif %}
                        {% endfor %}
                    </div>
                    <div class="ec-registerRole__actions">
                        <div class="ec-off4Grid">
                            <div class="ec-off4Grid__cell">
                                <div class="ec-checkbox{{ has_errors(form.user_policy_check) ? ' error' }}">
                                    <label>
                                        {{ form_widget(form.user_policy_check) }}
                                        {{ 'front.entry.agree_with_terms'|trans({ '%url%': url('help_agreement') })|raw }}
                                    </label>
                                    {{ form_errors(form.user_policy_check) }}
                                </div>
                                <button class="ec-blockBtn--action" type="submit" name="mode" value="confirm">{{ 'front.entry.agree'|trans }}</button>
                                <a class="ec-blockBtn--cancel" href="{{ url('homepage') }}">{{ 'front.entry.disagree'|trans }}</a>
                            </div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
{% endblock %}

確認画面も変更します。

  • src/Eccube/Resource/template/default/Entry/confirm.twig
{#
This file is part of EC-CUBE

Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.

http://www.ec-cube.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 = 'registration_page' %}

{% form_theme form 'Form/form_div_layout.twig' %}

{% block main %}
    <div class="ec-registerRole">
        <div class="ec-pageHeader">
            <h1>{{ 'front.entry.confirm_title'|trans }}</h1>
        </div>
        <div class="ec-off1Grid">
            <div class="ec-off1Grid__cell">
                <form method="post" action="{{ url('entry') }}">
                    {{ form_widget(form._token) }}
                    <p>{{ 'front.entry.confirm_message'|trans|nl2br }}</p>
                    <div class="ec-borderedDefs">
                        <dl>
                            <dt>
                                {{ form_label(form.nickname, 'ユーザー名', { 'label_attr': { 'class': 'ec-label'}}) }}
                            </dt>
                            <dd>
                                {{ form.nickname.vars.data }}
                                {{ form_widget(form.nickname, { type : 'hidden' }) }}
                            </dd>
                        </dl>
                        <dl>
                            <dt>
                                {{ form_label(form.email, 'common.mail_address', {'label_attr': {'class': 'ec-label'}}) }}
                            </dt>
                            <dd>
                                {{ form.email.vars.data }}
                                {{ form_widget(form.email, { type : 'hidden' }) }}
                            </dd>
                        </dl>
                        <dl>
                            <dt>
                                {{ form_label(form.password, 'common.password', { 'label_attr': { 'class': 'ec-label' }}) }}
                            </dt>
                            <dd>
                                ********
                                {{ form_widget(form.password.first, { type : 'hidden' }) }}
                                {{ form_widget(form.password.second, { type : 'hidden' }) }}
                            </dd>
                        </dl>

                        {{ form_widget(form.user_policy_check, { type: 'hidden'}) }}

                        {# エンティティ拡張の自動出力 #}
                        {% for f in form if f.vars.eccube_form_options.auto_render %}
                            {% if f.vars.eccube_form_options.form_theme %}
                                {% form_theme f f.vars.eccube_form_options.form_theme %}
                                {{ form_row(f) }}
                            {% else %}
                                <dl>
                                    <dt>
                                        {{ form_label(f) }}
                                    </dt>
                                    <dd>
                                        <div class="{{ f.vars.eccube_form_options.style_class }}{{ has_errors(f) ? ' error' }}">
                                            {{ f.vars.data }}
                                            {{ form_widget(f, { type: 'hidden'}) }}
                                        </div>
                                    </dd>
                                </dl>
                            {% endif %}
                        {% endfor %}
                    </div>
                    <div class="ec-registerRole__actions">
                        <div class="ec-off4Grid">
                            <div class="ec-off4Grid__cell">
                                <button class="ec-blockBtn--action" type="submit" name="mode" value="complete">{{ 'front.entry.do_register'|trans }}</button>
                                <button class="ec-blockBtn--cancel" type="submit" name="mode" value="back">{{ 'common.back'|trans }}</button>
                            </div>
                        </div>
                    </div>
                </form>
            </div>
        </div>
    </div>
{% endblock %}

次にメール本文の以下の箇所も修正します。

  • app/template/default/Mail/entry_complete.html.twig
  • app/template/default/Mail/entry_complete.twig
  • app/template/default/Mail/entry_confirm.html.twig
  • app/template/default/Mail/entry_confirm.twig
{{ Customer.name01 }} {{ Customer.name02 }} 様
↓
{{ Customer.nickname }}様

以上でユーザー名で会員登録ができるようになりました。

ログイン画面の修正

標準ではメールアドレスしか受け付けないため、ユーザー名も許容させるように修正を行います。

先ずは、ログイン画面でメールアドレス以外も入力できるようにします。

  • src/Eccube/Form/Type/Front/CustomerLoginType.php
<?php

/*
 * This file is part of EC-CUBE
 *
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
 *
 * http://www.ec-cube.co.jp/
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

namespace Eccube\Form\Type\Front;

use Eccube\Common\EccubeConfig;
use Eccube\Form\Validator\Email;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
use Symfony\Component\Validator\Constraints as Assert;

class CustomerLoginType extends AbstractType
{
    /**
     * @var EccubeConfig
     */
    protected $eccubeConfig;

    /**
     * @var AuthenticationUtils
     */
    protected $authenticationUtils;

    public function __construct(AuthenticationUtils $authenticationUtils, EccubeConfig $eccubeConfig)
    {
        $this->authenticationUtils = $authenticationUtils;
        $this->eccubeConfig = $eccubeConfig;
    }

    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('login_email', TextType::class, [
            'attr' => [
                'maxlength' => $this->eccubeConfig['eccube_stext_len'],
            ],
            'constraints' => [
                new Assert\NotBlank(),
                // new Email(['strict' => $this->eccubeConfig['eccube_rfc_email_check']]),
            ],
            'data' => $this->authenticationUtils->getLastUsername(),
        ]);
        $builder->add('login_memory', CheckboxType::class, [
            'required' => false,
        ]);
        $builder->add('login_pass', PasswordType::class, [
            'attr' => [
                'maxlength' => $this->eccubeConfig['eccube_stext_len'],
            ],
            'constraints' => [
                new Assert\NotBlank(),
            ],
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'customer_login';
    }
}

メールアドレスの箇所をTextTypeに変更して入力チェックも修正します。

次に、ログイン処理を行っている箇所を修正します。変更箇所はCustomerRepositoryクラスとCustomerProviderクラスを修正します。

CustomerRepositoryクラスに以下の関数を追加します。

  • src/Eccube/Repository/CustomerRepository.php
/**
 * @param $username
 * @return mixed
 * @throws \Doctrine\ORM\NonUniqueResultException
 */
public function loadUserByUsername($username)
{
    // 本会員ステータスの会員のみ有効.
    $query = $this->createQueryBuilder('c')
        ->where('c.email = :email or c.nickname = :nickname')
        ->andWhere('c.Status =:status')
        ->setParameters(array(
            'email' => $username,
            'nickname' => $username,
            'status' => CustomerStatus::REGULAR,
        ))
        ->setMaxResults(1)
        ->getQuery();

    $Customer = $query->getOneOrNullResult();

    return $Customer;
}

CustomerProviderクラスのloadUserByUsername関数を以下のように修正します。

  • src/Eccube/Security/Core/User/CustomerProvider.php
public function loadUserByUsername($username)
{
    // $Customer = $this->customerRepository->findOneBy([
    //     'email' => $username,
    //     'Status' => CustomerStatus::REGULAR,
    // ]);

    $Customer = $this->customerRepository->loadUserByUsername($username);

    if (null === $Customer) {
        throw new UsernameNotFoundException(sprintf('Username "%s" does not exist.', $username));
    }

    return $Customer;
}

ログイン画面のメールアドレス項目のplaceholderを修正します。

  • src/Eccube/Resource/template/default/Mypage/login.twig
{{ form_widget(form.login_email, {'attr': {'style' : 'ime-mode: disabled;', 'placeholder' : 'メールアドレスまたはユーザー名', 'autofocus': true}}) }}

以上で任意のユーザー名を利用してログインできるようになりました。 後、対応しておく必要のある箇所は、

  • src/Eccube/Resource/template/default/Mypage/change.twig
    →nickname項目の追加

  • src/Eccube/Resource/template/admin/Customer/edit.twig

  • src/Eccube/Resource/template/admin/Customer/index.twig

  • src/Eccube/Form/Type/Admin/CustomerType.php
    →会員管理画面でnickname項目の追加

要望は少ないとは思いますが、メールアドレスでログイン以外の方法が欲しいと言われたときはこちらの方法を使ってみるのが一番楽だと思います。