app/Plugin/NZCustomPlugin/Controller/FormController.php line 392

Open in your IDE?
  1. <?php
  2. namespace Plugin\NZCustomPlugin\Controller;
  3. use Eccube\Controller\AbstractController;
  4. use Eccube\Request\Context;
  5. use Plugin\NZCustomPlugin\Entity\FormSubmission;
  6. use Plugin\NZCustomPlugin\Repository\CustomFormRepository;
  7. use Plugin\NZCustomPlugin\Service\FormBuilderService;
  8. use Plugin\NZCustomPlugin\Service\MailService;
  9. use Plugin\NZCustomPlugin\Form\Type\Front\CustomFormType;
  10. use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\Routing\Annotation\Route;
  13. use Symfony\Component\HttpFoundation\File\UploadedFile;
  14. use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
  15. class FormController extends AbstractController
  16. {
  17.     protected $formRepository;
  18.     protected $formBuilder;
  19.     protected $authChecker;
  20.     protected $requestContext;
  21.     public function __construct(
  22.         CustomFormRepository $formRepository,
  23.         FormBuilderService $formBuilder,
  24.         AuthorizationCheckerInterface $authChecker,
  25.         Context $requestContext
  26.     ) {
  27.         $this->formRepository $formRepository;
  28.         $this->formBuilder $formBuilder;
  29.         $this->authChecker $authChecker;
  30.         $this->requestContext $requestContext;
  31.     }
  32.     
  33.     public function setMailService(MailService $mailService)
  34.     {
  35.         $this->mailService $mailService;
  36.     }
  37.     /**
  38.      * @Route("/nzcustomplugin/{key}", name="nzcustomplugin_form_display")
  39.      * @Template("@NZCustomPlugin/default/form.twig")
  40.      */
  41.     public function index(Request $request$key)
  42.     {
  43.         $customForm $this->formRepository->findOneBy(['form_key' => $key]);
  44.         if (!$customForm) {
  45.             throw $this->createNotFoundException('フォームが見つかりません。');
  46.         }
  47.         // レイアウトの取得(デバイスに応じて切り替え)
  48.         $Layout $this->getApplicableLayout($customForm$request);
  49.         // 公開期間チェック
  50.         if (!$customForm->isPublished()) {
  51.             return $this->handleNotPublished($customForm$Layout);
  52.         }
  53.         // 会員制限チェック(説明画面を表示)
  54.         if ($customForm->isRequireLogin() && !$this->authChecker->isGranted('ROLE_USER')) {
  55.             return $this->handleLoginRequired($request$customForm$key$Layout);
  56.         }
  57.         // ログインユーザーの情報を自動入力用データとして準備
  58.         $defaultData $this->prepareDefaultData($customForm);
  59.         $form $this->formBuilder->buildForm($customForm$defaultData);
  60.         $form->handleRequest($request);
  61.         if ($form->isSubmitted() && $form->isValid()) {
  62.             return $this->handleFormSubmission($request$customForm$form$Layout);
  63.         }
  64.         // フォーム表示
  65.         return [
  66.             'customForm' => $customForm,
  67.             'form' => $form->createView(),
  68.             'Layout' => $Layout,
  69.         ];
  70.     }
  71.     /**
  72.      * デバイスに応じて適用するレイアウトを取得
  73.      */
  74.     private function getApplicableLayout($customFormRequest $request)
  75.     {
  76.         // モバイルデバイスの判定
  77.         $userAgent $request->headers->get('User-Agent');
  78.         $isMobile $this->detectMobileDevice($userAgent);
  79.         
  80.         // デバイスに応じてレイアウトを取得
  81.         if ($isMobile && $customForm->getLayoutMobile()) {
  82.             return $customForm->getLayoutMobile();
  83.         }
  84.         
  85.         if ($customForm->getLayout()) {
  86.             return $customForm->getLayout();
  87.         }
  88.         
  89.         // デフォルトレイアウトを返す
  90.         return null;
  91.     }
  92.     /**
  93.      * User-Agentからモバイルデバイスかどうかを判定
  94.      */
  95.     private function detectMobileDevice($userAgent)
  96.     {
  97.         if (empty($userAgent)) {
  98.             return false;
  99.         }
  100.         // モバイルデバイスのパターン
  101.         $mobilePatterns = [
  102.             'iPhone',
  103.             'iPod',
  104.             'Android.*Mobile',
  105.             'Windows Phone',
  106.             'BlackBerry',
  107.             'webOS',
  108.             'Mobile',
  109.             'IEMobile',
  110.         ];
  111.         $pattern '/' implode('|'$mobilePatterns) . '/i';
  112.         return preg_match($pattern$userAgent) === 1;
  113.     }
  114.     /**
  115.      * 非公開時の処理
  116.      */
  117.     private function handleNotPublished($customForm$Layout)
  118.     {
  119.         $now = new \DateTime();
  120.         
  121.         // 公開前の場合
  122.         if ($customForm->getPublishStartDate() && $now $customForm->getPublishStartDate()) {
  123.             return $this->renderWithLayout('@NZCustomPlugin/default/not_published.twig', [
  124.                 'customForm' => $customForm,
  125.                 'message' => 'このフォームは公開前です。',
  126.                 'publish_start_date' => $customForm->getPublishStartDate(),
  127.                 'type' => 'before',
  128.             ], $Layout);
  129.         }
  130.         
  131.         // 公開終了の場合
  132.         if ($customForm->getPublishEndDate() && $now $customForm->getPublishEndDate()) {
  133.             return $this->renderWithLayout('@NZCustomPlugin/default/not_published.twig', [
  134.                 'customForm' => $customForm,
  135.                 'message' => 'このフォームは公開期間が終了しました。',
  136.                 'publish_end_date' => $customForm->getPublishEndDate(),
  137.                 'type' => 'after',
  138.             ], $Layout);
  139.         }
  140.         
  141.         // 非公開の場合
  142.         if (!$customForm->getIsActive()) {
  143.             return $this->renderWithLayout('@NZCustomPlugin/default/not_published.twig', [
  144.                 'customForm' => $customForm,
  145.                 'message' => 'このフォームは現在利用できません。',
  146.                 'type' => 'inactive',
  147.             ], $Layout);
  148.         }
  149.     }
  150.     /**
  151.      * ログイン必須時の処理
  152.      */
  153.     private function handleLoginRequired(Request $request$customForm$key$Layout)
  154.     {
  155.         // セッションにフォームキーを保存(ログイン後のリダイレクト用)
  156.         $session $request->getSession();
  157.         $session->set('redirect_to_form'$key);
  158.         
  159.         // 仮登録完了メッセージの確認
  160.         $showEmailConfirmMessage false;
  161.         if ($session->has('eccube.front.entry.complete')) {
  162.             $showEmailConfirmMessage true;
  163.             $session->remove('eccube.front.entry.complete');
  164.         }
  165.         
  166.         // 会員登録必須の説明画面を表示
  167.         return $this->renderWithLayout('@NZCustomPlugin/default/login_required.twig', [
  168.             'customForm' => $customForm,
  169.             'login_url' => $this->generateUrl('mypage_login'),
  170.             'register_url' => $this->generateUrl('entry'),
  171.             'form_key' => $key,
  172.             'show_email_confirm_message' => $showEmailConfirmMessage,
  173.         ], $Layout);
  174.     }
  175.     /**
  176.      * ログインユーザーの情報から自動入力用データを準備
  177.      */
  178.     private function prepareDefaultData($customForm)
  179.     {
  180.         $defaultData = [];
  181.         $customer $this->getUser();
  182.         
  183.         if ($customer && $this->isGranted('ROLE_USER')) {
  184.             // 各フィールドに対応するデータをマッピング
  185.             foreach ($customForm->getFormFields() as $field) {
  186.                 $fieldName $field->getFieldName();
  187.                 $fieldType $field->getFieldType();
  188.                 
  189.                 // 配列型フィールド(チェックボックス、複数選択)はスキップ
  190.                 if (in_array($fieldType, ['checkbox''select_multiple'])) {
  191.                     continue;
  192.                 }
  193.                 
  194.                 // よくあるフィールド名と会員情報のマッピング
  195.                 switch (strtolower($fieldName)) {
  196.                     case 'name':
  197.                     case 'full_name':
  198.                     case 'お名前':
  199.                     case '氏名':
  200.                         $defaultData[$fieldName] = $customer->getName01() . ' ' $customer->getName02();
  201.                         break;
  202.                         
  203.                     case 'name_kana':
  204.                     case 'full_name_kana':
  205.                     case 'フリガナ':
  206.                     case 'ふりがな':
  207.                         $defaultData[$fieldName] = $customer->getKana01() . ' ' $customer->getKana02();
  208.                         break;
  209.                         
  210.                     case 'email':
  211.                     case 'mail':
  212.                     case 'email_address':
  213.                     case 'メールアドレス':
  214.                     case 'メール':
  215.                         $defaultData[$fieldName] = $customer->getEmail();
  216.                         break;
  217.                         
  218.                     case 'tel':
  219.                     case 'phone':
  220.                     case 'phone_number':
  221.                     case '電話番号':
  222.                     case '電話':
  223.                         $defaultData[$fieldName] = $customer->getPhoneNumber();
  224.                         break;
  225.                         
  226.                     case 'postal_code':
  227.                     case 'zip':
  228.                     case 'zipcode':
  229.                     case '郵便番号':
  230.                         $defaultData[$fieldName] = $customer->getPostalCode();
  231.                         break;
  232.                         
  233.                     case 'address':
  234.                     case 'full_address':
  235.                     case '住所':
  236.                     case '所在地':
  237.                         $address $this->buildFullAddress($customer);
  238.                         $defaultData[$fieldName] = $address;
  239.                         break;
  240.                         
  241.                     case 'company':
  242.                     case 'company_name':
  243.                     case '会社名':
  244.                     case '企業名':
  245.                         $defaultData[$fieldName] = $customer->getCompanyName();
  246.                         break;
  247.                         
  248.                     case 'birth':
  249.                     case 'birthday':
  250.                     case '生年月日':
  251.                     case '誕生日':
  252.                         if ($customer->getBirth()) {
  253.                             $defaultData[$fieldName] = $customer->getBirth()->format('Y-m-d');
  254.                         }
  255.                         break;
  256.                         
  257.                     case 'sex':
  258.                     case 'gender':
  259.                     case '性別':
  260.                         if ($customer->getSex()) {
  261.                             $defaultData[$fieldName] = $customer->getSex()->getName();
  262.                         }
  263.                         break;
  264.                 }
  265.             }
  266.         }
  267.         
  268.         return $defaultData;
  269.     }
  270.     /**
  271.      * フォーム送信処理
  272.      */
  273.     private function handleFormSubmission(Request $request$customForm$form$Layout)
  274.     {
  275.         $customer $this->getUser();
  276.         
  277.         $submission = new FormSubmission();
  278.         $submission->setCustomForm($customForm);
  279.         $data $form->getData();
  280.         unset($data['submit']);
  281.         
  282.         // アップロードされたファイルを保持(メール添付用)
  283.         $uploadedFiles = [];
  284.         $dataForSave = [];
  285.         
  286.         foreach ($data as $fieldName => $value) {
  287.             if ($value instanceof UploadedFile) {
  288.                 // ファイルの場合はファイル名だけ保存、ファイル自体はメール添付用に保持
  289.                 $uploadedFiles[$fieldName] = $value;
  290.                 $dataForSave[$fieldName] = $value->getClientOriginalName();
  291.             } else {
  292.                 $dataForSave[$fieldName] = $value;
  293.             }
  294.         }
  295.         $submission->setData(json_encode($dataForSaveJSON_UNESCAPED_UNICODE));
  296.         $submission->setIpAddress($request->getClientIp());
  297.         $submission->setUserAgent($request->headers->get('User-Agent'));
  298.         // メールアドレスの抽出
  299.         $customerEmail null;
  300.         foreach ($dataForSave as $key => $value) {
  301.             if (in_array($key, ['email''mail''email_address']) && filter_var($valueFILTER_VALIDATE_EMAIL)) {
  302.                 $customerEmail $value;
  303.                 break;
  304.             }
  305.         }
  306.         // ログインユーザーの場合
  307.         if ($customer && $this->isGranted('ROLE_USER')) {
  308.             $submission->setCustomerId($customer->getId());
  309.             $submission->setCustomerEmail($customer->getEmail());
  310.             if (!$customerEmail) {
  311.                 $customerEmail $customer->getEmail();
  312.                 $dataForSave['email'] = $customerEmail// メール送信用にデータに追加
  313.             }
  314.         } else {
  315.             $submission->setCustomerEmail($customerEmail);
  316.         }
  317.         // データベースに保存
  318.         $this->entityManager->persist($submission);
  319.         $this->entityManager->flush();
  320.         // メール送信(ファイル添付付き)
  321.         if ($this->mailService) {
  322.             try {
  323.                 $this->mailService->sendFormEmails($customForm$submission$dataForSave$uploadedFiles);
  324.             } catch (\Exception $e) {
  325.                 // メール送信エラーはログに記録するが、処理は続行
  326.                 error_log('NZCustomPlugin mail error: ' $e->getMessage());
  327.             }
  328.         }
  329.         $this->addSuccess('フォームを送信しました。');
  330.         
  331.         return $this->renderWithLayout('@NZCustomPlugin/default/complete.twig', [
  332.             'customForm' => $customForm,
  333.         ], $Layout);
  334.     }
  335.     /**
  336.      * 住所を一行で結合
  337.      */
  338.     private function buildFullAddress($customer)
  339.     {
  340.         $address '';
  341.         
  342.         // 都道府県
  343.         if ($customer->getPref()) {
  344.             $address .= $customer->getPref()->getName();
  345.         }
  346.         
  347.         // 市区町村
  348.         if ($customer->getAddr01()) {
  349.             $address .= $customer->getAddr01();
  350.         }
  351.         
  352.         // 番地
  353.         if ($customer->getAddr02()) {
  354.             $address .= $customer->getAddr02();
  355.         }
  356.         
  357.         return $address;
  358.     }
  359.     /**
  360.      * レイアウトを考慮したレンダリング(配列を返す)
  361.      */
  362.     private function renderWithLayout($template, array $parameters$Layout null)
  363.     {
  364.         // Layoutを追加
  365.         if ($Layout) {
  366.             $parameters['Layout'] = $Layout;
  367.         }
  368.         
  369.         // @Templateアノテーションを使わない場合のために、renderメソッドを使用
  370.         return $this->render($template$parameters);
  371.     }
  372. }