src/Containers/CrmSection/GeneratedLinkContainer/Entities/GeneratedLinkEntity.php line 77

Open in your IDE?
  1. <?php
  2. namespace App\Containers\CrmSection\GeneratedLinkContainer\Entities;
  3. use ApiPlatform\Metadata\ApiResource;
  4. use ApiPlatform\Metadata\Delete;
  5. use ApiPlatform\Metadata\Get;
  6. use ApiPlatform\Metadata\GetCollection;
  7. use ApiPlatform\Metadata\Patch;
  8. use ApiPlatform\Metadata\ApiFilter;
  9. use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
  10. use ApiPlatform\Doctrine\Orm\Filter\NumericFilter;
  11. use ApiPlatform\Doctrine\Orm\Filter\RangeFilter;
  12. use ApiPlatform\Doctrine\Orm\Filter\DateFilter;
  13. use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
  14. use Symfony\Component\Serializer\Annotation\Groups;
  15. use Symfony\Component\Serializer\Annotation\Context;
  16. use Symfony\Component\Serializer\Normalizer\DateTimeNormalizer;
  17. use Doctrine\ORM\Mapping as ORM;
  18. use Doctrine\DBAL\Types\Types;
  19. use Gedmo\Mapping\Annotation as Gedmo;
  20. use App\Containers\CrmSection\GeneratedLinkContainer\Data\Repositories\GeneratedLinkRepository;
  21. use App\Containers\SettingSection\ListContainer\Entities\DepartmentEntity;
  22. use App\Containers\SettingSection\ListContainer\Entities\StatusEntity;
  23. use App\Ship\ApiPlatform\Filter\SimpleSearchFilter;
  24. use App\Ship\Utils\CarNumberUtil;
  25. /**
  26.  * Згенероване посилання
  27. */
  28. #[ApiResource(
  29.     routePrefix: '/crm',
  30.     normalizationContext: ['groups' => 'read'],
  31.     operations: [
  32.         new Get(
  33.             uriTemplate: '/generated_links/{id}', requirements: ['id' => '\d+'],
  34.             security: "is_granted('ROLE_ADMIN') or is_granted('ROLE_READONLY') or is_granted('ROLE_DEPARTMENT_EMPLOYEE')"
  35.         ),
  36.         new Patch(
  37.             uriTemplate: '/generated_links/{id}', requirements: ['id' => '\d+'],
  38.             security: "is_granted('ROLE_ADMIN')"
  39.         ),
  40.         new Delete(
  41.             uriTemplate: '/generated_links/{id}', requirements: ['id' => '\d+'],
  42.             security: "is_granted('ROLE_ADMIN')"
  43.         ),
  44.         // Post відсутній навмисно: запис створюється лише через
  45.         // /api/crm/generated_links/generate, інакше можна обійти upsert по парі
  46.         // (номер + департамент) і правило «статус лише вперед»
  47.         new GetCollection(
  48.             uriTemplate: '/generated_links',
  49.             security: "is_granted('ROLE_ADMIN') or is_granted('ROLE_READONLY') or is_granted('ROLE_DEPARTMENT_EMPLOYEE')"
  50.         ),
  51.     ],
  52.     shortName: 'CRM_GeneratedLinks'
  53. )]
  54. #[ApiFilter(SearchFilter::class, properties: [
  55.     'id' => 'exact',
  56.     'carNumber' => 'partial',
  57.     'status' => 'exact',
  58.     'status.name' => 'partial',
  59.     'department' => 'exact',
  60.     'department.name' => 'partial',
  61.     'comment' => 'partial',
  62. ])]
  63. #[ApiFilter(NumericFilter::class, properties: ['id'])]
  64. #[ApiFilter(RangeFilter::class, properties: ['id'])]
  65. #[ApiFilter(DateFilter::class, properties: ['createdAt', 'updatedAt'])]
  66. #[ApiFilter(OrderFilter::class)]
  67. #[ApiFilter(filterClass: SimpleSearchFilter::class, properties: [
  68.     'carNumber',
  69.     'comment',
  70. ])]
  71. #[ORM\Entity(repositoryClass: GeneratedLinkRepository::class)]
  72. #[ORM\Table(name: '`crm_generated_links`')]
  73. #[ORM\UniqueConstraint(name: 'crm_generated_links_car_number_department_idx', columns: ['car_number', 'department_id'])]
  74. class GeneratedLinkEntity
  75. {
  76.     public const STATUS_CREATED = 'generated_link_created';
  77.     public const STATUS_REGISTERED = 'generated_link_registered';
  78.     public const STATUS_CONTRACT = 'generated_link_contract';
  79.     /**
  80.      * Ранги статусів: статус рухається лише вперед.
  81.      * Навмисно не використовуємо StatusEntity::sorting — його редагує адмін у довіднику,
  82.      * і бізнес-правило залежало б від порядку елементів випадного списку
  83.      */
  84.     public const STATUS_RANKS = [
  85.         self::STATUS_CREATED => 10,
  86.         self::STATUS_REGISTERED => 20,
  87.         self::STATUS_CONTRACT => 30,
  88.     ];
  89.     #[ORM\Id]
  90.     #[ORM\GeneratedValue]
  91.     #[ORM\Column(type: Types::INTEGER)]
  92.     #[Groups(['read'])]
  93.     private ?int $id = null;
  94.     // Держномер
  95.     #[ORM\Column(type: Types::STRING, length: 50, nullable: false)]
  96.     #[Groups(['read'])]
  97.     private string $carNumber;
  98.     // Департамент: частина унікальної пари, тому NOT NULL —
  99.     // з nullable унікальний індекс у MySQL не працює (NULL != NULL)
  100.     #[ORM\ManyToOne(targetEntity: DepartmentEntity::class)]
  101.     #[ORM\JoinColumn(name: 'department_id', referencedColumnName: 'id', nullable: false)]
  102.     #[Groups(['read'])]
  103.     private DepartmentEntity $department;
  104.     // Статус
  105.     #[ORM\ManyToOne(targetEntity: StatusEntity::class)]
  106.     #[ORM\JoinColumn(name: 'status_id', referencedColumnName: 'id', nullable: true)]
  107.     #[Groups(['read'])]
  108.     private ?StatusEntity $status = null;
  109.     // Коментар
  110.     #[ORM\Column(type: Types::TEXT, nullable: true)]
  111.     #[Groups(['read'])]
  112.     private ?string $comment = null;
  113.     // Посилання: OneLink з base64 deep_link_value — до ~450 символів, у string(255) не вміщується
  114.     #[ORM\Column(type: Types::TEXT, nullable: false)]
  115.     #[Groups(['read'])]
  116.     private string $link;
  117.     #[Gedmo\Timestampable(on: 'create')]
  118.     #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
  119.     #[Context(
  120.         normalizationContext: [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s']
  121.     )]
  122.     #[Groups(['read'])]
  123.     protected $createdAt;
  124.     #[Gedmo\Timestampable(on: 'update')]
  125.     #[ORM\Column(type: Types::DATETIME_IMMUTABLE)]
  126.     #[Context(
  127.         normalizationContext: [DateTimeNormalizer::FORMAT_KEY => 'Y-m-d H:i:s']
  128.     )]
  129.     #[Groups(['read'])]
  130.     protected $updatedAt;
  131.     public static function rank(?string $statusId): int
  132.     {
  133.         return self::STATUS_RANKS[$statusId] ?? 0;
  134.     }
  135.     public function getId(): ?int
  136.     {
  137.         return $this->id;
  138.     }
  139.     public function getCarNumber()
  140.     {
  141.         return $this->carNumber;
  142.     }
  143.     public function setCarNumber(string $number)
  144.     {
  145.         $this->carNumber = CarNumberUtil::normalize($number);
  146.         return $this;
  147.     }
  148.     public function getDepartment()
  149.     {
  150.         return $this->department;
  151.     }
  152.     public function setDepartment(DepartmentEntity $department)
  153.     {
  154.         $this->department = $department;
  155.         return $this;
  156.     }
  157.     public function getStatus()
  158.     {
  159.         return $this->status;
  160.     }
  161.     public function setStatus($status)
  162.     {
  163.         $this->status = $status;
  164.         return $this;
  165.     }
  166.     public function getComment()
  167.     {
  168.         return $this->comment;
  169.     }
  170.     public function setComment(?string $comment)
  171.     {
  172.         $this->comment = $comment;
  173.         return $this;
  174.     }
  175.     public function getLink()
  176.     {
  177.         return $this->link;
  178.     }
  179.     public function setLink(string $link)
  180.     {
  181.         $this->link = $link;
  182.         return $this;
  183.     }
  184.     public function getCreatedAt()
  185.     {
  186.         return $this->createdAt;
  187.     }
  188.     public function getUpdatedAt()
  189.     {
  190.         return $this->updatedAt;
  191.     }
  192. }