src/Entity/Saloon/Saloon.php line 44

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by simpson <simpsonwork@gmail.com>
  4.  * Date: 2019-04-15
  5.  * Time: 19:50
  6.  */
  7. namespace App\Entity\Saloon;
  8. use AngelGamez\TranslatableBundle\Entity\TranslatableValue;
  9. use App\Entity\Account\Advertiser;
  10. use App\Entity\ApartmentsPricing;
  11. use App\Entity\Account\Customer;
  12. use App\Entity\ExpressPricing;
  13. use App\Entity\IProvidedService;
  14. use App\Entity\IProvidesServices;
  15. use App\Entity\Location\City;
  16. use App\Entity\Location\Station;
  17. use App\Entity\Location\MapCoordinate;
  18. use App\Entity\Messengers;
  19. use App\Entity\PhoneCallRestrictions;
  20. use App\Entity\Profile\Profile;
  21. use App\Entity\Sales\Saloon\AdBoardPlacement;
  22. use App\Entity\Saloon\Comment\CommentByCustomer;
  23. use App\Entity\Service;
  24. use App\Entity\Sales\Saloon\PlacementHiding;
  25. use App\Entity\ProvidedServiceTrait;
  26. use App\Entity\TakeOutPricing;
  27. use App\Helper\TopCardTrait;
  28. use App\Repository\SaloonRepository;
  29. use Carbon\Carbon;
  30. use Carbon\CarbonImmutable;
  31. use Doctrine\Common\Collections\ArrayCollection;
  32. use Doctrine\Common\Collections\Collection;
  33. use Doctrine\ORM\Mapping as ORM;
  34. use Gedmo\Mapping\Annotation as Gedmo;
  35. use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;
  36. use Symfony\Component\Serializer\Annotation\Groups;
  37. #[Gedmo\SoftDeleteable(fieldName: "deletedAt", timeAware: true)]
  38. #[ORM\Table(name: 'saloons')]
  39. #[ORM\Index(name: 'idx_saloon_deleted_moderation', columns: ['deleted_at', 'moderation_status'])]
  40. #[ORM\Entity(repositoryClass: SaloonRepository::class)]
  41. class Saloon implements IProvidesServices
  42. {
  43.     use SoftDeleteableEntity;
  44.     use ProvidedServiceTrait;
  45.     use TopCardTrait;
  46.     public const MODERATION_STATUS_NOT_PASSED = 0;
  47.     public const MODERATION_STATUS_APPROVED = 1;
  48.     public const MODERATION_STATUS_WAITING = 2;
  49.     public const MODERATION_STATUS_REJECTED = 3;
  50.     #[ORM\Id]
  51.     #[ORM\Column(name: 'id', type: 'integer')]
  52.     #[ORM\GeneratedValue(strategy: 'AUTO')]
  53.     #[Groups(['preview', 'listing'])]
  54.     protected int $id;
  55.     #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: true)]
  56.     #[ORM\ManyToOne(targetEntity: Advertiser::class, inversedBy: 'saloons')]
  57.     protected ?Advertiser $owner;
  58.     #[ORM\OneToOne(targetEntity: AdBoardPlacement::class, mappedBy: 'saloon', cascade: ['all'])]
  59.     protected ?AdBoardPlacement $adBoardPlacement;
  60.     #[ORM\OneToOne(targetEntity: PlacementHiding::class, mappedBy: 'saloon', cascade: ['all'])]
  61.     protected ?PlacementHiding $placementHiding;
  62.     #[ORM\Column(name: 'uri_identity', type: 'string', length: 128)]
  63.     #[Groups(['preview', 'listing'])]
  64.     protected string $uriIdentity;
  65.     #[ORM\Column(name: 'name', type: 'translatable')]
  66.     #[Groups(['preview', 'listing'])]
  67.     protected TranslatableValue $name;
  68.     #[ORM\Column(name: 'description', type: 'translatable')]
  69.     #[Groups('preview')]
  70.     protected TranslatableValue $description;
  71.     /** @var SaloonService[] */
  72.     #[ORM\OneToMany(targetEntity: SaloonService::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
  73.     #[ORM\Cache(usage: 'NONSTRICT_READ_WRITE')]
  74.     #[Groups(['preview', 'listing'])]
  75.     protected Collection $providedServices;
  76.     /** @var int[] */
  77.     #[ORM\Column(name: 'client_types', type: 'simple_array', nullable: true)]
  78.     #[Groups('preview')]
  79.     protected ?array $clientTypes;
  80.     /**
  81.      * Номера телефонов в свободной форме написания
  82.      */
  83.     #[ORM\Column(name: 'phone_number', type: 'string', length: 255)]
  84.     #[Groups(['preview', 'listing'])]
  85.     protected string $phoneNumber;
  86.     #[ORM\Embedded(class: Messengers::class, columnPrefix: false)]
  87.     #[Groups('preview')]
  88.     protected ?Messengers $messengers;
  89.     #[ORM\Embedded(class: PhoneCallRestrictions::class, columnPrefix: false)]
  90.     #[Groups('preview')]
  91.     protected ?PhoneCallRestrictions $phoneCallRestrictions;
  92.     #[ORM\Embedded(class: WorkingHours::class, columnPrefix: 'working_hours_')]
  93.     #[Groups('preview')]
  94.     protected ?WorkingHours $workingHours;
  95.     /**
  96.      * Расценки в свободной форме написания
  97.      */
  98.     #[ORM\Column(name: 'prices', type: 'text', nullable: true)]
  99.     #[Groups('preview')]
  100.     protected ?string $prices;
  101.     #[ORM\Embedded(class: ApartmentsPricing::class, columnPrefix: false)]
  102.     #[Groups('preview')]
  103.     protected ?ApartmentsPricing $apartmentsPricing;
  104.     #[ORM\Embedded(class: TakeOutPricing::class, columnPrefix: false)]
  105.     #[Groups('preview')]
  106.     protected ?TakeOutPricing $takeOutPricing;
  107.     #[ORM\Embedded(class: ExpressPricing::class, columnPrefix: false)]
  108.     #[Groups('preview')]
  109.     protected ?ExpressPricing $expressPricing;
  110.     #[ORM\Column(name: 'extra_charge', type: 'integer', nullable: true)]
  111.     #[Groups('preview')]
  112.     protected ?int $extraCharge;
  113.     #[ORM\OneToOne(targetEntity: Thumbnail::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
  114.     #[Groups(['preview', 'listing'])]
  115.     protected ?Thumbnail $thumbnail = null;
  116.     /** @var Photo[] */
  117.     #[ORM\OneToMany(targetEntity: Photo::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
  118.     #[Groups('preview')]
  119.     protected Collection $photos;
  120.     /** @var Video[] */
  121.     #[ORM\OneToMany(targetEntity: Video::class, mappedBy: 'saloon', cascade: ['all'], orphanRemoval: true)]
  122.     #[Groups('preview')]
  123.     protected Collection $videos;
  124.     /** @var CommentByCustomer[] */
  125.     #[ORM\OneToMany(targetEntity: CommentByCustomer::class, mappedBy: 'saloon')]
  126.     #[Groups('preview')]
  127.     protected Collection $comments;
  128.     /** @var Profile[] */
  129.     #[ORM\OneToMany(targetEntity: Profile::class, mappedBy: 'saloon')]
  130.     protected Collection $profiles;
  131.     #[ORM\JoinColumn(name: 'city_id', referencedColumnName: 'id')]
  132.     #[ORM\ManyToOne(targetEntity: City::class)]
  133.     #[Groups(['preview', 'listing'])]
  134.     protected City $city;
  135.     /** @var Station[] */
  136.     #[ORM\JoinTable(name: 'saloon_stations')]
  137.     #[ORM\JoinColumn(name: 'saloon_id', referencedColumnName: 'id')]
  138.     #[ORM\InverseJoinColumn(name: 'station_id', referencedColumnName: 'id')]
  139.     #[ORM\ManyToMany(targetEntity: Station::class)]
  140.     #[Groups(['preview', 'listing'])]
  141.     protected Collection $stations;
  142.     /**
  143.      * Районы в свободной форме написания
  144.      */
  145.     #[ORM\Column(name: 'districts', type: 'string', length: 255, nullable: true)]
  146.     #[Groups(['preview', 'listing'])]
  147.     protected ?string $districts;
  148.     #[ORM\Column(name: 'address', type: 'translatable', nullable: true)]
  149.     #[Groups(['preview', 'listing'])]
  150.     protected ?TranslatableValue $address;
  151.     #[ORM\Embedded(class: MapCoordinate::class, columnPrefix: false)]
  152.     #[Groups(['preview', 'listing'])]
  153.     protected MapCoordinate $mapCoordinate;
  154.     #[ORM\Column(name: 'created_at', type: 'datetimetz_immutable', nullable: true)]
  155.     #[Groups('preview')]
  156.     protected ?\DateTimeImmutable $createdAt;
  157.     #[Gedmo\Timestampable(on: "update")]
  158.     #[ORM\Column(name: 'updated_at', type: 'datetimetz_immutable', nullable: true)]
  159.     #[Groups('preview')]
  160.     protected ?\DateTimeImmutable $updatedAt;
  161.     #[ORM\Column(name: 'inactivated_at', type: 'datetimetz_immutable', nullable: true)]
  162.     protected ?\DateTimeImmutable $inactivatedAt;
  163.     #[ORM\Column(name: 'moderation_status', type: 'integer', options: ['default' => 0])]
  164.     protected int $moderationStatus = self::MODERATION_STATUS_NOT_PASSED;
  165.     #[Groups('preview')]
  166.     #[ORM\Column(name: 'email', type: 'string', length: 180, nullable: true)]
  167.     protected ?string $email = null;
  168.     private bool $draft = false;
  169.     #[ORM\Column(name: 'seo', type: 'json', nullable: true)]
  170.     #[Groups(['preview', 'listing'])]
  171.     private ?array $seo = null;
  172.     #[ORM\ManyToOne(targetEntity: Station::class)]
  173.     #[ORM\JoinColumn(name: 'primary_station_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL')]
  174.     #[Groups(['preview', 'listing'])]
  175.     private ?Station $primaryStation = null;
  176.     #[ORM\Column(type: 'smallint', options: ['default' => 0])]
  177.     private int $deleteMode = 0;
  178.     public function __construct(string $uriIdentity, ?\DateTimeImmutable $createdAt = null)
  179.     {
  180.         $this->draft = true;
  181.         $this->uriIdentity = $uriIdentity;
  182.         $this->createdAt = $createdAt ?? CarbonImmutable::now();
  183.         $this->photos = new ArrayCollection();
  184.         $this->videos = new ArrayCollection();
  185.         $this->providedServices = new ArrayCollection();
  186.         $this->profiles = new ArrayCollection();
  187.     }
  188.     public static function draft(?\DateTimeImmutable $createdAt = null): self
  189.     {
  190.         $saloon = new static($createdAt);
  191.         return $saloon;
  192.     }
  193.     public function defineUriIdentity(string $uriIdentity): void
  194.     {
  195.         if (!$this->isDraft()) {
  196.             throw new \DomainException('Saloon is already created and can\'t change its URI.');
  197.         }
  198.         $this->uriIdentity = $uriIdentity;
  199.         $this->draft = false;
  200.     }
  201.     public function isDraft(): bool
  202.     {
  203.         return $this->draft;
  204.     }
  205.     public function hasOwner(): bool
  206.     {
  207.         return null !== $this->owner;
  208.     }
  209.     public function setNameAndDescription(TranslatableValue $name, TranslatableValue $description): void
  210.     {
  211.         if (!empty($name)) {
  212.             $this->name = $name;
  213.         }
  214.         if (!empty($description)) {
  215.             $this->description = $description;
  216.         }
  217.     }
  218.     public function setLocation(City $city, $stations, $districts, $address, ?MapCoordinate $mapCoordinate): void
  219.     {
  220.         $this->city = $city;
  221.         if (null !== $stations) {
  222.             if (is_array($stations)) {
  223.                 $stations = new ArrayCollection($stations);
  224.             } elseif (!$stations instanceof ArrayCollection) {
  225.                 if (is_iterable($stations)) {
  226.                     $stations = new ArrayCollection(iterator_to_array($stations));
  227.                 } else {
  228.                     throw new \InvalidArgumentException('Stations list should be either an array or an ArrayCollection');
  229.                 }
  230.             }
  231.             $this->stations = $stations;
  232.         }
  233.         if (!empty($districts)) {
  234.             $this->districts = $districts;
  235.         }
  236.         if (!empty($address)) {
  237.             $this->address = $address;
  238.         }
  239.         $this->mapCoordinate = $mapCoordinate;
  240.         $this->normalizePrimaryStation();
  241.     }
  242.     private function normalizePrimaryStation(): void
  243.     {
  244.         if ($this->stations->isEmpty()) {
  245.             $this->primaryStation = null;
  246.             return;
  247.         }
  248.         if ($this->primaryStation === null || !$this->stations->contains($this->primaryStation)) {
  249.             $this->primaryStation = $this->stations->first();
  250.         }
  251.     }
  252.     public function setPhoneCallOptions(string $phoneNumber, ?PhoneCallRestrictions $restrictions, ?Messengers $messengers): void
  253.     {
  254.         $this->phoneNumber = $phoneNumber;
  255.         $this->phoneCallRestrictions = $restrictions;
  256.         $this->messengers = $messengers;
  257.     }
  258.     public function setPricing(?ApartmentsPricing $apartmentsPricing, ?TakeOutPricing $takeOutPricing, ?int $extraCharge, ?ExpressPricing $expressPricing = null): void
  259.     {
  260. //        $this->prices = $pricing;
  261.         $this->apartmentsPricing = $apartmentsPricing;
  262.         $this->takeOutPricing = $takeOutPricing;
  263.         $this->extraCharge = $extraCharge;
  264.         $this->expressPricing = $expressPricing;
  265.     }
  266.     public function isOwnedBy(Advertiser $account): bool
  267.     {
  268.         return $account->getId() === $this->owner->getId();
  269.     }
  270.     public function getId(): int
  271.     {
  272.         return $this->id;
  273.     }
  274.     public function getOwner(): ?Advertiser
  275.     {
  276.         return $this->owner;
  277.     }
  278.     public function setOwner(Advertiser $owner): void
  279.     {
  280.         $this->owner = $owner;
  281.     }
  282.     public function getAdBoardPlacement(): ?AdBoardPlacement
  283.     {
  284.         return $this->adBoardPlacement;
  285.     }
  286.     /**
  287.      * Салон оплачен и выводится в общих списках на сайте
  288.      */
  289.     public function isActive(): bool
  290.     {
  291.         return null !== $this->adBoardPlacement;
  292.     }
  293.     public function getUriIdentity(): string
  294.     {
  295.         return $this->uriIdentity;
  296.     }
  297.     public function getName(): TranslatableValue
  298.     {
  299.         return $this->name;
  300.     }
  301.     public function getDescription(): TranslatableValue
  302.     {
  303.         return $this->description;
  304.     }
  305.     public function updateDescription(TranslatableValue $description): void
  306.     {
  307.         $this->description = $description;
  308.     }
  309.     public function getModerationStatus(): int
  310.     {
  311.         return $this->moderationStatus;
  312.     }
  313.     public function isModerationPassed(): bool
  314.     {
  315.         return self::MODERATION_STATUS_APPROVED === $this->moderationStatus;
  316.     }
  317.     public function isModerationWaiting(): bool
  318.     {
  319.         return self::MODERATION_STATUS_WAITING === $this->moderationStatus;
  320.     }
  321.     public function isModerationRejected(): bool
  322.     {
  323.         return self::MODERATION_STATUS_REJECTED === $this->moderationStatus;
  324.     }
  325.     public function isEligibleToShowByModeration(): bool
  326.     {
  327.         return $this->isModerationPassed()
  328.             || ($this->isModerationWaiting() && null !== $this->owner && $this->owner->isTrusted());
  329.     }
  330.     public function waitForModeration(): void
  331.     {
  332.         $this->moderationStatus = self::MODERATION_STATUS_WAITING;
  333.     }
  334.     public function passModeration(): void
  335.     {
  336.         $this->moderationStatus = self::MODERATION_STATUS_APPROVED;
  337.     }
  338.     public function rejectModeration(): void
  339.     {
  340.         $this->moderationStatus = self::MODERATION_STATUS_REJECTED;
  341.     }
  342.     public function getPhoneNumber(): string
  343.     {
  344.         return $this->phoneNumber;
  345.     }
  346.     /**
  347.      * @return Profile[]
  348.      */
  349.     public function getProfiles(): Collection
  350.     {
  351.         return $this->profiles;
  352.     }
  353.     public function addProfile(Profile $profile): void
  354.     {
  355.         if (!$this->profiles->contains($profile)) {
  356.             $this->profiles->add($profile);
  357.         }
  358.         if ($profile->getSaloon() !== $this) {
  359.             $profile->attachToSaloon($this);
  360.         }
  361.     }
  362.     public function removeProfile(Profile $profile): void
  363.     {
  364.         if ($this->profiles->removeElement($profile) && $profile->getSaloon() === $this) {
  365.             $profile->detachFromSaloon();
  366.         }
  367.     }
  368.     public function getWorkingHours(): ?WorkingHours
  369.     {
  370.         return $this->workingHours;
  371.     }
  372.     public function setWorkingHours(?WorkingHours $workingHours): void
  373.     {
  374. //        $this->phoneNumber = $phoneNumber;
  375.         $this->workingHours = $workingHours;
  376.     }
  377.     //TODO return type
  378.     public function getEmail(): ?string
  379.     {
  380.         return $this->email;
  381.     }
  382.     public function setEmail(?string $email): void
  383.     {
  384.         $this->email = $email;
  385.     }
  386.     public function getPrices(): ?string
  387.     {
  388.         return $this->prices;
  389.     }
  390.     //TODO return type
  391.     public function getApartmentsPricing(): ?ApartmentsPricing
  392.     {
  393.         return $this->apartmentsPricing;
  394.     }
  395.     //TODO return type
  396.     public function getTakeOutPricing(): ?TakeOutPricing
  397.     {
  398.         return $this->takeOutPricing;
  399.     }
  400.     public function getExtraCharge(): ?int
  401.     {
  402.         return $this->extraCharge;
  403.     }
  404.     public function getThumbnail(): ?Thumbnail
  405.     {
  406.         return $this->thumbnail;
  407.     }
  408.     public function setThumbnail(string $path): void
  409.     {
  410.         $this->thumbnail = new Thumbnail($this, $path);
  411.     }
  412.     public function getCity(): City
  413.     {
  414.         return $this->city;
  415.     }
  416.     /**
  417.      * @return Station[]
  418.      */
  419.     public function getStations(): Collection
  420.     {
  421.         return $this->stations;
  422.     }
  423.     public function getDistricts(): ?string
  424.     {
  425.         return $this->districts;
  426.     }
  427.     public function getAddress(): TranslatableValue
  428.     {
  429.         return $this->address;
  430.     }
  431.     public function getMapCoordinate(): ?MapCoordinate
  432.     {
  433.         return $this->mapCoordinate;
  434.     }
  435.     public function getUpdatedAt(): ?\DateTimeImmutable
  436.     {
  437.         return $this->updatedAt;
  438.     }
  439.     //TODO return type
  440.     public function setUpdatedAt(\DateTimeImmutable $updatedAt): void
  441.     {
  442.         $this->updatedAt = $updatedAt;
  443.     }
  444.     public function addPhoto(string $path): Photo
  445.     {
  446.         $photos = $this->getPhotos();
  447.         $found = $photos->filter(function (Photo $photo) use ($path): bool {
  448.             return $path === $photo->getPath();
  449.         });
  450.         if (!$found->isEmpty()) {
  451.             return $found->first();
  452.         }
  453.         $photo = new Photo($this, $path);
  454.         $this->photos->add($photo);
  455.         return $photo;
  456.     }
  457.     /**
  458.      * @return Photo[]
  459.      */
  460.     public function getPhotos(): Collection
  461.     {
  462.         return $this->photos;
  463.     }
  464.     public function removePhoto(string $path): void
  465.     {
  466.         foreach ($this->getPhotos() as $photo) {
  467.             if ($path === $photo->getPath()) {
  468.                 $this->photos->removeElement($photo);
  469.             }
  470.         }
  471.     }
  472.     public function addVideo(string $path): Video
  473.     {
  474.         $found = $this->getVideos()->filter(function (Video $video) use ($path): bool {
  475.             return $path === $video->getPath();
  476.         });
  477.         if (!$found->isEmpty())
  478.             return $found->first();
  479.         $video = new Video($this, $path);
  480.         //теперь разрешаем много видео
  481.         //$this->videos->clear();
  482.         $this->videos->add($video);
  483.         return $video;
  484.     }
  485.     /**
  486.      * @return Photo[]
  487.      */
  488.     public function getVideos(): Collection
  489.     {
  490.         return $this->videos;
  491.     }
  492.     public function removeVideo(string $path): void
  493.     {
  494.         foreach ($this->getVideos() as $video) {
  495.             if ($path === $video->getPath()) {
  496.                 $this->videos->removeElement($video);
  497.             }
  498.         }
  499.     }
  500.     public function isMediaProcessed(): bool
  501.     {
  502.         foreach ($this->videos as $video)
  503.             if (null === $video->getPreviewPath())
  504.                 return false;
  505.         return true;
  506.     }
  507.     public function delete(): void
  508.     {
  509.         $this->deletePlacementHiding();
  510.         $this->deleteFromAdBoard();
  511.         $this->setDeletedAt(Carbon::now());
  512.     }
  513.     public function deletePlacementHiding(): void
  514.     {
  515.         $this->placementHiding = null;
  516.     }
  517.     public function deleteFromAdBoard(): void
  518.     {
  519.         $this->adBoardPlacement = null;
  520.     }
  521.     //TODO return type
  522.     public function undoDelete(): void
  523.     {
  524.         $this->setDeletedAt(); // will pass null by default
  525.     }
  526.     //TODO return type
  527.     public function getPhoneCallRestrictions(): ?PhoneCallRestrictions
  528.     {
  529.         return $this->phoneCallRestrictions;
  530.     }
  531.     public function getExpressPricing(): ?ExpressPricing
  532.     {
  533.         return $this->expressPricing;
  534.     }
  535.     public function getClientTypes(): ?array
  536.     {
  537.         return $this->clientTypes;
  538.     }
  539.     //TODO return type
  540.     /**
  541.      * @param int[] $clientTypes
  542.      */
  543.     public function setClientTypes(?array $clientTypes): void
  544.     {
  545.         $this->clientTypes = $clientTypes;
  546.     }
  547.     public function getMessengers(): ?Messengers
  548.     {
  549.         return $this->messengers;
  550.     }
  551.     public function getInactivatedAt(): ?\DateTimeImmutable
  552.     {
  553.         return $this->inactivatedAt;
  554.     }
  555.     public function setInactive(): void
  556.     {
  557.         $this->inactivatedAt = CarbonImmutable::now();
  558.     }
  559.     public function undoInactive(): void
  560.     {
  561.         $this->inactivatedAt = null;
  562.     }
  563.     public function isHidden(): bool
  564.     {
  565.         return null !== $this->getPlacementHiding();
  566.     }
  567.     public function getPlacementHiding(): ?PlacementHiding
  568.     {
  569.         return $this->placementHiding;
  570.     }
  571.     /**
  572.      * @return CommentByCustomer[]
  573.      */
  574.     public function getComments(): Collection
  575.     {
  576.         return $this->comments->filter(function (CommentByCustomer $comment): bool {
  577.             return null == $comment->getParent();
  578.         });
  579.     }
  580.     /**
  581.      * @return CommentByCustomer[]
  582.      */
  583.     public function getCommentsOrderedByNotReplied(): array
  584.     {
  585.         $comments = $this->comments->filter(function (CommentByCustomer $comment): bool {
  586.             return null == $comment->getParent();
  587.         })->toArray();
  588.         usort($comments, function (CommentByCustomer $commentA, CommentByCustomer $commentB): int {
  589.             if ((null == $commentA->getLastCommentByAdvertiser() && null == $commentB->getLastCommentByAdvertiser())
  590.                 || (null != $commentA->getLastCommentByAdvertiser() && null != $commentB->getLastCommentByAdvertiser())) {
  591.                 if ($commentA->getCreatedAt() == $commentB->getCreatedAt())
  592.                     return $commentA->getId() > $commentB->getId() ? -1 : 1;
  593.                 else
  594.                     return $commentA->getCreatedAt() > $commentB->getCreatedAt() ? -1 : 1;
  595.             }
  596.             if (null == $commentA->getLastCommentByAdvertiser() && null != $commentB->getLastCommentByAdvertiser())
  597.                 return -1;
  598.             else
  599.                 return 1;
  600.         });
  601.         return $comments;
  602.     }
  603.     public function getCreatedAt(): ?\DateTimeImmutable
  604.     {
  605.         return $this->createdAt;
  606.     }
  607.     /**
  608.      * @return CommentByCustomer[]
  609.      */
  610.     public function getCommentsWithoutReply(): Collection
  611.     {
  612.         return $this->comments->filter(function (CommentByCustomer $comment): bool {
  613.             return null == $comment->getParent() && false == $comment->isCommentedByAdvertiser();
  614.         });
  615.     }
  616.     /**
  617.      * @return CommentByCustomer[]
  618.      */
  619.     public function getCommentsWithReply(): Collection
  620.     {
  621.         return $this->comments->filter(function (CommentByCustomer $comment): bool {
  622.             return null == $comment->getParent() && true == $comment->isCommentedByAdvertiser();
  623.         });
  624.     }
  625.     /**
  626.      * @return CommentByCustomer[]
  627.      */
  628.     public function getNewComments(): Collection
  629.     {
  630.         $weekAgo = CarbonImmutable::now()->sub('7 days');
  631.         return $this->comments->filter(function (CommentByCustomer $comment) use ($weekAgo): bool {
  632.             return null == $comment->getParent()
  633.                 && (
  634.                     $comment->getCreatedAt() >= $weekAgo
  635.                     || null == $this->getCommentReply($comment)
  636.                 );
  637.         });
  638.     }
  639.     private function getCommentReply(CommentByCustomer $parent): ?CommentByCustomer
  640.     {
  641.         foreach ($this->comments as $comment)
  642.             if ($comment->getParent() == $parent)
  643.                 return $comment;
  644.         return null;
  645.     }
  646.     public function getOldComments(): Collection
  647.     {
  648.         $weekAgo = CarbonImmutable::now()->sub('7 days');
  649.         return $this->comments->filter(function (CommentByCustomer $comment) use ($weekAgo): bool {
  650.             return null == $comment->getParent()
  651.                 && false == (
  652.                     $comment->getCreatedAt() >= $weekAgo
  653.                     || null == $this->getCommentReply($comment)
  654.                 );
  655.         });
  656.     }
  657.     public function getCommentFromUser(Customer $user): ?CommentByCustomer
  658.     {
  659.         foreach ($this->comments as $comment)
  660.             if (null == $comment->getParent() && null != $comment->getUser() && $user->getId() == $comment->getUser()->getId())
  661.                 return $comment;
  662.         return null;
  663.     }
  664.     public function seo(): ?array
  665.     {
  666.         return $this->seo;
  667.     }
  668.     public function seoPhoneNumber(): ?string
  669.     {
  670.         return $this->seo['phone'] ?? null;
  671.     }
  672.     public function setSeoPhoneNumber(string $phoneNumber): void
  673.     {
  674.         if (null === $this->seo) {
  675.             $this->seo = [];
  676.         }
  677.         $this->seo['phone'] = $phoneNumber;
  678.     }
  679.     public function getPrimaryStation(): ?Station
  680.     {
  681.         $station = $this->primaryStation ?? $this->getStations()->first();
  682.         if (false === $station) {
  683.             return null;
  684.         }
  685.         return $station;
  686.     }
  687.     public function setPrimaryStation(?Station $station): void
  688.     {
  689.         $this->primaryStation = $station;
  690.         $this->normalizePrimaryStation();
  691.     }
  692.     public function getStationsSortedByPrimary(): array
  693.     {
  694.         $stations = $this->stations->toArray();
  695.         if (!$this->primaryStation) {
  696.             return $stations;
  697.         }
  698.         usort($stations, function (Station $a, Station $b) {
  699.             if ($a->getId() === $this->primaryStation->getId()) return -1;
  700.             if ($b->getId() === $this->primaryStation->getId()) return 1;
  701.             return 0;
  702.         });
  703.         return $stations;
  704.     }
  705.     public function getDeleteMode(): int
  706.     {
  707.         return $this->deleteMode;
  708.     }
  709.     public function setDeleteMode(int $deleteMode): self
  710.     {
  711.         $this->deleteMode = $deleteMode;
  712.         return $this;
  713.     }
  714.     public function isHardDeleted(): bool
  715.     {
  716.         return $this->deleteMode === 2;
  717.     }
  718. }