Skip to content

Commit

Permalink
Add redirect path Twig extension
Browse files Browse the repository at this point in the history
  • Loading branch information
loic425 committed Sep 18, 2024
1 parent 0539710 commit 40a2c58
Show file tree
Hide file tree
Showing 8 changed files with 379 additions and 0 deletions.
28 changes: 28 additions & 0 deletions src/TwigExtra/config/services/symfony.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Sylius\TwigExtra\Storage\FiltersStorageInterface;
use Sylius\TwigExtra\Symfony\Session\Storage\SessionFiltersStorage;

return function (ContainerConfigurator $configurator): void {
$services = $configurator->services();

$services->set('sylius_twig_extra.symfony.session.storage.filters', SessionFiltersStorage::class)
->args([
service('request_stack'),
])
;
$services->alias(FiltersStorageInterface::class, 'sylius_twig_extra.symfony.session.storage.filters');
};
9 changes: 9 additions & 0 deletions src/TwigExtra/config/services/twig.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Symfony\Component\DependencyInjection\Loader\Configurator;

use Sylius\TwigExtra\Twig\Extension\RedirectPathExtension;
use Sylius\TwigExtra\Twig\Extension\RouteExistsExtension;
use Sylius\TwigExtra\Twig\Extension\SortByExtension;
use Sylius\TwigExtra\Twig\Extension\TestFormAttributeExtension;
Expand Down Expand Up @@ -47,4 +48,12 @@
])
->tag(name: 'twig.extension')
;

$services->set('sylius_twig_extra.twig.extension.redirect_path', RedirectPathExtension::class)
->args([
service('sylius_twig_extra.symfony.session.storage.filters'),
service('router'),
])
->tag(name: 'twig.extension')
;
};
29 changes: 29 additions & 0 deletions src/TwigExtra/src/Storage/FiltersStorageInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\TwigExtra\Storage;

