src/Entity/Saloon/Saloon.php line 43

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