Skip to content

Commit 0eb6d1b

Browse files
committed
Add package list filters
1 parent 72d89ac commit 0eb6d1b

5 files changed

Lines changed: 106 additions & 14 deletions

File tree

src/Controller/Dashboard/DashboardPackagesController.php

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
use CodedMonkey\Dirigent\Attribute\IsGrantedAccess;
66
use CodedMonkey\Dirigent\Doctrine\Entity\Package;
77
use CodedMonkey\Dirigent\Doctrine\Entity\PackageFetchStrategy;
8+
use CodedMonkey\Dirigent\Doctrine\Entity\Registry;
89
use CodedMonkey\Dirigent\Doctrine\Repository\PackageRepository;
10+
use CodedMonkey\Dirigent\Doctrine\Repository\RegistryRepository;
911
use CodedMonkey\Dirigent\EasyAdmin\PackagePaginator;
1012
use CodedMonkey\Dirigent\Form\PackageAddMirroringFormType;
1113
use CodedMonkey\Dirigent\Form\PackageAddVcsFormType;
@@ -19,6 +21,7 @@
1921
use Symfony\Component\Messenger\MessageBusInterface;
2022
use Symfony\Component\Routing\Attribute\Route;
2123
use Symfony\Component\Security\Http\Attribute\IsGranted;
24+
use function Symfony\Component\String\u;
2225

