Skip to content
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
831be33
wip
pxpm Oct 11, 2024
3a61e54
wip
pxpm Oct 11, 2024
f8fec3f
Apply fixes from StyleCI
StyleCIBot Oct 11, 2024
c222441
Apply fixes from StyleCI
StyleCIBot Mar 12, 2025
5e59690
wip
pxpm Mar 19, 2025
792da51
wip
pxpm Mar 19, 2025
dd89d3d
wip
pxpm Mar 24, 2025
d8b8d16
Apply fixes from StyleCI
StyleCIBot Mar 24, 2025
f9b3117
wip
pxpm Mar 27, 2025
e6490ab
wip
pxpm Mar 28, 2025
2e58485
wip
pxpm Mar 28, 2025
f1fdd91
wip
pxpm Apr 1, 2025
693849b
Apply fixes from StyleCI
StyleCIBot Apr 1, 2025
861be11
wip
pxpm Apr 4, 2025
7357fc2
wip
pxpm Apr 7, 2025
74c0bab
wip
pxpm Apr 7, 2025
3ffacd9
Apply fixes from StyleCI
StyleCIBot Apr 7, 2025
60dbf9d
wip
pxpm Apr 24, 2025
863f179
Apply fixes from StyleCI
StyleCIBot Apr 24, 2025
49cc789
wip
pxpm May 2, 2025
868c74c
Apply fixes from StyleCI
StyleCIBot May 2, 2025
b255686
wip
pxpm May 2, 2025
6a92d63
wip
pxpm May 2, 2025
9760254
Apply fixes from StyleCI
StyleCIBot May 2, 2025
b246d64
wip
pxpm May 6, 2025
641ce12
wip
pxpm May 7, 2025
e94f20e
wip
pxpm May 8, 2025
5a71915
fix responsive table
pxpm May 12, 2025
f4a0aea
wip
pxpm May 12, 2025
7cc007d
wip
pxpm May 12, 2025
e3b5759
fix pagination
pxpm May 12, 2025
878ac24
wip
pxpm May 20, 2025
dae5da8
wip
pxpm May 21, 2025
2d583e8
Apply fixes from StyleCI
StyleCIBot May 21, 2025
f66a1f1
wip
pxpm May 27, 2025
9806d93
wip
pxpm May 30, 2025
a83d5b6
Apply fixes from StyleCI
StyleCIBot May 30, 2025
7f00a36
wip
pxpm May 30, 2025
cc93c39
Apply fixes from StyleCI
StyleCIBot May 30, 2025
b8f3157
wip
pxpm Jun 4, 2025
c12786c
Apply fixes from StyleCI
StyleCIBot Jun 4, 2025
e77738c
wip
pxpm Jun 4, 2025
70d6d31
Apply fixes from StyleCI
StyleCIBot Jun 4, 2025
801e5e9
add explanatory comments to CrudPanelManager class
tabacitu Jun 5, 2025
277721f
Apply fixes from StyleCI
StyleCIBot Jun 5, 2025
9bb9011
rename datatables_logic to datatable_logic
tabacitu Jun 5, 2025
262a1f0
Merge branch 'datatable-single-component' of https://github.com/Larav…
tabacitu Jun 5, 2025
1b6ba20
fix
pxpm Jun 5, 2025
0a47c05
Apply fixes from StyleCI
StyleCIBot Jun 5, 2025
3b89f24
wip
pxpm Jun 5, 2025
bd958bb
wip
pxpm Jun 6, 2025
762f513
Apply fixes from StyleCI
StyleCIBot Jun 6, 2025
a974b45
Create setup cache class and create the datatable cache class (#5807)
pxpm Jun 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions src/BackpackServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
use Backpack\Basset\Facades\Basset;
use Backpack\CRUD\app\Http\Middleware\EnsureEmailVerification;
use Backpack\CRUD\app\Http\Middleware\ThrottlePasswordRecovery;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanel;
use Backpack\CRUD\app\Library\Database\DatabaseSchema;
use Backpack\CRUD\app\Library\Datatable\Datatable;
use Backpack\CRUD\app\Library\Uploaders\Support\UploadersRepository;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Routing\Router;
Expand Down Expand Up @@ -63,6 +63,7 @@ public function boot(Router $router)
$this->sendUsageStats();

Basset::addViewPath(realpath(__DIR__.'/resources/views'));
Blade::component('datatable', Datatable::class);
}

