Chapter 03

バックエンド - CRUD

ta.toshio
ta.toshio
2021.06.06に更新

customerを例に

一覧

src/Eccube/Controller/Admin/Customer/CustomerController.php

    /**
     * @Route("/%eccube_admin_route%/customer", name="admin_customer")
     * @Route("/%eccube_admin_route%/customer/page/{page_no}", requirements={"page_no" = "\d+"}, name="admin_customer_page")
     * @Template("@admin/Customer/index.twig")
     */
    public function index(Request $request, $page_no = null, PaginatorInterface $paginator)
    {
        $session = $this->session;
        $builder = $this->formFactory->createBuilder(SearchCustomerType::class);
...
        $searchForm = $builder->getForm();

        $pageMaxis = $this->pageMaxRepository->findAll();
        $pageCount = $session->get('eccube.admin.customer.search.page_count', $this->eccubeConfig['eccube_default_page_count']);
        $pageCountParam = $request->get('page_count');
...

        if ('POST' === $request->getMethod()) {
	    // 検索実行時

            $searchForm->handleRequest($request);
            if ($searchForm->isValid()) {
                $searchData = $searchForm->getData();
                $page_no = 1;

		// Formオブジェクトを辞書に変換
		// どのような値に変換するか下に記載する
                $session->set('eccube.admin.customer.search', FormUtil::getViewData($searchForm));
                $session->set('eccube.admin.customer.search.page_no', $page_no);
            } else {
	        // 検索フォームが値が不正時
                return [
                    'searchForm' => $searchForm->createView(),
                    'pagination' => [],
                    'pageMaxis' => $pageMaxis,
                    'page_no' => $page_no,
                    'page_count' => $pageCount,
                    'has_errors' => true,
                ];
            }
        } else {
	    // GET(POST以外)時
            if (null !== $page_no || $request->get('resume')) {
                if ($page_no) {
                    $session->set('eccube.admin.customer.search.page_no', (int) $page_no);
                } else {
                    $page_no = $session->get('eccube.admin.customer.search.page_no', 1);
                }
                $viewData = $session->get('eccube.admin.customer.search', []);
            } else {
                $page_no = 1;
                $viewData = FormUtil::getViewData($searchForm);
                $session->set('eccube.admin.customer.search', $viewData);
                $session->set('eccube.admin.customer.search.page_no', $page_no);
            }
            $searchData = FormUtil::submitAndGetData($searchForm, $viewData);
        }

        /** @var QueryBuilder $qb */
        $qb = $this->customerRepository->getQueryBuilderBySearchData($searchData);

...
        $pagination = $paginator->paginate(
            $qb,
            $page_no,
            $pageCount
        );

        return [
            'searchForm' => $searchForm->createView(),
            'pagination' => $pagination,
            'pageMaxis' => $pageMaxis,
            'page_no' => $page_no,
            'page_count' => $pageCount,
            'has_errors' => false,
        ];
    }

検索フォーム

フォーム定義

src/Eccube/Form/Type/Admin/SearchCustomerType.php

ヘルパー

src/Eccube/Util/FormUtil.php

    /**
     * formオブジェクトからviewDataを取得する.
     *
     * @param FormInterface $form
     *   この例では最初は 
     *   `$builder = $this->formFactory->createBuilder(SearchCustomerType::class);`
     *   `$searchForm = $builder->getForm();`
     *   の`$searchForm`がセットされている
     *
     * @return array
     */
    public static function getViewData(FormInterface $form)
    {
        $viewData = [];
        $forms = $form->all();

        if (empty($forms)) {
            return $form->getViewData();
        }

        foreach ($forms as $key => $value) {
            // choice typeは各選択肢もFormとして扱われるため再帰しない.
            if ($value->getConfig()->hasOption('choices')) {
                $viewData[$key] = $value->getViewData();
            } else {
                $viewData[$key] = self::getViewData($value);
            }
        }

/*
$viewDataには以下のような値が格納された
array (
  'multi' => '',
  'customer_status' => 
  array (
    0 => '1',
    1 => '2',
  ),
  'sex' => 
  array (
    0 => '1',
  ),
  'birth_month' => '1',
  'birth_start' => '',
  'birth_end' => '2021-06-01',
  'pref' => '13',
  'phone_number' => '01012341234',
  'buy_product_name' => 'ジェラート',
  'buy_total_start' => '',
  'buy_total_end' => '200,000',
  'buy_times_start' => '',
  'buy_times_end' => '200',
  'create_date_start' => '',
  'create_datetime_start' => '',
  'create_date_end' => '',
  'create_datetime_end' => '2021-06-30 13:32:52',
  'update_date_start' => '',
  'update_datetime_start' => '',
  'update_date_end' => '',
  'update_datetime_end' => '2021-06-30 13:32:52',
  'last_buy_start' => '',
  'last_buy_datetime_start' => '',
  'last_buy_end' => '2021-06-30',
  'last_buy_datetime_end' => '',
)
*/
        return $viewData;
    }

