Skip to content

Commit 9821223

Browse files
authored
Merge pull request #55 from delyriand/feature/same-slug-multiple-pages
Allows 2 pages to have the same slug depending on channel and locale
2 parents 98d14bb + 18a83fc commit 9821223

File tree

11 files changed

+161
-23
lines changed

11 files changed

+161
-23
lines changed

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@
7272
"dealerdirect/phpcodesniffer-composer-installer": true,
7373
"symfony/thanks": true,
7474
"ergebnis/composer-normalize": true,
75-
"symfony/flex": true
75+
"symfony/flex": true,
76+
"php-http/discovery": true
7677
}
7778
}
7879
}

src/Repository/PageRepository.php

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
namespace MonsieurBiz\SyliusCmsPagePlugin\Repository;
1515

1616
use Doctrine\ORM\NonUniqueResultException;
17-
use Doctrine\ORM\NoResultException;
1817
use Doctrine\ORM\QueryBuilder;
1918
use MonsieurBiz\SyliusCmsPagePlugin\Entity\PageInterface;
2019
use Sylius\Bundle\ResourceBundle\Doctrine\ORM\EntityRepository;
@@ -31,22 +30,32 @@ public function createListQueryBuilder(string $localeCode): QueryBuilder
3130
;
3231
}
3332

34-
/**
35-
* @throws NoResultException
36-
* @throws NonUniqueResultException
37-
*/
38-
public function existsOneByChannelAndSlug(ChannelInterface $channel, ?string $locale, string $slug): bool
33+
public function existsOneByChannelAndSlug(ChannelInterface $channel, ?string $locale, string $slug, array $excludedPages = []): bool
3934
{
40-
$count = (int) $this
41-
->createQueryBuilder('p')
42-
->select('COUNT(p.id)')
43-
->innerJoin('p.translations', 'translation', 'WITH', 'translation.locale = :locale')
44-
->andWhere('translation.slug = :slug')
45-
->andWhere(':channel MEMBER OF p.channels')
35+
$queryBuilder = $this->createQueryBuilderExistOne($channel, $locale, $slug);
36+
if (!empty($excludedPages)) {
37+
$queryBuilder
38+
->andWhere('p.id NOT IN (:excludedPages)')
39+
->setParameter('excludedPages', $excludedPages)
40+
;
41+
}
42+
43+
$count = (int) $queryBuilder
44+
->getQuery()
45+
->getSingleScalarResult()
46+
;
47+
48+
return $count > 0;
49+
}
50+
51+
public function existsOneEnabledByChannelAndSlug(ChannelInterface $channel, ?string $locale, string $slug): bool
52+
{
53+
$queryBuilder = $this->createQueryBuilderExistOne($channel, $locale, $slug);
54+
$queryBuilder
4655
->andWhere('p.enabled = true')
47-
->setParameter('channel', $channel)
48-
->setParameter('locale', $locale)
49-
->setParameter('slug', $slug)
56+
;
57+
58+
$count = (int) $queryBuilder
5059
->getQuery()
5160
->getSingleScalarResult()
5261
;
@@ -73,4 +82,19 @@ public function findOneEnabledBySlugAndChannelCode(string $slug, string $localeC
7382
->getOneOrNullResult()
7483
;
7584
}
85+
86+
private function createQueryBuilderExistOne(ChannelInterface $channel, ?string $locale, string $slug): QueryBuilder
87+
{
88+
return $this
89+
->createQueryBuilder('p')
90+
->select('COUNT(p.id)')
91+
->innerJoin('p.translations', 'translation', 'WITH', 'translation.locale = :locale')
92+
->andWhere('translation.slug = :slug')
93+
->andWhere(':channel MEMBER OF p.channels')
94+
->andWhere('p.enabled = true')
95+
->setParameter('channel', $channel)
96+
->setParameter('locale', $locale)
97+
->setParameter('slug', $slug)
98+
;
99+
}
76100
}

src/Repository/PageRepositoryInterface.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ interface PageRepositoryInterface extends RepositoryInterface
2222
{
2323
public function createListQueryBuilder(string $localeCode): QueryBuilder;
2424

25-
public function existsOneByChannelAndSlug(ChannelInterface $channel, ?string $locale, string $slug): bool;
25+
public function existsOneByChannelAndSlug(ChannelInterface $channel, ?string $locale, string $slug, array $excludedPages = []): bool;
26+
27+
public function existsOneEnabledByChannelAndSlug(ChannelInterface $channel, ?string $locale, string $slug): bool;
2628

2729
public function findOneEnabledBySlugAndChannelCode(string $slug, string $localeCode, string $channelCode): ?PageInterface;
2830
}

src/Resources/config/sylius/grid.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ sylius_grid:
2121
type: string
2222
label: monsieurbiz_cms_page.ui.form.title
2323
sortable: translation.title
24+
channels:
25+
type: twig
26+
label: sylius.ui.channels
27+
options:
28+
template: '@SyliusAdmin/Grid/Field/_channels.html.twig'
2429
enabled:
2530
type: twig
2631
label: monsieurbiz_cms_page.ui.form.enabled
@@ -56,3 +61,10 @@ sylius_grid:
5661
label: monsieurbiz_cms_page.ui.form.content
5762
options:
5863
fields: [translation.content]
64+
channel:
65+
type: entity
66+
label: sylius.ui.channel
67+
options:
68+
fields: [channels.id]
69+
form_options:
70+
class: "%sylius.model.channel.class%"

src/Resources/config/validation/Page.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ MonsieurBiz\SyliusCmsPagePlugin\Entity\Page:
33
- Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
44
fields: [code]
55
groups: [monsieurbiz]
6+
- MonsieurBiz\SyliusCmsPagePlugin\Validator\Constraints\UniqueSlugByChannel:
7+
groups: [monsieurbiz]
68
properties:
79
code:
810
- NotBlank:

src/Resources/config/validation/PageTranslation.yaml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
MonsieurBiz\SyliusCmsPagePlugin\Entity\PageTranslation:
2-
constraints:
3-
- Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity:
4-
fields: [slug, locale]
5-
errorPath: slug
6-
groups: [monsieurbiz]
72
properties:
83
title:
94
- NotBlank:
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
monsieurbiz_cms_page:
2+
ui:
3+
slug:
4+
unique: 'This slug is already used for another page with channel %channel% and locale %locale%.'
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
monsieurbiz_cms_page:
2+
ui:
3+
slug:
4+
unique: 'Ce slug est déjà utilisé pour une autre page avec le canal %channel% et la locale %locale%.'

src/Routing/PageSlugConditionChecker.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public function __construct(
5151
public function isPageSlug(string $slug): bool
5252
{
5353
try {
54-
return $this->pageRepository->existsOneByChannelAndSlug(
54+
return $this->pageRepository->existsOneEnabledByChannelAndSlug(
5555
$this->channelContext->getChannel(),
5656
$this->localeContext->getLocaleCode(),
5757
$slug
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Monsieur Biz' Cms Page plugin for Sylius.
5+
*
6+
* (c) Monsieur Biz <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE.txt
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace MonsieurBiz\SyliusCmsPagePlugin\Validator\Constraints;
15+
16+
use Symfony\Component\Validator\Constraint;
17+
18+
final class UniqueSlugByChannel extends Constraint
19+
{
20+
public string $message = 'monsieurbiz_cms_page.ui.slug.unique';
21+
22+
public function validatedBy(): string
23+
{
24+
return self::class . 'Validator';
25+
}
26+
27+
public function getTargets()
28+
{
29+
return self::CLASS_CONSTRAINT;
30+
}
31+
}

0 commit comments

Comments
 (0)