2326
class DashboardPackagesController extends AbstractController
2427
{
@@ -32,19 +35,46 @@ public function __construct(
3235

3336
#[Route('/packages', name: 'dashboard_packages')]
3437
#[IsGrantedAccess]
35-
public function list(Request $request): Response
38+
public function list(Request $request, RegistryRepository $registryRepository): Response
3639
{
3740
$queryBuilder = $this->packageRepository->createQueryBuilder('package');
41+
$filtersActive = false;
42+
43+
if ($request->query->has('query')) {
44+
$filtersActive = true;
45+
$searchQuery = $request->query->getString('query');
46+
$searchQuery = u($searchQuery)->lower();
3847

39-
if (null !== $query = $request->query->get('query')) {
4048
$queryBuilder->andWhere($queryBuilder->expr()->like('package.name', ':query'));
41-
$queryBuilder->setParameter('query', "%{$query}%");
49+
$queryBuilder->setParameter('query', "%{$searchQuery}%");
50+
}
51+
52+
if ($request->query->has('registry')) {
53+
$registryId = $request->query->getInt('registry');
54+
55+
if (0 === $registryId) {
56+
$filtersActive = true;
57+
$selectedRegistry = 'local';
58+
59+
$queryBuilder->andWhere($queryBuilder->expr()->isNull('package.mirrorRegistry'));
60+
} elseif (null !== $selectedRegistry = $registryRepository->find($registryId)) {
61+
$filtersActive = true;
62+
63+
$queryBuilder->andWhere($queryBuilder->expr()->eq('package.mirrorRegistry', ':registry'));
64+
$queryBuilder->setParameter('registry', $selectedRegistry);
65+
}
4266
}
4367

4468
$paginator = PackagePaginator::fromRequest($request, $queryBuilder, $this->container->get('router'));
4569

70+
$registries = $this->entityManager->getRepository(Registry::class)->findAll();
71+
4672
return $this->render('dashboard/packages/list.html.twig', [
73+
'filtersActive' => $filtersActive,
4774
'paginator' => $paginator,
75+
'registries' => $registries,
76+
'searchQuery' => $searchQuery ?? null,
77+
'selectedRegistry' => $selectedRegistry ?? null,
4878
]);
4979
}
5080

src/Twig/PackageExtension.php

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,43 @@
55
use CodedMonkey\Dirigent\Doctrine\Entity\Package;
66
use CodedMonkey\Dirigent\Doctrine\Repository\PackageRepository;
77
use Composer\Pcre\Preg;
8+
use Symfony\Component\HttpFoundation\RequestStack;
9+
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
10+
use Twig\Attribute\AsTwigFunction;
11+
use Twig\Attribute\AsTwigTest;
812
use Twig\Extension\AbstractExtension;
913
use Twig\TwigTest;
1014

11-
class PackageExtension extends AbstractExtension
15+
readonly class PackageExtension
1216
{
1317
public function __construct(
14-
private readonly PackageRepository $packageRepository,
18+
private PackageRepository $packageRepository,
19+
private UrlGeneratorInterface $router,
20+
private RequestStack $requestStack,
1521
) {
1622
}
1723

18-
public function getTests(): array
24+
#[AsTwigFunction('packageFilterParameters')]
25+
public function getFilterParameters(string $parameter, string|int|null $value): array
1926
{
20-
return [
21-
new TwigTest('existing_package', [$this, 'packageExistsTest']),
27+
$request = $this->requestStack->getCurrentRequest();
28+
$filters = [
29+
'query' => $request->query->get('query'),
30+
'registry' => $request->query->get('registry'),
2231
];
32+
33+
$filters[$parameter] = $value;
34+
35+
return array_filter($filters, fn ($value) => null !== $value);
36+
}
37+
38+
#[AsTwigFunction('packageFilterUrl')]
39+
public function createFilterUrl(string $parameter, string|int|null $value): string
40+
{
41+
return $this->router->generate('dashboard_packages', $this->getFilterParameters($parameter, $value));
2342
}
2443

44+
#[AsTwigTest('existing_package')]
2545
public function packageExistsTest(mixed $package): bool
2646
{
2747
if ($package instanceof Package) {

templates/dashboard/packages/_search_box.html.twig

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,22 @@
77
<label class="content-search-label">
88
<input
99
name="query"
10-
class="form-control {{ app.request.get('query') is null ? 'is-blank' }}"
10+
class="form-control {{ searchQuery is not defined ? 'is-blank' }}"
1111
type="search"
12-
value="{{ app.request.get('query') ?? '' }}"
12+
value="{{ searchQuery ?? '' }}"
1313
placeholder="{{ 'Search packages'|trans }}"
1414
spellcheck="false"
1515
autocorrect="off"
1616
{% if autofocus ?? true %}autofocus="autofocus"{% endif %}
1717
>
1818
</label>
1919

20-
{% if app.request.get('query') %}
21-
{% set clearRouteParameters = app.request.get('_route_params')|filter((value, key) => key != 'query' and key != 'page') %}
22-
<a href="{{ path('dashboard_packages', clearRouteParameters) }}" class="content-search-reset">
20+
{% for parameter, value in packageFilterParameters('query', null) %}
21+
<input type="hidden" name="{{ parameter }}" value="{{ value }}">
22+
{% endfor %}
23+
24+
{% if searchQuery is defined and searchQuery is not null %}
25+
<a href="{{ packageFilterUrl('query', null) }}" class="content-search-reset">
2326
<i class="fas fa-fw fa-times"></i>
2427
</a>
2528
{% endif %}

templates/dashboard/packages/list.html.twig

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{% block page_title %}Packages{% endblock %}
44

55
{% block search_wrapper %}
6-
{{ include('dashboard/packages/_search_box.html.twig', with_context: false) }}
6+
{{ include('dashboard/packages/_search_box.html.twig', {searchQuery: searchQuery}, with_context: false) }}
77
{% endblock %}
88

99
{% block page_actions %}
@@ -25,5 +25,38 @@
2525
{% endblock %}
2626

2727
{% block page_content %}
28+
<div class="d-flex align-items-center gap-2 mb-3">
29+
<div>{{ 'Filter by'|trans }}</div>
30+
{% if registries|length %}
31+
<div>
32+
<div class="dropdown">
33+
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
34+
{{ selectedRegistry ? (selectedRegistry == 'local' ? ('Local'|trans) : selectedRegistry.name) : ('All registries'|trans) }}
35+
</button>
36+
<ul class="dropdown-menu">
37+
{{ _self.registryFilterItem(selectedRegistry, null, 'All registries'|trans) }}
38+
{{ _self.registryFilterItem(selectedRegistry, 0, 'Local'|trans) }}
39+
{% for registry in registries %}
40+
{{ _self.registryFilterItem(selectedRegistry, registry.id, registry.name) }}
41+
{% endfor %}
42+
</ul>
43+
</div>
44+
</div>
45+
{% endif %}
46+
{% if filtersActive %}
47+
<div>
48+
<a class="btn btn-secondary" href="{{ path('dashboard_packages') }}">{{ 'Clear filters'|trans }}</a>
49+
</div>
50+
{% endif %}
51+
</div>
52+
2853
{{ include('dashboard/packages/_package_list.html.twig', {paginator: paginator}, with_context: false) }}
2954
{% endblock %}
55+
56+
{% macro registryFilterItem(selectedRegistry, registry, label) %}
57+
{% if registry is same as(selectedRegistry) %}
58+
<li><span class="dropdown-item active">{{ label }}</span></li>
59+
{% else %}
60+
<li><a class="dropdown-item" href="{{ packageFilterUrl('registry', registry) }}">{{ label }}</a></li>
61+
{% endif %}
62+
{% endmacro %}

translations/messages.en.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ User: User
2525
Users: Users
2626
Versions: Versions
2727

28+
# Filter labels
29+
All registries: All registries
30+
Clear filters: Clear filters
31+
Filter by: Filter by
32+
Local: Local
33+
2834
# Form labels
2935
Current password: Current password
3036
Description: Description

0 commit comments

Comments
 (0)