<?php
namespace App\Service;
use App\Entity\Inquiry\InquiryInterface;
use App\Repository\Inquiry\InquiryRepositoryInterface;
use App\Utils\FormUtil;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
use ReCaptcha\ReCaptcha;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
class InquiryControllerService
{
private ContainerInterface $container;
private ReCaptcha $reCaptcha;
private EntityManagerInterface $em;
private MailService $mailer;
private LoggerInterface $logger;
public function __construct(
ContainerInterface $container,
ReCaptcha $reCaptcha,
EntityManagerInterface $em,
MailService $mailer,
LoggerInterface $logger
) {
$this->container = $container;
$this->reCaptcha = $reCaptcha;
$this->em = $em;
$this->mailer = $mailer;
$this->logger = $logger;
}
/**
* 問い合わせフォームを準備する
* セッションが残っている場合アサインする
*
* @param Request $request
* @param string $sessionName
* @param string $formType
* @param InquiryInterface $inquiry
* @param array $formOptions
* @return FormInterface
*/
public function prepareForm(
Request $request,
string $sessionName,
string $formType,
InquiryInterface $inquiry,
array $formOptions = []
): FormInterface {
$form = $this->container->get('form.factory')->create($formType, $inquiry, $formOptions);
if($request->getSession()->has($sessionName)) {
$formData = $request->getSession()->get($sessionName);
FormUtil::submitAndGetData($form, $formData);
}
return $form;
}
/**
* 確認ページでFormにRequestをアサインして送信済みFormを返す
*
* @param Request $request
* @param string $formType
* @param InquiryInterface $inquiry
* @param array $formOptions
* @return FormInterface
*/
public function getSubmittedForm(
Request $request,
string $formType,
InquiryInterface $inquiry,
array $formOptions = []
): FormInterface {
$form = $this->container->get('form.factory')->create($formType, $inquiry, $formOptions);
$form->handleRequest($request);
return $form;
}
/**
* フォームをバリデーションする
*
* @param Request $request
* @param FormInterface $form
* @param InquiryInterface $inquiry
* @return bool
*/
public function formValidation(
Request $request,
FormInterface $form,
InquiryInterface $inquiry,
bool $checkRecaptcha = true
): bool {
$isValid = true;
if($form->isSubmitted() && $form->isValid()) {
$inquiry->setIp($request->getClientIp());
$repository = $this->em->getRepository(get_class($inquiry));
if($repository->isContinuePost($inquiry)) {
$form->addError(new FormError('連続送信は時間をおいてください'));
$isValid = false;
}
if($checkRecaptcha) {
$reCaptchaResponse = $this->reCaptcha->verify(
$request->request->get('g-recaptcha-response'),
$request->getClientIp()
);
if(!$reCaptchaResponse->isSuccess()) {
$form->addError(new FormError("不正な処理を感知しました"));
$this->logger->notice(
"ReCaptcha is invalid. ".
print_r($reCaptchaResponse->getErrorCodes(), true)
);
$isValid = false;
}
}
} else {
$form->addError(new FormError('入力内容に不備がありました'));
$isValid = false;
}
return $isValid;
}
/**
* 入力データをセッションに保存、CSRFチェック用の空のフォームを返す
*
* @param Request $request
* @param FormInterface $form
* @param string $sessionName
* @return FormInterface
*/
public function saveSession(
Request $request,
FormInterface $form,
string $sessionName,
array $viewData = null
): FormInterface {
$request->getSession()->set($sessionName, $viewData ? $viewData : FormUtil::getViewData($form));
return $this->createConfirmForm();
}
/**
* 確認ページから送信へのCSRFトークンをもつ空のフォームを返す
* @return FormInterface
*/
public function createConfirmForm(): FormInterface
{
return $this->container->get('form.factory')->create();
}
/**
* 送信前のCSRFトークンチェックを行う
*
* @param Request $request
* @return bool
*/
public function handleConfirmForm(Request $request): bool
{
$confirmForm = $this->createConfirmForm();
$confirmForm->handleRequest($request);
return ($confirmForm->isSubmitted() && $confirmForm->isValid());
}
/**
* 送信ページでセッションにある入力データをフォームにアサインする
*
* @param Request $request
* @param InquiryInterface $inquiry
* @param string $formType
* @param string $sessionName
* @return FormInterface|null
*/
public function loadSessionAndGetForm(
Request $request,
InquiryInterface $inquiry,
string $formType,
string $sessionName
): ?FormInterface
{
if (!$request->getSession()->has($sessionName)) {
return null;
}
$form = $this->container->get('form.factory')->create($formType, $inquiry, [
"csrf_protection" => false
]);
$formData = $request->getSession()->get($sessionName);
FormUtil::submitAndGetData($form, $formData);
return $form;
}
/**
* DB永続化してセッションを削除
*
* @param Request $request
* @param InquiryInterface $inquiry
* @param string $sessionName
* @return $this
*/
public function persistAndSessionRemove(
Request $request,
InquiryInterface $inquiry,
string $sessionName
): self
{
$inquiry
->setSendAt(new \DateTime())
->setToken(null)
;
$this->em->persist($inquiry);
$this->em->flush();
$request->getSession()->remove($sessionName);
// pardot送信のエラーハンドリングのために永続化したエンティティのIDを保存
$request->getSession()->set($sessionName, $inquiry->getId());
return $this;
}
public function handlePardotError(
Request $request,
InquiryRepositoryInterface $repository,
string $formType,
string $sessionName,
callable $formOptionsFunc = null
): self {
if(!$request->getSession()->has($sessionName)) return $this;
$id = $request->getSession()->get($sessionName);
$id = intval($id);
if(!$id) return $this;
$inquiry = $repository->findOneBy(["id" => $id]);
if(!$inquiry) return $this;
$mailConfig = $inquiry->getPardotErrorMailConfig();
if(!$mailConfig) return $this;
if(is_callable($formOptionsFunc)) {
$formOptions = $formOptionsFunc($inquiry);
} else {
$formOptions = [];
}
$form = $this->container->get('form.factory')->create($formType, $inquiry, $formOptions);
$mailConfig['twigAssign']['form'] = $form->createView();
$this->mailer->send($mailConfig);
return $this;
}
public function removeSession(Request $request, string $sessionName): self
{
$request->getSession()->remove($sessionName);
return $this;
}
/**
* メール送信実行
* @param InquiryInterface $inquiry
* @param FormInterface $form
* @param callable|null $preSendConfig
* @return array
* @throws \Symfony\Component\Mailer\Exception\TransportExceptionInterface
* @throws \Twig\Error\LoaderError
* @throws \Twig\Error\RuntimeError
* @throws \Twig\Error\SyntaxError
*/
public function sendmail(
InquiryInterface $inquiry,
FormInterface $form,
callable $preSendConfig = null
): array {
$sendEmails = [];
foreach($inquiry->getMailSendConfig() as $k => $mailConfig) {
$mailConfig['twigAssign']['form'] = $form->createView();
if(is_callable($preSendConfig)) {
$mailConfig = $preSendConfig($mailConfig, $k);
}
$sendEmails[$k] = $this->mailer->send($mailConfig);
}
return $sendEmails;
}
}