From 3caecb59f68121bd386d81b07fdadcbbf5487b23 Mon Sep 17 00:00:00 2001 From: Dmitrii Derepko Date: Mon, 24 Jul 2023 01:31:22 +0300 Subject: [PATCH] Add redirect function --- README.md | 19 ++++++++++++ src/response.php | 36 +++++++++++++++++++++++ tests/ContainerTest.php | 2 +- tests/ResponseTest.php | 65 +++++++++++++++++++++++++++++++++++++++-- tests/RouterTest.php | 2 +- 5 files changed, 119 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0c37e60..c5ace0e 100644 --- a/README.md +++ b/README.md @@ -74,4 +74,23 @@ response('Hello world', 201, 'Created'); // => A response object with body 'Hell response('Hello world', 201, 'Created', ['X-My-Header' => 'My value']); // => A response object with body 'Hello world', code 201, status 'Created' and header 'X-My-Header' with value 'My value' response(['message' => 'Hello world']); // => A response object with body '{"message":"Hello world"}' and header 'Content-Type' with value 'application/json' +``` + +### `redirect(string $name, array $parameters = [], array $query = [], int $code = Status::TEMPORARY_REDIRECT, bool $absolute = false): \Psr\Http\Message\ResponseInterface` + +- `$name` is a route name or an absolute url if `$absolute` is `true` +- `$parameters` is a route parameters. Used only if `$absolute` is `false` +- `$query` is a query parameters +- `$code` is a response code +- `$absolute` is a flag to generate absolute url, default is `false` + +```php +// Route name 'site/index' is bound to '/index' +redirect('site/index'); // => A response object with code 307 and header 'Location' with value '/index' +redirect('site/index', ['page' => 2]); // => A response object with code 307 and header 'Location' with value '/index/2' +redirect('site/index', [], ['page' => 2]); // => A response object with code 307 and header 'Location' with value '/index?page=2' +redirect('site/index', [], ['page' => 2], Status::PERMANENT_REDIRECT); // => A response object with code 308 and header 'Location' with value '/index?page=2' + +// Generating absolute url +redirect('/path/to/redirect', [], ['page' => 2], Status::PERMANENT_REDIRECT, true); // => A response object with code 308 and header 'Location' with value 'http://localhost/path/to/redirect?page=2' ``` \ No newline at end of file diff --git a/src/response.php b/src/response.php index e32bd91..4306bdd 100644 --- a/src/response.php +++ b/src/response.php @@ -7,6 +7,8 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\StreamInterface; use Yiisoft\DataResponse\DataResponseFactoryInterface; +use Yiisoft\Http\Header; +use Yiisoft\Http\Status; function response( int|null|string|array|StreamInterface $body, @@ -48,3 +50,37 @@ function response( return $response; } + +function redirect( + string $name, + array $parameters = [], + array $query = [], + int $code = Status::TEMPORARY_REDIRECT, + bool $absolute = false +): ResponseInterface { + /** + * @var ResponseFactoryInterface $responseFactory + */ + $responseFactory = container(ResponseFactoryInterface::class); + + if ($absolute) { + if ($query) { + $url = sprintf('%s?%s', $name, http_build_query($query)); + } else { + $url = $name; + } + } else { + $url = route($name, $parameters, $query); + } + + return $responseFactory + ->createResponse( + $code, + Status::TEXTS[$code] ?? Status::TEXTS[Status::TEMPORARY_REDIRECT] + ) + ->withHeader( + Header::LOCATION, + $url + ); +} + diff --git a/tests/ContainerTest.php b/tests/ContainerTest.php index 358584c..40afa9d 100644 --- a/tests/ContainerTest.php +++ b/tests/ContainerTest.php @@ -9,7 +9,7 @@ class ContainerTest extends FunctionsTestCase { - public function testContainer() + public function testFunctionLoaded() { $this->assertTrue(function_exists('container')); } diff --git a/tests/ResponseTest.php b/tests/ResponseTest.php index 514b181..586f6d6 100644 --- a/tests/ResponseTest.php +++ b/tests/ResponseTest.php @@ -9,21 +9,37 @@ use PHPUnit\Framework\Attributes\DataProvider; use Psr\Http\Message\ResponseFactoryInterface; use Xepozz\YiiShort\State; +use Yiisoft\Http\Header; +use Yiisoft\Http\Status; +use Yiisoft\Router\FastRoute\UrlGenerator; +use Yiisoft\Router\Route; +use Yiisoft\Router\RouteCollection; +use Yiisoft\Router\RouteCollectionInterface; +use Yiisoft\Router\RouteCollector; +use Yiisoft\Router\UrlGeneratorInterface; use Yiisoft\Test\Support\Container\SimpleContainer; class ResponseTest extends FunctionsTestCase { - public function testContainer() + public function testFunctionLoaded() { $this->assertTrue(function_exists('response')); + $this->assertTrue(function_exists('redirect')); } - public function testContainerIsUnset() + #[DataProvider('dataContainerIsUnset')] + public function testContainerIsUnset(callable $callback) { State::$container = null; $this->expectException(\RuntimeException::class); - response('test'); + $callback(); + } + + public static function dataContainerIsUnset(): iterable + { + yield 'response' => [fn () => response('test')]; + yield 'redirect' => [fn () => redirect('name')]; } #[DataProvider('dataResponseBody')] @@ -96,9 +112,52 @@ public function testResponseHeaders() $this->assertEquals('test', $body->getContents()); } + public function testRedirectRoute() + { + $routeCollector = new RouteCollector(); + $routeCollection = new RouteCollection( + $routeCollector + ->addRoute(Route::get('/redirect')->name('test')) + ->addRoute(Route::get('/redirect/{id}')->name('test-id')) + ); + State::$container = new SimpleContainer([ + ResponseFactoryInterface::class => new ResponseFactory(), + RouteCollectionInterface::class => $routeCollection, + UrlGeneratorInterface::class => new UrlGenerator($routeCollection), + ]); + + $response = redirect('test'); + $this->assertEquals(Status::TEMPORARY_REDIRECT, $response->getStatusCode()); + $this->assertEquals('/redirect', $response->getHeaderLine(Header::LOCATION)); + + $response = redirect('test-id', ['id' => 123]); + $this->assertEquals(Status::TEMPORARY_REDIRECT, $response->getStatusCode()); + $this->assertEquals('/redirect/123', $response->getHeaderLine(Header::LOCATION)); + + $response = redirect('test-id', ['id' => 123], ['k' => 'v']); + $this->assertEquals(Status::TEMPORARY_REDIRECT, $response->getStatusCode()); + $this->assertEquals('/redirect/123?k=v', $response->getHeaderLine(Header::LOCATION)); + } + + public function testRedirectAbsolute() + { + State::$container = new SimpleContainer([ + ResponseFactoryInterface::class => new ResponseFactory(), + ]); + + $response = redirect('/path/to/redirect', [], [], Status::TEMPORARY_REDIRECT, true); + $this->assertEquals(Status::TEMPORARY_REDIRECT, $response->getStatusCode()); + $this->assertEquals('/path/to/redirect', $response->getHeaderLine(Header::LOCATION)); + + $response = redirect('/path/to/redirect', [], ['k' => 'v'], Status::TEMPORARY_REDIRECT, true); + $this->assertEquals(Status::TEMPORARY_REDIRECT, $response->getStatusCode()); + $this->assertEquals('/path/to/redirect?k=v', $response->getHeaderLine(Header::LOCATION)); + } + public function bootstrapFiles(): iterable { yield __DIR__ . '/../src/container.php'; + yield __DIR__ . '/../src/router.php'; yield __DIR__ . '/../src/response.php'; } } \ No newline at end of file diff --git a/tests/RouterTest.php b/tests/RouterTest.php index f9d481d..e9dc446 100644 --- a/tests/RouterTest.php +++ b/tests/RouterTest.php @@ -15,7 +15,7 @@ class RouterTest extends FunctionsTestCase { - public function testContainer() + public function testFunctionLoaded() { $this->assertTrue(function_exists('route')); }