Skip to content

Commit

Permalink
Make API singleton 'only' behavior consistent with API resource
Browse files Browse the repository at this point in the history
  • Loading branch information
jessarcher committed May 16, 2023
1 parent e0e443f commit 5594b54
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 33 deletions.
29 changes: 6 additions & 23 deletions src/Illuminate/Routing/ResourceRegistrar.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

namespace Illuminate\Routing;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;

class ResourceRegistrar
Expand Down Expand Up @@ -149,6 +148,12 @@ public function singleton($name, $controller, array $options = [])

$defaults = $this->singletonResourceDefaults;

if (isset($options['creatable'])) {
$defaults = array_merge($defaults, ['create', 'store', 'destroy']);
} elseif (isset($options['destroyable'])) {
$defaults = array_merge($defaults, ['destroy']);
}

$collection = new RouteCollection;

$resourceMethods = $this->getResourceMethods($defaults, $options);
Expand Down Expand Up @@ -249,28 +254,6 @@ protected function getResourceMethods($defaults, $options)
$methods = array_diff($methods, (array) $options['except']);
}

if (isset($options['apiSingleton'])) {
$methods = array_diff($methods, ['create', 'edit']);
}

if (isset($options['creatable'])) {
$methods = isset($options['apiSingleton'])
? array_merge(['store', 'destroy'], $methods)
: array_merge(['create', 'store', 'destroy'], $methods);

return $this->getResourceMethods(
$methods, Arr::except($options, ['creatable'])
);
}

if (isset($options['destroyable'])) {
$methods = array_merge(['destroy'], $methods);

return $this->getResourceMethods(
$methods, Arr::except($options, ['destroyable'])
);
}

return array_values($methods);
}

Expand Down
16 changes: 7 additions & 9 deletions src/Illuminate/Routing/Router.php
Original file line number Diff line number Diff line change
Expand Up @@ -427,17 +427,15 @@ public function apiSingletons(array $singletons, array $options = [])
*/
public function apiSingleton($name, $controller, array $options = [])
{
if ($this->container && $this->container->bound(ResourceRegistrar::class)) {
$registrar = $this->container->make(ResourceRegistrar::class);
} else {
$registrar = new ResourceRegistrar($this);
$only = ['store', 'show', 'update', 'destroy'];

if (isset($options['except'])) {
$only = array_diff($only, (array) $options['except']);
}

return new PendingSingletonResourceRegistration(
$registrar, $name, $controller, array_merge([
'apiSingleton' => true,
], $options),
);
return $this->singleton($name, $controller, array_merge([
'only' => $only,
], $options));
}

/**
Expand Down
2 changes: 1 addition & 1 deletion tests/Integration/Routing/RouteSingletonTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ public function testCreatableApiSingletonOnly()
$this->assertEquals(405, $response->getStatusCode());

$response = $this->get('/avatar/create');
$this->assertEquals(404, $response->getStatusCode());
$this->assertEquals(200, $response->getStatusCode());

$response = $this->post('/avatar');
$this->assertEquals(200, $response->getStatusCode());
Expand Down
149 changes: 149 additions & 0 deletions tests/Routing/RouteRegistrarTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,155 @@ public function testCanRemoveMiddlewareFromGroupUnregisteredGroup()
$this->assertEquals([], $this->router->getMiddlewareGroups());
}

public function testCanRegisterSingleton()
{
$this->router->singleton('user', RouteRegistrarControllerStub::class);

$this->assertCount(3, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.edit'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
}

public function testCanRegisterApiSingleton()
{
$this->router->apiSingleton('user', RouteRegistrarControllerStub::class);

$this->assertCount(2, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
}

public function testCanRegisterCreatableSingleton()
{
$this->router->singleton('user', RouteRegistrarControllerStub::class)->creatable();

$this->assertCount(6, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.create'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.store'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.edit'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.destroy'));
}

public function testCanRegisterCreatableApiSingleton()
{
$this->router->apiSingleton('user', RouteRegistrarControllerStub::class)->creatable();

$this->assertCount(4, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.store'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.destroy'));
}

public function testSingletonCreatableNotDestroyable()
{
$this->router->singleton('user', RouteRegistrarControllerStub::class)
->creatable()
->except('destroy');

$this->assertCount(5, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.create'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.store'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.edit'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
$this->assertFalse($this->router->getRoutes()->hasNamedRoute('user.destroy'));
}

public function testApiSingletonCreatableNotDestroyable()
{
$this->router->apiSingleton('user', RouteRegistrarControllerStub::class)
->creatable()
->except('destroy');

$this->assertCount(3, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.store'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
$this->assertFalse($this->router->getRoutes()->hasNamedRoute('user.destroy'));
}

public function testSingletonCanBeDestroyable()
{
$this->router->singleton('user', RouteRegistrarControllerStub::class)
->destroyable();

$this->assertCount(4, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.edit'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.destroy'));
}

public function testApiSingletonCanBeDestroyable()
{
$this->router->apiSingleton('user', RouteRegistrarControllerStub::class)
->destroyable();

$this->assertCount(3, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.show'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.update'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.destroy'));
}

public function testSingletonCanBeOnlyCreatable()
{
$this->router->singleton('user', RouteRegistrarControllerStub::class)
->creatable()
->only('create', 'store');

$this->assertCount(2, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.create'));
$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.store'));
}

public function testApiSingletonCanBeOnlyCreatable()
{
$this->router->apiSingleton('user', RouteRegistrarControllerStub::class)
->creatable()
->only('store');

$this->assertCount(1, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.store'));
}

public function testSingletonDoesntAllowIncludingUnsupportedMethods()
{
$this->router->singleton('post', RouteRegistrarControllerStub::class)
->only('index', 'store', 'create', 'destroy');

$this->assertCount(0, $this->router->getRoutes());

$this->router->apiSingleton('user', RouteRegistrarControllerStub::class)
->only('index', 'store', 'create', 'destroy');

$this->assertCount(0, $this->router->getRoutes());
}

public function testApiSingletonCanIncludeAnySingletonMethods()
{
// This matches the behavior of the apiResource method.
$this->router->apiSingleton('user', RouteRegistrarControllerStub::class)
->only('edit');

$this->assertCount(1, $this->router->getRoutes());

$this->assertTrue($this->router->getRoutes()->hasNamedRoute('user.edit'));
}

/**
* Get the last route registered with the router.
*
Expand Down

0 comments on commit 5594b54

Please sign in to comment.