/**
Expand All @@ -82,9 +83,12 @@ public function register()

$this->registerBackpackErrorViews();

// Bind the CrudPanel object to Laravel's service container
$this->app->scoped('crud', function ($app) {
return new CrudPanel();
$this->app->bind('crud', function ($app) {
return CrudManager::identifyCrudPanel();
});

$this->app->scoped('CrudManager', function ($app) {
return new CrudPanelManager();
});

$this->app->scoped('DatabaseSchema', function ($app) {
Expand Down Expand Up @@ -181,7 +185,7 @@ public function publishFiles()
/**
* Define the routes for the application.
*
* @param \Illuminate\Routing\Router $router
* @param Router $router
* @return void
*/
public function setupRoutes(Router $router)
Expand All @@ -200,7 +204,7 @@ public function setupRoutes(Router $router)
/**
* Load custom routes file.
*
* @param \Illuminate\Routing\Router $router
* @param Router $router
* @return void
*/
public function setupCustomRoutes(Router $router)
Expand Down Expand Up @@ -333,7 +337,7 @@ public function loadHelpers()
*/
public function provides()
{
return ['crud', 'widgets', 'BackpackViewNamespaces', 'DatabaseSchema', 'UploadersRepository'];
return ['widgets', 'BackpackViewNamespaces', 'DatabaseSchema', 'UploadersRepository', 'CrudManager'];
}

private function registerBackpackErrorViews()
Expand Down
16 changes: 16 additions & 0 deletions src/CrudManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Backpack\CRUD;

use Illuminate\Support\Facades\Facade;

/**
* @see CrudPanelManager
*/
class CrudManager extends Facade
{
protected static function getFacadeAccessor()
{
return 'CrudManager';
}
}
237 changes: 237 additions & 0 deletions src/CrudPanelManager.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
<?php

namespace Backpack\CRUD;

use Backpack\CRUD\app\Http\Controllers\Contracts\CrudControllerContract;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use Backpack\CRUD\app\Library\CrudPanel\CrudPanel;
use Illuminate\Support\Facades\Facade;