検索クエリビルダー

src/Eccube/Repository/CustomerRepository.php

    public function getQueryBuilderBySearchData($searchData)
    {
        $qb = $this->createQueryBuilder('c')
            ->select('c');

        if (isset($searchData['multi']) && StringUtil::isNotBlank($searchData['multi'])) {
            //スペース除去
            $clean_key_multi = preg_replace('/\s+|[ ]+/u', '', $searchData['multi']);
            $id = preg_match('/^\d{0,10}$/', $clean_key_multi) ? $clean_key_multi : null;
            if ($id && $id > '2147483647' && $this->isPostgreSQL()) {
                $id = null;
            }
            $qb
                ->andWhere('c.id = :customer_id OR CONCAT(c.name01, c.name02) LIKE :name OR CONCAT(c.kana01, c.kana02) LIKE :kana OR c.email LIKE :email')
                ->setParameter('customer_id', $id)
                ->setParameter('name', '%'.$clean_key_multi.'%')
                ->setParameter('kana', '%'.$clean_key_multi.'%')
                ->setParameter('email', '%'.$clean_key_multi.'%');
        }

        // Pref
        if (!empty($searchData['pref']) && $searchData['pref']) {
            $qb
                ->andWhere('c.Pref = :pref')
                ->setParameter('pref', $searchData['pref']->getId());
        }

        // sex
        if (!empty($searchData['sex']) && count($searchData['sex']) > 0) {
            $sexs = [];
            foreach ($searchData['sex'] as $sex) {
                $sexs[] = $sex->getId();
            }

            $qb
                ->andWhere($qb->expr()->in('c.Sex', ':sexs'))
                ->setParameter('sexs', $sexs);
        }

        if (!empty($searchData['birth_month']) && $searchData['birth_month']) {
            $qb
                ->andWhere('EXTRACT(MONTH FROM c.birth) = :birth_month')
                ->setParameter('birth_month', $searchData['birth_month']);
        }

        // birth
        if (!empty($searchData['birth_start']) && $searchData['birth_start']) {
            $qb
                ->andWhere('c.birth >= :birth_start')
                ->setParameter('birth_start', $searchData['birth_start']);
        }
        if (!empty($searchData['birth_end']) && $searchData['birth_end']) {
            $date = clone $searchData['birth_end'];
            $date->modify('+1 days');
            $qb
                ->andWhere('c.birth < :birth_end')
                ->setParameter('birth_end', $date);
        }

        // tel
        if (isset($searchData['phone_number']) && StringUtil::isNotBlank($searchData['phone_number'])) {
            $tel = preg_replace('/[^0-9]/ ', '', $searchData['phone_number']);
            $qb
                ->andWhere('c.phone_number LIKE :phone_number')
                ->setParameter('phone_number', '%'.$tel.'%');
        }

        // buy_total
        if (isset($searchData['buy_total_start']) && StringUtil::isNotBlank($searchData['buy_total_start'])) {
            $qb
                ->andWhere('c.buy_total >= :buy_total_start')
                ->setParameter('buy_total_start', $searchData['buy_total_start']);
        }
        if (isset($searchData['buy_total_end']) && StringUtil::isNotBlank($searchData['buy_total_end'])) {
            $qb
                ->andWhere('c.buy_total <= :buy_total_end')
                ->setParameter('buy_total_end', $searchData['buy_total_end']);
        }

        // buy_times
        if (isset($searchData['buy_times_start']) && StringUtil::isNotBlank($searchData['buy_times_start'])) {
            $qb
                ->andWhere('c.buy_times >= :buy_times_start')
                ->setParameter('buy_times_start', $searchData['buy_times_start']);
        }
        if (isset($searchData['buy_times_end']) && StringUtil::isNotBlank($searchData['buy_times_end'])) {
            $qb
                ->andWhere('c.buy_times <= :buy_times_end')
                ->setParameter('buy_times_end', $searchData['buy_times_end']);
        }

        // create_date
        if (!empty($searchData['create_datetime_start']) && $searchData['create_datetime_start']) {
            $date = $searchData['create_datetime_start'];
            $qb
                ->andWhere('c.create_date >= :create_date_start')
                ->setParameter('create_date_start', $date);
        } elseif (!empty($searchData['create_date_start']) && $searchData['create_date_start']) {
            $qb
                ->andWhere('c.create_date >= :create_date_start')
                ->setParameter('create_date_start', $searchData['create_date_start']);
        }

        if (!empty($searchData['create_datetime_end']) && $searchData['create_datetime_end']) {
            $date = $searchData['create_datetime_end'];
            $qb
                ->andWhere('c.create_date < :create_date_end')
                ->setParameter('create_date_end', $date);
        } elseif (!empty($searchData['create_date_end']) && $searchData['create_date_end']) {
            $date = clone $searchData['create_date_end'];
            $date->modify('+1 days');
            $qb
                ->andWhere('c.create_date < :create_date_end')
                ->setParameter('create_date_end', $date);
        }

        // update_date
        if (!empty($searchData['update_datetime_start']) && $searchData['update_datetime_start']) {
            $date = $searchData['update_datetime_start'];
            $qb
                ->andWhere('c.update_date >= :update_date_start')
                ->setParameter('update_date_start', $date);
        } elseif (!empty($searchData['update_date_start']) && $searchData['update_date_start']) {
            $qb
                ->andWhere('c.update_date >= :update_date_start')
                ->setParameter('update_date_start', $searchData['update_date_start']);
        }

        if (!empty($searchData['update_datetime_end']) && $searchData['update_datetime_end']) {
            $date = $searchData['update_datetime_end'];
            $qb
                ->andWhere('c.update_date < :update_date_end')
                ->setParameter('update_date_end', $date);
        } elseif (!empty($searchData['update_date_end']) && $searchData['update_date_end']) {
            $date = clone $searchData['update_date_end'];
            $date->modify('+1 days');
            $qb
                ->andWhere('c.update_date < :update_date_end')
                ->setParameter('update_date_end', $date);
        }

        // last_buy
        if (!empty($searchData['last_buy_datetime_start']) && $searchData['last_buy_datetime_start']) {
            $date = $searchData['last_buy_datetime_start'];
            $qb
                ->andWhere('c.last_buy_date >= :last_buy_start')
                ->setParameter('last_buy_start', $date);
        } elseif (!empty($searchData['last_buy_start']) && $searchData['last_buy_start']) {
            $qb
                ->andWhere('c.last_buy_date >= :last_buy_start')
                ->setParameter('last_buy_start', $searchData['last_buy_start']);
        }

        if (!empty($searchData['last_buy_datetime_end']) && $searchData['last_buy_datetime_end']) {
            $date = $searchData['last_buy_datetime_end'];
            $qb
                ->andWhere('c.last_buy_date < :last_buy_end')
                ->setParameter('last_buy_end', $date);
        } elseif (!empty($searchData['last_buy_end']) && $searchData['last_buy_end']) {
            $date = clone $searchData['last_buy_end'];
            $date->modify('+1 days');
            $qb
                ->andWhere('c.last_buy_date < :last_buy_end')
                ->setParameter('last_buy_end', $date);
        }

        // status
        if (!empty($searchData['customer_status']) && count($searchData['customer_status']) > 0) {
            $qb
                ->andWhere($qb->expr()->in('c.Status', ':statuses'))
                ->setParameter('statuses', $searchData['customer_status']);
        }

        // buy_product_name
        if (isset($searchData['buy_product_name']) && StringUtil::isNotBlank($searchData['buy_product_name'])) {
            $qb
                ->leftJoin('c.Orders', 'o')
                ->leftJoin('o.OrderItems', 'oi')
                ->andWhere('oi.product_name LIKE :buy_product_name')
                ->andWhere($qb->expr()->notIn('o.OrderStatus', ':order_status'))
                ->setParameter('buy_product_name', '%'.$searchData['buy_product_name'].'%')
                ->setParameter('order_status', [OrderStatus::PROCESSING, OrderStatus::PENDING]);
        }

        // Order By
        $qb->addOrderBy('c.update_date', 'DESC');

        return $this->queries->customize(QueryKey::CUSTOMER_SEARCH, $qb, $searchData);
    }

