<?php
/**
* Created by simpson <simpsonwork@gmail.com>
* Date: 2019-04-29
* Time: 18:00
*/
namespace App\Controller\Account;
use AngelGamez\TranslatableBundle\Entity\TranslatableValue;
use App\Entity\Location\City;
use App\Entity\Profile\Photo;
use App\Entity\Profile\Profile;
use App\Entity\Profile\Selfie;
use App\Entity\Profile\Video;
use App\Entity\User;
use App\Event\Profile\ProfileDataChanged;
use App\Form\CreateOrEditProfileForm;
use App\Service\ApprovalService;
use App\Service\Features;
use App\Service\ModerationService;
use App\Service\ProfilePersister;
use Doctrine\ORM\EntityManagerInterface;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
class ProfileEditingController extends AbstractController
{
const LEGACY_MASSEUR_PRECHECK_PARAM = 'massagers';
private bool $featureHasProfilesTranslation;
public function __construct(
private EntityManagerInterface $entityManager,
private Features $features
)
{
$this->featureHasProfilesTranslation = $features->has_translations() && $features->has_profile_translations();
}
/**
* @throws \Exception
*/
#[Route(path: '/add', name: 'account.profile_editing.create')]
#[Route(path: '/add/masseur', name: 'account.profile_editing.create_masseur')]
#[Route(path: '/edit/{profile}', name: 'account.profile_editing.edit')]
#[IsGranted('ROLE_ADVERTISER')]
public function editForm(Request $request, ProfilePersister $profilePersister, ApprovalService $approvalService,
ModerationService $moderationService, Profile $profile = null,
EventDispatcherInterface $eventDispatcher, ParameterBagInterface $parameterBag): Response
{
//если профиль деактивирован
if ('account.profile_editing.edit' == $request->get('_route') && null == $profile) {
throw $this->createNotFoundException();
}
//если ожидает модерации
if (true === $this->features->hard_moderation() && $profile && $moderationService->isProfileWaitingModeration($profile)) {
throw new AccessDeniedException('You cannot access profile editing while it is on moderation');
}
/** @var User $account */
$account = $this->getUser();
if (null !== $profile && !$profile->isOwnedBy($account)) {
throw $this->createNotFoundException();
}
$precheckMasseur = $request->query->has(self::LEGACY_MASSEUR_PRECHECK_PARAM)
|| 'account.profile_editing.create_masseur' === $request->attributes->get('_route');
$form = $this->createProfileForm(false, $profile, $account->getCity(), $precheckMasseur);
if ($request->request->has('submit')) { //submitted
try {
$successFlashMessage = sprintf('Анкета успешно %s', $profile ? 'отредактирована' : 'создана');
if($profile && $this->isProfileModerationNeeded($profile, $request)) {
$fakeForm = $this->createProfileForm(true, $profile, $account->getCity(), $precheckMasseur);
$profileFormValues = [];
foreach ($fakeForm->all() as $child) {
$profileFormValues[$child->getName()] = $request->request->get($child->getName());
}
$profileFormValues['un_approve'] = 0;
if ($this->isProfileApprovalResetNeeded($profile, $request)) {
if(true === $this->features->approval_by_video()) {
$approvalService->reApplyLastNotRejectedRequestForApproval($profile);
} else {
if ($this->features->hard_moderation()) {
$profileFormValues['un_approve'] = 1;
} else {
$approvalService->revokeProfileApproval($profile);
}
}
}
$fakeForm->handleRequest($request);
if ($fakeForm->isSubmitted() && $fakeForm->isValid()) {
//здесь чтобы узнать был ли файл и после флаша
if(null != $fakeForm->get('approval_photo')->getData()) {
$approvalService->sendForApproval($profile, $fakeForm->get('approval_photo')->getData()[0]);
}
if(false === $this->features->hard_moderation()) {
//пересоздаем основную форму, т.к. fake-форма после handleRequest почему-то перезаписывает конфиг основной
$form = $this->createProfileForm(false, $profile, $account->getCity(), $precheckMasseur);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var Profile $profile */
$profile = $form->getData();
$eventDispatcher->dispatch(new ProfileDataChanged($profile), ProfileDataChanged::NAME);
$this->entityManager->flush();
}
}
$moderationService->requestModeration($profile, $profileFormValues);
$redirectRoute = false == $this->features->non_masseur_on_account_profile_list() && $profile->isMasseur()
? 'account.profile_management.list_masseur'
: 'account.profile_management.list';
if($this->features->redirect_to_account_main_after_profile_edit()) {
$redirectRoute = 'account';
}
$this->addFlash('success', $successFlashMessage);
return $this->redirectToRoute($redirectRoute);
} else {
//переносим ошибки и данные в основную форму чтобы показать
foreach ($fakeForm->getErrors() as $error) {
$form->addError($error);
}
foreach ($fakeForm->all() as $item) {
foreach ($item->getErrors() as $error) {
$form->get($item->getName())->addError($error);
}
$form->get($item->getName())->setData($item->getData());
}
}
} else {
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
/** @var Profile $profile */
$profile = $form->getData();
$isDraft = $profile->isDraft();
if($isDraft) {
$profilePersister->persist($profile);
$moderationService->requestModeration($profile);
} else {
$eventDispatcher->dispatch(new ProfileDataChanged($profile), ProfileDataChanged::NAME);
}
$this->entityManager->flush();
//здесь чтобы узнать был ли файл и после флаша
if(null != $form->get('approval_photo')->getData()) {
$approvalService->sendForApproval($profile, $form->get('approval_photo')->getData()[0]);
}
$redirectRoute = false == $this->features->non_masseur_on_account_profile_list() && $profile->isMasseur()
? 'account.profile_management.list_masseur'
: 'account.profile_management.list';
if($this->features->redirect_to_account_main_after_profile_edit()) {
$redirectRoute = 'account';
}
$this->addFlash('success', $successFlashMessage);
return $this->redirectToRoute($redirectRoute);
}
}
} catch (\DomainException $ex) {
$this->addFlash('error', $ex->getMessage());
if (null !== $profile) {
$redirectResponse = $this->redirectToRoute('account.profile_editing.edit', ['profile' => $profile->getId()]);
} else {
$redirectRoute = $precheckMasseur ? 'account.profile_editing.create_masseur' : 'account.profile_editing.create';
$redirectResponse = $this->redirectToRoute($redirectRoute);
}
return $redirectResponse;
}
}
return $this->render('account/profiles/profile_form.html.twig', [
'form' => $form->createView(),
'videos_in_process' => $profile?->videosInProcess(),
'approval_media_upload_domain' => $parameterBag->get('app.approval_media_upload_domain'),
'mirror_domain' => $parameterBag->get('app.mirror_domain') ?: $parameterBag->get('app.main_domain'),
'approved_by_video' => null === $profile || $profile->isDraft() ? false : $approvalService->isProfileApprovedByVideo($profile),
]);
}
protected function createProfileForm(bool $isFake, ?Profile $profile, ?City $city, bool $precheckMasseur): FormInterface
{
if(true === $isFake && null === $profile) {
throw new \Exception('Fake form should only be used after editing with profile instance');
}
//Для fake формы обязательно нужно передать
//'original_profile_data' => [ 'id' => $profile->getId(), 'description' => $profile->getDescription() ]
$form = $this->createForm(CreateOrEditProfileForm::class, $isFake ? null : $profile, [
'default_city' => $city,
'precheck_masseur' => $precheckMasseur,
'check_description_is_unique' => true,
'original_profile_data' => [
'id' => $profile ? $profile->getId() : null,
'description' => $profile ? $profile->getDescription() : null,
],
'is_fake' => $isFake,
]);
return $form;
}
protected function isProfileModerationNeeded(Profile $profile, Request $request): bool
{
$needModeration = 0;
$submittedDescription = (new TranslatableValue())->ru($request->request->get('about'));
if ($this->featureHasProfilesTranslation) {
$submittedDescription = $submittedDescription->en($request->request->get('about_en'));
}
if($submittedDescription->getTranslation('ru') != $profile->getDescription()->getTranslation('ru')
|| ($this->featureHasProfilesTranslation && $submittedDescription->getTranslation('en') != $profile->getDescription()->getTranslation('en'))) {
$needModeration++;
}
$originalVideos = $profile->getVideos()->map(function(Video $video): string { return $video->getPath(); })->toArray();
$requestVideos = $request->request->get('videos') ?? [];
$videoPathsToAdd = array_diff($requestVideos, $originalVideos);
$videoPathsToRemove = array_diff($originalVideos, $videoPathsToAdd);
if (count($videoPathsToAdd) || count($videoPathsToRemove)) {
$needModeration++;
}
$needModeration += (int)$this->areProfilePhotosChanged($profile, $request);
return $needModeration > 0;
}
protected function isProfileApprovalResetNeeded(Profile $profile, Request $request): bool
{
$needModeration = 0;
$originalVideos = $profile->getVideos()->map(function(Video $video): string { return $video->getPath(); })->toArray();
$videoPathsToAdd = array_diff($request->request->get('videos') ?? [], $originalVideos);
if(count($videoPathsToAdd))
$needModeration++;
$needModeration += (int)$this->areProfilePhotosChanged($profile, $request);
return $needModeration > 0;
}
protected function areProfilePhotosChanged(Profile $profile, Request $request): bool
{
$needModeration = 0;
$originalPhotos = $profile->getPhotos()->map(function(Photo $photo): string { return $photo->getPath(); })->toArray();
$originalSelfies = $profile->getSelfies()->map(function(Selfie $selfie): string { return $selfie->getPath(); })->toArray();
if($profile->getAvatar() && $profile->getAvatar()->getPath() != $request->request->get('avatar'))
$needModeration++;
if($profile->getMainPhoto() && $profile->getMainPhoto()->getPath() != $request->request->get('main_photo'))
$needModeration++;
$photoPathsToAdd = array_diff($request->request->get('photos') ?? [], $originalPhotos);
if(count($photoPathsToAdd))
$needModeration++;
$selfiePathsToAdd = array_diff($request->request->get('selfies') ?? [], $originalSelfies);
if(count($selfiePathsToAdd))
$needModeration++;
return $needModeration > 0;
}
}