@@ -58,31 +58,273 @@ public function getCrudPanel(CrudControllerContract|string $controller): CrudPan
5858 */
5959 public function setupCrudPanel (string $ controller , ?string $ operation = null ): CrudPanel
6060 {
61+ // Resolve potential active controller and ensure we have an instance
6162 $ controller = $ this ->getActiveController () ?? $ controller ;
62-
6363 $ controller = is_string ($ controller ) ? app ($ controller ) : $ controller ;
6464
6565 $ crud = $ this ->getCrudPanel ($ controller );
6666
6767 // Use provided operation or default to 'list'
6868 $ operation = $ operation ?? 'list ' ;
69- $ crud ->setOperation ($ operation );
7069
70+ $ shouldIsolate = $ this ->shouldIsolateOperation ($ controller ::class, $ operation );
71+
72+ // primary controller request is used when doing a full initialization
7173 $ primaryControllerRequest = $ this ->cruds [array_key_first ($ this ->cruds )]->getRequest ();
74+
75+ // If the panel is already initialized but a different operation is requested
76+ // and we don't need to isolate that operation, do a simple setup and return early.
77+ if ($ crud ->isInitialized () && $ crud ->getOperation () !== $ operation && ! $ shouldIsolate ) {
78+ return $ this ->performSimpleOperationSwitch ($ controller , $ operation , $ crud );
79+ }
80+
81+ // If the panel (or this specific operation) hasn't been initialized yet,
82+ // perform the required initialization (full or operation-specific).
7283 if (! $ crud ->isInitialized () || ! $ this ->isOperationInitialized ($ controller ::class, $ operation )) {
73- self ::setActiveController ($ controller ::class);
84+ return $ this ->performInitialization ($ controller , $ operation , $ crud , $ primaryControllerRequest , $ shouldIsolate );
85+ }
86+
87+ // Already initialized and operation matches: nothing to do.
88+ return $ this ->cruds [$ controller ::class];
89+ }
90+
91+ /**
92+ * Perform a lightweight operation switch when the panel is initialized and
93+ * isolation is not required.
94+ */
95+ private function performSimpleOperationSwitch ($ controller , string $ operation , CrudPanel $ crud ): CrudPanel
96+ {
97+ self ::setActiveController ($ controller ::class);
98+
99+ $ crud ->setOperation ($ operation );
100+ $ this ->setupSpecificOperation ($ controller , $ operation , $ crud );
101+
102+ // Mark this operation as initialized
103+ $ this ->storeInitializedOperation ($ controller ::class, $ operation );
104+
105+ self ::unsetActiveController ();
106+
107+ return $ this ->cruds [$ controller ::class];
108+ }
109+
110+ /**
111+ * Perform full or operation-specific initialization when needed.
112+ */
113+ private function performInitialization ($ controller , string $ operation , CrudPanel $ crud , $ primaryControllerRequest , bool $ shouldIsolate ): CrudPanel
114+ {
115+ self ::setActiveController ($ controller ::class);
116+
117+ // If the panel isn't initialized at all, do full initialization
118+ if (! $ crud ->isInitialized ()) {
119+ // Set the operation for full initialization
120+ $ crud ->setOperation ($ operation );
74121 $ crud ->initialized = false ;
75- self ::setActiveController ($ controller ::class);
76122 $ controller ->initializeCrudPanel ($ primaryControllerRequest , $ crud );
77- self ::unsetActiveController ();
78- $ crud = $ this ->cruds [$ controller ::class];
79-
80- return $ this ->cruds [$ controller ::class];
123+ } else {
124+ // Panel is initialized, just setup this specific operation
125+ if ($ shouldIsolate ) {
126+ $ this ->setupIsolatedOperation ($ controller , $ operation , $ crud );
127+ } else {
128+ // Set the operation for standard setup
129+ $ crud ->setOperation ($ operation );
130+ $ this ->setupSpecificOperation ($ controller , $ operation , $ crud );
131+ }
81132 }
82133
134+ // Mark this operation as initialized
135+ $ this ->storeInitializedOperation ($ controller ::class, $ operation );
136+
137+ self ::unsetActiveController ();
138+
83139 return $ this ->cruds [$ controller ::class];
84140 }
85141
142+ /**
143+ * Determine if an operation should be isolated to prevent state interference.
144+ *
145+ * @param string $controller
146+ * @param string $operation
147+ * @return bool
148+ */
149+ private function shouldIsolateOperation (string $ controller , string $ operation ): bool
150+ {
151+ $ currentCrud = $ this ->cruds [$ controller ] ?? null ;
152+ if (! $ currentCrud ) {
153+ return false ;
154+ }
155+
156+ $ currentOperation = $ currentCrud ->getOperation ();
157+
158+ // If operations don't differ, no need to isolate
159+ if (! $ currentOperation || $ currentOperation === $ operation ) {
160+ return false ;
161+ }
162+
163+ // Check backtrace for components implementing IsolatesOperationSetup
164+ $ backtrace = debug_backtrace (DEBUG_BACKTRACE_PROVIDE_OBJECT , 10 );
165+
166+ foreach ($ backtrace as $ trace ) {
167+ if (isset ($ trace ['object ' ])) {
168+ $ object = $ trace ['object ' ];
169+
170+ // If we find a component that implements the marker interface,
171+ // it signals that the operation setup should be isolated.
172+ if ($ object instanceof \Backpack \CRUD \app \View \Components \Contracts \IsolatesOperationSetup) {
173+ return true ;
174+ }
175+ }
176+ }
177+
178+ return true ;
179+ }
180+
181+ /**
182+ * Setup an operation in isolation without affecting the main CRUD panel state.
183+ * This creates a temporary context for operation setup without state interference.
184+ *
185+ * @param object $controller The controller instance
186+ * @param string $operation The operation to setup
187+ * @param CrudPanel $crud The CRUD panel instance
188+ */
189+ private function setupIsolatedOperation ($ controller , string $ operation , CrudPanel $ crud ): void
190+ {
191+ // Store the complete current state
192+ $ originalOperation = $ crud ->getOperation ();
193+ $ originalSettings = $ crud ->settings ();
194+ $ originalColumns = $ crud ->columns (); // Use the direct method, not operation setting
195+ $ originalRoute = $ crud ->route ?? null ;
196+ $ originalEntityName = $ crud ->entity_name ?? null ;
197+ $ originalEntityNamePlural = $ crud ->entity_name_plural ?? null ;
198+
199+ // Store operation-specific settings generically
200+ $ originalOperationSettings = $ this ->extractOperationSettings ($ crud , $ originalOperation );
201+
202+ // Temporarily setup the requested operation
203+ $ crud ->setOperation ($ operation );
204+
205+ // Use the controller's own method to setup the operation properly
206+ $ reflection = new \ReflectionClass ($ controller );
207+ $ method = $ reflection ->getMethod ('setupConfigurationForCurrentOperation ' );
208+ $ method ->setAccessible (true );
209+ $ method ->invoke ($ controller , $ operation );
210+
211+ // Completely restore the original state
212+ $ crud ->setOperation ($ originalOperation );
213+
214+ // CRITICAL: Properly restore columns by clearing and re-adding them
215+ // This is essential to preserve list operation columns
216+ $ crud ->removeAllColumns ();
217+ foreach ($ originalColumns as $ column ) {
218+ $ crud ->addColumn ($ column );
219+ }
220+
221+ // Restore all original settings one by one, but skip complex objects
222+ foreach ($ originalSettings as $ key => $ value ) {
223+ try {
224+ // Skip complex objects that Laravel generates dynamically
225+ if (is_object ($ value ) && (
226+ $ value instanceof \Illuminate \Routing \UrlGenerator ||
227+ $ value instanceof \Illuminate \Http \Request ||
228+ $ value instanceof \Illuminate \Contracts \Foundation \Application ||
229+ $ value instanceof \Closure ||
230+ method_exists ($ value , '__toString ' ) === false
231+ )) {
232+ continue ;
233+ }
234+
235+ $ crud ->set ($ key , $ value );
236+ } catch (\Exception $ e ) {
237+ // Silently continue with restoration
238+ }
239+ }
240+
241+ // Restore operation-specific settings generically
242+ $ this ->restoreOperationSettings ($ crud , $ originalOperation , $ originalOperationSettings );
243+
244+ // Restore core properties if they were changed
245+ if ($ originalRoute !== null ) {
246+ $ crud ->route = $ originalRoute ;
247+ }
248+ if ($ originalEntityName !== null ) {
249+ $ crud ->entity_name = $ originalEntityName ;
250+ }
251+ if ($ originalEntityNamePlural !== null ) {
252+ $ crud ->entity_name_plural = $ originalEntityNamePlural ;
253+ }
254+ }
255+
256+ /**
257+ * Extract all settings for a specific operation.
258+ *
259+ * @param CrudPanel $crud The CRUD panel instance
260+ * @param string $operation The operation name
261+ * @return array Array of operation-specific settings
262+ */
263+ private function extractOperationSettings (CrudPanel $ crud , string $ operation ): array
264+ {
265+ $ settings = $ crud ->settings ();
266+ $ operationSettings = [];
267+ $ operationPrefix = $ operation .'. ' ;
268+
269+ foreach ($ settings as $ key => $ value ) {
270+ if (str_starts_with ($ key , $ operationPrefix )) {
271+ $ operationSettings [$ key ] = $ value ;
272+ }
273+ }
274+
275+ return $ operationSettings ;
276+ }
277+
278+ /**
279+ * Restore all settings for a specific operation.
280+ *
281+ * @param CrudPanel $crud The CRUD panel instance
282+ * @param string $operation The operation name
283+ * @param array $operationSettings The settings to restore
284+ */
285+ private function restoreOperationSettings (CrudPanel $ crud , string $ operation , array $ operationSettings ): void
286+ {
287+ foreach ($ operationSettings as $ key => $ value ) {
288+ try {
289+ // Skip complex objects that Laravel generates dynamically
290+ if (is_object ($ value ) && (
291+ $ value instanceof \Illuminate \Routing \UrlGenerator ||
292+ $ value instanceof \Illuminate \Http \Request ||
293+ $ value instanceof \Illuminate \Contracts \Foundation \Application ||
294+ $ value instanceof \Closure ||
295+ method_exists ($ value , '__toString ' ) === false
296+ )) {
297+ continue ;
298+ }
299+
300+ $ crud ->set ($ key , $ value );
301+ } catch (\Exception $ e ) {
302+ // Silently continue with restoration
303+ }
304+ }
305+ }
306+
307+ /**
308+ * Setup a specific operation without reinitializing the entire CRUD panel.
309+ *
310+ * @param object $controller The controller instance
311+ * @param string $operation The operation to setup
312+ * @param CrudPanel $crud The CRUD panel instance
313+ */
314+ private function setupSpecificOperation ($ controller , string $ operation , CrudPanel $ crud ): void
315+ {
316+ // Setup the specific operation using the existing CrudController infrastructure
317+ $ crud ->setOperation ($ operation );
318+
319+ $ controller ->setup ();
320+
321+ // Use the controller's own method to setup the operation properly
322+ $ reflection = new \ReflectionClass ($ controller );
323+ $ method = $ reflection ->getMethod ('setupConfigurationForCurrentOperation ' );
324+ $ method ->setAccessible (true );
325+ $ method ->invoke ($ controller , $ operation );
326+ }
327+
86328 /**
87329 * Check if a specific operation has been initialized for a controller.
88330 */
0 commit comments