以下、検索処理のシーケンスで実行されたSQLを記載

検索条件にヒットするcustomer.idを取得。

SELECT DISTINCT
    d0_.id AS id_0,
    d0_.update_date AS update_date_1
FROM
    dtb_customer d0_
    LEFT JOIN
        dtb_order d1_
    ON  d0_.id = d1_.customer_id
    AND d1_.discriminator_type IN('order')
    LEFT JOIN
        dtb_order_item d2_
    ON  d1_.id = d2_.order_id
    AND d2_.discriminator_type IN('orderitem')
WHERE
    (
        (
            d0_.id = NULL
        OR  CONCAT(d0_.name01, d0_.name02) LIKE '%鈴木%'
        OR  CONCAT(d0_.kana01, d0_.kana02) LIKE '%鈴木%'
        OR  d0_.email LIKE '%鈴木%'
        )
    AND d0_.pref_id = 13
    AND d0_.sex_id IN(1)
    AND EXTRACT(
            MONTH
            FROM
                d0_.birth + INTERVAL 32400 SECOND
        ) = 1
    AND d0_.birth < '2021-06-01 15:00:00'
    AND d0_.phone_number LIKE '%01012341234%'
    AND d0_.buy_total <= 200000
    AND d0_.buy_times <= 200
    AND d0_.create_date < '2021-06-30 04:32:52'
    AND d0_.update_date < '2021-06-30 04:32:52'
    AND d0_.last_buy_date < '2021-06-30 15:00:00'
    AND d0_.customer_status_id IN(1, 2)
    AND d2_.product_name LIKE '%ジェラート%'
    AND d1_.order_status_id NOT IN(8, 7)
    )
