<?php
/**
* Created by simpson <simpsonwork@gmail.com>
* Date: 2019-04-15
* Time: 19:50
*/
namespace App\Entity\Saloon;
use AngelGamez\TranslatableBundle\Entity\TranslatableValue;
use App\Entity\Account\Advertiser;
use App\Entity\ApartmentsPricing;
use App\Entity\Account\Customer;
use App\Entity\ExpressPricing;
use App\Entity\IProvidedService;
use App\Entity\IProvidesServices;
use App\Entity\Location\City;
use App\Entity\Location\Station;
use App\Entity\Location\MapCoordinate;
use App\Entity\Messengers;
use App\Entity\PhoneCallRestrictions;
use App\Entity\Sales\Saloon\AdBoardPlacement;
use App\Entity\Saloon\Comment\CommentByCustomer;
use App\Entity\Service;
use App\Entity\Sales\Saloon\PlacementHiding;
use App\Entity\ProvidedServiceTrait;
use App\Entity\TakeOutPricing;
use App\Repository\SaloonRepository;
use Carbon\Carbon;
use Carbon\CarbonImmutable;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;
#[Gedmo\SoftDeleteable(fieldName: "deletedAt", timeAware: true)]
#[ORM\Table(name: 'saloons')]
#[ORM\Entity(repositoryClass: SaloonRepository::class)]
class Saloon implements IProvidesServices
{
use SoftDeleteableEntity;
use ProvidedServiceTrait;
#[ORM\Id]
#[ORM\Column(name: 'id', type: 'integer')]
#[ORM\GeneratedValue(strategy: 'AUTO')]
protected int $id;
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true)]
#[ORM\ManyToOne(targetEntity: Advertiser::class, inversedBy: 'saloons')]
protected ?Advertiser $owner;
#[ORM\OneToOne(targetEntity: AdBoardPlacement::class, mappedBy: 'saloon', cascade: ['all'])]
protected ?AdBoardPlacement $adBoardPlacement;
#[ORM\OneToOne(targetEntity: PlacementHiding::class, mappedBy: 'saloon', cascade: ['all'])]
protected ?PlacementHiding $placementHiding;
#[ORM\Column(name: 'uri_identity', type: 'string', length: 128)]
protected string $uriIdentity;
#[ORM\Column(name: 'name', type: 'translatable')]
protected TranslatableValue $name;
#[ORM\Column(name: 'description', type: 'translatable')]
protected TranslatableValue $description;
/** @var SaloonService[] */
#[ORM\OneToMany(targetEntity: SaloonService::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
#[ORM\Cache(usage: 'NONSTRICT_READ_WRITE')]
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: 255)]
protected string $phoneNumber;
#[ORM\Embedded(class: Messengers::class, columnPrefix: false)]
protected ?Messengers $messengers;
#[ORM\Embedded(class: PhoneCallRestrictions::class, columnPrefix: false)]
protected ?PhoneCallRestrictions $phoneCallRestrictions;
#[ORM\Embedded(class: WorkingHours::class, columnPrefix: 'working_hours_')]
protected ?WorkingHours $workingHours;
/**
* Расценки в свободной форме написания
*/
#[ORM\Column(name: 'prices', type: 'text', nullable: true)]
protected ?string $prices;
#[ORM\Embedded(class: ApartmentsPricing::class, columnPrefix: false)]
protected ?ApartmentsPricing $apartmentsPricing;
#[ORM\Embedded(class: TakeOutPricing::class, columnPrefix: false)]
protected ?TakeOutPricing $takeOutPricing;
#[ORM\Embedded(class: ExpressPricing::class, columnPrefix: false)]
protected ?ExpressPricing $expressPricing;
#[ORM\Column(name: 'extra_charge', type: 'integer', nullable: true)]
protected ?int $extraCharge;
#[ORM\OneToOne(targetEntity: Thumbnail::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
protected ?Thumbnail $thumbnail = null;
/** @var Photo[] */
#[ORM\OneToMany(targetEntity: Photo::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
protected Collection $photos;
/** @var Video[] */
#[ORM\OneToMany(targetEntity: Video::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
protected Collection $videos;
/** @var CommentByCustomer[] */
#[ORM\OneToMany(targetEntity: CommentByCustomer::class, mappedBy: 'saloon')]
protected Collection $comments;
#[ORM\JoinColumn(name: 'city_id', referencedColumnName: 'id')]
#[ORM\ManyToOne(targetEntity: City::class)]
protected City $city;
/** @var Station[] */
#[ORM\JoinTable(name: 'saloon_stations')]
#[ORM\JoinColumn(name: 'saloon_id', referencedColumnName: 'id')]
#[ORM\InverseJoinColumn(name: 'station_id', referencedColumnName: 'id')]
#[ORM\ManyToMany(targetEntity: Station::class)]
protected Collection $stations;
/**
* Районы в свободной форме написания
*/
#[ORM\Column(name: 'districts', type: 'string', length: 255, nullable: true)]
protected ?string $districts;
#[ORM\Column(name: 'address', type: 'translatable', nullable: true)]
protected ?TranslatableValue $address;
#[ORM\Embedded(class: MapCoordinate::class, columnPrefix: false)]
protected MapCoordinate $mapCoordinate;
#[ORM\Column(name: 'created_at', type: 'datetimetz_immutable', nullable: true)]
protected ?\DateTimeImmutable $createdAt;
#[Gedmo\Timestampable(on: "update")]
#[ORM\Column(name: 'updated_at', type: 'datetimetz_immutable', nullable: true)]
protected ?\DateTimeImmutable $updatedAt;
#[ORM\Column(name: 'inactivated_at', type: 'datetimetz_immutable', nullable: true)]
protected ?\DateTimeImmutable $inactivatedAt;
#[ORM\Column(name: 'email', type: 'string', length: 180)]
protected string $email;
private bool $draft = false;
#[ORM\Column(name: 'seo', type: 'json', nullable: true)]
private ?array $seo = null;
public static function draft(?\DateTimeImmutable $createdAt = null): self
{
$saloon = new static($createdAt);
return $saloon;
}
public function __construct(string $uriIdentity, ?\DateTimeImmutable $createdAt = null)
{
$this->draft = true;
$this->uriIdentity = $uriIdentity;
$this->createdAt = $createdAt ?? CarbonImmutable::now();
$this->photos = new ArrayCollection();
$this->videos = new ArrayCollection();
$this->providedServices = new ArrayCollection();
}
public function defineUriIdentity(string $uriIdentity): void
{
if (!$this->isDraft()) {
throw new \DomainException('Saloon is already created and can\'t change its URI.');
}
$this->uriIdentity = $uriIdentity;
$this->draft = false;
}
public function setOwner(Advertiser $owner): void
{
$this->owner = $owner;
}
public function hasOwner(): bool
{
return null !== $this->owner;
}
public function setNameAndDescription(TranslatableValue $name, TranslatableValue $description): void
{
if (!empty($name)) {
$this->name = $name;
}
if (!empty($description)) {
$this->description = $description;
}
}
public function setLocation(City $city, $stations, $districts, $address, ?MapCoordinate $mapCoordinate): void
{
$this->city = $city;
if (null !== $stations) {
if (is_array($stations)) {
$stations = new ArrayCollection($stations);
} elseif (!$stations instanceof ArrayCollection) {
if (is_iterable($stations)) {
$stations = new ArrayCollection(iterator_to_array($stations));
} else {
throw new \InvalidArgumentException('Stations list should be either an array or an ArrayCollection');
}
}
$this->stations = $stations;
}
if (!empty($districts)) {
$this->districts = $districts;
}
if (!empty($address)) {
$this->address = $address;
}
$this->mapCoordinate = $mapCoordinate;
}
public function setPhoneCallOptions(string $phoneNumber, ?PhoneCallRestrictions $restrictions, ?Messengers $messengers): void
{
$this->phoneNumber = $phoneNumber;
$this->phoneCallRestrictions = $restrictions;
$this->messengers = $messengers;
}
public function setWorkingHours(?WorkingHours $workingHours): void
{
// $this->phoneNumber = $phoneNumber;
$this->workingHours = $workingHours;
}
public function setEmail(string $email): void
{
$this->email = $email;
}
public function setPricing(?ApartmentsPricing $apartmentsPricing, ?TakeOutPricing $takeOutPricing, ?int $extraCharge, ?ExpressPricing $expressPricing = null): void
{
// $this->prices = $pricing;
$this->apartmentsPricing = $apartmentsPricing;
$this->takeOutPricing = $takeOutPricing;
$this->extraCharge = $extraCharge;
$this->expressPricing = $expressPricing;
}
public function setUpdatedAt(\DateTimeImmutable $updatedAt): void
{
$this->updatedAt = $updatedAt;
}
public function isDraft(): bool
{
return $this->draft;
}
public function isOwnedBy(Advertiser $account): bool
{
return $account->getId() === $this->owner->getId();
}
public function getId(): int
{
return $this->id;
}
public function getOwner(): ?Advertiser
{
return $this->owner;
}
public function getAdBoardPlacement(): ?AdBoardPlacement
{
return $this->adBoardPlacement;
}
/**
* Салон оплачен и выводится в общих списках на сайте
*/
public function isActive(): bool
{
return null !== $this->adBoardPlacement;
}
public function getUriIdentity(): string
{
return $this->uriIdentity;
}
public function getName(): TranslatableValue
{
return $this->name;
}
public function getDescription(): TranslatableValue
{
return $this->description;
}
public function getPhoneNumber(): string
{
return $this->phoneNumber;
}
//TODO return type
public function getWorkingHours(): ?WorkingHours
{
return $this->workingHours;
}
public function getEmail(): ?string
{
return $this->email;
}
public function getPrices(): ?string
{
return $this->prices;
}
//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;
}
public function getThumbnail(): ?Thumbnail
{
return $this->thumbnail;
}
public function setThumbnail(string $path): void
{
$this->thumbnail = new Thumbnail($this, $path);
}
/**
* @return Photo[]
*/
public function getPhotos(): Collection
{
return $this->photos;
}
/**
* @return Photo[]
*/
public function getVideos(): Collection
{
return $this->videos;
}
public function getCity(): City
{
return $this->city;
}
/**
* @return Station[]
*/
public function getStations(): Collection
{
return $this->stations;
}
public function getDistricts(): ?string
{
return $this->districts;
}
public function getAddress(): TranslatableValue
{
return $this->address;
}
//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 addPhoto(string $path): Photo
{
$photos = $this->getPhotos();
$found = $photos->filter(function (Photo $photo) use ($path): bool {
return $path === $photo->getPath();
});
if (!$found->isEmpty()) {
return $found->first();
}
$photo = new Photo($this, $path);
$this->photos->add($photo);
return $photo;
}
public function removePhoto(string $path): void
{
foreach ($this->getPhotos() as $photo) {
if ($path === $photo->getPath()) {
$this->photos->removeElement($photo);
}
}
}
public function addVideo(string $path): Video
{
$found = $this->getVideos()->filter(function (Video $video) use ($path): bool {
return $path === $video->getPath();
});
if (!$found->isEmpty())
return $found->first();
$video = new Video($this, $path);
//теперь разрешаем много видео
//$this->videos->clear();
$this->videos->add($video);
return $video;
}
public function removeVideo(string $path): void
{
foreach ($this->getVideos() as $video) {
if ($path === $video->getPath()) {
$this->videos->removeElement($video);
}
}
}
public function isMediaProcessed(): bool
{
foreach ($this->videos as $video)
if(null === $video->getPreviewPath())
return false;
return true;
}
public function delete(): void
{
$this->deletePlacementHiding();
$this->deleteFromAdBoard();
$this->setDeletedAt(Carbon::now());
}
public function undoDelete(): void
{
$this->setDeletedAt(); // will pass null by default
}
public function deleteFromAdBoard(): void
{
$this->adBoardPlacement = null;
}
//TODO return type
public function getPhoneCallRestrictions(): ?PhoneCallRestrictions
{
return $this->phoneCallRestrictions;
}
//TODO return type
public function getExpressPricing(): ?ExpressPricing
{
return $this->expressPricing;
}
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 isHidden(): bool
{
return null !== $this->getPlacementHiding();
}
public function deletePlacementHiding(): void
{
$this->placementHiding = null;
}
/**
* @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 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;
}
}