diff --git a/app/RSpade/Core/Bundle/BundleCompiler.php b/app/RSpade/Core/Bundle/BundleCompiler.php index 0a2e51ac2..fcb33ac68 100755 --- a/app/RSpade/Core/Bundle/BundleCompiler.php +++ b/app/RSpade/Core/Bundle/BundleCompiler.php @@ -2088,7 +2088,7 @@ JS; console_debug('BUNDLE', "Found controller: {$class_name}"); // Process methods with Route attributes - foreach ($file_info['methods'] ?? [] as $method_name => $method_data) { + foreach ($file_info['public_static_methods'] ?? [] as $method_name => $method_data) { foreach ($method_data['attributes'] ?? [] as $attr_name => $attr_instances) { if ($attr_name === 'Route') { // Get the first route pattern (index 0 is the first argument) @@ -2143,7 +2143,7 @@ JS; } // Process methods with Route attributes - foreach ($file_info['methods'] ?? [] as $method_name => $method_data) { + foreach ($file_info['public_static_methods'] ?? [] as $method_name => $method_data) { foreach ($method_data['attributes'] ?? [] as $attr_name => $attr_instances) { if ($attr_name === 'Route') { // Get the first route pattern (index 0 is the first argument) diff --git a/app/RSpade/Core/Dispatch/Dispatcher.php b/app/RSpade/Core/Dispatch/Dispatcher.php index 3e905bf6c..533f02c97 100755 --- a/app/RSpade/Core/Dispatch/Dispatcher.php +++ b/app/RSpade/Core/Dispatch/Dispatcher.php @@ -115,43 +115,108 @@ class Dispatcher \Log::debug("Dispatcher: No route found for: $url"); console_debug('DISPATCH', 'No route found for:', $url); - // No route found - try Main pre_dispatch and unhandled_route hooks - $params = array_merge(request()->all(), $extra_params); + // Check if this matches the default route pattern: /_/{controller}/{action} + if (preg_match('#^/_/([A-Za-z_][A-Za-z0-9_]*)/([A-Za-z_][A-Za-z0-9_]*)/?$#', $url, $matches)) { + $controller_name = $matches[1]; + $action_name = $matches[2]; - // First try Main pre_dispatch - $main_classes = Manifest::php_get_extending('Main_Abstract'); - foreach ($main_classes as $main_class) { - if (isset($main_class['fqcn']) && $main_class['fqcn']) { - $main_class_name = $main_class['fqcn']; - if (method_exists($main_class_name, 'pre_dispatch')) { - Debugger::console_debug('[DISPATCH]', 'Main::pre_dispatch'); - $result = $main_class_name::pre_dispatch($request, $params); - if ($result !== null) { - $response = static::__build_response($result); + console_debug('DISPATCH', 'Matched default route pattern:', $controller_name, '::', $action_name); - return static::__transform_response($response, $original_method); - } + // Try to find the controller using manifest + try { + $metadata = Manifest::php_get_metadata_by_class($controller_name); + $controller_fqcn = $metadata['fqcn']; + + // Verify it extends Rsx_Controller_Abstract + if (!Manifest::php_is_subclass_of($controller_name, 'Rsx_Controller_Abstract')) { + console_debug('DISPATCH', 'Class does not extend Rsx_Controller_Abstract:', $controller_name); + return null; } + + // Verify the method exists and has a Route attribute + if (!isset($metadata['public_static_methods'][$action_name])) { + console_debug('DISPATCH', 'Method not found:', $action_name); + return null; + } + + $method_data = $metadata['public_static_methods'][$action_name]; + if (!isset($method_data['attributes']['Route'])) { + console_debug('DISPATCH', 'Method does not have Route attribute:', $action_name); + return null; + } + + // For POST requests: execute the action + if ($route_method === 'POST') { + // Collect parameters from POST data and query string + $params = array_merge($request->query->all(), $request->request->all(), $extra_params); + + // Create synthetic route match + $route_match = [ + 'class' => $controller_fqcn, + 'method' => $action_name, + 'params' => $params, + 'pattern' => "/_/{$controller_name}/{$action_name}", + 'require' => $method_data['attributes']['Auth'] ?? [] + ]; + + // Continue with normal dispatch (will handle auth, pre_dispatch, etc.) + // Fall through to normal route handling below + } else { + // For GET requests: redirect to the proper route + $params = array_merge($request->query->all(), $extra_params); + + // Generate proper URL using Rsx::Route + $proper_url = Rsx::Route($controller_name, $action_name)->url($params); + + console_debug('DISPATCH', 'Redirecting GET to proper route:', $proper_url); + + return redirect($proper_url, 302); + } + } catch (\RuntimeException $e) { + console_debug('DISPATCH', 'Controller not found in manifest:', $controller_name); + return null; } } - // Then try unhandled_route hook - foreach ($main_classes as $main_class) { - if (isset($main_class['fqcn']) && $main_class['fqcn']) { - $main_class_name = $main_class['fqcn']; - if (method_exists($main_class_name, 'unhandled_route')) { - $result = $main_class_name::unhandled_route($request, $params); - if ($result !== null) { - $response = static::__build_response($result); + if (!$route_match) { + // No route found - try Main pre_dispatch and unhandled_route hooks + $params = array_merge(request()->all(), $extra_params); - return static::__transform_response($response, $original_method); + // First try Main pre_dispatch + $main_classes = Manifest::php_get_extending('Main_Abstract'); + foreach ($main_classes as $main_class) { + if (isset($main_class['fqcn']) && $main_class['fqcn']) { + $main_class_name = $main_class['fqcn']; + if (method_exists($main_class_name, 'pre_dispatch')) { + Debugger::console_debug('[DISPATCH]', 'Main::pre_dispatch'); + $result = $main_class_name::pre_dispatch($request, $params); + if ($result !== null) { + $response = static::__build_response($result); + + return static::__transform_response($response, $original_method); + } } } } - } - // Default 404 - return null to let Laravel handle it - return null; + // Then try unhandled_route hook + foreach ($main_classes as $main_class) { + if (isset($main_class['fqcn']) && $main_class['fqcn']) { + $main_class_name = $main_class['fqcn']; + if (method_exists($main_class_name, 'unhandled_route')) { + $result = $main_class_name::unhandled_route($request, $params); + if ($result !== null) { + $response = static::__build_response($result); + + return static::__transform_response($response, $original_method); + } + } + } + } + + // Default 404 - return null to let Laravel handle it + return null; + } } // Extract route information diff --git a/app/RSpade/Core/Js/Rsx.js b/app/RSpade/Core/Js/Rsx.js index eae2a6c89..94ce1c67f 100755 --- a/app/RSpade/Core/Js/Rsx.js +++ b/app/RSpade/Core/Js/Rsx.js @@ -156,6 +156,9 @@ class Rsx { * The proxy ensures all required route parameters are provided and handles extra parameters * as query string values. * + * If the route is not found in the route definitions, a default pattern is used: + * `/_/{controller}/{action}` with all parameters appended as query strings. + * * Usage examples: * ```javascript * // Simple route without parameters (defaults to 'index' action) @@ -177,6 +180,10 @@ class Rsx { * }); * // Returns: /clients/view/C001?tab=history * + * // Route not found - uses default pattern + * const url = Rsx.Route('Unimplemented_Controller', 'some_action').url({foo: 'bar'}); + * // Returns: /_/Unimplemented_Controller/some_action?foo=bar + * * // Generate absolute URL * const absolute = Rsx.Route('Frontend_Index_Controller').absolute_url(); * // Returns: https://example.com/dashboard @@ -194,20 +201,17 @@ class Rsx { * @param {string} class_name The controller class name (e.g., 'User_Controller') * @param {string} [action_name='index'] The action/method name (defaults to 'index') * @returns {Rsx_Route_Proxy} Route proxy instance for URL generation - * @throws {Error} If route not found */ static Route(class_name, action_name = 'index') { - // Check if route exists - if (!Rsx._routes[class_name]) { - throw new Error(`Class ${class_name} not found in routes`); + // Check if route exists in definitions + if (Rsx._routes[class_name] && Rsx._routes[class_name][action_name]) { + const pattern = Rsx._routes[class_name][action_name]; + return new Rsx_Route_Proxy(class_name, action_name, pattern); } - if (!Rsx._routes[class_name][action_name]) { - throw new Error(`Method ${action_name} not found in class ${class_name}`); - } - - const pattern = Rsx._routes[class_name][action_name]; - return new Rsx_Route_Proxy(class_name, action_name, pattern); + // Route not found - use default pattern /_/{controller}/{action} + const default_pattern = `/_/${class_name}/${action_name}`; + return new Rsx_Route_Proxy(class_name, action_name, default_pattern); } /**