AND d0_.discriminator_type IN('customer')
ORDER BY
    d0_.update_date DESC
LIMIT 50
;

ヒット件数取得

SELECT
    count(DISTINCT d0_.id) AS sclr_0
FROM
    dtb_customer d0_
    LEFT JOIN
        dtb_order d1_
    ON  d0_.id = d1_.customer_id
    AND d1_.discriminator_type IN('order')
    LEFT JOIN
        dtb_order_item d2_
    ON  d1_.id = d2_.order_id
    AND d2_.discriminator_type IN('orderitem')
WHERE
    (
        (
            d0_.id = NULL
        OR  CONCAT(d0_.name01, d0_.name02) LIKE '%鈴木%'
        OR  CONCAT(d0_.kana01, d0_.kana02) LIKE '%鈴木%'
        OR  d0_.email LIKE '%鈴木%'
        )
    AND d0_.pref_id = 13
    AND d0_.sex_id IN(1)
    AND EXTRACT(
            MONTH
            FROM
                d0_.birth + INTERVAL 32400 SECOND
        ) = 1
    AND d0_.birth < '2021-06-01 15:00:00'
    AND d0_.phone_number LIKE '%01012341234%'
    AND d0_.buy_total <= 200000
    AND d0_.buy_times <= 200
    AND d0_.create_date < '2021-06-30 04:32:52'
    AND d0_.update_date < '2021-06-30 04:32:52'
    AND d0_.last_buy_date < '2021-06-30 15:00:00'
    AND d0_.customer_status_id IN(1, 2)
    AND d2_.product_name LIKE '%ジェラート%'
    AND d1_.order_status_id NOT IN(8, 7)
    )
AND d0_.discriminator_type IN('customer')
;

データ取得?

SELECT
    d0_.id AS id_0,
    d0_.name01 AS name01_1,
    d0_.name02 AS name02_2,
    d0_.kana01 AS kana01_3,
    d0_.kana02 AS kana02_4,
    d0_.company_name AS company_name_5,
    d0_.postal_code AS postal_code_6,
    d0_.addr01 AS addr01_7,
    d0_.addr02 AS addr02_8,
    d0_.email AS email_9,
    d0_.phone_number AS phone_number_10,
    d0_.birth AS birth_11,
    d0_.password AS password_12,
    d0_.salt AS salt_13,
    d0_.secret_key AS secret_key_14,
    d0_.first_buy_date AS first_buy_date_15,
    d0_.last_buy_date AS last_buy_date_16,
    d0_.buy_times AS buy_times_17,
    d0_.buy_total AS buy_total_18,
    d0_.note AS note_19,
    d0_.reset_key AS reset_key_20,
    d0_.reset_expire AS reset_expire_21,
    d0_.point AS point_22,
    d0_.create_date AS create_date_23,
    d0_.update_date AS update_date_24,
    d0_.discriminator_type AS discriminator_type_25,
    d0_.customer_status_id AS customer_status_id_26,
    d0_.sex_id AS sex_id_27,
    d0_.job_id AS job_id_28,
    d0_.country_id AS country_id_29,
    d0_.pref_id AS pref_id_30
