Feat: Add teamer filter
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace App\Controller\Admin\Teamer;
|
||||
|
||||
use App\Repository\TeamerRepository;
|
||||
use App\Service\Common\TeamerFilterHandler;
|
||||
use Knp\Component\Pager\PaginatorInterface;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
@@ -13,6 +14,7 @@ use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
class IndexController extends AbstractController
|
||||
{
|
||||
public function __construct(
|
||||
protected readonly TeamerFilterHandler $filterHandler,
|
||||
private readonly TeamerRepository $teamerRepository,
|
||||
private readonly PaginatorInterface $paginator
|
||||
) {
|
||||
@@ -22,9 +24,11 @@ class IndexController extends AbstractController
|
||||
#[IsGranted('ROLE_ADMINISTRATIVE')]
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$filterDto = $this->filterHandler->getFilterSettings();
|
||||
|
||||
$query = $this
|
||||
->teamerRepository
|
||||
->getListQuery()
|
||||
->getListQuery($filterDto)
|
||||
;
|
||||
|
||||
$pagination = $this->paginator->paginate(
|
||||
@@ -39,6 +43,7 @@ class IndexController extends AbstractController
|
||||
|
||||
return $this->render('admin/teamer/index.html.twig', [
|
||||
'pagination' => $pagination,
|
||||
'filterDto' => $filterDto,
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Common;
|
||||
|
||||
use App\Controller\Traits\ReturnUrlTrait;
|
||||
use App\Form\TeamerFilterType;
|
||||
use App\Model\AjaxModalResponseDto;
|
||||
use App\Repository\TeamerRepository;
|
||||
use App\Service\Common\TeamerFilterHandler;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class TeamerFilterController extends AbstractController
|
||||
{
|
||||
use ReturnUrlTrait;
|
||||
|
||||
public function __construct(
|
||||
private readonly TeamerFilterHandler $filterHandler,
|
||||
private readonly TeamerRepository $teamerRepository
|
||||
) {
|
||||
}
|
||||
|
||||
#[Route('/common/teamer/filter', name: 'app_common_teamer_filter')]
|
||||
#[IsGranted('ROLE_USER')]
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
$response = new AjaxModalResponseDto();
|
||||
$formAction = $this->generateUrl('app_common_teamer_filter', ['r' => $request->query->get('r')]);
|
||||
$formData = $this->filterHandler->getFilterSettings();
|
||||
$form = $this->createForm(TeamerFilterType::class, $formData, [
|
||||
'action' => $formAction,
|
||||
'ajax_submit' => true,
|
||||
]);
|
||||
$form->handleRequest($request);
|
||||
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$this->filterHandler->handleRequest($form);
|
||||
$returnUrl = $this->getReturnUrl($request, 'app_admin_teamer_index');
|
||||
$response->setCloseAndRedirect($returnUrl);
|
||||
} else {
|
||||
$response->setContent($this->renderView('common/teamer_filter.html.twig', [
|
||||
'filterForm' => $form,
|
||||
'filterDto' => $form->getData(),
|
||||
]));
|
||||
}
|
||||
|
||||
return $this->json($response);
|
||||
}
|
||||
|
||||
#[Route('/common/teamer/filter/reset', name: 'app_common_teamer_filter_reset')]
|
||||
public function reset(Request $request): Response
|
||||
{
|
||||
$this->filterHandler->resetFilterSettings();
|
||||
$returnUrl = $this->getReturnUrl($request, 'app_admin_teamer_index');
|
||||
|
||||
return $this->redirect($returnUrl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\JobProfile;
|
||||
use App\Model\TeamerFilterDto;
|
||||
use Doctrine\ORM\EntityRepository;
|
||||
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class TeamerFilterType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('name', TextType::class, [
|
||||
'label' => 'Name',
|
||||
'required' => false,
|
||||
])
|
||||
->add('dateFrom', DatepickerType::class, [
|
||||
'label' => 'verfügbar ab',
|
||||
])
|
||||
->add('dateTo', DatepickerType::class, [
|
||||
'label' => 'verfügbar bis',
|
||||
])
|
||||
->add('pickupId', BpnPickupType::class, [
|
||||
'label' => 'Buszustieg',
|
||||
'required' => false,
|
||||
])
|
||||
->add('skiLicense', CheckboxType::class, [
|
||||
'label' => 'Skilehrer:innen-Lizenz',
|
||||
'required' => false,
|
||||
])
|
||||
->add('snowboardLicense', CheckboxType::class, [
|
||||
'label' => 'Snowboardlehrer:innen-Lizenz',
|
||||
'required' => false,
|
||||
])
|
||||
->add('jobProfile', EntityType::class, [
|
||||
'label' => 'Job-Profil',
|
||||
'class' => JobProfile::class,
|
||||
'required' => false,
|
||||
'choice_label' => 'name',
|
||||
'query_builder' => function (EntityRepository $repository) {
|
||||
return $repository->createQueryBuilder('job_profile')
|
||||
->orderBy('job_profile.name', 'ASC');
|
||||
},
|
||||
])
|
||||
->add('apply', SubmitType::class, [
|
||||
'label' => 'filtern',
|
||||
])
|
||||
->add('reset', SubmitType::class, [
|
||||
'label' => 'reset',
|
||||
])
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver
|
||||
->setDefaults([
|
||||
'data_class' => TeamerFilterDto::class,
|
||||
])
|
||||
;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
abstract class AbstractFilterDto
|
||||
{
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->getActiveFiltersCount() > 0;
|
||||
}
|
||||
|
||||
public function getActiveFiltersCount(): int
|
||||
{
|
||||
$freshInstance = new static();
|
||||
$filterCount = 0;
|
||||
$reflection = new \ReflectionClass(static::class);
|
||||
|
||||
foreach ($reflection->getProperties() as $property) {
|
||||
$name = $property->getName();
|
||||
if ($this->$name !== $freshInstance->$name) {
|
||||
++$filterCount;
|
||||
}
|
||||
}
|
||||
|
||||
return $filterCount;
|
||||
}
|
||||
}
|
||||
@@ -5,39 +5,18 @@ namespace App\Model;
|
||||
use App\Entity\Destination;
|
||||
use App\Entity\JobProfile;
|
||||
|
||||
class AssignmentFilterDto
|
||||
class AssignmentFilterDto extends AbstractFilterDto
|
||||
{
|
||||
public const DURATION_WEEKEND = 1;
|
||||
public const DURATION_MID_WEEK = 2;
|
||||
public const DURATION_FULL_WEEK = 3;
|
||||
public const DURATION_MORE = 4;
|
||||
|
||||
private ?\DateTimeImmutable $dateFrom = null;
|
||||
private ?\DateTimeImmutable $dateTo = null;
|
||||
private ?JobProfile $jobProfile = null;
|
||||
private ?Destination $destination = null;
|
||||
private ?int $duration = null;
|
||||
|
||||
public function isActive(): bool
|
||||
{
|
||||
return $this->getActiveFiltersCount() > 0;
|
||||
}
|
||||
|
||||
public function getActiveFiltersCount(): int
|
||||
{
|
||||
$freshInstance = new static();
|
||||
$filterCount = 0;
|
||||
$reflection = new \ReflectionClass(static::class);
|
||||
|
||||
foreach ($reflection->getProperties() as $property) {
|
||||
$name = $property->getName();
|
||||
if ($this->$name !== $freshInstance->$name) {
|
||||
++$filterCount;
|
||||
}
|
||||
}
|
||||
|
||||
return $filterCount;
|
||||
}
|
||||
protected ?\DateTimeImmutable $dateFrom = null;
|
||||
protected ?\DateTimeImmutable $dateTo = null;
|
||||
protected ?JobProfile $jobProfile = null;
|
||||
protected ?Destination $destination = null;
|
||||
protected ?int $duration = null;
|
||||
|
||||
public function getDateFrom(): ?\DateTimeImmutable
|
||||
{
|
||||
@@ -98,9 +77,4 @@ class AssignmentFilterDto
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isReset(): bool
|
||||
{
|
||||
return $this->reset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
namespace App\Model;
|
||||
|
||||
use App\Entity\JobProfile;
|
||||
|
||||
class TeamerFilterDto extends AbstractFilterDto
|
||||
{
|
||||
protected ?string $name = null;
|
||||
protected ?\DateTimeImmutable $dateFrom = null;
|
||||
protected ?\DateTimeImmutable $dateTo = null;
|
||||
protected ?JobProfile $jobProfile = null;
|
||||
protected string $pickupId = '';
|
||||
protected bool $skiLicense = false;
|
||||
protected bool $snowboardLicense = false;
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(?string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateFrom(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->dateFrom;
|
||||
}
|
||||
|
||||
public function setDateFrom(?\DateTimeImmutable $dateFrom): static
|
||||
{
|
||||
$this->dateFrom = $dateFrom;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateTo(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->dateTo;
|
||||
}
|
||||
|
||||
public function setDateTo(?\DateTimeImmutable $dateTo): static
|
||||
{
|
||||
$this->dateTo = $dateTo;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getJobProfile(): ?JobProfile
|
||||
{
|
||||
return $this->jobProfile;
|
||||
}
|
||||
|
||||
public function setJobProfile(?JobProfile $jobProfile): static
|
||||
{
|
||||
$this->jobProfile = $jobProfile;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPickupId(): string
|
||||
{
|
||||
return $this->pickupId;
|
||||
}
|
||||
|
||||
public function setPickupId(string $pickupId): static
|
||||
{
|
||||
$this->pickupId = $pickupId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasSkiLicense(): bool
|
||||
{
|
||||
return $this->skiLicense;
|
||||
}
|
||||
|
||||
public function setSkiLicense(bool $skiLicense): static
|
||||
{
|
||||
$this->skiLicense = $skiLicense;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasSnowboardLicense(): bool
|
||||
{
|
||||
return $this->snowboardLicense;
|
||||
}
|
||||
|
||||
public function setSnowboardLicense(bool $snowboardLicense): static
|
||||
{
|
||||
$this->snowboardLicense = $snowboardLicense;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,10 @@
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\License;
|
||||
use App\Entity\Teamer;
|
||||
use App\Model\TeamerFilterDto;
|
||||
use App\Repository\Traits\QueryHelperTrait;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\ORM\Query;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
@@ -17,18 +20,74 @@ use Doctrine\Persistence\ManagerRegistry;
|
||||
*/
|
||||
class TeamerRepository extends ServiceEntityRepository
|
||||
{
|
||||
use QueryHelperTrait;
|
||||
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Teamer::class);
|
||||
}
|
||||
|
||||
public function getListQuery(): Query
|
||||
public function getListQuery(TeamerFilterDto $filterDto): Query
|
||||
{
|
||||
$qb = $this->createQueryBuilder('teamer');
|
||||
|
||||
return $qb
|
||||
$qb
|
||||
->select('teamer', 'user')
|
||||
->leftJoin('teamer.availabilities', 'availability')
|
||||
->leftJoin('teamer.jobProfiles', 'job_profile')
|
||||
->leftJoin('teamer.licenses', 'license')
|
||||
->leftJoin('teamer.user', 'user')
|
||||
->getQuery();
|
||||
;
|
||||
|
||||
if (null !== $filterDto->getName()) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->like('teamer.lastName', ':name'))
|
||||
->setParameter('name', '%'.$this->escapeLikeWildcards($filterDto->getName()).'%')
|
||||
;
|
||||
}
|
||||
|
||||
if (null !== $filterDto->getDateFrom()) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->gte('availability.dateFrom', ':dateFrom'))
|
||||
->setParameter('dateFrom', $filterDto->getDateFrom())
|
||||
;
|
||||
}
|
||||
|
||||
if (null !== $filterDto->getDateTo()) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->lte('availability.dateTo', ':dateTo'))
|
||||
->setParameter('dateTo', $filterDto->getDateTo())
|
||||
;
|
||||
}
|
||||
|
||||
if (null !== $filterDto->getJobProfile()) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->eq('job_profile', ':jobProfile'))
|
||||
->setParameter('jobProfile', $filterDto->getJobProfile())
|
||||
;
|
||||
}
|
||||
|
||||
if ('' !== $filterDto->getPickupId()) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->like('teamer.pickups', ':pickup'))
|
||||
->setParameter('pickup', '%"'.$this->escapeLikeWildcards($filterDto->getPickupId()).'"%')
|
||||
;
|
||||
}
|
||||
|
||||
if (true === $filterDto->hasSkiLicense()) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->eq('license.type', ':licenseSki'))
|
||||
->setParameter('licenseSki', License::TYPE_SKI)
|
||||
;
|
||||
}
|
||||
|
||||
if (true === $filterDto->hasSnowboardLicense()) {
|
||||
$qb
|
||||
->andWhere($qb->expr()->eq('license.type', ':licenseSnowboard'))
|
||||
->setParameter('licenseSnowboard', License::TYPE_SNOWBOARD)
|
||||
;
|
||||
}
|
||||
|
||||
return $qb->getQuery();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +55,10 @@ class AssignmentFilterHandler
|
||||
$filterDto = new AssignmentFilterDto();
|
||||
|
||||
if (isset($data['date_from'])) {
|
||||
$filterDto->setDateFrom(new \DateTimeImmutable($data['date_from']));
|
||||
$filterDto->setDateFrom($data['date_from']);
|
||||
}
|
||||
if (isset($data['date_to'])) {
|
||||
$filterDto->setDateTo(new \DateTimeImmutable($data['date_to']));
|
||||
$filterDto->setDateTo($data['date_to']);
|
||||
}
|
||||
if (isset($data['job_profile']) && 0 < (int) $data['job_profile']) {
|
||||
$jobProfile = $this
|
||||
@@ -86,8 +86,8 @@ class AssignmentFilterHandler
|
||||
private function saveFilterSettings(AssignmentFilterDto $filterDto): void
|
||||
{
|
||||
$this->getSession()->set($this->namespace, [
|
||||
'date_from' => $filterDto->getDateFrom()?->format('Y-m-d'),
|
||||
'date_to' => $filterDto->getDateTo()?->format('Y-m-d'),
|
||||
'date_from' => $filterDto->getDateFrom(),
|
||||
'date_to' => $filterDto->getDateTo(),
|
||||
'job_profile' => $filterDto->getJobProfile()?->getId(),
|
||||
'destination' => $filterDto->getDestination()?->getId(),
|
||||
'duration' => $filterDto->getDuration(),
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Service\Common;
|
||||
|
||||
use App\Entity\JobProfile;
|
||||
use App\Model\TeamerFilterDto;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\HttpFoundation\RequestStack;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
|
||||
class TeamerFilterHandler
|
||||
{
|
||||
private string $namespace = 'filter:teamer';
|
||||
|
||||
public function __construct(
|
||||
private readonly RequestStack $requestStack,
|
||||
private readonly EntityManagerInterface $entityManager
|
||||
) {
|
||||
}
|
||||
|
||||
public function setNamespace(string $namespace): static
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function handleRequest(FormInterface $form): TeamerFilterDto
|
||||
{
|
||||
$filterDto = $form->getData();
|
||||
|
||||
if ($form->get('reset')->isClicked()) {
|
||||
$filterDto = new TeamerFilterDto();
|
||||
$this->resetFilterSettings();
|
||||
} elseif ($form->get('apply')->isClicked()) {
|
||||
$this->saveFilterSettings($filterDto);
|
||||
}
|
||||
|
||||
return $filterDto;
|
||||
}
|
||||
|
||||
public function resetFilterSettings(): void
|
||||
{
|
||||
$this->getSession()->remove($this->namespace);
|
||||
}
|
||||
|
||||
public function getFilterSettings(): TeamerFilterDto
|
||||
{
|
||||
if (null === $data = $this->getSession()->get($this->namespace)) {
|
||||
return new TeamerFilterDto();
|
||||
}
|
||||
|
||||
$filterDto = new TeamerFilterDto();
|
||||
|
||||
$filterDto
|
||||
->setName($data['name'])
|
||||
->setDateFrom($data['date_from'])
|
||||
->setDateTo($data['date_to'])
|
||||
->setPickupId($data['pickup_id'])
|
||||
->setSkiLicense($data['ski_license'])
|
||||
->setSnowboardLicense($data['snowboard_license'])
|
||||
;
|
||||
|
||||
if (0 < (int) $data['job_profile']) {
|
||||
$jobProfile = $this
|
||||
->entityManager
|
||||
->getRepository(JobProfile::class)
|
||||
->find($data['job_profile'])
|
||||
;
|
||||
$filterDto->setJobProfile($jobProfile);
|
||||
}
|
||||
|
||||
return $filterDto;
|
||||
}
|
||||
|
||||
private function saveFilterSettings(TeamerFilterDto $filterDto): void
|
||||
{
|
||||
$this->getSession()->set($this->namespace, [
|
||||
'name' => $filterDto->getName(),
|
||||
'date_from' => $filterDto->getDateFrom(),
|
||||
'date_to' => $filterDto->getDateTo(),
|
||||
'pickup_id' => $filterDto->getPickupId(),
|
||||
'ski_license' => $filterDto->hasSkiLicense(),
|
||||
'snowboard_license' => $filterDto->hasSnowboardLicense(),
|
||||
'job_profile' => $filterDto->getJobProfile()?->getId(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function getSession(): SessionInterface
|
||||
{
|
||||
return $this->requestStack->getSession();
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,34 @@
|
||||
{% block title %}Teamerübersicht{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-2xl font-bold pb-8">
|
||||
Teamerübersicht
|
||||
</h1>
|
||||
<div class="flex items-start justify-between pb-4">
|
||||
<h1 class="text-2xl font-bold">
|
||||
Teamerübersicht
|
||||
</h1>
|
||||
<div class="flex flex-col items-end space-y-2 md:flex-row md:items-center md:space-x-2 md:space-y-0">
|
||||
<button type="button"
|
||||
class="{{ html_classes('btn btn--small', { 'btn--secondary': filterDto.active }) }}"
|
||||
{{ stimulus_controller('modal-button', [], [], {'ajax-modal': '#ajax-modal'}) }}
|
||||
{{ stimulus_action('modal-button', 'ajax', null, {
|
||||
'title': 'Teamer filtern',
|
||||
'url': path('app_common_teamer_filter', { 'r': return_url() })
|
||||
}) }}>
|
||||
<div class="flex items-center space-x-1">
|
||||
{{ icon('filter', 'w-4 h-4 shrink-0') }}
|
||||
{% if filterDto.active %}
|
||||
<span class="whitespace-nowrap">{{ filterDto.activeFiltersCount }} Filter aktiv</span>
|
||||
{% else %}
|
||||
<span>Filter</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</button>
|
||||
{% if filterDto.active %}
|
||||
<a href="{{ path('app_common_teamer_filter_reset', { 'r': return_url() }) }}" class="btn btn--small btn--secondary">
|
||||
Reset
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="data-table-wrapper">
|
||||
<div class="data-table-wrapper__inner">
|
||||
<table class="data-table">
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{{ form_start(filterForm) }}
|
||||
<div class="flex flex-col space-y-2 pb-2">
|
||||
{{ form_row(filterForm.name) }}
|
||||
{{ form_row(filterForm.dateFrom) }}
|
||||
{{ form_row(filterForm.dateTo) }}
|
||||
{{ form_row(filterForm.jobProfile) }}
|
||||
{{ form_row(filterForm.pickupId) }}
|
||||
{{ form_row(filterForm.skiLicense) }}
|
||||
{{ form_row(filterForm.snowboardLicense) }}
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
{{ form_widget(filterForm.apply, { 'attr': { 'class': 'btn' } }) }}
|
||||
{{ form_widget(filterForm.reset, { 'attr': { 'class': 'btn btn--secondary' } }) }}
|
||||
</div>
|
||||
{{ form_rest(filterForm) }}
|
||||
{{ form_end(filterForm) }}
|
||||
Reference in New Issue
Block a user