Skip to content

Commit

Permalink
Merge pull request #7 from sheridans/5.5.x
Browse files Browse the repository at this point in the history
Add reuse_query_params to UrlHelper to allow reuse of existing request query params
  • Loading branch information
weierophinney committed Apr 20, 2021
2 parents 1acb60a + 2a32eab commit 3ceaaa5
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 3 deletions.
59 changes: 58 additions & 1 deletion src/UrlHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
use InvalidArgumentException;
use Mezzio\Router\RouteResult;
use Mezzio\Router\RouterInterface;
use Psr\Http\Message\ServerRequestInterface;

use function array_merge;
use function count;
Expand Down Expand Up @@ -40,6 +41,11 @@ class UrlHelper
*/
private $result;

/**
* @var ServerRequestInterface
*/
private $request;

/**
* @var RouterInterface
*/
Expand Down Expand Up @@ -102,6 +108,13 @@ public function __invoke(
$routeParams = $this->mergeParams($routeName, $result, $routeParams);
}

$reuseQueryParams = ! isset($options['reuse_query_params']) || (bool) $options['reuse_query_params'];

if ($result && $reuseQueryParams) {
// Merge current request params with passed query params
$queryParams = $this->mergeQueryParams($routeName, $result, $queryParams);
}

// Generate the route
$path = $basePath . $this->router->generateUri($routeName, $routeParams, $routerOptions);

Expand Down Expand Up @@ -153,6 +166,19 @@ public function getRouteResult() : ?RouteResult
return $this->result;
}

/**
* Set request instance
*/
public function setRequest(ServerRequestInterface $request) : void
{
$this->request = $request;
}

public function getRequest() : ?ServerRequestInterface
{
return $this->request;
}

/**
* Internal accessor for retrieving the base path.
*/
Expand Down Expand Up @@ -190,7 +216,9 @@ private function generateUriFromResult(array $params, RouteResult $result, array
* invocation, with the latter having precedence.
*
* @param string $route Route name.
* @param array $params Parameters provided at invocation.
* @param RouteResult $result RouteResult instance
* @param array $params Route parameters
* @return array Merged parameters
*/
private function mergeParams(string $route, RouteResult $result, array $params) : array
{
Expand All @@ -205,6 +233,35 @@ private function mergeParams(string $route, RouteResult $result, array $params)
return array_merge($result->getMatchedParams(), $params);
}

/**
* Merge requested route query params with existing request query parameters.
*
* If route result represents routing failure, returns the params verbatim
*
* If the route result does not represent the same route name requested,
* returns the params verbatim.
*
* Otherwise, merges the current request query parameters with the specified query
* parameters with the latter having precedence.
*
* @param string $route Route name
* @param RouteResult $result RouteResult instance
* @param array $params Params to be merged with request params
* @return array
*/
private function mergeQueryParams(string $route, RouteResult $result, array $params) : array
{
if ($result->isFailure()) {
return $params;
}

if ($result->getMatchedRouteName() !== $route) {
return $params;
}

return array_merge($this->getRequest()->getQueryParams(), $params);
}

/**
* Append query string arguments to a URI string, if any are present.
*/
Expand Down
4 changes: 4 additions & 0 deletions src/UrlHelperMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,15 @@ public function __construct(UrlHelper $helper)
}