FROM
    dtb_customer d0_
    LEFT JOIN
        dtb_order d1_
    ON  d0_.id = d1_.customer_id
    AND d1_.discriminator_type IN('order')
    LEFT JOIN
        dtb_order_item d2_
    ON  d1_.id = d2_.order_id
    AND d2_.discriminator_type IN('orderitem')
WHERE
    (
        (
            d0_.id = NULL
        OR  CONCAT(d0_.name01, d0_.name02) LIKE '%鈴木%'
        OR  CONCAT(d0_.kana01, d0_.kana02) LIKE '%鈴木%'
        OR  d0_.email LIKE '%鈴木%'
        )
    AND d0_.pref_id = 13
    AND d0_.sex_id IN(1)
    AND EXTRACT(
            MONTH
            FROM
                d0_.birth + INTERVAL 32400 SECOND
        ) = 1
    AND d0_.birth < '2021-06-01 15:00:00'
    AND d0_.phone_number LIKE '%01012341234%'
    AND d0_.buy_total <= 200000
    AND d0_.buy_times <= 200
    AND d0_.create_date < '2021-06-30 04:32:52'
    AND d0_.update_date < '2021-06-30 04:32:52'
    AND d0_.last_buy_date < '2021-06-30 15:00:00'
    AND d0_.customer_status_id IN(1, 2)
    AND d2_.product_name LIKE '%ジェラート%'
    AND d1_.order_status_id NOT IN(8, 7)
    AND d0_.id IN(1)
    )
AND d0_.discriminator_type IN('customer')
ORDER BY
    d0_.update_date DESC
;

上のSQLで実行した結果。値は重複して取得された。どこで纏めているのか分からない(pagnatorクラスの中?)。

id_0 name01_1 name02_2 kana01_3 kana02_4 company_name_5 postal_code_6 addr01_7 addr02_8 email_9 phone_number_10 birth_11 password_12 salt_13 secret_key_14 first_buy_date_15 last_buy_date_16 buy_times_17 buy_total_18 note_19 reset_key_20 reset_expire_21 point_22 create_date_23 update_date_24 discriminator_type_25 customer_status_id_26 sex_id_27 job_id_28 country_id_29 pref_id_30
1 鈴木 一郎 スズキ イチロウ NULL 1500001 渋谷区神宮前 渋谷ビル101 suzuki@test.com 01012341234 1999-12-31 15:00:00 c86c916455b755244defacef128cf082ef4b4ade95b9726f7f9cccbb128bdd44 0767db4d87 thYBr39oI1kLtHHf00jU24js0MT7j6Iy 2021-05-31 05:14:47 2021-05-31 05:14:47 1 129150.00 NULL NULL NULL 0 2021-05-28 07:20:25 2021-05-31 05:14:47 customer 2 1 NULL NULL 13
1 鈴木 一郎 スズキ イチロウ NULL 1500001 渋谷区神宮前 渋谷ビル101 suzuki@test.com 01012341234 1999-12-31 15:00:00 c86c916455b755244defacef128cf082ef4b4ade95b9726f7f9cccbb128bdd44 0767db4d87 thYBr39oI1kLtHHf00jU24js0MT7j6Iy 2021-05-31 05:14:47 2021-05-31 05:14:47 1 129150.00 NULL NULL NULL 0 2021-05-28 07:20:25 2021-05-31 05:14:47 customer 2 1 NULL NULL 13
1 鈴木 一郎 スズキ イチロウ NULL 1500001 渋谷区神宮前 渋谷ビル101 suzuki@test.com 01012341234 1999-12-31 15:00:00 c86c916455b755244defacef128cf082ef4b4ade95b9726f7f9cccbb128bdd44 0767db4d87 thYBr39oI1kLtHHf00jU24js0MT7j6Iy 2021-05-31 05:14:47 2021-05-31 05:14:47 1 129150.00 NULL NULL NULL 0 2021-05-28 07:20:25 2021-05-31 05:14:47 customer 2 1 NULL NULL 13
1 鈴木 一郎 スズキ イチロウ NULL 1500001 渋谷区神宮前 渋谷ビル101 suzuki@test.com 01012341234 1999-12-31 15:00:00 c86c916455b755244defacef128cf082ef4b4ade95b9726f7f9cccbb128bdd44 0767db4d87 thYBr39oI1kLtHHf00jU24js0MT7j6Iy 2021-05-31 05:14:47 2021-05-31 05:14:47 1 129150.00 NULL NULL NULL 0 2021-05-28 07:20:25 2021-05-31 05:14:47 customer 2 1 NULL NULL 13

