Skip to content

Commit c80d637

Browse files
pxpmStyleCIBottabacitu
authored
datatable component (#5688)
Co-authored-by: StyleCI Bot <[email protected]> Co-authored-by: Cristian Tabacitu <[email protected]>
1 parent d901ea7 commit c80d637

38 files changed

+4058
-1133
lines changed

src/BackpackServiceProvider.php

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Backpack\Basset\Facades\Basset;
66
use Backpack\CRUD\app\Http\Middleware\EnsureEmailVerification;
77
use Backpack\CRUD\app\Http\Middleware\ThrottlePasswordRecovery;
8-
use Backpack\CRUD\app\Library\CrudPanel\CrudPanel;
98
use Backpack\CRUD\app\Library\Database\DatabaseSchema;
109
use Backpack\CRUD\app\Library\Uploaders\Support\UploadersRepository;
1110
use Illuminate\Contracts\Debug\ExceptionHandler;
@@ -78,13 +77,18 @@ public function register()
7877
$this->loadViewsWithFallbacks('crud');
7978
$this->loadViewsWithFallbacks('ui', 'backpack.ui');
8079
$this->loadViewNamespace('widgets', 'backpack.ui::widgets');
80+
ViewNamespaces::addFor('widgets', 'crud::widgets');
81+
8182
$this->loadViewComponents();
8283

8384
$this->registerBackpackErrorViews();
8485

85-
// Bind the CrudPanel object to Laravel's service container
86-
$this->app->scoped('crud', function ($app) {
87-
return new CrudPanel();
86+
$this->app->bind('crud', function ($app) {
87+
return CrudManager::identifyCrudPanel();
88+
});
89+
90+
$this->app->scoped('CrudManager', function ($app) {
91+
return new CrudPanelManager();
8892
});
8993

9094
$this->app->scoped('DatabaseSchema', function ($app) {
@@ -181,7 +185,7 @@ public function publishFiles()
181185
/**
182186
* Define the routes for the application.
183187
*
184-
* @param \Illuminate\Routing\Router $router
188+
* @param Router $router
185189
* @return void
186190
*/
187191
public function setupRoutes(Router $router)
@@ -200,7 +204,7 @@ public function setupRoutes(Router $router)
200204
/**
201205
* Load custom routes file.
202206
*
203-
* @param \Illuminate\Routing\Router $router
207+
* @param Router $router
204208
* @return void
205209
*/
206210
public function setupCustomRoutes(Router $router)
@@ -333,7 +337,7 @@ public function loadHelpers()
333337
*/
334338
public function provides()
335339
{
336-
return ['crud', 'widgets', 'BackpackViewNamespaces', 'DatabaseSchema', 'UploadersRepository'];
340+
return ['widgets', 'BackpackViewNamespaces', 'DatabaseSchema', 'UploadersRepository', 'CrudManager'];
337341
}
338342

339343
private function registerBackpackErrorViews()

src/CrudManager.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
namespace Backpack\CRUD;
4+
5+
use Illuminate\Support\Facades\Facade;
6+
7+
/**
8+
* @see CrudPanelManager
9+
*/
10+
class CrudManager extends Facade
11+
{
12+
protected static function getFacadeAccessor()
13+
{
14+
return 'CrudManager';
15+
}
16+
}

src/CrudPanelManager.php

Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
<?php
2+
3+
namespace Backpack\CRUD;
4+
5+
use Backpack\CRUD\app\Http\Controllers\Contracts\CrudControllerContract;
6+
use Backpack\CRUD\app\Http\Controllers\CrudController;
7+
use Backpack\CRUD\app\Library\CrudPanel\CrudPanel;
8+
use Illuminate\Support\Facades\Facade;
9+
10+
/**
11+
* CrudPanelManager - Central registry and factory for CRUD panels.
12+
*
13+
* This class manages multiple CrudPanel instances across different controllers.
14+
* It acts as a singleton registry that:
15+
* - Creates and stores CrudPanel instances for each controller
16+
* - Tracks which operations have been initialized for each controller
17+
* - Manages the currently active controller context
18+
* - Provides methods to retrieve the appropriate CrudPanel based on context
19+
*
20+
* This allows multiple CRUD controllers to coexist and share state properly
21+
* within a single request lifecycle.
22+
*/
23+
final class CrudPanelManager
24+
{
25+
/** @var array<string, CrudPanel> Registry of CrudPanel instances indexed by controller class name */
26+
private array $cruds = [];
27+
28+
/** @var array<string, array<string>> Tracks which operations have been initialized for each controller */
29+
private array $initializedOperations = [];
30+
31+
/** @var string|null The currently active controller class name */
32+
private ?string $currentlyActiveCrudController = null;
33+
34+
/**
35+
* Get or create a CrudPanel instance for the given controller.
36+
*/
37+
public function getCrudPanel(CrudControllerContract|string $controller): CrudPanel
38+
{
39+
$controllerClass = is_string($controller) ? $controller : get_class($controller);
40+
41+
if (isset($this->cruds[$controllerClass])) {
42+
return $this->cruds[$controllerClass];
43+
}
44+
45+
$instance = new CrudPanel();
46+
47+
$this->cruds[$controllerClass] = $instance;
48+
49+
return $this->cruds[$controllerClass];
50+
}
51+
52+
/**
53+
* Setup and initialize a CrudPanel for the given controller and operation.
54+
*
55+
* @param string $controller The controller class name
56+
* @param string|null $operation The operation to set (defaults to 'list')
57+
* @return CrudPanel The initialized CrudPanel instance
58+
*/
59+
public function setupCrudPanel(string $controller, ?string $operation = null): CrudPanel
60+
{
61+
$controller = $this->getActiveController() ?? $controller;
62+
63+
$controller = is_string($controller) ? app($controller) : $controller;
64+
65+
$crud = $this->getCrudPanel($controller);
66+
67+
// Use provided operation or default to 'list'
68+
$operation = $operation ?? 'list';
69+
$crud->setOperation($operation);
70+
71+
$primaryControllerRequest = $this->cruds[array_key_first($this->cruds)]->getRequest();
72+
if (! $crud->isInitialized()) {
73+
self::setActiveController($controller::class);
74+
$controller->initializeCrudPanel($primaryControllerRequest, $crud);
75+
self::unsetActiveController();
76+
$crud = $this->cruds[$controller::class];
77+
78+
return $this->cruds[$controller::class];
79+
}
80+
81+
return $this->cruds[$controller::class];
82+
}
83+
84+
/**
85+
* Record that an operation has been initialized for a controller.
86+
*
87+
* @param string $controller The controller class name
88+
* @param string $operation The operation name (e.g., 'list', 'create', 'update')
89+
*/
90+
public function storeInitializedOperation(string $controller, ?string $operation): void
91+
{
92+
if (! $operation) {
93+
return;
94+
}
95+
$this->initializedOperations[$controller][] = $operation;
96+
}
97+
98+
/**
99+
* Get the list of operations that have been initialized for a controller.
100+
*
101+
* @param string $controller The controller class name
102+
* @return array<string> Array of initialized operation names
103+
*/
104+
public function getInitializedOperations(string $controller): array
105+
{
106+
return $this->initializedOperations[$controller] ?? [];
107+
}
108+
109+
/**
110+
* Store a CrudPanel instance for a specific controller.
111+
*/
112+
public function storeCrudPanel(string $controller, CrudPanel $crud): void
113+
{
114+
$this->cruds[$controller] = $crud;
115+
}
116+
117+
/**
118+
* Check if a CrudPanel exists for the given controller.
119+
*/
120+
public function hasCrudPanel(string $controller): bool
121+
{
122+
return isset($this->cruds[$controller]);
123+
}
124+
125+
/**
126+
* Get the active CrudPanel for a controller, with fallback logic.
127+
*
128+
* @param string $controller The controller class name
129+
* @return CrudPanel The CrudPanel instance, creating one if necessary
130+
*/
131+
public function getActiveCrudPanel(string $controller): CrudPanel
132+
{
133+
if (! isset($this->cruds[$controller])) {
134+
return $this->getCrudPanel($this->getActiveController() ?? $this->getParentController() ?? $controller);
135+
}
136+
137+
return $this->cruds[$controller];
138+
}
139+
140+
/**
141+
* Get the parent (first registered) controller class name.
142+
*
143+
* @return string|null The parent controller class name or null if none exists
144+
*/
145+
public function getParentController(): ?string
146+
{
147+
if (! empty($this->cruds)) {
148+
return array_key_first($this->cruds);
149+
}
150+
151+
return $this->getActiveController();
152+
}
153+
154+
/**
155+
* Set the currently active controller and clear the CRUD facade cache.
156+
*
157+
* @param string $controller The controller class name to set as active
158+
*/
159+
public function setActiveController(string $controller): void
160+
{
161+
Facade::clearResolvedInstance('crud');
162+
$this->currentlyActiveCrudController = $controller;
163+
}
164+
165+
/**
166+
* Get the currently active controller class name.
167+
*
168+
* @return string|null The active controller class name or null if none is set
169+
*/
170+
public function getActiveController(): ?string
171+
{
172+
return $this->currentlyActiveCrudController;
173+
}
174+
175+
/**
176+
* Clear the currently active controller.
177+
*/
178+
public function unsetActiveController(): void
179+
{
180+
$this->currentlyActiveCrudController = null;
181+
}
182+
183+
/**
184+
* Intelligently identify and return the appropriate CrudPanel based on context.
185+
*
186+
* This method uses multiple strategies to find the correct CrudPanel:
187+
* 1. Use the currently active controller if set
188+
* 2. Analyze the call stack to find a CRUD controller in the backtrace
189+
* 3. Return the first available CrudPanel if any exist
190+
* 4. Create a default CrudPanel as a last resort
191+
*
192+
* @return CrudPanel The identified or created CrudPanel instance
193+
*/
194+
public function identifyCrudPanel(): CrudPanel
195+
{
196+
if ($this->getActiveController()) {
197+
return $this->getCrudPanel($this->getActiveController());
198+
}
199+
200+
// Prioritize explicit controller context
201+
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
202+
$controller = null;
203+
204+
foreach ($trace as $step) {
205+
if (isset($step['class']) &&
206+
is_a($step['class'], CrudControllerContract::class, true)) {
207+
$controller = (string) $step['class'];
208+
break;
209+
}
210+
}
211+
212+
if ($controller) {
213+
$crudPanel = $this->getActiveCrudPanel($controller);
214+
215+
return $crudPanel;
216+
}
217+
218+
$cruds = $this->getCrudPanels();
219+
220+
if (! empty($cruds)) {
221+
$crudPanel = reset($cruds);
222+
223+
return $crudPanel;
224+
}
225+
226+
$this->cruds[CrudController::class] = new CrudPanel();
227+
228+
return $this->cruds[CrudController::class];
229+
}
230+
231+
/**
232+
* Get all registered CrudPanel instances.
233+
*
234+
* @return array<string, CrudPanel> Array of CrudPanel instances indexed by controller class name
235+
*/
236+
public function getCrudPanels(): array
237+
{
238+
return $this->cruds;
239+
}
240+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Backpack\CRUD\app\Http\Controllers\Contracts;
4+
5+
interface CrudControllerContract
6+
{
7+
}

0 commit comments

Comments
 (0)