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

Lifecycle hooks for CrudPanel functionality #5687

Open
wants to merge 13 commits into
base: next
Choose a base branch
from
4 changes: 4 additions & 0 deletions src/BackpackServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public function register()
return new DatabaseSchema();
});

$this->app->scoped('lifecycle-hook', function ($app) {
return new app\Library\CrudPanel\Hooks\LifecycleHooks();
});

$this->app->singleton('BackpackViewNamespaces', function ($app) {
return new ViewNamespaces();
});
Expand Down
14 changes: 13 additions & 1 deletion src/app/Http/Controllers/CrudController.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Backpack\CRUD\app\Http\Controllers;

use Backpack\CRUD\app\Library\Attributes\DeprecatedIgnoreOnRuntime;
use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller;
Expand All @@ -19,6 +20,7 @@ class CrudController extends Controller
use DispatchesJobs, ValidatesRequests;

public $crud;

public $data = [];

public function __construct()
Expand All @@ -40,8 +42,14 @@ public function __construct()

$this->crud->setRequest($request);

LifecycleHook::trigger('crud:before_setup_defaults', [$this]);
$this->setupDefaults();
LifecycleHook::trigger('crud:after_setup_defaults', [$this]);

LifecycleHook::trigger('crud:before_setup', [$this]);
$this->setup();
LifecycleHook::trigger('crud:after_setup', [$this]);

$this->setupConfigurationForCurrentOperation();

return $next($request);
Expand Down Expand Up @@ -109,13 +117,15 @@ protected function setupConfigurationForCurrentOperation()
/*
* FIRST, run all Operation Closures for this operation.
*
* It's preferred for this to closures first, because
* It's preferred for this to run closures first, because
* (1) setup() is usually higher in a controller than any other method, so it's more intuitive,
* since the first thing you write is the first thing that is being run;
* (2) operations use operation closures themselves, inside their setupXxxDefaults(), and
* you'd like the defaults to be applied before anything you write. That way, anything you
* write is done after the default, so you can remove default settings, etc;
*/
LifecycleHook::trigger($operationName.':before_setup', [$this]);

$this->crud->applyConfigurationFromSettings($operationName);

