<?php
/**
* Created by simpson <simpsonwork@gmail.com>
* Date: 2019-03-19
* Time: 15:36
*/
namespace App\Entity\Profile;
use AngelGamez\TranslatableBundle\Entity\TranslatableValue;
//use ApiPlatform\Core\Annotation\ApiProperty;
use App\Entity\Account\Advertiser;
use App\Entity\ApartmentsPricing;
use App\Entity\ContainsDomainEvents;
use App\Entity\Account\Customer;
use App\Entity\DomainEventsRecorderTrait;
use App\Entity\ExpressPricing;
use App\Entity\IProvidesServices;
use App\Entity\Location\City;
use App\Entity\Location\MapCoordinate;
use App\Entity\Location\Station;
use App\Entity\Messengers;
use App\Entity\PhoneCallRestrictions;
use App\Entity\Profile\Comment\CommentByCustomer;
use App\Entity\Profile\Confirmation\ModerationRequest;
use App\Entity\Sales\Profile\AdBoardPlacement;
use App\Entity\Sales\Profile\PlacementHiding;
use App\Entity\Sales\Profile\TopPlacement;
use App\Entity\ProvidedServiceTrait;
use App\Entity\TakeOutPricing;
use App\Repository\ProfileRepository;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Index;
//use ApiPlatform\Core\Annotation\ApiResource;
//use ApiPlatform\Core\Annotation\ApiFilter;
//use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
//use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\RangeFilter;
use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;
use Symfony\Component\Serializer\Annotation\Groups;
use Gedmo\Mapping\Annotation as Gedmo;
use App\Validator\Constraints\ValidPhoneForCountry as ValidPhoneForCountryAssert;
use App\Validator\Constraints\PhoneNotBlack as PhoneNotBlackAssert;
/**
* ApiResource(collectionOperations={"get"}, itemOperations={"get"}, normalizationContext={"groups"={"profile"}}, attributes={"pagination_client_enabled"=true, "pagination_client_items_per_page"=true})
* ApiFilter(SearchFilter::class, properties={"city": "exact", "providedServices": "exact"})
* ApiFilter(RangeFilter::class, properties={"personParameters.age", "personParameters.height", "personParameters.weight", "personParameters.breastSize", "apartmentsPricing.oneHourPrice"})
* @ValidPhoneForCountryAssert/ProtocolClass
* @PhoneNotBlackAssert/ProtocolClass
*/
#[Gedmo\SoftDeleteable(fieldName: "deletedAt", timeAware: true)]
#[ORM\Table(name: 'profiles')]
#[Index(name: 'idx_deleted_at', columns: ['deleted_at'])]
#[Index(name: 'idx_created_at', columns: ['created_at'])]
#[Index(name: 'idx_gender', columns: ['person_gender'])]
#[Index(name: 'idx_apartments_one_hour_price', columns: ['apartments_one_hour_price'])]
#[Index(name: 'idx_is_dummy', columns: ['is_dummy'])]
#[Index(name: 'idx_city_deleted', columns: ['city_id', 'deleted_at'])]
#[Index(name: 'idx_city_deleted_moderation', columns: ['city_id', 'deleted_at', 'moderation_status'])]
#[Index(name: 'idx_city_masseur_deleted', columns: ['city_id', 'is_masseur', 'deleted_at'])]
#[Index(name: 'idx_city_masseur_deleted_moderation', columns: ['city_id', 'is_masseur', 'deleted_at', 'moderation_status'])]
#[Index(name: 'idx_city_deleted_gender', columns: ['city_id', 'deleted_at', 'person_gender'])]
#[Index(name: 'idx_city_deleted_moderation_gender', columns: ['city_id', 'deleted_at', 'moderation_status', 'person_gender'])]
#[Index(name: 'idx_city_masseur_deleted_gender', columns: ['city_id', 'is_masseur', 'deleted_at', 'person_gender'])]
#[Index(name: 'idx_city_masseur_deleted_moderation_gender', columns: ['city_id', 'is_masseur', 'deleted_at', 'moderation_status', 'person_gender'])]
#[ORM\Entity(repositoryClass: ProfileRepository::class)]
#[ORM\HasLifecycleCallbacks]
class Profile implements ContainsDomainEvents, IProvidesServices
{
use SoftDeleteableEntity;
use DomainEventsRecorderTrait;
use ProvidedServiceTrait;
const MODERATION_STATUS_NOT_PASSED = 0;
const MODERATION_STATUS_APPROVED = 1;
const MODERATION_STATUS_WAITING = 2;
const MODERATION_STATUS_REJECTED = 3;
#[ORM\Id]
#[ORM\Column(name: 'id', type: 'integer')]
#[ORM\GeneratedValue(strategy: 'AUTO')]
#[Groups('profile')]
protected int $id;
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true)]
#[ORM\ManyToOne(targetEntity: Advertiser::class, inversedBy: 'profiles')]
protected ?Advertiser $owner;
#[ORM\Column(name: 'is_dummy', type: 'boolean', options: ['default' => 0])]
protected bool $dummy = false;
/** @var TopPlacement[] */
#[ORM\OneToMany(targetEntity: TopPlacement::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
protected Collection $topPlacements;
#[ORM\OneToOne(targetEntity: AdBoardPlacement::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
protected ?AdBoardPlacement $adBoardPlacement = null;
#[ORM\OneToOne(targetEntity: PlacementHiding::class, mappedBy: 'profile', cascade: ['all'])]
protected ?PlacementHiding $placementHiding = null;
#[ORM\Column(name: 'uri_identity', type: 'string', length: 64)]
#[Groups('profile')]
protected string $uriIdentity;
#[ORM\Column(name: 'name', type: 'translatable')]
#[Groups('profile')]
protected TranslatableValue $name;
#[ORM\Column(name: 'description', type: 'translatable')]
#[Groups('profile')]
protected ?TranslatableValue $description = null;
#[ORM\Embedded(class: PersonParameters::class, columnPrefix: 'person_')]
#[Groups('profile')]
protected PersonParameters $personParameters;
/** var ProfileService[] */
#[ORM\OneToMany(targetEntity: ProfileService::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
#[ORM\Cache(usage: 'NONSTRICT_READ_WRITE', region: 'profiles')]
protected Collection $providedServices;
/** @var int[] */
#[ORM\Column(name: 'client_types', type: 'simple_array', nullable: true)]
protected ?array $clientTypes;
#[ORM\Column(name: 'phone_number', type: 'string', length: 24)]
#[Groups('profile')]
protected string $phoneNumber;
#[ORM\Embedded(class: Messengers::class, columnPrefix: false)]
#[Groups('profile')]
protected ?Messengers $messengers = null;
#[ORM\Embedded(class: PhoneCallRestrictions::class, columnPrefix: false)]
protected ?PhoneCallRestrictions $phoneCallRestrictions = null;
#[ORM\Column(name: 'is_masseur', type: 'boolean')]
protected bool $masseur = false;
#[ORM\Embedded(class: ClientRestrictions::class, columnPrefix: false)]
protected ?ClientRestrictions $clientRestrictions = null;
#[ORM\Embedded(class: ApartmentsPricing::class, columnPrefix: false)]
#[Groups('profile')]
protected ?ApartmentsPricing $apartmentsPricing = null;
#[ORM\Embedded(class: TakeOutPricing::class, columnPrefix: false)]
#[Groups('profile')]
protected ?TakeOutPricing $takeOutPricing = null;
#[ORM\Embedded(class: ExpressPricing::class, columnPrefix: false)]
#[Groups('profile')]
protected ?ExpressPricing $expressPricing = null;
#[ORM\Embedded(class: CarPricing::class, columnPrefix: false)]
#[Groups('profile')]
protected ?CarPricing $carPricing = null;
#[ORM\Column(name: 'extra_charge', type: 'integer', nullable: true)]
#[Groups('profile')]
protected ?int $extraCharge;
/** @var Photo[] */
#[ORM\OneToMany(targetEntity: Photo::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
#[Groups('profile')]
protected Collection $photos;
/** @var Selfie[] */
#[ORM\OneToMany(targetEntity: Selfie::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
#[Groups('profile')]
protected Collection $selfies;
/** @var Video[] */
#[ORM\OneToMany(targetEntity: Video::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
protected Collection $videos;
#[ORM\OneToMany(targetEntity: FileProcessingTask::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
protected Collection $processingFiles;
#[ORM\OneToOne(targetEntity: AdminApprovalPhoto::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
protected ?AdminApprovalPhoto $adminApprovalPhoto = null;
#[ORM\OneToOne(targetEntity: Avatar::class, mappedBy: 'profile', cascade: ['all'], orphanRemoval: true)]
protected ?Avatar $avatar = null;
/** @var CommentByCustomer[] */
#[ORM\OneToMany(targetEntity: CommentByCustomer::class, mappedBy: 'profile')]
protected Collection $comments;
#[ORM\Column(name: 'is_approved', type: 'boolean')]
#[Groups('profile')]
protected bool $approved = false;
#[ORM\Column(name: 'moderation_status', type: 'integer')]
#[Groups('profile')]
protected int $moderationStatus = 0;
#[ORM\JoinColumn(name: 'city_id', referencedColumnName: 'id')]
#[ORM\ManyToOne(targetEntity: City::class)]
#[ORM\Cache(usage: 'NONSTRICT_READ_WRITE', region: 'profiles')]
protected City $city;
/** @var Station[] */
//, indexBy="id"
#[ORM\JoinTable(name: 'profile_stations')]
#[ORM\JoinColumn(name: 'profile_id', referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name: 'station_id', referencedColumnName: 'id')]
#[ORM\ManyToMany(targetEntity: Station::class)]
#[Groups('profile')]
#[ORM\Cache(usage: 'NONSTRICT_READ_WRITE', region: 'profiles')]
protected Collection $stations;
#[ORM\Embedded(class: MapCoordinate::class, columnPrefix: false)] // ApiProperty()
#[Groups('profile')]
protected ?MapCoordinate $mapCoordinate;
#[ORM\Column(name: 'created_at', type: 'datetimetz_immutable', nullable: true)]
protected ?\DateTimeImmutable $createdAt;
#[Gedmo\Timestampable(on:"change", field:["name", "description", "personParameters", "providedServices", "clientTypes", "phoneNumber", "messengers", "phoneCallrestrictions", "masseur", "clientRestrictions", "apartmentsPricing", "takeOutPricing", "expressPricing", "carPricing", "extraCharge", "photos", "selfies", "videos", "avatar", "stations", "mapCoordinate"])]
#[ORM\Column(name: 'updated_at', type: 'datetimetz_immutable', nullable: true)]
#[Groups('profile')]
protected ?\DateTimeImmutable $updatedAt;
#[ORM\Column(name: 'inactivated_at', type: 'datetimetz_immutable', nullable: true)]
protected ?\DateTimeImmutable $inactivatedAt;
private bool $draft = false;
#[ORM\Column(name: 'seo', type: 'json', nullable: true)]
#[Groups('profile')]
private ?array $seo = null;
public static function draft(?\DateTimeImmutable $createdAt = null, ?bool $dummy = null): self
{
$profile = new static($createdAt);
if(null !== $dummy)
$profile->dummy = $dummy;
return $profile;
}
public static function create(string $uriIdentity, ?\DateTimeImmutable $createdAt = null): self
{
$profile = new static($createdAt);
$profile->defineUriIdentity($uriIdentity);
$profile->toggleMasseur(false);
return $profile;
}
public static function createMasseur(string $uriIdentity, ?\DateTimeImmutable $createdAt = null): self
{
$profile = new static($createdAt);
$profile->defineUriIdentity($uriIdentity);
$profile->toggleMasseur(true);
return $profile;
}
protected function __construct(?\DateTimeImmutable $createdAt)
{
$this->draft = true;
$this->createdAt = $createdAt;
$this->photos = new ArrayCollection();
$this->selfies = new ArrayCollection();
$this->videos = new ArrayCollection();
$this->processingFiles = new ArrayCollection();
$this->comments = new ArrayCollection();
$this->topPlacements = new ArrayCollection();
$this->providedServices = new ArrayCollection();
$this->stations = new ArrayCollection();
$this->inactivatedAt = CarbonImmutable::now();
}
public function isDraft(): bool
{
return $this->draft;
}
public function isOwnedBy(Advertiser $account): bool
{
return $account->getId() === $this->owner->getId();
}
public function defineUriIdentity(string $uriIdentity): void
{
if (!$this->isDraft()) {
throw new \DomainException('Profile is already created and can\'t change its URI.');
}
$this->uriIdentity = $uriIdentity;
$this->draft = false;
}
public function toggleMasseur(bool $isMasseur): void
{
if ($this->masseur !== $isMasseur && (null !== $this->adBoardPlacement && false == $this->adBoardPlacement->getType()->isFree())) {
throw new \DomainException('Impossible to toggle profile type while it is displaying on adboard.');
}
$this->masseur = $isMasseur;
}
public function setBio(TranslatableValue $name, TranslatableValue $description): void
{
$this->name = $name;
$this->description = $description;
}
public function setLocation(City $city, $stations, ?MapCoordinate $mapCoordinate): void
{
if (!$this->isDraft() && !$this->city->equals($city)) {
throw new \DomainException('City change for a saved profile is forbidden.');
}
$this->city = $city;
$this->changeStations($stations);
$this->mapCoordinate = $mapCoordinate;
}
protected function changeStations($stations)
{
if(null === $stations)
return;
if(false === is_array($stations) && false === is_iterable($stations))
throw new \InvalidArgumentException('Stations list should be either an array or an ArrayCollection');
$stationsArray = is_iterable($stations) && !is_array($stations) ? iterator_to_array($stations) : $stations;
$stations = [];
foreach ($stationsArray as $station) {
$stations[$station->getId()] = $station;
}
$stationIds = array_map(function (Station $station): int {
return $station->getId();
}, $stations);
$existingStationIds = $this->stations->map(function (Station $station): int {
return $station->getId();
})->getValues();
$stationIdsToAdd = array_diff($stationIds, $existingStationIds);
$stationIdsToRemove = array_diff($existingStationIds, $stationIds);
foreach ($stationIdsToAdd as $stationId) {
$this->stations->add($stations[$stationId]);
}
foreach ($stationIdsToRemove as $stationId) {
$this->stations->remove($stationId);
}
}
public function setPersonParameters(PersonParameters $personParameters): void
{
$this->personParameters = $personParameters;
}
public function setEnabledProvidedServices($services): void
{
if (null !== $services) {
if (is_array($services)) {
$services = new ArrayCollection($services);
} elseif (!$services instanceof ArrayCollection) {
if (is_iterable($services)) {
$services = new ArrayCollection(iterator_to_array($services));
} else {
throw new \InvalidArgumentException('Services list should be either an array or an ArrayCollection');
}
}
$this->providedServices = $services;
}
}
public function setPhoneCallOptions(string $phoneNumber, ?PhoneCallRestrictions $restrictions, ?Messengers $messengers): void
{
$this->phoneNumber = $phoneNumber;
$this->phoneCallRestrictions = $restrictions;
$this->messengers = $messengers;
}
public function setClientRestrictions(?ClientRestrictions $restrictions): void
{
$this->clientRestrictions = $restrictions;
}
public function setPricing(?ApartmentsPricing $apartmentsPricing, ?TakeOutPricing $takeOutPricing, ?int $extraCharge, ?ExpressPricing $expressPricing = null, ?CarPricing $carPricing = null): void
{
$this->apartmentsPricing = $apartmentsPricing;
$this->takeOutPricing = $takeOutPricing;
$this->extraCharge = $extraCharge;
$this->expressPricing = $expressPricing;
$this->carPricing = $carPricing;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
public function isApproved(): bool
{
return $this->approved;
}
public function approve(): void
{
$this->approved = true;
}
public function unApprove(): void
{
$this->approved = false;
}
public function getId(): int
{
return $this->id;
}
public function getOwner(): ?Advertiser
{
return $this->owner;
}
public function setOwner(Advertiser $owner): void
{
$this->owner = $owner;
}
public function hasOwner(): bool
{
return null !== $this->owner;
}
public function getTopPlacements(): Collection
{
return $this->topPlacements;
}
public function addTopPlacement(TopPlacement $topPlacement): void
{
$this->topPlacements->add($topPlacement);
}
public function getAdBoardPlacement(): ?AdBoardPlacement
{
return $this->adBoardPlacement;
}
public function setAdBoardPlacement(AdBoardPlacement $adBoardPlacement): void
{
$this->adBoardPlacement = $adBoardPlacement;
}
/**
* Анкета оплачена и выводится в общих списках на сайте
* или в ТОПе, то есть "АКТИВНА"
*/
public function isActive(): bool
{
return null !== $this->adBoardPlacement || $this->hasRunningTopPlacement();
}
public function getUriIdentity(): string
{
return $this->uriIdentity;
}
public function getName(): TranslatableValue
{
return $this->name;
}
public function getDescription(): ?TranslatableValue
{
return $this->description;
}
public function getPersonParameters(): PersonParameters
{
return $this->personParameters;
}
public function getPhoneNumber(): string
{
return $this->phoneNumber;
}
//TODO return type
public function getPhoneCallRestrictions(): ?PhoneCallRestrictions
{
return $this->phoneCallRestrictions;
}
public function isMasseur(): bool
{
return $this->masseur;
}
//TODO return type
public function getClientRestrictions(): ?ClientRestrictions
{
return $this->clientRestrictions;
}
//TODO return type
public function getApartmentsPricing(): ?ApartmentsPricing
{
return $this->apartmentsPricing;
}
//TODO return type
public function getTakeOutPricing(): ?TakeOutPricing
{
return $this->takeOutPricing;
}
public function getExtraCharge(): ?int
{
return $this->extraCharge;
}
/**
* @return Photo[]
*/
public function getPhotos(): Collection
{
return $this->photos->filter(function ($mediaFile): bool {
return get_class($mediaFile) == Photo::class;
});
}
public function addPhoto(string $path, bool $isMain): Photo
{
$photos = $this->getPhotos();
$found = $photos->filter(function (Photo $photo) use ($path): bool {
return $path === $photo->getPath();
});
if (!$found->isEmpty())
return $found->first();
if (true === $isMain) {
$photos->forAll(function ($index, Photo $photo): true {
$photo->unsetMain();
return true;
});
}
$photo = new Photo($this, $path, $isMain);
$this->photos->add($photo);
return $photo;
}
public function removePhoto(string $path): bool
{
foreach ($this->getPhotos() as $photo) {
if ($path === $photo->getPath()) {
$this->photos->removeElement($photo);
return true;
}
}
return false;
}
public function getMainPhotoOrFirstPhoto(): ?Photo
{
$photos = $this->getPhotos();
if ($photos->isEmpty()) {
return null;
}
$mainPhoto = $this->getMainPhoto();
if (null === $mainPhoto) {
$mainPhoto = $photos->first();
}
return $mainPhoto;
}
public function getMainPhoto(): ?Photo
{
$photos = $this->getPhotos();
if ($photos->isEmpty()) {
return null;
}
$mainPhoto = null;
$photos->forAll(function ($index, Photo $photo) use (&$mainPhoto): bool {
if ($photo->isMain()) {
$mainPhoto = $photo;
return false; // Stop the cycle
}
return true;
});
return $mainPhoto;
}
public function changeMainPhoto(string $path): void
{
$photos = $this->getPhotos();
$found = $photos->filter(function (Photo $photo) use ($path): bool {
return $path === $photo->getPath();
});
if ($found->isEmpty()) {
return;
}
$mainPhoto = $found->first();
$photos->forAll(function ($index, Photo $photo): true {
$photo->unsetMain();
return true;
});
$mainPhoto->setMain();
}
/**
* @return Selfie[]
*/
public function getSelfies(): Collection
{
return $this->selfies;
}
public function addSelfie(string $path): Selfie
{
$found = $this->getSelfies()->filter(function (Selfie $selfie) use ($path): bool {
return $path === $selfie->getPath();
});
if (!$found->isEmpty())
return $found->first();
$selfie = new Selfie($this, $path);
$this->selfies->add($selfie);
return $selfie;
}
public function removeSelfie(string $path): bool
{
foreach ($this->getSelfies() as $selfie) {
if ($path === $selfie->getPath()) {
$this->selfies->removeElement($selfie);
return true;
}
}
return false;
}
/**
* @return Video[]
*/
public function getVideos(): Collection
{
return $this->videos->filter(function ($mediaFile): bool {
return ($mediaFile instanceof Video);
});
}
public function getConfirmedVideos(): Collection
{
return $this->videos->filter(function ($mediaFile): bool {
if (!$mediaFile instanceof Video) {
return false;
}
return $mediaFile->isConfirmed();
});
}
/**
* Храним только 1 видео для анкеты
*/
public function addVideo(string $videoPath, ?string $posterPath = null): Video
{
$found = $this->getVideos()->filter(function (Video $video) use ($videoPath): bool {
return $videoPath === $video->getPath();
});
if (!$found->isEmpty())
return $found->first();
$video = new Video($this, $videoPath);
if (null !== $posterPath) {
$video->setPreviewPath($posterPath);
}
//теперь разрешаем много видео
//$this->videos->clear();
$this->videos->add($video);
return $video;
}
public function removeVideo(string $path): bool
{
foreach ($this->getVideos() as $video) {
if ($path === $video->getPath()) {
$this->videos->removeElement($video);
$this->photos->removeElement($video);
return true;
}
}
return false;
}
/**
* Добавляет таск на обработку оригинала видео в подходящий формат
*
* @param string $path Путь к файлу оригинала относительно фс очередей
*/
public function addRawVideo(string $path): FileProcessingTask
{
$file = new FileProcessingTask($this, $path);
$this->processingFiles->add($file);
return $file;
}
public function videosInProcess(): int
{
$inProcess = $this->processingFiles->filter(function (FileProcessingTask $task): bool {
return !$task->isCompleted();
});
return $inProcess->count();
}
public function hasFilesInProcess(): bool
{
return $this->videosInProcess() > 0;
}
public function isMediaProcessed(): bool
{
foreach ($this->videos as $video)
if(null === $video->getPreviewPath())
return false;
return true;
}
public function getAvatar(): ?Avatar
{
return $this->avatar;
}
public function setAvatar(string $path): void
{
$this->avatar = new Avatar($this, $path);
}
public function removeAvatar(): bool
{
if(null == $this->avatar)
return false;
foreach ($this->photos as $photo) {
if ($this->avatar->getPath() === $photo->getPath()) {
$this->photos->removeElement($photo);
break;
}
}
$this->avatar = null;
return true;
}
/**
* @return CommentByCustomer[]
*/
public function getComments(): Collection
{
return $this->comments->filter(function(CommentByCustomer $comment): bool {
return null == $comment->getParent();
});
}
/**
* @return CommentByCustomer[]
*/
public function getCommentsOrderedByNotReplied(): array
{
$comments = $this->comments->filter(function(CommentByCustomer $comment): bool {
return null == $comment->getParent();
})->toArray();
usort($comments, function (CommentByCustomer $commentA, CommentByCustomer $commentB): int {
if((null == $commentA->getLastCommentByAdvertiser() && null == $commentB->getLastCommentByAdvertiser())
|| (null != $commentA->getLastCommentByAdvertiser() && null != $commentB->getLastCommentByAdvertiser())) {
if($commentA->getCreatedAt() == $commentB->getCreatedAt())
return $commentA->getId() > $commentB->getId() ? -1 : 1;
else
return $commentA->getCreatedAt() > $commentB->getCreatedAt() ? -1 : 1;
}
if(null == $commentA->getLastCommentByAdvertiser() && null != $commentB->getLastCommentByAdvertiser())
return -1;
else
return 1;
});
return $comments;
}
/**
* @return CommentByCustomer[]
*/
public function getCommentsWithoutReply(): Collection
{
return $this->comments->filter(function(CommentByCustomer $comment): bool {
return null == $comment->getParent() && false == $comment->isCommentedByAdvertiser();
});
}
/**
* @return CommentByCustomer[]
*/
public function getCommentsWithReply(): Collection
{
return $this->comments->filter(function(CommentByCustomer $comment): bool {
return null == $comment->getParent() && true == $comment->isCommentedByAdvertiser();
});
}
/**
* @return CommentByCustomer[]
*/
public function getNewComments(): Collection
{
$weekAgo = CarbonImmutable::now()->sub('7 days');
return $this->comments->filter(function(CommentByCustomer $comment) use ($weekAgo): bool {
return null == $comment->getParent()
&& (
$comment->getCreatedAt() >= $weekAgo
|| null == $this->getCommentReply($comment)
);
});
}
public function getOldComments(): Collection
{
$weekAgo = CarbonImmutable::now()->sub('7 days');
return $this->comments->filter(function(CommentByCustomer $comment) use ($weekAgo): bool {
return null == $comment->getParent()
&& false == (
$comment->getCreatedAt() >= $weekAgo
|| null == $this->getCommentReply($comment)
);
});
}
private function getCommentReply(CommentByCustomer $parent): ?CommentByCustomer
{
foreach ($this->comments as $comment)
if($comment->getParent() == $parent)
return $comment;
return null;
}
public function getCommentFromUser(Customer $user): ?CommentByCustomer
{
foreach ($this->comments as $comment)
if(null == $comment->getParent() && null != $comment->getUser() && $user->getId() == $comment->getUser()->getId())
return $comment;
return null;
}
public function getCity(): City
{
return $this->city;
}
/**
* @return Station[]
*/
public function getStations(): Collection
{
return $this->stations;
}
//TODO return type
public function getMapCoordinate(): ?MapCoordinate
{
return $this->mapCoordinate;
}
public function getCreatedAt(): ?\DateTimeImmutable
{
return $this->createdAt;
}
public function getUpdatedAt(): ?\DateTimeImmutable
{
return $this->updatedAt;
}
public function getModerationStatus(): int
{
return $this->moderationStatus;
}
public function isModerationPassed(): bool
{
return $this->moderationStatus == self::MODERATION_STATUS_APPROVED;
}
public function isModerationWaiting(): bool
{
return $this->moderationStatus == self::MODERATION_STATUS_WAITING;
}
public function isModerationRejected(): bool
{
return $this->moderationStatus == self::MODERATION_STATUS_REJECTED;
}
public function setModerationStatus(int $status): void
{
if (self::MODERATION_STATUS_APPROVED === $status) {
throw new \RuntimeException(sprintf('Use %s::passModeration() method instead', static::class));
}
$validStatuses = [self::MODERATION_STATUS_NOT_PASSED, self::MODERATION_STATUS_APPROVED, self::MODERATION_STATUS_WAITING, self::MODERATION_STATUS_REJECTED];
if(false === array_search($status, $validStatuses))
throw new \LogicException('Trying to set an invalid moderation status');
$this->moderationStatus = $status;
}
public function passModeration(?ModerationRequest $moderationRequest = null): void
{
$this->moderationStatus = self::MODERATION_STATUS_APPROVED;
$func = static function($k, Photo|Video $file) use ($moderationRequest): bool {
if (!$file->isConfirmed()) {
$file->passModeration($moderationRequest);
}
return true;
};
$this->videos->forAll($func);
}
public function delete(): void
{
$this->deletePlacementHiding();
$this->deleteFromAdBoard();
$now = new \DateTimeImmutable('now');
$toDelete = [];
foreach ($this->topPlacements as $topPlacement) {
if ($topPlacement->getExpiresAt() > $now)
$toDelete[] = $topPlacement;
}
foreach ($toDelete as $topPlacement)
$this->topPlacements->removeElement($topPlacement);
$this->setDeletedAt(Carbon::now());
}
public function undoDelete(): void
{
$this->setDeletedAt(); // will pass null by default
}
public function deleteFromAdBoard(): void
{
$this->adBoardPlacement = null;
}
public function deleteFromTopPlacement(): void
{
//здесь нужна логика отмены конретного размещения
// $this->topPlacement = null;
}
public function hasRunningTopPlacement(): bool
{
$now = new \DateTimeImmutable('now');
foreach ($this->topPlacements as /** @var TopPlacement $topPlacement */ $topPlacement) {
if($topPlacement->getPlacedAt() <= $now && $now <= $topPlacement->getExpiresAt())
return true;
}
return false;
}
//TODO return type
public function getExpressPricing(): ?ExpressPricing
{
return $this->expressPricing;
}
//TODO return type
public function getCarPricing(): ?CarPricing
{
return $this->carPricing;
}
/**
* @return int[]
*/
public function getClientTypes(): array
{
return $this->clientTypes ?? [];
}
/**
* @param int[] $clientTypes
*/
public function setClientTypes(array $clientTypes): void
{
$this->clientTypes = $clientTypes;
}
//TODO return type
public function getMessengers(): ?Messengers
{
return $this->messengers;
}
public function getInactivatedAt(): ?\DateTimeImmutable
{
return $this->inactivatedAt;
}
public function setInactive(): void
{
$this->inactivatedAt = CarbonImmutable::now();
}
public function undoInactive(): void
{
$this->inactivatedAt = null;
}
public function getPlacementHiding(): ?PlacementHiding
{
return $this->placementHiding;
}
public function setPlacementHiding(PlacementHiding $placementHiding): void
{
$this->placementHiding = $placementHiding;
}
public function isHidden(): bool
{
return null !== $this->getPlacementHiding();
}
public function deletePlacementHiding(): void
{
$this->placementHiding = null;
}
public function hasSelfie(): bool
{
return $this->selfies->count() > 0;
}
public function hasVideo(): bool
{
return $this->videos->count() > 0;
}
public function isCommented(): bool
{
return $this->comments->count() > 0;
}
public function adminApprovalPhoto(): ?AdminApprovalPhoto
{
return $this->adminApprovalPhoto;
}
public function setAdminApprovalPhoto(?string $path): void
{
$this->adminApprovalPhoto = $path ? new AdminApprovalPhoto($this, $path) : null;
}
public function seo(): ?array
{
return $this->seo;
}
public function seoPhoneNumber(): ?string
{
return $this->seo['phone'] ?? null;
}
public function setSeoPhoneNumber(string $phoneNumber): void
{
if(null === $this->seo) {
$this->seo = [];
}
$this->seo['phone'] = $phoneNumber;
}
}