/**
* Inject the helper with the request instance.
*
* Inject the UrlHelper instance with a RouteResult, if present as a request attribute.
* Injects the helper, and then dispatches the next middleware.
*/
public function process(ServerRequestInterface $request, RequestHandlerInterface $handler) : ResponseInterface
{
$this->helper->setRequest($request);

$result = $request->getAttribute(RouteResult::class, false);

if ($result instanceof RouteResult) {
Expand Down
2 changes: 2 additions & 0 deletions test/UrlHelperMiddlewareTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ public function testInvocationInjectsHelperWithRouteResultWhenPresentInRequest()
$request = $this->prophesize(ServerRequestInterface::class);
$request->getAttribute(RouteResult::class, false)->willReturn($routeResult);
$this->helper->setRouteResult($routeResult)->shouldBeCalled();
$this->helper->setRequest($request)->shouldBeCalled();

$handler = $this->prophesize(RequestHandlerInterface::class);
$handler->handle(Argument::type(ServerRequestInterface::class))->will([$response, 'reveal']);
Expand All @@ -65,6 +66,7 @@ public function testInvocationDoesNotInjectHelperWithRouteResultWhenAbsentInRequ

$request = $this->prophesize(ServerRequestInterface::class);
$request->getAttribute(RouteResult::class, false)->willReturn(false);
$this->helper->setRequest($request)->shouldBeCalled();
$this->helper->setRouteResult(Argument::any())->shouldNotBeCalled();

$handler = $this->prophesize(RequestHandlerInterface::class);
Expand Down
87 changes: 85 additions & 2 deletions test/UrlHelperTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
use PHPUnit\Framework\TestCase;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use ReflectionProperty;
use stdClass;
use TypeError;
Expand All @@ -43,7 +45,12 @@ public function setUp(): void

public function createHelper()
{
return new UrlHelper($this->router->reveal());
$request = $this->prophesize(ServerRequestInterface::class);
$request->getQueryParams()->willReturn([]);

$helper = new UrlHelper($this->router->reveal());
$helper->setRequest($request->reveal());
return $helper;
}

public function testRaisesExceptionOnInvocationIfNoRouteProvidedAndNoResultPresent()
Expand Down Expand Up @@ -231,7 +238,7 @@ public function testGenerateProxiesToInvokeMethod()
$fragmentIdentifier = 'foobar';
$options = ['router' => ['foobar' => 'baz'], 'reuse_result_params' => false];

$helper = Mockery::mock(UrlHelper::class)->shouldDeferMissing();
$helper = Mockery::mock(UrlHelper::class)->makePartial();
$helper->shouldReceive('__invoke')
->once()
->with($routeName, $routeParams, $queryParams, $fragmentIdentifier, $options)
Expand Down Expand Up @@ -398,6 +405,82 @@ public function testGetRouteResultWithRouteResultSet()
$this->assertInstanceOf(RouteResult::class, $helper->getRouteResult());
}

public function testWillNotReuseQueryParamsIfReuseQueryParamsFlagIsFalseWhenGeneratingUri()
{
$result = $this->prophesize(RouteResult::class);
$result->isFailure()->willReturn(false);
$result->getMatchedRouteName()->willReturn('resource');
$result->getMatchedParams()->willReturn([]);

$this->router->generateUri('resource', [], [])->willReturn('URL');

$request = $this->prophesize(ServerRequestInterface::class);
$request->getQueryParams()->wilLReturn(['foo' => 'bar']);

$helper = $this->createHelper();
$helper->setRouteResult($result->reveal());
$helper->setRequest($request->reveal());

$this->assertEquals('URL', $helper('resource', [], [], null, ['reuse_query_params' => false]));
}

public function testWillReuseQueryParamsIfReuseQueryParamsFlagIsTrueWhenGeneratingUri()
{
$result = $this->prophesize(RouteResult::class);
$result->isFailure()->willReturn(false);
$result->getMatchedRouteName()->willReturn('resource');
$result->getMatchedParams()->willReturn([]);

$this->router->generateUri('resource', [], [])->willReturn('URL');

$request = $this->prophesize(ServerRequestInterface::class);
$request->getQueryParams()->wilLReturn(['foo' => 'bar']);

$helper = $this->createHelper();
$helper->setRouteResult($result->reveal());
$helper->setRequest($request->reveal());

$this->assertEquals('URL?foo=bar', $helper('resource', [], [], null, ['reuse_query_params' => true]));
}

public function testWillReuseQueryParamsIfReuseQueryParamsFlagIsMissingGeneratingUri()
{
$result = $this->prophesize(RouteResult::class);
$result->isFailure()->willReturn(false);
$result->getMatchedRouteName()->willReturn('resource');
$result->getMatchedParams()->willReturn([]);

$this->router->generateUri('resource', [], [])->willReturn('URL');

$request = $this->prophesize(ServerRequestInterface::class);
$request->getQueryParams()->wilLReturn(['foo' => 'bar']);

$helper = $this->createHelper();
$helper->setRouteResult($result->reveal());
$helper->setRequest($request->reveal());

$this->assertEquals('URL?foo=bar', $helper('resource'));
}

public function testCanOverrideRequestQueryParams()
{
$result = $this->prophesize(RouteResult::class);
$result->isFailure()->willReturn(false);
$result->getMatchedRouteName()->willReturn('resource');
$result->getMatchedParams()->willReturn([]);

$this->router->generateUri('resource', [], [])->willReturn('URL');

$request = $this->prophesize(ServerRequestInterface::class);
$request->getQueryParams()->wilLReturn(['foo' => 'bar']);

$helper = $this->createHelper();
$helper->setRouteResult($result->reveal());
$helper->setRequest($request->reveal());

$this->assertEquals('URL?foo=foo', $helper('resource', [], ['foo' => 'foo']));
}

private function assertAttributeSame($expected, $attribute, $object)
{
$r = new ReflectionProperty($object, $attribute);
Expand Down

0 comments on commit 3ceaaa5

Please sign in to comment.