登録・更新

src/Eccube/Controller/Admin/Customer/CustomerEditController.php

    /**
     * @Route("/%eccube_admin_route%/customer/new", name="admin_customer_new")
     * @Route("/%eccube_admin_route%/customer/{id}/edit", requirements={"id" = "\d+"}, name="admin_customer_edit")
     * @Template("@admin/Customer/edit.twig")
     */
    public function index(Request $request, $id = null)
    {
        $this->entityManager->getFilters()->enable('incomplete_order_status_hidden');
        // 編集
        if ($id) {
            $Customer = $this->customerRepository
                ->find($id);

            if (is_null($Customer)) {
                throw new NotFoundHttpException();
            }

            $oldStatusId = $Customer->getStatus()->getId();
            // 編集用にデフォルトパスワードをセット
            $previous_password = $Customer->getPassword();
            $Customer->setPassword($this->eccubeConfig['eccube_default_password']);
        // 新規登録
        } else {
            $Customer = $this->customerRepository->newCustomer();

            $oldStatusId = null;
            $previous_password = null;
        }

        // 会員登録フォーム
        $builder = $this->formFactory
            ->createBuilder(CustomerType::class, $Customer);

...
        $form = $builder->getForm();

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            log_info('会員登録開始', [$Customer->getId()]);

            $encoder = $this->encoderFactory->getEncoder($Customer);

            if ($Customer->getPassword() === $this->eccubeConfig['eccube_default_password']) {
                $Customer->setPassword($previous_password);
            } else {
                if ($Customer->getSalt() === null) {
                    $Customer->setSalt($encoder->createSalt());
                    $Customer->setSecretKey($this->customerRepository->getUniqueSecretKey());
                }
                $Customer->setPassword($encoder->encodePassword($Customer->getPassword(), $Customer->getSalt()));
            }

            // 退会ステータスに更新の場合、ダミーのアドレスに更新
            $newStatusId = $Customer->getStatus()->getId();
            if ($oldStatusId != $newStatusId && $newStatusId == CustomerStatus::WITHDRAWING) {
                $Customer->setEmail(StringUtil::random(60).'@dummy.dummy');
            }

            $this->entityManager->persist($Customer);
            $this->entityManager->flush();

            log_info('会員登録完了', [$Customer->getId()]);
...

encoder

以下の設定が上のソースとどう関わっているかは分からないが、おそらく関係はある?

app/config/eccube/packages/security.yaml

security:
    encoders:
        # Our user class and the algorithm we'll use to encode passwords
        # https://symfony.com/doc/current/security.html#c-encoding-the-user-s-password
        Eccube\Entity\Member:
          id: Eccube\Security\Core\Encoder\PasswordEncoder
        Eccube\Entity\Customer:
          id: Eccube\Security\Core\Encoder\PasswordEncoder

フォーム定義

src/Eccube/Form/Type/Admin/CustomerType.php

削除

src/Eccube/Controller/Admin/Customer/CustomerController.php

    /**
     * @Route("/%eccube_admin_route%/customer/{id}/delete", requirements={"id" = "\d+"}, name="admin_customer_delete", methods={"DELETE"})
     */
    public function delete(Request $request, $id, TranslatorInterface $translator)
    {
        $this->isTokenValid();

        log_info('会員削除開始', [$id]);

        $page_no = intval($this->session->get('eccube.admin.customer.search.page_no'));
        $page_no = $page_no ? $page_no : Constant::ENABLED;

        $Customer = $this->customerRepository
            ->find($id);

        if (!$Customer) {
            $this->deleteMessage();

            return $this->redirect($this->generateUrl('admin_customer_page',
                    ['page_no' => $page_no]).'?resume='.Constant::ENABLED);
        }

        try {
            $this->entityManager->remove($Customer);
            $this->entityManager->flush();
            $this->addSuccess('admin.common.delete_complete', 'admin');
        } catch (ForeignKeyConstraintViolationException $e) {
            log_error('会員削除失敗', [$e]);

            $message = trans('admin.common.delete_error_foreign_key', ['%name%' => $Customer->getName01().' '.$Customer->getName02()]);
            $this->addError($message, 'admin');
        }

        log_info('会員削除完了', [$id]);

...
        return $this->redirect($this->generateUrl('admin_customer_page',
                ['page_no' => $page_no]).'?resume='.Constant::ENABLED);
    }