src/Entity/Profile/Profile.php line 75

Open in your IDE?
  1. <?php
  2. /**
  3.  * Created by simpson <simpsonwork@gmail.com>
  4.  * Date: 2019-03-19
  5.  * Time: 15:36
  6.  */
  7. namespace App\Entity\Profile;
  8. use AngelGamez\TranslatableBundle\Entity\TranslatableValue;
  9. //use ApiPlatform\Core\Annotation\ApiProperty;
  10. use App\Entity\Account\Advertiser;
  11. use App\Entity\ApartmentsPricing;
  12. use App\Entity\ContainsDomainEvents;
  13. use App\Entity\Account\Customer;
  14. use App\Entity\DomainEventsRecorderTrait;
  15. use App\Entity\ExpressPricing;
  16. use App\Entity\IProvidesServices;
  17. use App\Entity\Location\City;
  18. use App\Entity\Location\MapCoordinate;
  19. use App\Entity\Location\Station;
  20. use App\Entity\Messengers;
  21. use App\Entity\PhoneCallRestrictions;
  22. use App\Entity\Profile\Comment\CommentByCustomer;
  23. use App\Entity\Profile\Confirmation\ModerationRequest;
  24. use App\Entity\Sales\Profile\AdBoardPlacement;
  25. use App\Entity\Sales\Profile\PlacementHiding;
  26. use App\Entity\Sales\Profile\TopPlacement;
  27. use App\Entity\ProvidedServiceTrait;
  28. use App\Entity\TakeOutPricing;
  29. use App\Helper\TopCardTrait;
  30. use App\Repository\ProfileRepository;
  31. use Carbon\Carbon;
  32. use Carbon\CarbonImmutable;
  33. use Doctrine\Common\Collections\ArrayCollection;
  34. use Doctrine\Common\Collections\Collection;
  35. use Doctrine\ORM\Mapping as ORM;
  36. use Doctrine\ORM\Mapping\Index;
  37. //use ApiPlatform\Core\Annotation\ApiResource;
  38. //use ApiPlatform\Core\Annotation\ApiFilter;
  39. //use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\SearchFilter;
  40. //use ApiPlatform\Core\Bridge\Doctrine\Orm\Filter\RangeFilter;
  41. use Gedmo\SoftDeleteable\Traits\SoftDeleteableEntity;
  42. use Symfony\Component\Serializer\Annotation\Groups;
  43. use Gedmo\Mapping\Annotation as Gedmo;
  44. use App\Validator\Constraints\ValidPhoneForCountry as ValidPhoneForCountryAssert;
  45. use App\Validator\Constraints\PhoneNotBlack as PhoneNotBlackAssert;
  46. /**
  47.  * ApiResource(collectionOperations={"get"}, itemOperations={"get"}, normalizationContext={"groups"={"profile"}}, attributes={"pagination_client_enabled"=true, "pagination_client_items_per_page"=true})
  48.  * ApiFilter(SearchFilter::class, properties={"city": "exact", "providedServices": "exact"})
  49.  * ApiFilter(RangeFilter::class, properties={"personParameters.age", "personParameters.height", "personParameters.weight", "personParameters.breastSize", "apartmentsPricing.oneHourPrice"})
  50.  * @ValidPhoneForCountryAssert/ProtocolClass
  51.  * @PhoneNotBlackAssert/ProtocolClass
  52.  */
  53. #[Gedmo\SoftDeleteable(fieldName"deletedAt"timeAwaretrue)]
  54. #[ORM\Table(name'profiles')]
  55. #[Index(name'idx_deleted_at'columns: ['deleted_at'])]
  56. #[Index(name'idx_created_at'columns: ['created_at'])]
  57. #[Index(name'idx_gender'columns: ['person_gender'])]
  58. #[Index(name'idx_apartments_one_hour_price'columns: ['apartments_one_hour_price'])]
  59. #[Index(name'idx_is_dummy'columns: ['is_dummy'])]
  60. #[Index(name'idx_city_deleted'columns: ['city_id''deleted_at'])]
  61. #[Index(name'idx_city_deleted_moderation'columns: ['city_id''deleted_at''moderation_status'])]
  62. #[Index(name'idx_city_masseur_deleted'columns: ['city_id''is_masseur''deleted_at'])]
  63. #[Index(name'idx_city_masseur_deleted_moderation'columns: ['city_id''is_masseur''deleted_at''moderation_status'])]
  64. #[Index(name'idx_city_deleted_gender'columns: ['city_id''deleted_at''person_gender'])]
  65. #[Index(name'idx_city_deleted_moderation_gender'columns: ['city_id''deleted_at''moderation_status''person_gender'])]
  66. #[Index(name'idx_city_masseur_deleted_gender'columns: ['city_id''is_masseur''deleted_at''person_gender'])]
  67. #[Index(name'idx_city_masseur_deleted_moderation_gender'columns: ['city_id''is_masseur''deleted_at''moderation_status''person_gender'])]
  68. #[ORM\Entity(repositoryClassProfileRepository::class)]
  69. #[ORM\HasLifecycleCallbacks]
  70. class Profile implements ContainsDomainEventsIProvidesServices
  71. {
  72.     use SoftDeleteableEntity;
  73.     use DomainEventsRecorderTrait;
  74.     use ProvidedServiceTrait;
  75.     use TopCardTrait;
  76.     const MODERATION_STATUS_NOT_PASSED 0;
  77.     const MODERATION_STATUS_APPROVED 1;
  78.     const MODERATION_STATUS_WAITING 2;
  79.     const MODERATION_STATUS_REJECTED 3;
  80.     #[ORM\Id]
  81.     #[ORM\Column(name'id'type'integer')]
  82.     #[ORM\GeneratedValue(strategy'AUTO')]
  83.     #[Groups('profile')]
  84.     protected int $id;
  85.     #[ORM\JoinColumn(name'user_id'referencedColumnName'id'nullabletrue)]
  86.     #[ORM\ManyToOne(targetEntityAdvertiser::class, inversedBy'profiles')]
  87.     protected ?Advertiser $owner;
  88.     #[ORM\Column(name'is_dummy'type'boolean'options: ['default' => 0])]
  89.     protected bool $dummy false;
  90.     /** @var TopPlacement[] */
  91.     #[ORM\OneToMany(targetEntityTopPlacement::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  92.     protected Collection $topPlacements;
  93.     #[ORM\OneToOne(targetEntityAdBoardPlacement::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  94.     protected ?AdBoardPlacement $adBoardPlacement null;
  95.     #[ORM\OneToOne(targetEntityPlacementHiding::class, mappedBy'profile'cascade: ['all'])]
  96.     protected ?PlacementHiding $placementHiding null;
  97.     #[ORM\Column(name'uri_identity'type'string'length64)]
  98.     #[Groups('profile')]
  99.     protected string $uriIdentity;
  100.     #[ORM\Column(name'name'type'translatable')]
  101.     #[Groups('profile')]
  102.     protected TranslatableValue $name;
  103.     #[ORM\Column(name'description'type'translatable')]
  104.     #[Groups('profile')]
  105.     protected ?TranslatableValue $description null;
  106.     #[ORM\Embedded(class: PersonParameters::class, columnPrefix'person_')]
  107.     #[Groups('profile')]
  108.     protected PersonParameters $personParameters;
  109.     /** var ProfileService[] */
  110.     #[ORM\OneToMany(targetEntityProfileService::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  111.     #[ORM\Cache(usage'NONSTRICT_READ_WRITE'region'profiles')]
  112.     protected Collection $providedServices;
  113.     /** @var int[] */
  114.     #[ORM\Column(name'client_types'type'simple_array'nullabletrue)]
  115.     protected ?array $clientTypes;
  116.     #[ORM\Column(name'phone_number'type'string'length24)]
  117.     #[Groups('profile')]
  118.     protected string $phoneNumber;
  119.     #[ORM\Embedded(class: Messengers::class, columnPrefixfalse)]
  120.     #[Groups('profile')]
  121.     protected ?Messengers $messengers null;
  122.     #[ORM\Embedded(class: PhoneCallRestrictions::class, columnPrefixfalse)]
  123.     protected ?PhoneCallRestrictions $phoneCallRestrictions null;
  124.     #[ORM\Column(name'is_masseur'type'boolean')]
  125.     protected bool $masseur false;
  126.     #[ORM\Embedded(class: ClientRestrictions::class, columnPrefixfalse)]
  127.     protected ?ClientRestrictions $clientRestrictions null;
  128.     #[ORM\Embedded(class: ApartmentsPricing::class, columnPrefixfalse)]
  129.     #[Groups('profile')]
  130.     protected ?ApartmentsPricing $apartmentsPricing null;
  131.     #[ORM\Embedded(class: TakeOutPricing::class, columnPrefixfalse)]
  132.     #[Groups('profile')]
  133.     protected ?TakeOutPricing $takeOutPricing null;
  134.     #[ORM\Embedded(class: ExpressPricing::class, columnPrefixfalse)]
  135.     #[Groups('profile')]
  136.     protected ?ExpressPricing $expressPricing null;
  137.     #[ORM\Embedded(class: CarPricing::class, columnPrefixfalse)]
  138.     #[Groups('profile')]
  139.     protected ?CarPricing $carPricing null;
  140.     #[ORM\Column(name'extra_charge'type'integer'nullabletrue)]
  141.     #[Groups('profile')]
  142.     protected ?int $extraCharge;
  143.     #[ORM\Column(name'prepayment'type'boolean'nullabletrue)]
  144.     protected ?bool $prepayment null;
  145.     #[ORM\Column(name'prepayment_amount'type'integer'nullabletrue)]
  146.     protected ?int $prepaymentAmount null;
  147.     #[ORM\Column(name'prepayment_comment'type'string'length512nullabletrue)]
  148.     protected ?string $prepaymentComment null;
  149.     /** @var Photo[] */
  150.     #[ORM\OneToMany(targetEntityPhoto::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  151.     #[Groups('profile')]
  152.     protected Collection $photos;
  153.     /** @var Selfie[] */
  154.     #[ORM\OneToMany(targetEntitySelfie::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  155.     #[Groups('profile')]
  156.     protected Collection $selfies;
  157.     /** @var Video[] */
  158.     #[ORM\OneToMany(targetEntityVideo::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  159.     protected Collection $videos;
  160.     #[ORM\OneToMany(targetEntityFileProcessingTask::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  161.     protected Collection $processingFiles;
  162.     #[ORM\OneToOne(targetEntityAdminApprovalPhoto::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  163.     protected ?AdminApprovalPhoto $adminApprovalPhoto null;
  164.     #[ORM\OneToOne(targetEntityAvatar::class, mappedBy'profile'cascade: ['all'], orphanRemovaltrue)]
  165.     protected ?Avatar $avatar null;
  166.     /** @var CommentByCustomer[] */
  167.     #[ORM\OneToMany(targetEntityCommentByCustomer::class, mappedBy'profile')]
  168.     protected Collection $comments;
  169.     #[ORM\Column(name'is_approved'type'boolean')]
  170.     #[Groups('profile')]
  171.     protected bool $approved false;
  172.     #[ORM\Column(name'moderation_status'type'integer')]
  173.     #[Groups('profile')]
  174.     protected int $moderationStatus 0;
  175.     #[ORM\JoinColumn(name'city_id'referencedColumnName'id')]
  176.     #[ORM\ManyToOne(targetEntityCity::class)]
  177.     #[ORM\Cache(usage'NONSTRICT_READ_WRITE'region'profiles')]
  178.     protected City $city;
  179.     /** @var Station[] */
  180.     //, indexBy="id"
  181.     #[ORM\JoinTable(name'profile_stations')]
  182.     #[ORM\JoinColumn(name'profile_id'referencedColumnName'id')]
  183.     #[ORM\InverseJoinColumn(name'station_id'referencedColumnName'id')]
  184.     #[ORM\ManyToMany(targetEntityStation::class)]
  185.     #[Groups('profile')]
  186.     #[ORM\Cache(usage'NONSTRICT_READ_WRITE'region'profiles')]
  187.     protected Collection $stations;
  188.     #[ORM\Embedded(class: MapCoordinate::class, columnPrefixfalse)] // ApiProperty()
  189.     #[Groups('profile')]
  190.     protected ?MapCoordinate $mapCoordinate;
  191.     #[ORM\Column(name'created_at'type'datetimetz_immutable'nullabletrue)]
  192.     protected ?\DateTimeImmutable $createdAt;
  193.     #[Gedmo\Timestampable(on"change"field: ["name""description""personParameters""providedServices""clientTypes""phoneNumber""messengers""phoneCallrestrictions""masseur""clientRestrictions""apartmentsPricing""takeOutPricing""expressPricing""carPricing""extraCharge""prepayment""prepaymentAmount""prepaymentComment""photos""selfies""videos""avatar""stations""mapCoordinate"])]
  194.     #[ORM\Column(name'updated_at'type'datetimetz_immutable'nullabletrue)]
  195.     #[Groups('profile')]
  196.     protected ?\DateTimeImmutable $updatedAt;
  197.     #[ORM\Column(name'inactivated_at'type'datetimetz_immutable'nullabletrue)]
  198.     protected ?\DateTimeImmutable $inactivatedAt;
  199.     private bool $draft false;
  200.     #[ORM\Column(name'seo'type'json'nullabletrue)]
  201.     #[Groups('profile')]
  202.     private ?array $seo null;
  203.     #[ORM\ManyToOne(targetEntityStation::class)]
  204.     #[ORM\JoinColumn(name'primary_station_id'referencedColumnName'id'nullabletrueonDelete'SET NULL')]
  205.     #[Groups('profile')]
  206.     #[ORM\Cache(usage'READ_ONLY')]
  207.     private ?Station $primaryStation null;
  208.     #[ORM\Column(type'smallint'options: ['default' => 0])]
  209.     private int $deleteMode 0;
  210.     protected function __construct(?\DateTimeImmutable $createdAt)
  211.     {
  212.         $this->draft true;
  213.         $this->createdAt $createdAt;
  214.         $this->photos = new ArrayCollection();
  215.         $this->selfies = new ArrayCollection();
  216.         $this->videos = new ArrayCollection();
  217.         $this->processingFiles = new ArrayCollection();
  218.         $this->comments = new ArrayCollection();
  219.         $this->topPlacements = new ArrayCollection();
  220.         $this->providedServices = new ArrayCollection();
  221.         $this->stations = new ArrayCollection();
  222.         $this->inactivatedAt CarbonImmutable::now();
  223.     }
  224.     public static function draft(?\DateTimeImmutable $createdAt null, ?bool $dummy null): self
  225.     {
  226.         $profile = new static($createdAt);
  227.         if (null !== $dummy)
  228.             $profile->dummy $dummy;
  229.         return $profile;
  230.     }
  231.     public static function create(string $uriIdentity, ?\DateTimeImmutable $createdAt null): self
  232.     {
  233.         $profile = new static($createdAt);
  234.         $profile->defineUriIdentity($uriIdentity);
  235.         $profile->toggleMasseur(false);
  236.         return $profile;
  237.     }
  238.     public function defineUriIdentity(string $uriIdentity): void
  239.     {
  240.         if (!$this->isDraft()) {
  241.             throw new \DomainException('Profile is already created and can\'t change its URI.');
  242.         }
  243.         $this->uriIdentity $uriIdentity;
  244.         $this->draft false;
  245.     }
  246.     public function isDraft(): bool
  247.     {
  248.         return $this->draft;
  249.     }
  250.     public function toggleMasseur(bool $isMasseur): void
  251.     {
  252.         if ($this->masseur !== $isMasseur && (null !== $this->adBoardPlacement && false == $this->adBoardPlacement->getType()->isFree())) {
  253.             throw new \DomainException('Impossible to toggle profile type while it is displaying on adboard.');
  254.         }
  255.         $this->masseur $isMasseur;
  256.     }
  257.     public static function createMasseur(string $uriIdentity, ?\DateTimeImmutable $createdAt null): self
  258.     {
  259.         $profile = new static($createdAt);
  260.         $profile->defineUriIdentity($uriIdentity);
  261.         $profile->toggleMasseur(true);
  262.         return $profile;
  263.     }
  264.     public function isOwnedBy(Advertiser $account): bool
  265.     {
  266.         return $account->getId() === $this->owner->getId();
  267.     }
  268.     public function getId(): int
  269.     {
  270.         return $this->id;
  271.     }
  272.     public function setBio(TranslatableValue $nameTranslatableValue $description): void
  273.     {
  274.         $this->name $name;
  275.         $this->description $description;
  276.     }
  277.     public function setLocation(City $city$stations, ?MapCoordinate $mapCoordinate): void
  278.     {
  279.         if (!$this->isDraft() && !$this->city->equals($city)) {
  280.             throw new \DomainException('City change for a saved profile is forbidden.');
  281.         }
  282.         $this->city $city;
  283.         $this->changeStations($stations);
  284.         $this->normalizePrimaryStation();
  285.         $this->mapCoordinate $mapCoordinate;
  286.     }
  287.     protected function changeStations($stations)
  288.     {
  289.         if (null === $stations)
  290.             return;
  291.         if (false === is_array($stations) && false === is_iterable($stations))
  292.             throw new \InvalidArgumentException('Stations list should be either an array or an ArrayCollection');
  293.         $stationsArray is_iterable($stations) && !is_array($stations) ? iterator_to_array($stations) : $stations;
  294.         $stations = [];
  295.         foreach ($stationsArray as $station) {
  296.             $stations[$station->getId()] = $station;
  297.         }
  298.         $stationIds array_map(function (Station $station): int {
  299.             return $station->getId();
  300.         }, $stations);
  301.         $existingStationIds $this->stations->map(function (Station $station): int {
  302.             return $station->getId();
  303.         })->getValues();
  304.         $stationIdsToAdd array_diff($stationIds$existingStationIds);
  305.         $stationIdsToRemove array_diff($existingStationIds$stationIds);
  306.         foreach ($stationIdsToAdd as $stationId) {
  307.             $this->stations->add($stations[$stationId]);
  308.         }
  309.         foreach ($stationIdsToRemove as $stationId) {
  310.             $this->stations->remove($stationId);
  311.         }
  312.     }
  313.     public function normalizePrimaryStation(): void
  314.     {
  315.         if ($this->stations->isEmpty()) {
  316.             $this->primaryStation null;
  317.             return;
  318.         }
  319.         if ($this->primaryStation === null || !$this->stations->contains($this->primaryStation)) {
  320.             $this->primaryStation $this->stations->first();
  321.         }
  322.     }
  323.     public function setEnabledProvidedServices($services): void
  324.     {
  325.         if (null !== $services) {
  326.             if (is_array($services)) {
  327.                 $services = new ArrayCollection($services);
  328.             } elseif (!$services instanceof ArrayCollection) {
  329.                 if (is_iterable($services)) {
  330.                     $services = new ArrayCollection(iterator_to_array($services));
  331.                 } else {
  332.                     throw new \InvalidArgumentException('Services list should be either an array or an ArrayCollection');
  333.                 }
  334.             }
  335.             $this->providedServices $services;
  336.         }
  337.     }
  338.     public function setPhoneCallOptions(string $phoneNumber, ?PhoneCallRestrictions $restrictions, ?Messengers $messengers): void
  339.     {
  340.         $this->phoneNumber $phoneNumber;
  341.         $this->phoneCallRestrictions $restrictions;
  342.         $this->messengers $messengers;
  343.     }
  344.     public function setPricing(?ApartmentsPricing $apartmentsPricing, ?TakeOutPricing $takeOutPricing, ?int $extraCharge, ?ExpressPricing $expressPricing null, ?CarPricing $carPricing null): void
  345.     {
  346.         $this->apartmentsPricing $apartmentsPricing;
  347.         $this->takeOutPricing $takeOutPricing;
  348.         $this->extraCharge $extraCharge;
  349.         $this->expressPricing $expressPricing;
  350.         $this->carPricing $carPricing;
  351.     }
  352.     public function setPrepaymentOptions(?bool $prepayment, ?int $prepaymentAmount, ?string $prepaymentComment): void
  353.     {
  354.         $this->prepayment $prepayment;
  355.         if ($prepayment !== true) {
  356.             $this->prepaymentAmount null;
  357.             $this->prepaymentComment null;
  358.             return;
  359.         }
  360.         $this->prepaymentAmount $prepaymentAmount;
  361.         $this->prepaymentComment $prepaymentComment;
  362.     }
  363.     public function isApproved(): bool
  364.     {
  365.         return $this->approved;
  366.     }
  367.     public function approve(): void
  368.     {
  369.         $this->approved true;
  370.     }
  371.     public function unApprove(): void
  372.     {
  373.         $this->approved false;
  374.     }
  375.     public function getOwner(): ?Advertiser
  376.     {
  377.         return $this->owner;
  378.     }
  379.     public function setOwner(Advertiser $owner): void
  380.     {
  381.         $this->owner $owner;
  382.     }
  383.     public function hasOwner(): bool
  384.     {
  385.         return null !== $this->owner;
  386.     }
  387.     public function getTopPlacements(): Collection
  388.     {
  389.         return $this->topPlacements;
  390.     }
  391.     public function addTopPlacement(TopPlacement $topPlacement): void
  392.     {
  393.         $this->topPlacements->add($topPlacement);
  394.     }
  395.     public function getAdBoardPlacement(): ?AdBoardPlacement
  396.     {
  397.         return $this->adBoardPlacement;
  398.     }
  399.     public function setAdBoardPlacement(AdBoardPlacement $adBoardPlacement): void
  400.     {
  401.         $this->adBoardPlacement $adBoardPlacement;
  402.     }
  403.     /**
  404.      * Анкета оплачена и выводится в общих списках на сайте
  405.      * или в ТОПе, то есть "АКТИВНА"
  406.      */
  407.     public function isActive(): bool
  408.     {
  409.         return null !== $this->adBoardPlacement || $this->hasRunningTopPlacement();
  410.     }
  411.     public function hasRunningTopPlacement(): bool
  412.     {
  413.         $now = new \DateTimeImmutable('now');
  414.         foreach ($this->topPlacements as /** @var TopPlacement $topPlacement */ $topPlacement) {
  415.             if ($topPlacement->getPlacedAt() <= $now && $now <= $topPlacement->getExpiresAt())
  416.                 return true;
  417.         }
  418.         return false;
  419.     }
  420.     public function getUriIdentity(): string
  421.     {
  422.         return $this->uriIdentity;
  423.     }
  424.     public function getName(): TranslatableValue
  425.     {
  426.         return $this->name;
  427.     }
  428.     public function getDescription(): ?TranslatableValue
  429.     {
  430.         return $this->description;
  431.     }
  432.     public function getPersonParameters(): PersonParameters
  433.     {
  434.         return $this->personParameters;
  435.     }
  436.     public function setPersonParameters(PersonParameters $personParameters): void
  437.     {
  438.         $this->personParameters $personParameters;
  439.     }
  440.     public function getPhoneNumber(): string
  441.     {
  442.         return $this->phoneNumber;
  443.     }
  444.     //TODO return type
  445.     public function getPhoneCallRestrictions(): ?PhoneCallRestrictions
  446.     {
  447.         return $this->phoneCallRestrictions;
  448.     }
  449.     public function isMasseur(): bool
  450.     {
  451.         return $this->masseur;
  452.     }
  453.     //TODO return type
  454.     public function getClientRestrictions(): ?ClientRestrictions
  455.     {
  456.         return $this->clientRestrictions;
  457.     }
  458.     //TODO return type
  459.     public function setClientRestrictions(?ClientRestrictions $restrictions): void
  460.     {
  461.         $this->clientRestrictions $restrictions;
  462.     }
  463.     //TODO return type
  464.     public function getApartmentsPricing(): ?ApartmentsPricing
  465.     {
  466.         return $this->apartmentsPricing;
  467.     }
  468.     public function getTakeOutPricing(): ?TakeOutPricing
  469.     {
  470.         return $this->takeOutPricing;
  471.     }
  472.     public function getExtraCharge(): ?int
  473.     {
  474.         return $this->extraCharge;
  475.     }
  476.     public function isPrepayment(): ?bool
  477.     {
  478.         return $this->prepayment;
  479.     }
  480.     public function isWithoutPrepayment(): bool
  481.     {
  482.         return $this->prepayment === false;
  483.     }
  484.     public function getPrepaymentAmount(): ?int
  485.     {
  486.         return $this->prepaymentAmount;
  487.     }
  488.     public function getPrepaymentComment(): ?string
  489.     {
  490.         return $this->prepaymentComment;
  491.     }
  492.     public function addPhoto(string $pathbool $isMain): Photo
  493.     {
  494.         $photos $this->getPhotos();
  495.         $found $photos->filter(function (Photo $photo) use ($path): bool {
  496.             return $path === $photo->getPath();
  497.         });
  498.         if (!$found->isEmpty())
  499.             return $found->first();
  500.         if (true === $isMain) {
  501.             $photos->forAll(function ($indexPhoto $photo): true {
  502.                 $photo->unsetMain();
  503.                 return true;
  504.             });
  505.         }
  506.         $photo = new Photo($this$path$isMain);
  507.         $this->photos->add($photo);
  508.         return $photo;
  509.     }
  510.     /**
  511.      * @return Photo[]
  512.      */
  513.     public function getPhotos(): Collection
  514.     {
  515.         return $this->photos->filter(function ($mediaFile): bool {
  516.             return get_class($mediaFile) == Photo::class;
  517.         });
  518.     }
  519.     public function removePhoto(string $path): bool
  520.     {
  521.         foreach ($this->getPhotos() as $photo) {
  522.             if ($path === $photo->getPath()) {
  523.                 $this->photos->removeElement($photo);
  524.                 return true;
  525.             }
  526.         }
  527.         return false;
  528.     }
  529.     public function getMainPhotoOrFirstPhoto(): ?Photo
  530.     {
  531.         $photos $this->getPhotos();
  532.         if ($photos->isEmpty()) {
  533.             return null;
  534.         }
  535.         $mainPhoto $this->getMainPhoto();
  536.         if (null === $mainPhoto) {
  537.             $mainPhoto $photos->first();
  538.         }
  539.         return $mainPhoto;
  540.     }
  541.     public function getMainPhoto(): ?Photo
  542.     {
  543.         $photos $this->getPhotos();
  544.         if ($photos->isEmpty()) {
  545.             return null;
  546.         }
  547.         $mainPhoto null;
  548.         $photos->forAll(function ($indexPhoto $photo) use (&$mainPhoto): bool {
  549.             if ($photo->isMain()) {
  550.                 $mainPhoto $photo;
  551.                 return false// Stop the cycle
  552.             }
  553.             return true;
  554.         });
  555.         return $mainPhoto;
  556.     }
  557.     public function changeMainPhoto(string $path): void
  558.     {
  559.         $photos $this->getPhotos();
  560.         $found $photos->filter(function (Photo $photo) use ($path): bool {
  561.             return $path === $photo->getPath();
  562.         });
  563.         if ($found->isEmpty()) {
  564.             return;
  565.         }
  566.         $mainPhoto $found->first();
  567.         $photos->forAll(function ($indexPhoto $photo): true {
  568.             $photo->unsetMain();
  569.             return true;
  570.         });
  571.         $mainPhoto->setMain();
  572.     }
  573.     public function addSelfie(string $path): Selfie
  574.     {
  575.         $found $this->getSelfies()->filter(function (Selfie $selfie) use ($path): bool {
  576.             return $path === $selfie->getPath();
  577.         });
  578.         if (!$found->isEmpty())
  579.             return $found->first();
  580.         $selfie = new Selfie($this$path);
  581.         $this->selfies->add($selfie);
  582.         return $selfie;
  583.     }
  584.     /**
  585.      * @return Selfie[]
  586.      */
  587.     public function getSelfies(): Collection
  588.     {
  589.         return $this->selfies;
  590.     }
  591.     public function removeSelfie(string $path): bool
  592.     {
  593.         foreach ($this->getSelfies() as $selfie) {
  594.             if ($path === $selfie->getPath()) {
  595.                 $this->selfies->removeElement($selfie);
  596.                 return true;
  597.             }
  598.         }
  599.         return false;
  600.     }
  601.     public function getConfirmedVideos(): Collection
  602.     {
  603.         return $this->videos->filter(function ($mediaFile): bool {
  604.             if (!$mediaFile instanceof Video) {
  605.                 return false;
  606.             }
  607.             return $mediaFile->isConfirmed();
  608.         });
  609.     }
  610.     /**
  611.      * Храним только 1 видео для анкеты
  612.      */
  613.     public function addVideo(string $videoPath, ?string $posterPath null): Video
  614.     {
  615.         $found $this->getVideos()->filter(function (Video $video) use ($videoPath): bool {
  616.             return $videoPath === $video->getPath();
  617.         });
  618.         if (!$found->isEmpty())
  619.             return $found->first();
  620.         $video = new Video($this$videoPath);
  621.         if (null !== $posterPath) {
  622.             $video->setPreviewPath($posterPath);
  623.         }
  624.         //теперь разрешаем много видео
  625.         //$this->videos->clear();
  626.         $this->videos->add($video);
  627.         return $video;
  628.     }
  629.     /**
  630.      * @return Video[]
  631.      */
  632.     public function getVideos(): Collection
  633.     {
  634.         return $this->videos->filter(function ($mediaFile): bool {
  635.             return ($mediaFile instanceof Video);
  636.         });
  637.     }
  638.     public function removeVideo(string $path): bool
  639.     {
  640.         foreach ($this->getVideos() as $video) {
  641.             if ($path === $video->getPath()) {
  642.                 $this->videos->removeElement($video);
  643.                 $this->photos->removeElement($video);
  644.                 return true;
  645.             }
  646.         }
  647.         return false;
  648.     }
  649.     /**
  650.      * Добавляет таск на обработку оригинала видео в подходящий формат
  651.      *
  652.      * @param string $path Путь к файлу оригинала относительно фс очередей
  653.      */
  654.     public function addRawVideo(string $path): FileProcessingTask
  655.     {
  656.         $file = new FileProcessingTask($this$path);
  657.         $this->processingFiles->add($file);
  658.         return $file;
  659.     }
  660.     public function hasFilesInProcess(): bool
  661.     {
  662.         return $this->videosInProcess() > 0;
  663.     }
  664.     public function videosInProcess(): int
  665.     {
  666.         $inProcess $this->processingFiles->filter(function (FileProcessingTask $task): bool {
  667.             return !$task->isCompleted();
  668.         });
  669.         return $inProcess->count();
  670.     }
  671.     public function isMediaProcessed(): bool
  672.     {
  673.         foreach ($this->videos as $video)
  674.             if (null === $video->getPreviewPath())
  675.                 return false;
  676.         return true;
  677.     }
  678.     public function getAvatar(): ?Avatar
  679.     {
  680.         return $this->avatar;
  681.     }
  682.     public function setAvatar(string $path): void
  683.     {
  684.         $this->avatar = new Avatar($this$path);
  685.     }
  686.     public function removeAvatar(): bool
  687.     {
  688.         if (null == $this->avatar)
  689.             return false;
  690.         foreach ($this->photos as $photo) {
  691.             if ($this->avatar->getPath() === $photo->getPath()) {
  692.                 $this->photos->removeElement($photo);
  693.                 break;
  694.             }
  695.         }
  696.         $this->avatar null;
  697.         return true;
  698.     }
  699.     /**
  700.      * @return CommentByCustomer[]
  701.      */
  702.     public function getComments(): Collection
  703.     {
  704.         return $this->comments->filter(function (CommentByCustomer $comment): bool {
  705.             return null == $comment->getParent();
  706.         });
  707.     }
  708.     /**
  709.      * @return CommentByCustomer[]
  710.      */
  711.     public function getCommentsOrderedByNotReplied(): array
  712.     {
  713.         $comments $this->comments->filter(function (CommentByCustomer $comment): bool {
  714.             return null == $comment->getParent();
  715.         })->toArray();
  716.         usort($comments, function (CommentByCustomer $commentACommentByCustomer $commentB): int {
  717.             if ((null == $commentA->getLastCommentByAdvertiser() && null == $commentB->getLastCommentByAdvertiser())
  718.                 || (null != $commentA->getLastCommentByAdvertiser() && null != $commentB->getLastCommentByAdvertiser())) {
  719.                 if ($commentA->getCreatedAt() == $commentB->getCreatedAt())
  720.                     return $commentA->getId() > $commentB->getId() ? -1;
  721.                 else
  722.                     return $commentA->getCreatedAt() > $commentB->getCreatedAt() ? -1;
  723.             }
  724.             if (null == $commentA->getLastCommentByAdvertiser() && null != $commentB->getLastCommentByAdvertiser())
  725.                 return -1;
  726.             else
  727.                 return 1;
  728.         });
  729.         return $comments;
  730.     }
  731.     public function getCreatedAt(): ?\DateTimeImmutable
  732.     {
  733.         return $this->createdAt;
  734.     }
  735.     /**
  736.      * @return CommentByCustomer[]
  737.      */
  738.     public function getCommentsWithoutReply(): Collection
  739.     {
  740.         return $this->comments->filter(function (CommentByCustomer $comment): bool {
  741.             return null == $comment->getParent() && false == $comment->isCommentedByAdvertiser();
  742.         });
  743.     }
  744.     /**
  745.      * @return CommentByCustomer[]
  746.      */
  747.     public function getCommentsWithReply(): Collection
  748.     {
  749.         return $this->comments->filter(function (CommentByCustomer $comment): bool {
  750.             return null == $comment->getParent() && true == $comment->isCommentedByAdvertiser();
  751.         });
  752.     }
  753.     /**
  754.      * @return CommentByCustomer[]
  755.      */
  756.     public function getNewComments(): Collection
  757.     {
  758.         $weekAgo CarbonImmutable::now()->sub('7 days');
  759.         return $this->comments->filter(function (CommentByCustomer $comment) use ($weekAgo): bool {
  760.             return null == $comment->getParent()
  761.                 && (
  762.                     $comment->getCreatedAt() >= $weekAgo
  763.                     || null == $this->getCommentReply($comment)
  764.                 );
  765.         });
  766.     }
  767.     private function getCommentReply(CommentByCustomer $parent): ?CommentByCustomer
  768.     {
  769.         foreach ($this->comments as $comment)
  770.             if ($comment->getParent() == $parent)
  771.                 return $comment;
  772.         return null;
  773.     }
  774.     public function getOldComments(): Collection
  775.     {
  776.         $weekAgo CarbonImmutable::now()->sub('7 days');
  777.         return $this->comments->filter(function (CommentByCustomer $comment) use ($weekAgo): bool {
  778.             return null == $comment->getParent()
  779.                 && false == (
  780.                     $comment->getCreatedAt() >= $weekAgo
  781.                     || null == $this->getCommentReply($comment)
  782.                 );
  783.         });
  784.     }
  785.     public function getCommentFromUser(Customer $user): ?CommentByCustomer
  786.     {
  787.         foreach ($this->comments as $comment)
  788.             if (null == $comment->getParent() && null != $comment->getUser() && $user->getId() == $comment->getUser()->getId())
  789.                 return $comment;
  790.         return null;
  791.     }
  792.     //TODO return type
  793.     public function getCity(): City
  794.     {
  795.         return $this->city;
  796.     }
  797.     /**
  798.      * @return Station[]
  799.      */
  800.     public function getStations(): Collection
  801.     {
  802.         return $this->stations;
  803.     }
  804.     public function getMapCoordinate(): ?MapCoordinate
  805.     {
  806.         return $this->mapCoordinate;
  807.     }
  808.     public function getUpdatedAt(): ?\DateTimeImmutable
  809.     {
  810.         return $this->updatedAt;
  811.     }
  812.     public function setUpdatedAt(\DateTimeImmutable $updatedAt): void
  813.     {
  814.         $this->updatedAt $updatedAt;
  815.     }
  816.     public function getModerationStatus(): int
  817.     {
  818.         return $this->moderationStatus;
  819.     }
  820.     public function setModerationStatus(int $status): void
  821.     {
  822.         if (self::MODERATION_STATUS_APPROVED === $status) {
  823.             throw new \RuntimeException(sprintf('Use %s::passModeration() method instead', static::class));
  824.         }
  825.         $validStatuses = [self::MODERATION_STATUS_NOT_PASSEDself::MODERATION_STATUS_APPROVEDself::MODERATION_STATUS_WAITINGself::MODERATION_STATUS_REJECTED];
  826.         if (false === array_search($status$validStatuses))
  827.             throw new \LogicException('Trying to set an invalid moderation status');
  828.         $this->moderationStatus $status;
  829.     }
  830.     public function isModerationPassed(): bool
  831.     {
  832.         return $this->moderationStatus == self::MODERATION_STATUS_APPROVED;
  833.     }
  834.     public function isModerationWaiting(): bool
  835.     {
  836.         return $this->moderationStatus == self::MODERATION_STATUS_WAITING;
  837.     }
  838.     public function isModerationRejected(): bool
  839.     {
  840.         return $this->moderationStatus == self::MODERATION_STATUS_REJECTED;
  841.     }
  842.     public function passModeration(?ModerationRequest $moderationRequest null): void
  843.     {
  844.         $this->moderationStatus self::MODERATION_STATUS_APPROVED;
  845.         $func = static function ($kPhoto|Video $file) use ($moderationRequest): bool {
  846.             if (!$file->isConfirmed()) {
  847.                 $file->passModeration($moderationRequest);
  848.             }
  849.             return true;
  850.         };
  851.         $this->videos->forAll($func);
  852.     }
  853.     public function delete(): void
  854.     {
  855.         $this->deletePlacementHiding();
  856.         $this->deleteFromAdBoard();
  857.         $now = new \DateTimeImmutable('now');
  858.         $toDelete = [];
  859.         foreach ($this->topPlacements as $topPlacement) {
  860.             if ($topPlacement->getExpiresAt() > $now)
  861.                 $toDelete[] = $topPlacement;
  862.         }
  863.         foreach ($toDelete as $topPlacement)
  864.             $this->topPlacements->removeElement($topPlacement);
  865.         $this->setDeletedAt(Carbon::now());
  866.     }
  867.     public function deletePlacementHiding(): void
  868.     {
  869.         $this->placementHiding null;
  870.     }
  871.     public function deleteFromAdBoard(): void
  872.     {
  873.         $this->adBoardPlacement null;
  874.     }
  875.     //TODO return type
  876.     public function undoDelete(): void
  877.     {
  878.         $this->setDeletedAt(); // will pass null by default
  879.     }
  880.     //TODO return type
  881.     public function deleteFromTopPlacement(): void
  882.     {
  883.         //здесь нужна логика отмены конретного размещения
  884. //        $this->topPlacement = null;
  885.     }
  886.     public function getExpressPricing(): ?ExpressPricing
  887.     {
  888.         return $this->expressPricing;
  889.     }
  890.     public function getCarPricing(): ?CarPricing
  891.     {
  892.         return $this->carPricing;
  893.     }
  894.     //TODO return type
  895.     /**
  896.      * @return int[]
  897.      */
  898.     public function getClientTypes(): array
  899.     {
  900.         return $this->clientTypes ?? [];
  901.     }
  902.     /**
  903.      * @param int[] $clientTypes
  904.      */
  905.     public function setClientTypes(array $clientTypes): void
  906.     {
  907.         $this->clientTypes $clientTypes;
  908.     }
  909.     public function getMessengers(): ?Messengers
  910.     {
  911.         return $this->messengers;
  912.     }
  913.     public function getInactivatedAt(): ?\DateTimeImmutable
  914.     {
  915.         return $this->inactivatedAt;
  916.     }
  917.     public function setInactive(): void
  918.     {
  919.         $this->inactivatedAt CarbonImmutable::now();
  920.     }
  921.     public function undoInactive(): void
  922.     {
  923.         $this->inactivatedAt null;
  924.     }
  925.     public function isHidden(): bool
  926.     {
  927.         return null !== $this->getPlacementHiding();
  928.     }
  929.     public function getPlacementHiding(): ?PlacementHiding
  930.     {
  931.         return $this->placementHiding;
  932.     }
  933.     public function setPlacementHiding(PlacementHiding $placementHiding): void
  934.     {
  935.         $this->placementHiding $placementHiding;
  936.     }
  937.     public function hasSelfie(): bool
  938.     {
  939.         return $this->selfies->count() > 0;
  940.     }
  941.     public function hasVideo(): bool
  942.     {
  943.         return $this->videos->count() > 0;
  944.     }
  945.     public function isCommented(): bool
  946.     {
  947.         return $this->comments->count() > 0;
  948.     }
  949.     public function adminApprovalPhoto(): ?AdminApprovalPhoto
  950.     {
  951.         return $this->adminApprovalPhoto;
  952.     }
  953.     public function setAdminApprovalPhoto(?string $path): void
  954.     {
  955.         $this->adminApprovalPhoto $path ? new AdminApprovalPhoto($this$path) : null;
  956.     }
  957.     public function seo(): ?array
  958.     {
  959.         return $this->seo;
  960.     }
  961.     public function seoPhoneNumber(): ?string
  962.     {
  963.         return $this->seo['phone'] ?? null;
  964.     }
  965.     public function setSeoPhoneNumber(string $phoneNumber): void
  966.     {
  967.         if (null === $this->seo) {
  968.             $this->seo = [];
  969.         }
  970.         $this->seo['phone'] = $phoneNumber;
  971.     }
  972.     public function getPrimaryStation(): ?Station
  973.     {
  974.         $station $this->primaryStation ?? $this->getStations()->first();
  975.         if (false === $station) {
  976.             return null;
  977.         }
  978.         return $station;
  979.     }
  980.     public function setPrimaryStation(?Station $station): void
  981.     {
  982.         $this->primaryStation $station;
  983.         $this->normalizePrimaryStation();
  984.     }
  985.     public function getStationsSortedByPrimary(): array
  986.     {
  987.         $stations $this->stations->toArray();
  988.         if (!$this->primaryStation) {
  989.             return $stations;
  990.         }
  991.         usort($stations, function (Station $aStation $b) {
  992.             if ($a->getId() === $this->primaryStation->getId()) return -1;
  993.             if ($b->getId() === $this->primaryStation->getId()) return 1;
  994.             return 0;
  995.         });
  996.         return $stations;
  997.     }
  998.     public function getDeleteMode(): int
  999.     {
  1000.         return $this->deleteMode;
  1001.     }
  1002.     public function setDeleteMode(int $deleteMode): self
  1003.     {
  1004.         $this->deleteMode $deleteMode;
  1005.         return $this;
  1006.     }
  1007.     public function isHardDeleted(): bool
  1008.     {
  1009.         return $this->deleteMode === 2;
  1010.     }
  1011. }