<?php
namespace Plugin\NZCustomPlugin\Controller;
use Eccube\Controller\AbstractController;
use Eccube\Request\Context;
use Plugin\NZCustomPlugin\Entity\FormSubmission;
use Plugin\NZCustomPlugin\Repository\CustomFormRepository;
use Plugin\NZCustomPlugin\Service\FormBuilderService;
use Plugin\NZCustomPlugin\Service\MailService;
use Plugin\NZCustomPlugin\Form\Type\Front\CustomFormType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
class FormController extends AbstractController
{
protected $formRepository;
protected $formBuilder;
protected $authChecker;
protected $requestContext;
public function __construct(
CustomFormRepository $formRepository,
FormBuilderService $formBuilder,
AuthorizationCheckerInterface $authChecker,
Context $requestContext
) {
$this->formRepository = $formRepository;
$this->formBuilder = $formBuilder;
$this->authChecker = $authChecker;
$this->requestContext = $requestContext;
}
public function setMailService(MailService $mailService)
{
$this->mailService = $mailService;
}
/**
* @Route("/nzcustomplugin/{key}", name="nzcustomplugin_form_display")
* @Template("@NZCustomPlugin/default/form.twig")
*/
public function index(Request $request, $key)
{
$customForm = $this->formRepository->findOneBy(['form_key' => $key]);
if (!$customForm) {
throw $this->createNotFoundException('フォームが見つかりません。');
}
// レイアウトの取得(デバイスに応じて切り替え)
$Layout = $this->getApplicableLayout($customForm, $request);
// 公開期間チェック
if (!$customForm->isPublished()) {
return $this->handleNotPublished($customForm, $Layout);
}
// 会員制限チェック(説明画面を表示)
if ($customForm->isRequireLogin() && !$this->authChecker->isGranted('ROLE_USER')) {
return $this->handleLoginRequired($request, $customForm, $key, $Layout);
}
// ログインユーザーの情報を自動入力用データとして準備
$defaultData = $this->prepareDefaultData($customForm);
$form = $this->formBuilder->buildForm($customForm, $defaultData);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
return $this->handleFormSubmission($request, $customForm, $form, $Layout);
}
// フォーム表示
return [
'customForm' => $customForm,
'form' => $form->createView(),
'Layout' => $Layout,
];
}
/**
* デバイスに応じて適用するレイアウトを取得
*/
private function getApplicableLayout($customForm, Request $request)
{
// モバイルデバイスの判定
$userAgent = $request->headers->get('User-Agent');
$isMobile = $this->detectMobileDevice($userAgent);
// デバイスに応じてレイアウトを取得
if ($isMobile && $customForm->getLayoutMobile()) {
return $customForm->getLayoutMobile();
}
if ($customForm->getLayout()) {
return $customForm->getLayout();
}
// デフォルトレイアウトを返す
return null;
}
/**
* User-Agentからモバイルデバイスかどうかを判定
*/
private function detectMobileDevice($userAgent)
{
if (empty($userAgent)) {
return false;
}
// モバイルデバイスのパターン
$mobilePatterns = [
'iPhone',
'iPod',
'Android.*Mobile',
'Windows Phone',
'BlackBerry',
'webOS',
'Mobile',
'IEMobile',
];
$pattern = '/' . implode('|', $mobilePatterns) . '/i';
return preg_match($pattern, $userAgent) === 1;
}
/**
* 非公開時の処理
*/
private function handleNotPublished($customForm, $Layout)
{
$now = new \DateTime();
// 公開前の場合
if ($customForm->getPublishStartDate() && $now < $customForm->getPublishStartDate()) {
return $this->renderWithLayout('@NZCustomPlugin/default/not_published.twig', [
'customForm' => $customForm,
'message' => 'このフォームは公開前です。',
'publish_start_date' => $customForm->getPublishStartDate(),
'type' => 'before',
], $Layout);
}
// 公開終了の場合
if ($customForm->getPublishEndDate() && $now > $customForm->getPublishEndDate()) {
return $this->renderWithLayout('@NZCustomPlugin/default/not_published.twig', [
'customForm' => $customForm,
'message' => 'このフォームは公開期間が終了しました。',
'publish_end_date' => $customForm->getPublishEndDate(),
'type' => 'after',
], $Layout);
}
// 非公開の場合
if (!$customForm->getIsActive()) {
return $this->renderWithLayout('@NZCustomPlugin/default/not_published.twig', [
'customForm' => $customForm,
'message' => 'このフォームは現在利用できません。',
'type' => 'inactive',
], $Layout);
}
}
/**
* ログイン必須時の処理
*/
private function handleLoginRequired(Request $request, $customForm, $key, $Layout)
{
// セッションにフォームキーを保存(ログイン後のリダイレクト用)
$session = $request->getSession();
$session->set('redirect_to_form', $key);
// 仮登録完了メッセージの確認
$showEmailConfirmMessage = false;
if ($session->has('eccube.front.entry.complete')) {
$showEmailConfirmMessage = true;
$session->remove('eccube.front.entry.complete');
}
// 会員登録必須の説明画面を表示
return $this->renderWithLayout('@NZCustomPlugin/default/login_required.twig', [
'customForm' => $customForm,
'login_url' => $this->generateUrl('mypage_login'),
'register_url' => $this->generateUrl('entry'),
'form_key' => $key,
'show_email_confirm_message' => $showEmailConfirmMessage,
], $Layout);
}
/**
* ログインユーザーの情報から自動入力用データを準備
*/
private function prepareDefaultData($customForm)
{
$defaultData = [];
$customer = $this->getUser();
if ($customer && $this->isGranted('ROLE_USER')) {
// 各フィールドに対応するデータをマッピング
foreach ($customForm->getFormFields() as $field) {
$fieldName = $field->getFieldName();
$fieldType = $field->getFieldType();
// 配列型フィールド(チェックボックス、複数選択)はスキップ
if (in_array($fieldType, ['checkbox', 'select_multiple'])) {
continue;
}
// よくあるフィールド名と会員情報のマッピング
switch (strtolower($fieldName)) {
case 'name':
case 'full_name':
case 'お名前':
case '氏名':
$defaultData[$fieldName] = $customer->getName01() . ' ' . $customer->getName02();
break;
case 'name_kana':
case 'full_name_kana':
case 'フリガナ':
case 'ふりがな':
$defaultData[$fieldName] = $customer->getKana01() . ' ' . $customer->getKana02();
break;
case 'email':
case 'mail':
case 'email_address':
case 'メールアドレス':
case 'メール':
$defaultData[$fieldName] = $customer->getEmail();
break;
case 'tel':
case 'phone':
case 'phone_number':
case '電話番号':
case '電話':
$defaultData[$fieldName] = $customer->getPhoneNumber();
break;
case 'postal_code':
case 'zip':
case 'zipcode':
case '郵便番号':
$defaultData[$fieldName] = $customer->getPostalCode();
break;
case 'address':
case 'full_address':
case '住所':
case '所在地':
$address = $this->buildFullAddress($customer);
$defaultData[$fieldName] = $address;
break;
case 'company':
case 'company_name':
case '会社名':
case '企業名':
$defaultData[$fieldName] = $customer->getCompanyName();
break;
case 'birth':
case 'birthday':
case '生年月日':
case '誕生日':
if ($customer->getBirth()) {
$defaultData[$fieldName] = $customer->getBirth()->format('Y-m-d');
}
break;
case 'sex':
case 'gender':
case '性別':
if ($customer->getSex()) {
$defaultData[$fieldName] = $customer->getSex()->getName();
}
break;
}
}
}
return $defaultData;
}
/**
* フォーム送信処理
*/
private function handleFormSubmission(Request $request, $customForm, $form, $Layout)
{
$customer = $this->getUser();
$submission = new FormSubmission();
$submission->setCustomForm($customForm);
$data = $form->getData();
unset($data['submit']);
// アップロードされたファイルを保持(メール添付用)
$uploadedFiles = [];
$dataForSave = [];
foreach ($data as $fieldName => $value) {
if ($value instanceof UploadedFile) {
// ファイルの場合はファイル名だけ保存、ファイル自体はメール添付用に保持
$uploadedFiles[$fieldName] = $value;
$dataForSave[$fieldName] = $value->getClientOriginalName();
} else {
$dataForSave[$fieldName] = $value;
}
}
$submission->setData(json_encode($dataForSave, JSON_UNESCAPED_UNICODE));
$submission->setIpAddress($request->getClientIp());
$submission->setUserAgent($request->headers->get('User-Agent'));
// メールアドレスの抽出
$customerEmail = null;
foreach ($dataForSave as $key => $value) {
if (in_array($key, ['email', 'mail', 'email_address']) && filter_var($value, FILTER_VALIDATE_EMAIL)) {
$customerEmail = $value;
break;
}
}
// ログインユーザーの場合
if ($customer && $this->isGranted('ROLE_USER')) {
$submission->setCustomerId($customer->getId());
$submission->setCustomerEmail($customer->getEmail());
if (!$customerEmail) {
$customerEmail = $customer->getEmail();
$dataForSave['email'] = $customerEmail; // メール送信用にデータに追加
}
} else {
$submission->setCustomerEmail($customerEmail);
}
// データベースに保存
$this->entityManager->persist($submission);
$this->entityManager->flush();
// メール送信(ファイル添付付き)
if ($this->mailService) {
try {
$this->mailService->sendFormEmails($customForm, $submission, $dataForSave, $uploadedFiles);
} catch (\Exception $e) {
// メール送信エラーはログに記録するが、処理は続行
error_log('NZCustomPlugin mail error: ' . $e->getMessage());
}
}
$this->addSuccess('フォームを送信しました。');
return $this->renderWithLayout('@NZCustomPlugin/default/complete.twig', [
'customForm' => $customForm,
], $Layout);
}
/**
* 住所を一行で結合
*/
private function buildFullAddress($customer)
{
$address = '';
// 都道府県
if ($customer->getPref()) {
$address .= $customer->getPref()->getName();
}
// 市区町村
if ($customer->getAddr01()) {
$address .= $customer->getAddr01();
}
// 番地
if ($customer->getAddr02()) {
$address .= $customer->getAddr02();
}
return $address;
}
/**
* レイアウトを考慮したレンダリング(配列を返す)
*/
private function renderWithLayout($template, array $parameters, $Layout = null)
{
// Layoutを追加
if ($Layout) {
$parameters['Layout'] = $Layout;
}
// @Templateアノテーションを使わない場合のために、renderメソッドを使用
return $this->render($template, $parameters);
}
}