Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ability to define resourceful Action routes #295

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
4 changes: 4 additions & 0 deletions src/ActionServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
namespace Lorisleiva\Actions;

use Illuminate\Foundation\Application;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
use Lorisleiva\Actions\Console\MakeActionCommand;
use Lorisleiva\Actions\DesignPatterns\CommandDesignPattern;
use Lorisleiva\Actions\DesignPatterns\ControllerDesignPattern;
use Lorisleiva\Actions\DesignPatterns\ListenerDesignPattern;
use Lorisleiva\Actions\Macros\ActionRouteMacros;

class ActionServiceProvider extends ServiceProvider
{
Expand Down Expand Up @@ -43,6 +45,8 @@ public function boot(): void
MakeActionCommand::class,
]);
}

Route::mixin(new ActionRouteMacros);
}

protected function extendActions(): void
Expand Down
23 changes: 23 additions & 0 deletions src/Macros/ActionRouteMacros.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Lorisleiva\Actions\Macros;

use Illuminate\Routing\PendingResourceRegistration;
use Illuminate\Routing\Router;
use Lorisleiva\Actions\Routing\ActionResourceRegistrar;

class ActionRouteMacros
{
public function resourceActions(): callable
{
return function (string $name, string $namespace = 'App\Actions', array $options = []): PendingResourceRegistration {
/** @var Router $router */
$router = $this;
$registrar = new ActionResourceRegistrar($router);

return new PendingResourceRegistration(
$registrar, $name, $namespace, $options
);
};
}
}
62 changes: 62 additions & 0 deletions src/Routing/ActionResourceRegistrar.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?php

namespace Lorisleiva\Actions\Routing;

use Closure;
use Illuminate\Routing\ResourceRegistrar;
use Illuminate\Support\Str;

class ActionResourceRegistrar extends ResourceRegistrar
{
/**
* @var array<string, Closure>
*/
private static array $actionResolver = [];

protected function getResourceAction($resource, $controller, $method, $options): array
{
$action = parent::getResourceAction($resource, $controller, $method, $options);

$resource = Str::camel(str_replace('.', '_', $resource));
$actionName = Str::singular($resource);

if (! empty(static::$actionResolver[$method])) {
$actionClass = call_user_func(static::$actionResolver[$method], $resource);
}

if (empty($actionClass)) {
$actionClass = match ($method) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can $method be a value that isn't listed in the match values?

Copy link
Author

@CWAscend CWAscend Feb 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed this to Route::resourceActions as requested :)

For this comment, I've replicated what is in the parent class:

image

So my answer your question is no - there's also no method that allows change the $resourceDefaults array, unless the developer extends this class and forces a change that way.

In this case, if they need the Action class names to be resolved in a different way to the default functionality provided, then can customise it by leveraging this in their RouteServiceProvider:

ActionResourceRegistrar::resolveActionClassNameUsing(
    function ($resource, $method): ?string {
        // Add their custom functionality here
    }
);

So lets say they extended the ResourceRegistrar and appended a 'restore' to the resource defaults:

ActionResourceRegistrar::resolveActionClassNameUsing(
    function ($resource, $method): ?string {
        return match ($method) {
            'restore' => 'Restore'.ucfirst($resource),
            default => null        
        }
    }
);

'index' => 'Get'.ucfirst($resource),
'create' => 'ShowCreate'.ucfirst($actionName),
'show' => 'Show'.ucfirst($actionName),
'edit' => 'ShowEdit'.ucfirst($actionName),
'store' => 'Create'.ucfirst($actionName),
'update' => 'Update'.ucfirst($actionName),
'destroy' => 'Delete'.ucfirst($actionName),
};
}

// Replaces the Controller@action string with the ActionClass string
$action['uses'] = str_replace('\\\\', '\\', "{$controller}\\{$actionClass}");

return $action;
}

/**
* Use this in your RouteServiceProvider to override the default action classes
*
* @example
*
* ActionResourceRegistrar::resolveResourceAction('index', function (string $resource) {
* return 'GetAll'.ucfirst($resource); // e.g. GetAllUsers
* });
*
* @param string $method
* @param Closure $resolver
* @return void
*/
public static function resolveResourceAction(string $method, Closure $resolver): void
{
static::$actionResolver[$method] = $resolver;
}
}
Loading