<?php
namespace Aqarmap\Bundle\ListingBundle\EventListener;
use Aqarmap\Bundle\CreditBundle\Constant\CreditStatus;
use Aqarmap\Bundle\CreditBundle\Contract\CreditManagerInterface;
use Aqarmap\Bundle\CreditBundle\Entity\Credit;
use Aqarmap\Bundle\ListingBundle\Constant\ListingCategories;
use Aqarmap\Bundle\ListingBundle\Constant\ListingFeaturedStatus;
use Aqarmap\Bundle\ListingBundle\Constant\ListingSource;
use Aqarmap\Bundle\ListingBundle\Constant\ListingStatus;
use Aqarmap\Bundle\ListingBundle\Entity\Listing;
use Aqarmap\Bundle\ListingBundle\Event\ListingEvent;
use Aqarmap\Bundle\ListingBundle\Event\ListingFeatureEvent;
use Aqarmap\Bundle\ListingBundle\Service\Contracts\ListingFeatureServiceInterface;
use Aqarmap\Bundle\ListingBundle\Service\ListingFeatureService;
use Aqarmap\Bundle\ListingBundle\Service\ListingManager;
use Aqarmap\Bundle\ListingBundle\Service\ListingRuleMatcher;
use Aqarmap\Bundle\ListingBundle\Service\SpecialAddListingService;
use Aqarmap\Bundle\UserBundle\Constant\UserServicesType;
use Aqarmap\Bundle\UserBundle\Services\UserServicesManager;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class ListingRuleListener implements EventSubscriberInterface
{
/**
* @var ListingRuleMatcher
*/
private $listingRuleMatcher;
/**
* @var CreditManagerInterface
*/
private $creditManager;
/**
* @var ListingManager
*/
private $listingManager;
/**
* @var EntityManagerInterface
*/
private $entityManager;
/**
* @var ListingFeatureServiceInterface
*/
private $listingFeatureService;
/**
* @var SessionInterface
*/
private $session;
/**
* @var TranslatorInterface
*/
private $translator;
/**
* @var RouterInterface
*/
private $router;
/** @var EventDispatcherInterface */
private $dispatcher;
public function __construct(
ListingRuleMatcher $listingRuleMatcher,
CreditManagerInterface $creditManager,
ListingManager $listingManager,
EntityManagerInterface $entityManager,
ListingFeatureServiceInterface $listingFeatureService,
SessionInterface $session,
TranslatorInterface $translator,
RouterInterface $router,
SpecialAddListingService $specialAddListingService,
UserServicesManager $userServicesManager,
EventDispatcherInterface $dispatcher
) {
$this->listingRuleMatcher = $listingRuleMatcher;
$this->creditManager = $creditManager;
$this->listingManager = $listingManager;
$this->entityManager = $entityManager;
$this->listingFeatureService = $listingFeatureService;
$this->session = $session;
$this->translator = $translator;
$this->router = $router;
$this->specialAddListingService = $specialAddListingService;
$this->userServicesManager = $userServicesManager;
$this->dispatcher = $dispatcher;
}
public function preSaveListingEvent(ListingEvent $event): void
{
$listing = $event->getListing();
if (
!$listing->isDraft()
&& !$listing->isLive()
&& ListingCategories::FIRST_LISTING_FOR_FREE === $listing->getCategory()
) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING, false);
return;
}
$publicationCredit = $listing->getPublicationCredit();
/** @var ListingRuleMatcher $matcher */
$matcher = $this->listingRuleMatcher;
$listingRule = $matcher->match($listing);
$listingRulesRequirePhotos = ($listingRule['required_photos'] && $listing->getPhotos()->count() < $listingRule['required_photos']);
$listingHasTitle = $listing->getTitle();
if ($listingRulesRequirePhotos && $listingHasTitle) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING_PHOTOS);
}
if (
!$listing->isDraft()
&& !$listing->isLive()
&& !$listing->isProjectOrUnit()
&& ListingStatus::PENDING != $listing->getStatus()
&& $listing->getPhotos()->count() >= $listingRule['required_photos']
) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING_PAYMENT, false);
}
// If listing is PENDING_PAYMENT, the user already paid the fees
if (ListingStatus::PENDING_PAYMENT === $listing->getStatus()) {
if ($publicationCredit) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING, false);
}
}
// ----------------------------------------------------------------------
// Toggle the listing's category on location change.
// ----------------------------------------------------------------------
$isPaid = ListingCategories::PAID === $listing->getCategory();
$isScrapped = ListingCategories::SCRAPPED === $listing->getCategory();
$isUnlimited = ListingCategories::UNLIMITED === $listing->getCategory();
if (!$isPaid && !$isScrapped && !$isUnlimited && $listingRule['publication_fees'] > 0) {
$listing->setCategory(ListingCategories::PAID);
if (ListingStatus::PENDING === $listing->getStatus() && !$publicationCredit) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING_PAYMENT, false);
}
}
if (
!$isPaid && !$isScrapped && !$isUnlimited && $listingHasTitle && $listingRule['publication_fees'] <= 0
&& \in_array($listing->getStatus(), [ListingStatus::PENDING_PAYMENT, ListingStatus::DRAFT])
) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING, false);
}
if ($isPaid && !$isScrapped && !$isUnlimited && $listingRule['publication_fees'] <= 0) {
$listing->setCategory(ListingCategories::NORMAL);
if (ListingStatus::PENDING_PAYMENT === $listing->getStatus()) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING_PAYMENT, false);
}
}
if ($this->getListingManager()->isListingUserEbawab($listing)) {
$listing->setCategory(ListingCategories::EBAWAB);
}
if ($this->getListingManager()->isListingUserManualScraped($listing)) {
$listing->setCategory(ListingCategories::SCRAPPED);
}
}
public function onSubmittedEvent(ListingEvent $event): void
{
/** @var Listing $listing */
$listing = $event->getListing();
$matcher = $this->listingRuleMatcher;
$listingRule = $matcher->match($listing);
if (!$listingRule['flf2'] && ListingCategories::PAID === $listing->getCategory() && $this->listingManager->isEligibleForFreePublishing($listing)) {
$this->listingManager->createFirstListingForFree($listing);
}
if (
ListingSource::LITE == $listing->getSource()
|| ListingSource::SCRAPPING == $listing->getSource()
|| ListingCategories::FIRST_LISTING_FOR_FREE === $listing->getCategory()
) {
return;
}
if ($listingRule['publication_fees'] > 0 && $listing->getPhotos()->count() >= $listingRule['required_photos']) {
if ((
$this->specialAddListingService->hasSpecialAddListingGroup($listing->getUser())
&& !$listing->getSpecialPublicationCredit()
)
|| (
!$this->specialAddListingService->hasSpecialAddListingGroup($listing->getUser())
&& !$listing->getPublicationCredit()
)
) {
$this->getListingManager()->changeStatus($listing, ListingStatus::PENDING_PAYMENT, true, true);
}
}
}
public function onSubmittedStatusRedirectionEvent(ListingEvent $event): void
{
/** @var Listing $listing */
$listing = $event->getListing();
$userId = $listing->getUser();
if ($this->specialAddListingService->hasSpecialAddListingGroup($userId)) {
$userUnlimitedListings = $this->userServicesManager->hasActiveService(UserServicesType::UNLIMITED_LISTINGS, $userId);
if (!$userUnlimitedListings || ($userUnlimitedListings && 0 == $userUnlimitedListings['remainingQuota'])) {
$event->setResponse($this->redirect(
'listing_slug',
['id' => $listing->getId(), 'slug' => $listing->getSlug()]
));
return;
}
}
switch ($listing->getStatus()) {
case ListingStatus::PENDING_PAYMENT:
$event->setResponse($this->redirect(
'listing_confirm_publish_credit',
['id' => $listing->getId()]
));
break;
case ListingStatus::PENDING_PHOTOS:
$this->setFlashMessage('info', $this->getTranslator()->trans('add_listing_page.success_messages.add_photos_message'));
$event->setResponse($this->redirect(
'listing_upload',
['id' => $event->getListing()->getId()]
));
break;
}
}
/**
* @throws \Exception
*/
public function onListingPublishEvent(ListingEvent $event): void
{
$listing = $event->getListing();
$listingPublishPaid = $listing->getPublicationCredit();
$listingPublishFeatured = $listing->getFeaturedPublicationCredit();
$listingRule = $this->listingRuleMatcher->match($listing);
if (ListingCategories::FIRST_LISTING_FOR_FREE === $listing->getCategory()) {
$listing->setExpiresAt(new \DateTime('+'.$listingRule['first_free_duration'].' days'));
return;
}
if ($listingRule['duration'] && (!$listing->getExpiresAt() || new \DateTime() >= $listing->getExpiresAt())) {
$listing->setExpiresAt(new \DateTime('+'.$listingRule['duration'].' days'));
}
if ($listingPublishPaid) {
if (!$listingPublishPaid->getExpiresAt()) {
$listingPublishPaid->setExpiresAt($listing->getExpiresAt());
}
}
if ($listingPublishFeatured) {
if (!$listingPublishFeatured->getExpiresAt() && !$listingPublishFeatured->getFeaturingStatus()) {
$duration = $this->getListingFeaturingService()->getFeaturedTypeDuration($listingRule, $listingPublishFeatured->getType());
$listingPublishFeatured->setExpiresAt(new \DateTime('+'.$duration.' days'));
$this->dispatcher->dispatch(new ListingFeatureEvent($listingPublishFeatured), 'aqarmap.listing.feature.bumpup');
}
}
}
public function onFeaturingListingApprovalEvent(ListingEvent $event): void
{
$listing = $event->getListing();
$listingPublishFeatured = $listing->getFeaturedPublicationCredit();
$listingManager = $this->getListingManager();
$listingFeatureService = $this->getListingFeaturingService();
$listingRule = $listingManager->getListingRules($listing);
if ($listingPublishFeatured && ListingStatus::LIVE == $listing->getStatus()) {
$listing->setFeatured($listingFeatureService->getListingFeaturedType($listingPublishFeatured->getType()));
$listingPublishFeatured->setFeaturingStatus(ListingFeaturedStatus::APPROVED);
$listingPublishFeatured->setApprovedAt(new \DateTime());
if (!$listingPublishFeatured->getExpiresAt()) {
$duration = $this->getListingFeaturingService()->getFeaturedTypeDuration($listingRule, $listingPublishFeatured->getType());
$listingPublishFeatured->setExpiresAt(new \DateTime('+'.$duration.' days'));
}
}
$em = $this->getEntityManager();
$em->persist($listing);
$em->flush();
}
/**
* @throws \Exception
*/
public function onFeaturingListingRejectEvent(ListingEvent $event): void
{
$listing = $event->getListing();
$listingFeatured = $listing->getLastListingFeature();
if (ListingStatus::LIVE == $listing->getStatus()) {
$listingFeatured->setFeaturingStatus(ListingFeaturedStatus::REJECTED);
$listingFeatured->setExpiresAt(new \DateTime('-2 days'));
}
$em = $this->getEntityManager();
$em->persist($listingFeatured);
$featureTypeLabel = $this->getTranslator()->trans($listingFeatured->getPendingFeaturedLabel());
$balance = $this->creditManager->getBalance($listing->getUser());
$amount = abs($listingFeatured->getCredit()->getAmount());
$credit = new Credit();
$credit
->setUser($listing->getUser())
->setAmount(abs($listingFeatured->getCredit()->getAmount()))
->setDescription('Rejected to be '.$featureTypeLabel)
->setBalance($balance + $amount)
->setStatus(CreditStatus::SUCCESS)
->setCreatedAt(new \DateTime());
$em->persist($credit);
$em->flush();
}
public function getEntityManager()
{
return $this->entityManager;
}
/**
* @return TranslatorInterface
*/
public function getTranslator()
{
return $this->translator;
}
public static function getSubscribedEvents()
{
return [
'aqarmap.listing.pre_save' => ['preSaveListingEvent'],
'aqarmap.listing.submitted' => [['onSubmittedEvent'], ['onSubmittedStatusRedirectionEvent']],
'aqarmap.listing.resubmitted' => [['onSubmittedEvent'], ['onSubmittedStatusRedirectionEvent']],
'aqarmap.listing.publish' => ['onListingPublishEvent'],
'aqarmap.listing.featuring.approved' => ['onFeaturingListingApprovalEvent'],
'aqarmap.listing.free_publish' => ['onListingPublishEvent'],
'aqarmap.listing.publish_without_photos' => ['onListingPublishEvent'],
'aqarmap.listing.featuring.rejected' => ['onFeaturingListingRejectEvent'],
];
}
/**
* @return ListingManager
*/
protected function getListingManager()
{
return $this->listingManager;
}
/**
* @return ListingFeatureService
*/
protected function getListingFeaturingService()
{
return $this->listingFeatureService;
}
/**
* @param string $type
* @param string $message
*/
private function setFlashMessage($type, $message)
{
return $this->session->getFlashBag()->add($type, $message);
}
private function redirect($route, $parameters = [])
{
return new RedirectResponse(
$this->router->generate($route, $parameters),
\Symfony\Component\HttpFoundation\Response::HTTP_FOUND
);
}
}