<?php
declare(strict_types=1);
namespace BitBag\OpenMarketplace\Component\Core\Shop\Controller\Contact;
use BitBag\OpenMarketplace\Component\Channel\Entity\ChannelInterface;
use BitBag\OpenMarketplace\Component\Core\Common\Encrypter\EncrypterInterface;
use BitBag\OpenMarketplace\Component\Core\Shop\Email\ContactEmailManagerInterface;
use BitBag\OpenMarketplace\Component\Core\Shop\Enum\ContactTypes;
use BitBag\OpenMarketplace\Component\Core\Shop\Form\Type\Contact\ContactType;
use Sylius\Component\Channel\Context\ChannelContextInterface;
use Sylius\Component\Locale\Context\LocaleContextInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Templating\EngineInterface;
use Twig\Environment;
use Webmozart\Assert\Assert;
final class ContactController
{
private const NEWSLETTER_TOKEN_KEY_STORAGE_PREFIX = 'newsletter_token:';
public function __construct(
private RouterInterface $router,
private FormFactoryInterface $formFactory,
private EngineInterface|Environment $templatingEngine,
private ChannelContextInterface $channelContext,
private LocaleContextInterface $localeContext,
private ContactEmailManagerInterface $contactEmailManager,
private EncrypterInterface $encrypter,
private \Redis $redis
) {
}
public function requestAction(Request $request): Response
{
$contactType = $this->getSyliusAttribute($request, 'type', null);
Assert::inArray($contactType, ContactTypes::ALL_TYPES, 'Invalid contact request type');
Assert::notNull($contactType, 'Invalid contact request type');
$formType = $this->getSyliusAttribute($request, 'form', ContactType::class);
Assert::string($formType, 'Form type must be a string');
$form = $this->formFactory->create($formType, null, $this->getFormOptions($contactType));
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$data = $form->getData();
Assert::inArray($contactType, ContactTypes::ALL_TYPES, 'Invalid contact request type');
$channel = $this->channelContext->getChannel();
Assert::isInstanceOf($channel, ChannelInterface::class);
$contactEmail = $channel->getContactEmail();
if (null === $contactEmail) {
return $this->prepareRedirectOnMissingContactEmail($request);
}
$localeCode = $this->localeContext->getLocaleCode();
if (ContactTypes::NEWSLETTER_TYPE === $contactType) {
$encryptedString = $this->encrypter->encrypt($data);
$data = array_merge($data, [
'localeCode' => $localeCode,
'url' => $this->router->generate(
'app_newsletter_confirm',
[
'token' => urlencode($encryptedString),
],
RouterInterface::ABSOLUTE_URL
),
]);
$this->contactEmailManager->sendNewsletterConfirmationEmail($data);
} else {
$this->contactEmailManager->sendContactRequest($data, [$contactEmail], $channel, $localeCode);
}
$successMessage = $this->getSyliusAttribute(
$request,
'success_flash',
'sylius.contact.request_success',
);
/** @var FlashBagInterface $flashBag */
$flashBag = $request->getSession()->getBag('flashes');
$flashBag->add('success', $successMessage);
$redirectRoute = $this->getSyliusAttribute($request, 'redirect', null);
return new RedirectResponse(null === $redirectRoute
? $request->headers->get('referer') ?? '/'
: $this->router->generate($redirectRoute))
;
}
if ($form->isSubmitted() && !$form->isValid()) {
/** @var FlashBagInterface $flashBag */
$flashBag = $request->getSession()->getBag('flashes');
$flashBag->add('error', 'open_marketplace.contact.form_invalid');
}
$template = $this->getSyliusAttribute($request, 'template', $this->resolveTemplate($contactType));
Assert::notNull($template, 'Invalid template');
return new Response($this->templatingEngine->render($template, ['form' => $form->createView()]));
}
public function newsletterConfirmAction(Request $request): Response
{
$token = (string) $request->get('token');
if ($this->redis->get(self::NEWSLETTER_TOKEN_KEY_STORAGE_PREFIX . $token)) {
return new RedirectResponse($this->router->generate('sylius_shop_homepage'));
}
$channel = $this->channelContext->getChannel();
Assert::isInstanceOf($channel, ChannelInterface::class);
$contactEmail = $channel->getContactEmail();
if (null === $contactEmail) {
return $this->prepareRedirectOnMissingContactEmail($request);
}
$localeCode = $this->localeContext->getLocaleCode();
$data = $this->encrypter->decrypt($token);
$this->contactEmailManager->sendContactRequest($data, [$contactEmail], $channel, $localeCode);
$successMessage = $this->getSyliusAttribute(
$request,
'success_flash',
'sylius.contact.request_success',
);
/** @var FlashBagInterface $flashBag */
$flashBag = $request->getSession()->getBag('flashes');
$flashBag->add('success', $successMessage);
$this->redis->set(self::NEWSLETTER_TOKEN_KEY_STORAGE_PREFIX . $token, true);
return new RedirectResponse($this->router->generate('sylius_shop_homepage'));
}
private function getSyliusAttribute(
Request $request,
string $attributeName,
?string $default
): ?string {
$attributes = $request->attributes->get('_sylius');
return $attributes[$attributeName] ?? $default;
}
private function getFormOptions(string $type): array
{
return [
ContactType::CONTACT_TYPE => $type,
'validation_groups' => $type,
];
}
private function resolveTemplate(mixed $contactType): string
{
if (ContactTypes::TECH_ADVISORY_TYPE === $contactType) {
return 'Context/TechAdvisory/index.html.twig';
}
if (ContactTypes::NEWSLETTER_TYPE === $contactType) {
return 'Context/Newsletter/index.html.twig';
}
return '@SyliusShop/Contact/request.html.twig';
}
private function prepareRedirectOnMissingContactEmail(Request $request): RedirectResponse
{
$errorMessage = $this->getSyliusAttribute(
$request,
'error_flash',
'sylius.contact.request_error',
);
/** @var FlashBagInterface $flashBag */
$flashBag = $request->getSession()->getBag('flashes');
$flashBag->add('error', $errorMessage);
return new RedirectResponse($request->headers->get('referer') ?? '/');
}
}