Skip to content

Commit

Permalink
Add redirect function
Browse files Browse the repository at this point in the history
  • Loading branch information
xepozz committed Jul 23, 2023
1 parent d92e562 commit 3caecb5
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 5 deletions.
19 changes: 19 additions & 0 deletions README.md
Expand Up @@ -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'
```
36 changes: 36 additions & 0 deletions src/response.php
Expand Up @@ -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,
Expand Down Expand Up @@ -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

Check failure on line 62 in src/response.php

View workflow job for this annotation

GitHub Actions / PHP 8.1-ubuntu-latest

UnnecessaryVarAnnotation

src/response.php:62:13: UnnecessaryVarAnnotation: The @var Psr\Http\Message\ResponseFactoryInterface annotation for $responseFactory is unnecessary (see https://psalm.dev/212)

Check failure on line 62 in src/response.php

View workflow job for this annotation

GitHub Actions / PHP 8.2-ubuntu-latest

UnnecessaryVarAnnotation

src/response.php:62:13: UnnecessaryVarAnnotation: The @var Psr\Http\Message\ResponseFactoryInterface annotation for $responseFactory is unnecessary (see https://psalm.dev/212)
*/
$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
);
}

2 changes: 1 addition & 1 deletion tests/ContainerTest.php
Expand Up @@ -9,7 +9,7 @@

class ContainerTest extends FunctionsTestCase
{
public function testContainer()
public function testFunctionLoaded()
{
$this->assertTrue(function_exists('container'));
}
Expand Down
65 changes: 62 additions & 3 deletions tests/ResponseTest.php
Expand Up @@ -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')]
Expand Down Expand Up @@ -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';
}
}
2 changes: 1 addition & 1 deletion tests/RouterTest.php
Expand Up @@ -15,7 +15,7 @@

class RouterTest extends FunctionsTestCase
{
public function testContainer()
public function testFunctionLoaded()
{
$this->assertTrue(function_exists('route'));
}
Expand Down

0 comments on commit 3caecb5

Please sign in to comment.