src/Containers/CompanySection/EmployeeContainer/Entities/EmployeeEntity.php line 108

Open in your IDE?
  1. <?php
  2. namespace App\Containers\CompanySection\EmployeeContainer\Entities;
  3. use ApiPlatform\Metadata\ApiFilter;
  4. use ApiPlatform\Metadata\ApiResource;
  5. use ApiPlatform\Metadata\Delete;
  6. use ApiPlatform\Metadata\Get;
  7. use ApiPlatform\Metadata\GetCollection;
  8. use ApiPlatform\Metadata\Patch;
  9. use ApiPlatform\Metadata\Post;
  10. use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
  11. use ApiPlatform\Metadata\ApiProperty;
  12. use App\Ship\Entities\UserEntity;
  13. use Doctrine\ORM\Mapping as ORM;
  14. use Doctrine\DBAL\Types\Types;
  15. use Gedmo\Mapping\Annotation as Gedmo;
  16. use Symfony\Component\Serializer\Annotation\Groups;
  17. use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
  18. use Symfony\Component\Serializer\Annotation\MaxDepth;
  19. use App\Ship\Utils\FormattingUtil;
  20. use App\Containers\CompanySection\EmployeeContainer\Data\Repositories\EmployeeRepository;
  21. use App\Containers\CompanySection\StructureContainer\Entities\StructureEntity;
  22. use App\Containers\CompanySection\IndexContainer\Entities\TranslationEntity;
  23. use App\Ship\ApiPlatform\Filter\SimpleSearchFilter;
  24. use App\Ship\ApiPlatform\Filter\FilterLogic;
  25. use App\Containers\CompanySection\EmployeeContainer\UI\ApiPlatform\Controllers\PostSetAvatarByBinaryController;
  26. use App\Ship\Entities\MediaEntity;
  27. /** 
  28.  * Сотрудники
  29. */
  30. #[ApiResource(
  31.     routePrefix: '/company',
  32.     normalizationContext: ['groups' => 'read', 'enable_max_depth' => true],
  33.     operations: [
  34.         new Get(
  35.             uriTemplate: '/employees/{id}', requirements: ['id' => '\d+'],
  36.             security: "is_granted('ROLE_ADMIN') or is_granted('ROLE_READONLY')"
  37.         ),
  38.         new Patch(
  39.             uriTemplate: '/employees/{id}', requirements: ['id' => '\d+'],
  40.             security: "is_granted('ROLE_ADMIN')"
  41.         ),
  42.         new Post(
  43.             uriTemplate: '/employees',
  44.             security: "is_granted('ROLE_ADMIN')"
  45.         ),
  46.         new Post(
  47.             uriTemplate: '/employee/set_avatar_by_binary',
  48.             controller: PostSetAvatarByBinaryController::class,
  49.             deserialize: false, 
  50.             validationContext: ['groups' => ['read', 'media_object_create']], 
  51.             openapiContext: [
  52.                 'requestBody' => [
  53.                     'content' => [
  54.                         'multipart/form-data' => [
  55.                             'schema' => [
  56.                                 'type' => 'object', 
  57.                                 'properties' => [
  58.                                     'file' => [
  59.                                         'type' => 'string', 
  60.                                         'format' => 'binary'
  61.                                     ]
  62.                                 ],
  63.                                 'required' => [
  64.                                     'file'
  65.                                 ]
  66.                             ]
  67.                         ]
  68.                     ]
  69.                 ]
  70.             ],
  71.             security: "is_granted('ROLE_ADMIN')"
  72.         ),
  73.         new Delete(
  74.             uriTemplate: '/employees/{id}', requirements: ['id' => '\d+'],
  75.             security: "is_granted('ROLE_ADMIN')"
  76.         ),
  77.         new GetCollection(
  78.             uriTemplate: '/employees',
  79.             security: "is_granted('ROLE_ADMIN') or is_granted('ROLE_READONLY')"
  80.         ),
  81.     ],
  82.     shortName: 'Company_Employees'
  83. )]
  84. #[ORM\Entity(repositoryClass: EmployeeRepository::class)]
  85. #[ORM\Table(name: '`company_employee`')]
  86. #[ApiFilter(SearchFilter::class, properties: [
  87.     'id' => 'exact',
  88.     'name' => 'partial',
  89.     'lastName' => 'partial',
  90.     'patronymic' => 'partial',
  91.     'structure' => 'exact',
  92.     'workPhone' => 'partial',
  93.     'internalPhone' => 'partial'
  94. ])]
  95. #[ApiFilter(filterClass: SimpleSearchFilter::class, properties: [
  96.     'name',
  97.     'lastName',
  98.     'patronymic',
  99.     'email',
  100.     'workPhone',
  101.     'internalPhone'
  102. ])]
  103. #[ApiFilter(FilterLogic::class)]
  104. #[Gedmo\TranslationEntity(class: TranslationEntity::class)]
  105. class EmployeeEntity extends UserEntity implements PasswordAuthenticatedUserInterface
  106. {
  107.     public const ROLE_DEPARTMENT_EMPLOYEE = 'ROLE_DEPARTMENT_EMPLOYEE';
  108.     public const ROLE_DEPARTMENT_ADMIN = 'ROLE_DEPARTMENT_ADMIN';
  109.     // Пароль
  110.     #[ORM\Column(type: Types::STRING, length: 255, nullable: false)]
  111.     private string $password;
  112.     // Активность
  113.     #[ORM\Column(type: Types::BOOLEAN)]
  114.     #[Groups(['read'])]
  115.     private bool $isActive = false;
  116.     // Имя
  117.     #[ORM\Column(type: Types::STRING, length: 150)]
  118.     #[Gedmo\Translatable]
  119.     #[Groups(['read'])]
  120.     private string $name;
  121.     
  122.     // Фамилия
  123.     #[ORM\Column(type: Types::STRING, length: 150)]
  124.     #[Gedmo\Translatable]
  125.     #[Groups(['read'])]
  126.     private string $lastName;
  127.     // Отчество
  128.     #[ORM\Column(type: Types::STRING, length: 150, nullable: true)]
  129.     #[Gedmo\Translatable]
  130.     #[Groups(['read'])]
  131.     private ?string $patronymic = null;
  132.     // Фото
  133.     #[ORM\ManyToOne(targetEntity: MediaEntity::class)]
  134.     #[ORM\JoinColumn(name: 'photo_id', referencedColumnName: 'id', nullable: true)]
  135.     private ?MediaEntity $photo = null;
  136.     #[ApiProperty(types: ['https://schema.org/contentUrl'])]
  137.     #[Groups(['read', 'media_object:read'])]
  138.     public ?string $photoUrl = null;
  139.     // Рабочий телефон
  140.     #[ORM\Column(type: Types::STRING, length: 150, nullable: false, unique: true)]
  141.     #[Groups(['read'])]
  142.     private string $workPhone;
  143.     // Внутренний номер телефона
  144.     #[ORM\Column(type: Types::STRING, length: 50, nullable: true, unique: true)]
  145.     #[Groups(['read'])]
  146.     private ?string $internalPhone = null;
  147.     
  148.     // Должность
  149.     #[ORM\Column(type: Types::STRING, length: 150, nullable: true)]
  150.     #[Gedmo\Translatable]
  151.     #[Groups(['read'])]
  152.     private ?string $position = null;
  153.     // Структура
  154.     #[ORM\ManyToOne(targetEntity: StructureEntity::class)]
  155.     #[ORM\JoinColumn(name: 'structure_id', referencedColumnName: 'id', nullable: true)]
  156.     #[Groups(['read'])]
  157.     #[MaxDepth(1)]
  158.     private ?StructureEntity $structure = null;
  159.     #[ORM\Column(type: Types::JSON, length: 150, nullable: true)]
  160.     #[Groups(['read'])]
  161.     #[ApiProperty(
  162.         security: "is_granted('ROLE_ADMIN')"
  163.     )]
  164.     private $accessProperties = [];
  165.     /**
  166.      * Used locale to override Translation listener`s locale
  167.      * this is not a mapped field of entity metadata, just a simple property
  168.      */
  169.     #[Gedmo\Locale]
  170.     private $locale;
  171.     
  172.     public function getPassword(): string
  173.     {
  174.         return (string) $this->password;
  175.     }
  176.     public function setPassword(string $password): void
  177.     {
  178.         $this->password = $password;
  179.     }
  180.     public function getName()
  181.     {
  182.         return $this->name;
  183.     }
  184.     public function setName($name)
  185.     {
  186.         $this->name = $name;
  187.         return $this;
  188.     }
  189.  
  190.     public function getLastName()
  191.     {
  192.         return $this->lastName;
  193.     }
  194.     public function setLastName($lastName)
  195.     {
  196.         $this->lastName = $lastName;
  197.         return $this;
  198.     }
  199.     public function getPatronymic()
  200.     {
  201.         return $this->patronymic;
  202.     }
  203.     public function setPatronymic($patronymic)
  204.     {
  205.         $this->patronymic = $patronymic;
  206.         return $this;
  207.     }
  208.     public function getRoles(): array
  209.     {
  210.         $roles = $this->roles;
  211.         // Адмін департаменту = співробітник департаменту + доступ до налаштувань.
  212.         // Підразумівана роль: інакше її довелося б дублювати в усіх security-виразах
  213.         // і в ORM-розширеннях, які перевіряють роль через in_array (role_hierarchy там не працює)
  214.         if (in_array(self::ROLE_DEPARTMENT_ADMIN, $roles, true))
  215.             $roles[] = self::ROLE_DEPARTMENT_EMPLOYEE;
  216.         // guarantee every user at least has ROLE_USER
  217.         $roles[] = 'ROLE_USER';
  218.         // array_values обовʼязковий: array_unique зберігає ключі, і при дублі в середині
  219.         // масиву json_encode віддав би обʼєкт замість масиву — на фронті впав би roles.includes()
  220.         return \array_values(\array_unique($roles));
  221.     }
  222.     public function setRoles(array $roles): void
  223.     {
  224.         $roles = \array_values(\array_unique($roles));
  225.         // Підразумівані ролі в базі не зберігаємо
  226.         if (in_array(self::ROLE_DEPARTMENT_ADMIN, $roles, true))
  227.             $roles = \array_diff($roles, [self::ROLE_DEPARTMENT_EMPLOYEE]);
  228.         $this->roles = \array_values(\array_diff($roles, ['ROLE_USER']));
  229.     }
  230.     public function getPhoto()
  231.     {
  232.         return $this->photo;
  233.     }
  234.     public function setPhoto($photo)
  235.     {
  236.         $this->photo = $photo;
  237.         return $this;
  238.     }
  239.     public function getWorkPhone()
  240.     {
  241.         return $this->workPhone;
  242.     }
  243.     public function setWorkPhone($value)
  244.     {
  245.         $this->workPhone = (!empty($value))
  246.             ? FormattingUtil::phoneNumber($value)
  247.             : $value;
  248.         return $this;
  249.     }
  250.     public function getInternalPhone()
  251.     {
  252.         return $this->internalPhone;
  253.     }
  254.     public function setInternalPhone($value)
  255.     {
  256.         $this->internalPhone = (!empty($value))
  257.             ? FormattingUtil::phoneNumber($value)
  258.             : $value;
  259.         return $this;
  260.     }
  261.     public function getPosition()
  262.     {
  263.         return $this->position;
  264.     }
  265.     public function setPosition($position)
  266.     {
  267.         $this->position = $position;
  268.         return $this;
  269.     }
  270.     public function getIsActive()
  271.     {
  272.         return $this->isActive;
  273.     }
  274.  
  275.     public function setIsActive($isActive)
  276.     {
  277.         $this->isActive = $isActive;
  278.         return $this;
  279.     }
  280.     public function getStructure()
  281.     {
  282.         return $this->structure;
  283.     }
  284.     public function setStructure($structure)
  285.     {
  286.         $this->structure = $structure;
  287.         return $this;
  288.     }
  289.     public function setTranslatableLocale($locale)
  290.     {
  291.         $this->locale = $locale;
  292.     }
  293.     /**
  294.      * Get the value of accessProperties
  295.      */ 
  296.     public function getAccessProperties()
  297.     {
  298.         return $this->accessProperties;
  299.     }
  300.     /**
  301.      * Set the value of accessProperties
  302.      *
  303.      * @return  self
  304.      */ 
  305.     public function setAccessProperties($accessProperties)
  306.     {
  307.         $this->accessProperties = $accessProperties;
  308.         return $this;
  309.     }
  310. }