interface FiltersStorageInterface
{
/**
* @param mixed[] $filters
*/
public function set(array $filters): void;

/**
* @return mixed[]
*/
public function all(): array;

public function hasFilters(): bool;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\TwigExtra\Symfony\Session\Storage;

use Sylius\TwigExtra\Storage\FiltersStorageInterface;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\SessionInterface;

final class SessionFiltersStorage implements FiltersStorageInterface
{
public function __construct(private readonly RequestStack $requestStack)
{
}

public function set(array $filters): void
{
$this->getSession()->set('filters', $filters);
}

public function all(): array
{
return $this->getSession()->get('filters', []);
}

public function hasFilters(): bool
{
return [] !== $this->getSession()->get('filters', []);
}

private function getSession(): SessionInterface
{
return $this->requestStack->getSession();
}
}
69 changes: 69 additions & 0 deletions src/TwigExtra/src/Twig/Extension/RedirectPathExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Sylius\TwigExtra\Twig\Extension;

use Sylius\TwigExtra\Storage\FiltersStorageInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;

final class RedirectPathExtension extends AbstractExtension
{
private const NUMBER_OF_ROUTE_PROPERTIES = 3;

public function __construct(
private readonly FiltersStorageInterface $filterStorage,
private readonly RouterInterface $router,
) {
}

public function getFunctions(): array
{
return [
new TwigFunction('sylius_generate_redirect_path', [$this, 'generateRedirectPath']),
];
}

public function generateRedirectPath(?string $path): ?string
{
if (null === $path) {
return null;
}

$request = Request::create($path);

try {
$routeInfo = $this->router->match($request->getPathInfo());
} catch (\Throwable) {
return $path;
}

if ([] !== $request->query->all() || $this->hasAdditionalParameters($routeInfo)) {
return $path;
}

$route = $routeInfo['_route'];

return $this->router->generate($route, $this->filterStorage->all());
}

/**
* @param mixed[] $routeInfo
*/
private function hasAdditionalParameters(array $routeInfo): bool
{
return count($routeInfo) > self::NUMBER_OF_ROUTE_PROPERTIES;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Tests\Sylius\TwigExtra\Functional\Symfony\HttpFoundation\Storage;

use Sylius\TwigExtra\Symfony\HttpFoundation\Storage\SessionFiltersStorage;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

final class FilterStorageTest extends KernelTestCase
{
public function testTheContainerContainsTheService(): void
{
$this->bootKernel();

$container = $this->getContainer();

$this->assertTrue($container->has('sylius_twig_extra.symfony.http_foundation.storage.filter'));
$this->assertInstanceOf(SessionFiltersStorage::class, $container->get('sylius_twig_extra.symfony.http_foundation.storage.filter'));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Tests\Sylius\TwigExtra\Functional\Twig\Extension;

use Sylius\TwigExtra\Twig\Extension\RedirectPathExtension;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\Request;

final class RedirectPathExtensionTest extends KernelTestCase
{
private RedirectPathExtension $redirectPathExtension;

protected function setUp(): void
{
$container = self::getContainer();

$session = $container->get('session.factory')->createSession();
$request = new Request();
$request->setSession($session);
$container->get('request_stack')->push($request);

$this->redirectPathExtension = $container->get('sylius_twig_extra.twig.extension.redirect_path');
$container->get('sylius_twig_extra.symfony.session.storage.filters')->set(['criteria' => ['enabled' => true]]);
}

public function testItReturnsRedirectPathWithFiltersFromStorageApplied(): void
{
// Note, it requires an existing route path.
$redirectPath = $this->redirectPathExtension->generateRedirectPath('/admin/books');

$this->assertSame('/admin/books?criteria%5Benabled%5D=1', $redirectPath);
}

public function testItReturnsGivenPathIfRouteHasSomeMoreConfiguration(): void
{
$redirectPath = $this->redirectPathExtension->generateRedirectPath('/admin/ajax/products/search');

$this->assertSame('/admin/ajax/products/search', $redirectPath);
}

public function testItReturnsGivenPathIfRouteIsNotMatched(): void
{
$redirectPath = $this->redirectPathExtension->generateRedirectPath('/admin/invalid-path');

$this->assertSame('/admin/invalid-path', $redirectPath);
}

public function testItReturnsNullIfThePathIsNullAsWell(): void
{
$redirectPath = $this->redirectPathExtension->generateRedirectPath(null);

$this->assertNull($redirectPath);
}
}
104 changes: 104 additions & 0 deletions src/TwigExtra/tests/Unit/Symfony/Session/SessionFilterStorageTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

/*
* This file is part of the Sylius package.
*
* (c) Sylius Sp. z o.o.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare(strict_types=1);

namespace Tests\Sylius\TwigExtra\Unit\Symfony\Session;

use PHPUnit\Framework\TestCase;
use Sylius\TwigExtra\Storage\FiltersStorageInterface;
use Sylius\TwigExtra\Symfony\Session\Storage\SessionFiltersStorage;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;

final class SessionFilterStorageTest extends TestCase
{
public function testItImplementsFilterStorageInterface(): void
{
$this->assertInstanceOf(FiltersStorageInterface::class, new SessionFiltersStorage(new RequestStack()));
}

public function testItSetsFiltersInASession(): void
{
$filters = [
'filter' => 'value',
];

$requestStack = new RequestStack();
$request = new Request();
$session = new Session(new MockArraySessionStorage());

$request->setSession($session);
$requestStack->push($request);

$filterStorage = new SessionFiltersStorage($requestStack);
$filterStorage->set($filters);

$this->assertEquals($filters, $session->get('filters'));
}

public function testItReturnsAllFiltersFromASession(): void
{
$filters = [
'filter' => 'value',
];

$requestStack = new RequestStack();
$request = new Request();
$session = new Session(new MockArraySessionStorage());

$session->set('filters', $filters);
$request->setSession($session);
$requestStack->push($request);

$filterStorage = new SessionFiltersStorage($requestStack);

$this->assertEquals($filters, $filterStorage->all());
}

public function testItReturnsTrueIfFiltersAreSet(): void
{
$filters = [
'filter' => 'value',
];

$requestStack = new RequestStack();
$request = new Request();
$session = new Session(new MockArraySessionStorage());

$session->set('filters', $filters);
$request->setSession($session);
$requestStack->push($request);

$filterStorage = new SessionFiltersStorage($requestStack);

$this->assertTrue($filterStorage->hasFilters());
}

public function testItReturnsFalseIfFiltersAreSet(): void
{
$filters = [];

$requestStack = new RequestStack();
$request = new Request();
$session = new Session(new MockArraySessionStorage());

$session->set('filters', $filters);
$request->setSession($session);
$requestStack->push($request);

$filterStorage = new SessionFiltersStorage($requestStack);

$this->assertFalse($filterStorage->hasFilters());
}
}

0 comments on commit 40a2c58

Please sign in to comment.