src/Nolimit/AnualLeave/Admin/AnualLeaveAdmin.php line 25

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace App\Nolimit\AnualLeave\Admin;
  4. use App\Entity\AnualLeave;
  5. use App\Entity\AnualLeaveType;
  6. use App\Repository\AnualLeaveRepository;
  7. use Sonata\AdminBundle\Admin\AbstractAdmin;
  8. use Sonata\AdminBundle\Datagrid\DatagridMapper;
  9. use Sonata\AdminBundle\Datagrid\ListMapper;
  10. use Sonata\AdminBundle\Form\FormMapper;
  11. use Sonata\AdminBundle\Show\ShowMapper;
  12. use Sonata\DoctrineORMAdminBundle\Filter\CallbackFilter;
  13. use Sonata\Form\Type\DatePickerType;
  14. use Sonata\Form\Type\DateRangePickerType;
  15. use Symfony\Bridge\Doctrine\Form\Type\EntityType;
  16. use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
  17. use Symfony\Component\Form\Extension\Core\Type\DateType;
  18. use Symfony\Component\Security\Core\Security;
  19. use Sonata\AdminBundle\Route\RouteCollectionInterface;
  20. final class AnualLeaveAdmin extends AbstractAdmin
  21. {
  22.     public function __construct(
  23.         private readonly Security $security,
  24.         private readonly AnualLeaveRepository $anualLeaveRepository
  25.     ) {
  26.         parent::__construct();
  27.     }
  28.     protected function configureRoutes(RouteCollectionInterface $collection): void
  29.     {
  30.         $collection
  31.             ->remove('delete')
  32.             ->add('delete_log'$this->getRouterIdParameter().'/delete_log');
  33.     }
  34.     protected function configureDatagridFilters(DatagridMapper $filter): void
  35.     {
  36.         $filter
  37.             ->add('userOnHoliday')
  38.             ->add('name')
  39.             ->add('description')
  40.             ->add('dateInterval'CallbackFilter::class, [
  41.                 'label' => 'Dátum intervallum',
  42.                 'show_filter' => true,
  43.                 'field_type' => DateRangePickerType::class,
  44.                 'field_options' => [
  45.                     'field_options' => [
  46.                         'format' => 'yyyy-MM-dd',
  47.                     ],
  48.                 ],
  49.                 'callback' => function ($query$alias$field$data) {
  50.                     $value $data->getValue();
  51.                     if (!\is_array($value) || !isset($value['start'], $value['end'])) {
  52.                         return false;
  53.                     }
  54.                     $start $value['start'];
  55.                     $end $value['end'];
  56.                     if (!$start instanceof \DateTimeInterface || !$end instanceof \DateTimeInterface) {
  57.                         return false;
  58.                     }
  59.                     $qb $query->getQueryBuilder();
  60.                     $rootAlias current($query->getRootAliases());
  61.                     $suffix substr(uniqid(''true), -8);
  62.                     $endParam 'dt_end_' $suffix;
  63.                     $startParam 'dt_start_' $suffix;
  64.                     $qb->andWhere($rootAlias '.startDate <= :' $endParam);
  65.                     $qb->andWhere($rootAlias '.endDate >= :' $startParam);
  66.                     $qb->setParameter($endParam$end\Doctrine\DBAL\Types\Types::DATE_MUTABLE);
  67.                     $qb->setParameter($startParam$start\Doctrine\DBAL\Types\Types::DATE_MUTABLE);
  68.                     return true;
  69.                 },
  70.             ])
  71.             ->add('accepted')
  72.             ->add('anualLeaveType'null, [
  73.                 'show_filter' => true,
  74.                 'label' => 'Szabadság típusa',
  75.             ])
  76.         ;
  77.     }
  78.     protected function configureDefaultFilterValues(array &$filterValues): void
  79.     {
  80.         $filterValues['dateInterval'] = [
  81.             'type' => null,
  82.             'value' => [
  83.                 'start' => (new \DateTime('first day of January ' date('Y')))->format('Y-m-d'),
  84.                 'end' => (new \DateTime('last day of December ' date('Y')))->format('Y-m-d'),
  85.             ],
  86.         ];
  87.         $filterValues['anualLeaveType'] = [
  88.             'type'  => null,
  89.             'value' => '1',
  90.         ];
  91.     }
  92.     protected function configureQuery($query): \Sonata\AdminBundle\Datagrid\ProxyQueryInterface
  93.     {
  94.         $query parent::configureQuery($query);
  95.         $rootAlias current($query->getRootAliases());
  96.         $query->andWhere($query->expr()->eq($rootAlias '.deleted'':deleted'));
  97.         $query->setParameter('deleted'false);
  98.         if (!$this->isGranted('ROLE_SUPER_ADMIN')) {
  99.             $query->andWhere(
  100.                 $query->expr()->eq($rootAlias '.userOnHoliday'':userId')
  101.             );
  102.             $query->setParameter('userId'$this->security->getUser()->getId());
  103.         }
  104.         $filters $this->getRequest()->query->get('filter', []);
  105.         $dateIntervalValue $filters['dateInterval']['value'] ?? null;
  106.         $hasDateInterval \is_array($dateIntervalValue)
  107.             && !empty($dateIntervalValue['start'])
  108.             && !empty($dateIntervalValue['end']);
  109.         if (!$hasDateInterval) {
  110.             $query->andWhere($query->expr()->between(
  111.                 $rootAlias '.startDate',
  112.                 ':start_date',
  113.                 ':end_date'
  114.             ));
  115.             $query->setParameter('start_date', new \DateTime('first day of January ' date('Y')));
  116.             $query->setParameter('end_date', new \DateTime('last day of December ' date('Y')));
  117.         }
  118.         return $query;
  119.     }
  120.     protected function configureListFields(ListMapper $list): void
  121.     {
  122.         $list
  123.             ->addIdentifier('name')
  124.             ->add('userOnHoliday')
  125.             ->add('accepted')
  126.             ->add('description')
  127.             ->add('startDate','date', array('sortable' => true))
  128.             ->add('endDate''date')
  129.             ->add('anualLeaveType')
  130.             ->add(ListMapper::NAME_ACTIONSnull, [
  131.                 'actions' => [
  132.                     'show' => [],
  133.                     'edit' => [],
  134.                     'delete_log' => [
  135.                         'template' => '/CRUD/logical_delete_with_date_condition.html.twig',
  136.                     ]
  137.                     ],
  138.             ]);
  139.     }
  140.     protected function configureFormFields(FormMapper $form): void
  141.     {
  142.         $disabledInput false;
  143.         $isOfficialHoliday false;
  144.         if ($this->isCurrentRoute('edit')) {
  145.             $subject $this->getSubject();
  146.             $startDate $subject->getStartDate();
  147.             $isOfficialHoliday $this->isOfficialHolidayLeave($subject);
  148.             if ($startDate < new \DateTime('now') && !$this->isGranted('ROLE_SUPER_ADMIN')) {
  149.                 $disabledInput true;
  150.             }
  151.             $user $subject->getUserOnHoliday();
  152.         } else {
  153.             $user $this->security->getUser();
  154.         }
  155.         $isEditing $this->isCurrentRoute('edit');
  156.         $form
  157.             ->add('userOnHoliday'EntityType::class, [
  158.                 'class' => 'App\Entity\User',
  159.                 'label' => 'Felhasználó',
  160.                 'choice_label' => 'username',
  161.                 'query_builder' => function ($repository) {
  162.                     return $repository->createQueryBuilder('u')
  163.                         ->orderBy('u.username''ASC');
  164.                 },
  165.                 'data' => $user,
  166.                 'disabled' => !$this->isGranted('ROLE_SUPER_ADMIN'),
  167.             ])
  168.             ->add('accepted'null, ['disabled' => !$this->isGranted('ROLE_SUPER_ADMIN')])
  169.             ->add('name')
  170.             ->add('description')
  171.             ->add('startDate'DatePickerType::class, [
  172.                 'label' => 'Start Date',
  173.                 'format' => 'yyyy-MM-dd',
  174.                 'required' => true,
  175.             ])
  176.         ;
  177.         // Hivatalos szabadnap: endDate = startDate (preUpdate biztosítja mentéskor)
  178.         if ($isOfficialHoliday) {
  179.             $form->add('endDate'DatePickerType::class, [
  180.                 'label' => 'End Date',
  181.                 'required' => true,
  182.                 'format' => 'yyyy-MM-dd',
  183.                 'attr' => ['readonly' => 'readonly'],
  184.             ]);
  185.         } else {
  186.             $form->add('endDate'DatePickerType::class, [
  187.                 'label' => 'End Date',
  188.                 'required' => true,
  189.                 'format' => 'yyyy-MM-dd',
  190.             ]);
  191.         }
  192.         // Típus mező:
  193.         // - Szerkesztésnél: DISABLED, de MINDEN típus látszik (query_builder = null)
  194.         // - Hozzáadásnál + nem super admin: csak pihenő szabadság (type 1)
  195.         // - Hozzáadásnál + super admin: minden típus
  196.         $isTypeDisabled $isEditing;
  197.         $typeQueryBuilder null;
  198.         if (!$isEditing && !$this->isGranted('ROLE_SUPER_ADMIN')) {
  199.             $typeQueryBuilder = fn ($repo) => $repo->createQueryBuilder('t')
  200.                 ->andWhere('t.id = 1')
  201.                 ->orderBy('t.name''ASC');
  202.         }
  203.         $form->add('anualLeaveType'EntityType::class, [
  204.             'disabled' => $isTypeDisabled,
  205.             'class' => AnualLeaveType::class,
  206.             'choice_label' => 'name',
  207.             'label' => 'Tipus',
  208.             'query_builder' => $typeQueryBuilder,
  209.             'attr' => [
  210.                 'class' => 'select2',
  211.             ],
  212.         ]);
  213.         // Hivatalos szabadnapnál fixed nem szerkeszthető (generálásból jön, isWeekendWorkDay alapján)
  214.         $form->add('fixed'null, [
  215.             'label' => 'LeaveFixed',
  216.             'disabled' => $isOfficialHoliday,
  217.         ]);
  218.     }
  219.     private function isOfficialHolidayLeave($subject): bool
  220.     {
  221.         $type $subject->getAnualLeaveType();
  222.         return $type && (int) $type->getId() === 2;
  223.     }
  224.     protected function configureShowFields(ShowMapper $show): void
  225.     {
  226.         $show
  227.             ->add('id')
  228.             ->add('name')
  229.             ->add('description')
  230.             ->add('startDate')
  231.             ->add('endDate')
  232.             ;
  233.     }
  234.     /**
  235.      * preValidate: validáció ELŐTT fut le.
  236.      * - Hivatalos nap: endDate = startDate szinkronizálás + duplikátum ellenőrzés
  237.      * - Nem super admin: dátumok nem lehetnek az aktuális évnél régebbiek
  238.      */
  239.     public function preValidate(object $object): void
  240.     {
  241.         if (!$this->isGranted('ROLE_SUPER_ADMIN')) {
  242.             $firstDayOfYear = new \DateTimeImmutable('first day of January ' date('Y'));
  243.             $startDate $object->getStartDate();
  244.             $endDate $object->getEndDate();
  245.             if ($startDate && $startDate $firstDayOfYear) {
  246.                 $this->getForm()->addError(new \Symfony\Component\Form\FormError(
  247.                     'A kezdő dátum nem lehet az aktuális évnél régebbi.'
  248.                 ));
  249.             }
  250.             if ($endDate && $endDate $firstDayOfYear) {
  251.                 $this->getForm()->addError(new \Symfony\Component\Form\FormError(
  252.                     'A befejezés dátuma nem lehet az aktuális évnél régebbi.'
  253.                 ));
  254.             }
  255.         }
  256.         if ($this->isOfficialHolidayLeave($object)) {
  257.             $object->setEndDate($object->getStartDate());
  258.             $excludeId $object->getId();
  259.             if ($this->hasDuplicateOfficialHoliday($object$excludeId)) {
  260.                 $this->getForm()->addError(new \Symfony\Component\Form\FormError(
  261.                     'Ehhez a felhasználóhoz már létezik hivatalos szabadnap erre a dátumra.'
  262.                 ));
  263.             }
  264.         }
  265.     }
  266.     public function prePersist($object): void
  267.     {
  268.         parent::prePersist($object);
  269.         if (!$this->isGranted('ROLE_SUPER_ADMIN')) {
  270.             $object->setUserOnHoliday($this->security->getUser());
  271.             $this->ensureVacationTypeOnly($object);
  272.         }
  273.         if ($this->isOfficialHolidayLeave($object)) {
  274.             $object->setEndDate($object->getStartDate());
  275.         }
  276.     }
  277.     public function preUpdate($object): void
  278.     {
  279.         parent::preUpdate($object);
  280.         if ($this->isOfficialHolidayLeave($object)) {
  281.             $object->setEndDate($object->getStartDate());
  282.         }
  283.     }
  284.     /**
  285.      * @return bool true ha duplikátum van
  286.      */
  287.     private function hasDuplicateOfficialHoliday(AnualLeave $object, ?int $excludeId): bool
  288.     {
  289.         $user $object->getUserOnHoliday();
  290.         if (!$user) {
  291.             return false;
  292.         }
  293.         return $this->anualLeaveRepository->hasOfficialHolidayOverlap(
  294.             $user->getId(),
  295.             $object->getStartDate(),
  296.             $object->getEndDate(),
  297.             $excludeId
  298.         );
  299.     }
  300.     private function ensureVacationTypeOnly($object): void
  301.     {
  302.         $type $object->getAnualLeaveType();
  303.         if ($type && (int) $type->getId() !== 1) {
  304.             $vacationType $this->getModelManager()->find(AnualLeaveType::class, 1);
  305.             if ($vacationType) {
  306.                 $object->setAnualLeaveType($vacationType);
  307.             }
  308.         }
  309.     }
  310. }