/**
* CrudPanelManager - Central registry and factory for CRUD panels.
*
* This class manages multiple CrudPanel instances across different controllers.
* It acts as a singleton registry that:
* - Creates and stores CrudPanel instances for each controller
* - Tracks which operations have been initialized for each controller
* - Manages the currently active controller context
* - Provides methods to retrieve the appropriate CrudPanel based on context
*
* This allows multiple CRUD controllers to coexist and share state properly
* within a single request lifecycle.
*/
final class CrudPanelManager
{
/** @var array<string, CrudPanel> Registry of CrudPanel instances indexed by controller class name */
private array $cruds = [];

/** @var array<string, array<string>> Tracks which operations have been initialized for each controller */
private array $initializedOperations = [];

/** @var string|null The currently active controller class name */
private ?string $currentlyActiveCrudController = null;

/**
* Get or create a CrudPanel instance for the given controller.
*/
public function getCrudPanel(CrudControllerContract|string $controller): CrudPanel
{
$controllerClass = is_string($controller) ? $controller : get_class($controller);

if (isset($this->cruds[$controllerClass])) {
return $this->cruds[$controllerClass];
}

$instance = new CrudPanel();

$this->cruds[$controllerClass] = $instance;

return $this->cruds[$controllerClass];
}

/**
* Setup and initialize a CrudPanel for the given controller and operation.
*
* @param string $controller The controller class name
* @param string|null $operation The operation to set (defaults to 'list')
* @return CrudPanel The initialized CrudPanel instance
*/
public function setupCrudPanel(string $controller, ?string $operation = null): CrudPanel
{
$controller = $this->getActiveController() ?? $controller;

$controller = is_string($controller) ? app($controller) : $controller;

$crud = $this->getCrudPanel($controller);

// Use provided operation or default to 'list'
$operation = $operation ?? 'list';
$crud->setOperation($operation);

$primaryControllerRequest = $this->cruds[array_key_first($this->cruds)]->getRequest();
if (! $crud->isInitialized()) {
self::setActiveController($controller::class);
$controller->initializeCrudPanel($primaryControllerRequest, $crud);
self::unsetActiveController();
$crud = $this->cruds[$controller::class];

return $this->cruds[$controller::class];
}

return $this->cruds[$controller::class];
}

/**
* Record that an operation has been initialized for a controller.
*
* @param string $controller The controller class name
* @param string $operation The operation name (e.g., 'list', 'create', 'update')
*/
public function storeInitializedOperation(string $controller, string $operation): void
{
$this->initializedOperations[$controller][] = $operation;
}

/**
* Get the list of operations that have been initialized for a controller.
*
* @param string $controller The controller class name
* @return array<string> Array of initialized operation names
*/
public function getInitializedOperations(string $controller): array
{
return $this->initializedOperations[$controller] ?? [];
}

/**
* Store a CrudPanel instance for a specific controller.
*/
public function storeCrudPanel(string $controller, CrudPanel $crud): void
{
$this->cruds[$controller] = $crud;
}

/**
* Check if a CrudPanel exists for the given controller.
*/
public function hasCrudPanel(string $controller): bool
{
return isset($this->cruds[$controller]);
}

/**
* Get the active CrudPanel for a controller, with fallback logic.
*
* @param string $controller The controller class name
* @return CrudPanel The CrudPanel instance, creating one if necessary
*/
public function getActiveCrudPanel(string $controller): CrudPanel
{
if (! isset($this->cruds[$controller])) {
return $this->getCrudPanel($this->getActiveController() ?? $this->getParentController() ?? $controller);
}

return $this->cruds[$controller];
}

/**
* Get the parent (first registered) controller class name.
*
* @return string|null The parent controller class name or null if none exists
*/
public function getParentController(): ?string
{
if (! empty($this->cruds)) {
return array_key_first($this->cruds);
}

return $this->getActiveController();
}

/**
* Set the currently active controller and clear the CRUD facade cache.
*
* @param string $controller The controller class name to set as active
*/
public function setActiveController(string $controller): void
{
Facade::clearResolvedInstance('crud');
$this->currentlyActiveCrudController = $controller;
}

/**
* Get the currently active controller class name.
*
* @return string|null The active controller class name or null if none is set
*/
public function getActiveController(): ?string
{
return $this->currentlyActiveCrudController;
}

/**
* Clear the currently active controller.
*/
public function unsetActiveController(): void
{
$this->currentlyActiveCrudController = null;
}

/**
* Intelligently identify and return the appropriate CrudPanel based on context.
*
* This method uses multiple strategies to find the correct CrudPanel:
* 1. Use the currently active controller if set
* 2. Analyze the call stack to find a CRUD controller in the backtrace
* 3. Return the first available CrudPanel if any exist
* 4. Create a default CrudPanel as a last resort
*
* @return CrudPanel The identified or created CrudPanel instance
*/
public function identifyCrudPanel(): CrudPanel
{
if ($this->getActiveController()) {
return $this->getCrudPanel($this->getActiveController());
}

// Prioritize explicit controller context
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$controller = null;

foreach ($trace as $step) {
if (isset($step['class']) &&
is_a($step['class'], CrudControllerContract::class, true)) {
$controller = (string) $step['class'];
break;
}
}

if ($controller) {
$crudPanel = $this->getActiveCrudPanel($controller);

return $crudPanel;
}

$cruds = $this->getCrudPanels();

if (! empty($cruds)) {
$crudPanel = reset($cruds);

return $crudPanel;
}

$this->cruds[CrudController::class] = new CrudPanel();

return $this->cruds[CrudController::class];
}

/**
* Get all registered CrudPanel instances.
*
* @return array<string, CrudPanel> Array of CrudPanel instances indexed by controller class name
*/
public function getCrudPanels(): array
{
return $this->cruds;
}
}
2 changes: 1 addition & 1 deletion src/ThemeServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class ThemeServiceProvider extends ServiceProvider
protected string $packageName = 'theme-name';
protected array $commands = [];
protected bool $theme = true;
protected null|string $componentsNamespace = null;
protected ?string $componentsNamespace = null;

/**
* -------------------------
Expand Down
2 changes: 1 addition & 1 deletion src/app/Http/Controllers/Auth/VerifyEmailController.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

class VerifyEmailController extends Controller
{
public null|string $redirectTo = null;
public ?string $redirectTo = null;

/**
* Create a new controller instance.
Expand Down
7 changes: 7 additions & 0 deletions src/app/Http/Controllers/Contracts/CrudControllerContract.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

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

interface CrudControllerContract
{
}
Loading
Loading