/*
Expand All @@ -124,5 +134,7 @@ protected function setupConfigurationForCurrentOperation()
if (method_exists($this, $setupClassName)) {
$this->{$setupClassName}();
}

LifecycleHook::trigger($operationName.':after_setup', [$this]);
}
}
7 changes: 3 additions & 4 deletions src/app/Http/Controllers/Operations/Concerns/HasForm.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Http\Controllers\Operations\Concerns;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Str;

Expand Down Expand Up @@ -38,8 +39,7 @@ protected function formDefaults(string $operationName, string $buttonStack = 'li
// Access
$this->crud->allowAccess($operationName);

// Config
$this->crud->operation($operationName, function () use ($operationName) {
LifecycleHook::hookInto($operationName.':before_setup', function () use ($operationName) {
// if the backpack.operations.{operationName} config exists, use that one
// otherwise, use the generic backpack.operations.form config
if (config()->has('backpack.operations.'.$operationName)) {
Expand All @@ -61,8 +61,7 @@ protected function formDefaults(string $operationName, string $buttonStack = 'li
]);
});

// Default Button
$this->crud->operation(['list', 'show'], function () use ($operationName, $buttonStack, $buttonMeta) {
LifecycleHook::hookInto(['list:before_setup', 'show:before_setup'], function () use ($operationName, $buttonStack, $buttonMeta) {
$this->crud->button($operationName)->view('crud::buttons.quick')->stack($buttonStack)->meta($buttonMeta);
});
}
Expand Down
6 changes: 3 additions & 3 deletions src/app/Http/Controllers/Operations/CreateOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Http\Controllers\Operations;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\Route;

trait CreateOperation
Expand Down Expand Up @@ -35,12 +36,11 @@ protected function setupCreateDefaults()
{
$this->crud->allowAccess('create');

$this->crud->operation('create', function () {
$this->crud->loadDefaultOperationSettingsFromConfig();
LifecycleHook::hookInto('create:before_setup', function () {
$this->crud->setupDefaultSaveActions();
});

$this->crud->operation('list', function () {
LifecycleHook::hookInto('list:before_setup', function () {
$this->crud->addButton('top', 'create', 'view', 'crud::buttons.create');
});
}
Expand Down
5 changes: 3 additions & 2 deletions src/app/Http/Controllers/Operations/DeleteOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Http\Controllers\Operations;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\Route;

trait DeleteOperation
Expand Down Expand Up @@ -29,11 +30,11 @@ protected function setupDeleteDefaults()
{
$this->crud->allowAccess('delete');

$this->crud->operation('delete', function () {
LifecycleHook::hookInto('delete:before_setup', function () {
$this->crud->loadDefaultOperationSettingsFromConfig();
});

$this->crud->operation(['list', 'show'], function () {
LifecycleHook::hookInto(['list:before_setup', 'show:before_setup'], function () {
$this->crud->addButton('line', 'delete', 'view', 'crud::buttons.delete', 'end');
});
}
Expand Down
3 changes: 2 additions & 1 deletion src/app/Http/Controllers/Operations/ListOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Http\Controllers\Operations;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\Route;

trait ListOperation
Expand Down Expand Up @@ -43,7 +44,7 @@ protected function setupListDefaults()
{
$this->crud->allowAccess('list');

$this->crud->operation('list', function () {
LifecycleHook::hookInto('list:before_setup', function () {
$this->crud->loadDefaultOperationSettingsFromConfig();
});
}
Expand Down
5 changes: 3 additions & 2 deletions src/app/Http/Controllers/Operations/ReorderOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Http\Controllers\Operations;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\Route;

trait ReorderOperation
Expand Down Expand Up @@ -36,7 +37,7 @@ protected function setupReorderDefaults()
$this->crud->set('reorder.enabled', true);
$this->crud->allowAccess('reorder');

$this->crud->operation('reorder', function () {
LifecycleHook::hookInto('reorder:before_setup', function () {
$this->crud->loadDefaultOperationSettingsFromConfig();
$this->crud->setOperationSetting('reorderColumnNames', [
'parent_id' => 'parent_id',
Expand All @@ -46,7 +47,7 @@ protected function setupReorderDefaults()
]);
});

$this->crud->operation('list', function () {
LifecycleHook::hookInto('list:before_setup', function () {
$this->crud->addButton('top', 'reorder', 'view', 'crud::buttons.reorder');
});
}
Expand Down
7 changes: 4 additions & 3 deletions src/app/Http/Controllers/Operations/ShowOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Http\Controllers\Operations;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\Route;

trait ShowOperation
Expand Down Expand Up @@ -30,19 +31,19 @@ protected function setupShowDefaults()
$this->crud->allowAccess('show');
$this->crud->setOperationSetting('setFromDb', true);

$this->crud->operation('show', function () {
LifecycleHook::hookInto('show:before_setup', function () {
$this->crud->loadDefaultOperationSettingsFromConfig();

if (! method_exists($this, 'setupShowOperation')) {
$this->autoSetupShowOperation();
}
});

$this->crud->operation('list', function () {
LifecycleHook::hookInto(['list:before_setup'], function () {
$this->crud->addButton('line', 'show', 'view', 'crud::buttons.show', 'beginning');
});

$this->crud->operation(['create', 'update'], function () {
LifecycleHook::hookInto(['create:before_setup', 'update:before_setup'], function () {
$this->crud->addSaveAction([
'name' => 'save_and_preview',
'visible' => function ($crud) {
Expand Down
5 changes: 3 additions & 2 deletions src/app/Http/Controllers/Operations/UpdateOperation.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Http\Controllers\Operations;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\Route;

trait UpdateOperation
Expand Down Expand Up @@ -35,7 +36,7 @@ protected function setupUpdateDefaults()
{
$this->crud->allowAccess('update');

$this->crud->operation('update', function () {
LifecycleHook::hookInto('update:before_setup', function () {
$this->crud->loadDefaultOperationSettingsFromConfig();

if ($this->crud->getModel()->translationEnabled()) {
Expand All @@ -49,7 +50,7 @@ protected function setupUpdateDefaults()
$this->crud->setupDefaultSaveActions();
});

$this->crud->operation(['list', 'show'], function () {
LifecycleHook::hookInto(['list:before_setup', 'show:before_setup'], function () {
$this->crud->addButton('line', 'update', 'view', 'crud::buttons.update', 'end');
});
}
Expand Down
5 changes: 5 additions & 0 deletions src/app/Library/CrudPanel/CrudRouter.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Backpack\CRUD\app\Library\CrudPanel;

use Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades\LifecycleHook;
use Illuminate\Support\Facades\App;
use ReflectionClass;

Expand All @@ -18,7 +19,9 @@ public static function setupControllerRoutes(string $name, string $routeName, st
if (empty($setupRoutesMethod->getAttributes(\Backpack\CRUD\app\Library\Attributes\DeprecatedIgnoreOnRuntime::class))) {
// when the attribute is not found the developer has overwritten the method
// we will keep the old behavior for backwards compatibility
LifecycleHook::trigger('crud:before_setup_routes', [$name, $routeName, $controller]);
$setupRoutesMethod->invoke(App::make($namespacedController), $name, $routeName, $controller);
LifecycleHook::trigger('crud:after_setup_routes', [$name, $routeName, $controller]);

return;
}
Expand All @@ -32,7 +35,9 @@ public static function setupControllerRoutes(string $name, string $routeName, st
str_ends_with($method->getName(), 'Routes')
) {
$method->setAccessible(true);
LifecycleHook::trigger('crud:before_setup_routes', [$name, $routeName, $controller]);
$method->invoke($controllerInstance, $name, $routeName, $controller);
LifecycleHook::trigger('crud:after_setup_routes', [$name, $routeName, $controller]);
}
}
}
Expand Down
20 changes: 20 additions & 0 deletions src/app/Library/CrudPanel/Hooks/Facades/LifecycleHook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Backpack\CRUD\app\Library\CrudPanel\Hooks\Facades;

use Illuminate\Support\Facades\Facade;

/**
* @method static void register(string $hook, string|array $operations, callable $callback)
* @method static void run(string $hook, string|array $operations, array $parameters)
* @method static bool has(string $hook, string $operation)
*
* @see \Backpack\CRUD\app\Library\CrudPanel\Hooks\OperationHooks
*/
class LifecycleHook extends Facade
{
protected static function getFacadeAccessor()
{
return 'lifecycle-hook';
}
}
35 changes: 35 additions & 0 deletions src/app/Library/CrudPanel/Hooks/LifecycleHooks.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php

namespace Backpack\CRUD\app\Library\CrudPanel\Hooks;

final class LifecycleHooks
{
public array $hooks = [];

public function hookInto(string|array $hooks, callable $callback): void
{
$hooks = is_array($hooks) ? $hooks : [$hooks];
foreach ($hooks as $hook) {
$this->hooks[$hook][] = $callback;
}
}

public function trigger(string|array $hooks, array $parameters = []): void
{
$hooks = is_array($hooks) ? $hooks : [$hooks];
foreach ($hooks as $hook) {
if (isset($this->hooks[$hook])) {
foreach ($this->hooks[$hook] as $callback) {
if ($callback instanceof \Closure) {
$callback(...$parameters);
}
}
}
}
}

public function has(string $hook): bool
{
return isset($this->hooks[$hook]);
}
}
Loading