Skip to content

Commit

Permalink
Removed RouteGroup attribute, replaced its functionality with the Con…
Browse files Browse the repository at this point in the history
…troller attribute (#295)
  • Loading branch information
davidbyoung committed Sep 16, 2023
1 parent a209d8f commit fd5f0fe
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 105 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -12,6 +12,7 @@
- `Aphiria\Validation\Builders` => `Aphiria\Validation`
- Updated to PHPUnit 10.1 ([#248](https://github.com/aphiria/aphiria/pull/248), [#250](https://github.com/aphiria/aphiria/pull/250))
- Updated `IAuthenticator::authenticate()`, `IAuthenticator::challenge()`, `IAuthenticator::forbid()`, `IAuthenticator::logIn()`, and `IAuthenticator::logOut()` to take in no, one, or many authentication scheme names ([#269](https://github.com/aphiria/aphiria/pull/269))
- Replaced `#[RouteGroup]` attribute with `#[Controller]` ([#295](https://github.com/aphiria/aphiria/pull/295))
- Added the authentication scheme name(s) to `AuthenticationResult` ([#269](https://github.com/aphiria/aphiria/pull/269))
- Removed `IUserAccessor` property from `Authenticator` ([#269](https://github.com/aphiria/aphiria/pull/269))
- Renamed `SchemeNotFoundException` to `AuthenticationSchemeNotFoundException` ([#269](https://github.com/aphiria/aphiria/pull/269))
Expand Down
5 changes: 2 additions & 3 deletions README.md
Expand Up @@ -26,20 +26,19 @@ Aphiria is a suite of small, decoupled PHP libraries that make up a REST API fra

```php
// Define some controller endpoints
#[RouteGroup('/users')]
class UserController extends Controller
{
public function __construct(private IUserService $users) {}

#[Post('')]
#[Post('/users')]
public function createUser(User $user): IResponse
{
$this->users->create($user);

return $this->created("/users/{$user->id}", $user);
}

#[Get('/:id')]
#[Get('/users/:id')]
#[AuthorizeRoles('admin')]
public function getUserById(int $id): User
{
Expand Down
14 changes: 7 additions & 7 deletions src/Router/src/Attributes/AttributeRouteRegistrant.php
Expand Up @@ -113,19 +113,19 @@ private function createRouteGroupOptions(ReflectionClass $controller): ?RouteGro
$routeConstraints[] = new $routeConstraintClassName(...$routeConstraintAttributeInstance->constructorParameters);
}

foreach ($controller->getAttributes(RouteGroup::class) as $routeGroupAttribute) {
$routeGroupAttributeInstance = $routeGroupAttribute->newInstance();
foreach ($controller->getAttributes(ControllerAttribute::class) as $controllerAttribute) {
$controllerAttributeInstance = $controllerAttribute->newInstance();
$routeGroupOptions = new RouteGroupOptions(
$routeGroupAttributeInstance->path,
$routeGroupAttributeInstance->host,
$routeGroupAttributeInstance->isHttpsOnly,
$controllerAttributeInstance->path,
$controllerAttributeInstance->host,
$controllerAttributeInstance->isHttpsOnly,
$routeConstraints,
$middlewareBindings,
$routeGroupAttributeInstance->parameters
$controllerAttributeInstance->parameters
);
}

// If there was no route group options attributes, but there were constraints or middleware, then create some route group options and add them
// If there was no controller attributes, but there were constraints or middleware, then create some route group options and add them
if ($routeGroupOptions === null && (!empty($routeConstraints) || !empty($middlewareBindings))) {
$routeGroupOptions = new RouteGroupOptions('');
$routeGroupOptions->constraints = [...$routeGroupOptions->constraints, ...$routeConstraints];
Expand Down
14 changes: 13 additions & 1 deletion src/Router/src/Attributes/Controller.php
Expand Up @@ -20,5 +20,17 @@
#[Attribute(Attribute::TARGET_CLASS)]
final class Controller
{
// Don't do anything
/**
* @param string $path The path prefix to apply to all the routes in the controller (defaults to an empty path)
* @param string|null $host The host to apply to all the routes in the controller
* @param bool $isHttpsOnly Whether or not all the routes in the controller are HTTPS only
* @param array<string, mixed> $parameters The mapping of custom parameter names to values for all the routes in the controller
*/
public function __construct(
public string $path = '',
public ?string $host = null,
public bool $isHttpsOnly = false,
public array $parameters = []
) {
}
}
36 changes: 0 additions & 36 deletions src/Router/src/Attributes/RouteGroup.php

This file was deleted.

102 changes: 51 additions & 51 deletions src/Router/tests/Attributes/AttributeRouteRegistrantTest.php
Expand Up @@ -16,10 +16,10 @@
use Aphiria\Middleware\Attributes\Middleware as MiddlewareLibraryMiddlewareAttribute;
use Aphiria\Reflection\ITypeFinder;
use Aphiria\Routing\Attributes\AttributeRouteRegistrant;
use Aphiria\Routing\Attributes\Controller as ControllerAttribute;
use Aphiria\Routing\Attributes\Get;
use Aphiria\Routing\Attributes\Middleware;
use Aphiria\Routing\Attributes\RouteConstraint;
use Aphiria\Routing\Attributes\RouteGroup;
use Aphiria\Routing\Matchers\Constraints\HttpMethodRouteConstraint;
use Aphiria\Routing\RouteCollection;
use Aphiria\Routing\Tests\Attributes\Mocks\CustomMiddleware;
Expand All @@ -41,22 +41,9 @@ protected function setUp(): void
$this->registrant = new AttributeRouteRegistrant(self::PATH, $this->typeFinder);
}

public function testRegisteringRouteForNonControllerRegistersNothing(): void
{
$nonController = new class () {
};
$this->typeFinder->expects($this->once())
->method('findAllClasses')
->with([self::PATH])
->willReturn([$nonController::class]);
$routes = new RouteCollection();
$this->registrant->registerRoutes($routes);
$this->assertEmpty($routes->getAll());
}

public function testRegisteringRouteGroupWithMiddlewareLibraryMiddlewareIsAddedToRouteGroup(): void
public function testRegisteringControllerWithMiddlewareLibraryMiddlewareIsAddedToRouteGroup(): void
{
$controller = new #[RouteGroup(''), MiddlewareLibraryMiddlewareAttribute(MiddlewareLibraryMiddleware::class)] class () extends Controller {
$controller = new #[ControllerAttribute(''), MiddlewareLibraryMiddlewareAttribute(MiddlewareLibraryMiddleware::class)] class () extends Controller {
#[Get('')]
public function route(): void
{
Expand All @@ -75,9 +62,9 @@ public function route(): void
$this->assertSame(MiddlewareLibraryMiddleware::class, $routeArr[0]->middlewareBindings[0]->className);
}

public function testRegisteringRouteGroupWithMiddlewareThatExtendsMiddlewareAttributeIsAddedToRouteGroup(): void
public function testRegisteringControllerWithMiddlewareThatExtendsMiddlewareAttributeIsAddedToRouteGroup(): void
{
$controller = new #[RouteGroup(''), CustomMiddleware] class () extends Controller {
$controller = new #[ControllerAttribute(''), CustomMiddleware] class () extends Controller {
#[Get('')]
public function route(): void
{
Expand All @@ -96,41 +83,22 @@ public function route(): void
$this->assertSame(DummyMiddleware::class, $routeArr[0]->middlewareBindings[0]->className);
}

public function testRegisteringRoutesWithRouteConstraintsAppliesConstraintsToChildRoutes(): void
public function testRegisteringRouteForNonControllerRegistersNothing(): void
{
$controller = new #[RouteConstraint(DummyConstraint::class, ['foo'])] class () extends Controller {
#[Get('')]
public function routeWithNoExtraConstraints(): void
{
// Empty
}

#[
Get(''),
/** @psalm-suppress ArgumentTypeCoercion https://github.com/vimeo/psalm/issues/4871 */
RouteConstraint(DummyConstraint::class, ['bar'])
]
public function routeWithExtraConstraints(): void
{
// Empty
}
$nonController = new class () {
};
$this->typeFinder->expects($this->once())
->method('findAllClasses')
->with([self::PATH])
->willReturn([$controller::class]);
->willReturn([$nonController::class]);
$routes = new RouteCollection();
$this->registrant->registerRoutes($routes);
$routeArr = $routes->getAll();
$this->assertCount(2, $routeArr);
// Note: The HTTP method constraint gets automatically added, too
$this->assertCount(2, $routeArr[0]->constraints);
$this->assertCount(3, $routeArr[1]->constraints);
$this->assertEmpty($routes->getAll());
}

public function testRegisteringRoutesWithRouteGroupThatIsHttpsOnlyMakesChildRoutesHttpsOnly(): void
public function testRegisteringRoutesWithControllerThatIsHttpsOnlyMakesChildRoutesHttpsOnly(): void
{
$controller = new #[RouteGroup(isHttpsOnly: true)] class () extends Controller {
$controller = new #[ControllerAttribute(isHttpsOnly: true)] class () extends Controller {
#[Get('', isHttpsOnly: true)]
public function routeThatIsAlreadyHttpsOnly(): void
{
Expand All @@ -155,9 +123,9 @@ public function routeThatIsNotHttpsOnly(): void
$this->assertTrue($routeArr[1]->uriTemplate->isHttpsOnly);
}

public function testRegisteringRoutesWithRouteGroupWithEmptyPathPrependsNothingToRoutePaths(): void
public function testRegisteringRoutesWithControllerWithEmptyPathPrependsNothingToRoutePaths(): void
{
$controller = new #[RouteGroup('')] class () extends Controller {
$controller = new #[ControllerAttribute('')] class () extends Controller {
#[Get('foo')]
public function route(): void
{
Expand All @@ -176,9 +144,9 @@ public function route(): void
$this->assertSame('/foo', $route->uriTemplate->pathTemplate);
}

public function testRegisteringRoutesWithRouteGroupWithHostAppendsHostToRouteHost(): void
public function testRegisteringRoutesWithControllerWithHostAppendsHostToRouteHost(): void
{
$controller = new #[RouteGroup(host: 'example.com')] class () extends Controller {
$controller = new #[ControllerAttribute(host: 'example.com')] class () extends Controller {
#[Get('', 'api')]
public function route(): void
{
Expand All @@ -197,9 +165,9 @@ public function route(): void
$this->assertSame('api.example.com', $route->uriTemplate->hostTemplate);
}

public function testRegisteringRoutesWithRouteGroupWithParametersAppliesParametersToChildRoutes(): void
public function testRegisteringRoutesWithControllerWithParametersAppliesParametersToChildRoutes(): void
{
$controller = new #[RouteGroup('', parameters: ['foo' => 'bar'])] class () extends Controller {
$controller = new #[ControllerAttribute('', parameters: ['foo' => 'bar'])] class () extends Controller {
#[Get('')]
public function routeWithNoParameters(): void
{
Expand All @@ -224,9 +192,9 @@ public function routeWithParameters(): void
$this->assertEquals(['foo' => 'bar', 'baz' => 'blah'], $routeArr[1]->parameters);
}

public function testRegisteringRoutesWithRouteGroupWithPathPrependsPathToRoutePaths(): void
public function testRegisteringRoutesWithControllerWithPathPrependsPathToRoutePaths(): void
{
$controller = new #[RouteGroup('foo')] class () extends Controller {
$controller = new #[ControllerAttribute('foo')] class () extends Controller {
#[Get('bar')]
public function route(): void
{
Expand All @@ -245,6 +213,38 @@ public function route(): void
$this->assertSame('/foo/bar', $route->uriTemplate->pathTemplate);
}

public function testRegisteringRoutesWithRouteConstraintsAppliesConstraintsToChildRoutes(): void
{
$controller = new #[RouteConstraint(DummyConstraint::class, ['foo'])] class () extends Controller {
#[Get('')]
public function routeWithNoExtraConstraints(): void
{
// Empty
}

#[
Get(''),
/** @psalm-suppress ArgumentTypeCoercion https://github.com/vimeo/psalm/issues/4871 */
RouteConstraint(DummyConstraint::class, ['bar'])
]
public function routeWithExtraConstraints(): void
{
// Empty
}
};
$this->typeFinder->expects($this->once())
->method('findAllClasses')
->with([self::PATH])
->willReturn([$controller::class]);
$routes = new RouteCollection();
$this->registrant->registerRoutes($routes);
$routeArr = $routes->getAll();
$this->assertCount(2, $routeArr);
// Note: The HTTP method constraint gets automatically added, too
$this->assertCount(2, $routeArr[0]->constraints);
$this->assertCount(3, $routeArr[1]->constraints);
}

public function testRegisteringRouteWithAllPropertiesSetCreatesRouteWithAllThosePropertiesSet(): void
{
$controller = new class () extends Controller {
Expand Down
Expand Up @@ -12,17 +12,17 @@

namespace Aphiria\Routing\Tests\Attributes;

use Aphiria\Routing\Attributes\RouteGroup;
use Aphiria\Routing\Attributes\Controller;
use PHPUnit\Framework\TestCase;

class RouteGroupTest extends TestCase
class ControllerTest extends TestCase
{
public function testPropertiesAreSetInConstructor(): void
{
$routeGroup = new RouteGroup('path', 'example.com', true, ['foo' => 'bar']);
$this->assertSame('path', $routeGroup->path);
$this->assertSame('example.com', $routeGroup->host);
$this->assertTrue($routeGroup->isHttpsOnly);
$this->assertSame(['foo' => 'bar'], $routeGroup->parameters);
$controller = new Controller('path', 'example.com', true, ['foo' => 'bar']);
$this->assertSame('path', $controller->path);
$this->assertSame('example.com', $controller->host);
$this->assertTrue($controller->isHttpsOnly);
$this->assertSame(['foo' => 'bar'], $controller->parameters);
}
}

0 comments on commit fd5f0fe

Please sign in to comment.