Add semantic token highlighting for 'that' variable and comment file references in VS Code extension Add Phone_Text_Input and Currency_Input components with formatting utilities Implement client widgets, form standardization, and soft delete functionality Add modal scroll lock and update documentation Implement comprehensive modal system with form integration and validation Fix modal component instantiation using jQuery plugin API Implement modal system with responsive sizing, queuing, and validation support Implement form submission with validation, error handling, and loading states Implement country/state selectors with dynamic data loading and Bootstrap styling Revert Rsx::Route() highlighting in Blade/PHP files Target specific PHP scopes for Rsx::Route() highlighting in Blade Expand injection selector for Rsx::Route() highlighting Add custom syntax highlighting for Rsx::Route() and Rsx.Route() calls Update jqhtml packages to v2.2.165 Add bundle path validation for common mistakes (development mode only) Create Ajax_Select_Input widget and Rsx_Reference_Data controller Create Country_Select_Input widget with default country support Initialize Tom Select on Select_Input widgets Add Tom Select bundle for enhanced select dropdowns Implement ISO 3166 geographic data system for country/region selection Implement widget-based form system with disabled state support 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
458 lines
14 KiB
Plaintext
Executable File
458 lines
14 KiB
Plaintext
Executable File
CONTROLLER(3) RSX Framework Manual CONTROLLER(3)
|
|
|
|
NAME
|
|
Controller - RSX request handling and routing system
|
|
|
|
SYNOPSIS
|
|
use App\RSpade\Core\Controller\Rsx_Controller_Abstract;
|
|
|
|
class User_Controller extends Rsx_Controller_Abstract
|
|
{
|
|
#[Auth('Permission::authenticated()')]
|
|
#[Route('/users', methods: ['GET'])]
|
|
public static function index(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('User_List');
|
|
}
|
|
|
|
#[Auth('Permission::authenticated()')]
|
|
#[Ajax_Endpoint]
|
|
public static function get_profile(Request $request, array $params = [])
|
|
{
|
|
return ['name' => 'John', 'email' => 'john@example.com'];
|
|
}
|
|
}
|
|
|
|
DESCRIPTION
|
|
RSX Controllers provide a simplified approach to request handling through
|
|
static methods and automatic discovery. Unlike Laravel's dependency
|
|
injection heavy controllers that require constructor injection and
|
|
service container resolution, RSX uses static methods that can be
|
|
called from anywhere without instantiation.
|
|
|
|
The framework automatically discovers controllers through the manifest
|
|
system - no manual registration in route files required. Routes are
|
|
defined directly on methods using attributes, keeping routing logic
|
|
with the code it affects.
|
|
|
|
Key differences from Laravel:
|
|
- Laravel: Instance methods with dependency injection
|
|
- RSX: Static methods with explicit parameters
|
|
|
|
- Laravel: Routes defined in separate routes/*.php files
|
|
- RSX: Routes defined via #[Route] attributes on methods
|
|
|
|
- Laravel: Manual API resource controllers
|
|
- RSX: Automatic JavaScript stub generation for Ajax methods
|
|
|
|
- Laravel: Middleware defined in route files or constructors
|
|
- RSX: pre_dispatch hooks for authentication/authorization
|
|
|
|
Benefits:
|
|
- No dependency injection complexity
|
|
- Routes live with their handlers
|
|
- Automatic Ajax/JavaScript integration
|
|
- Simple static method calls
|
|
- No service container overhead
|
|
|
|
CREATING A CONTROLLER
|
|
1. Extend Rsx_Controller_Abstract
|
|
2. Add static methods with Request and params parameters
|
|
3. Use attributes for routing
|
|
|
|
class Dashboard_Controller extends Rsx_Controller_Abstract
|
|
{
|
|
#[Route('/dashboard')]
|
|
public static function index(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('Dashboard');
|
|
}
|
|
}
|
|
|
|
ROUTE ATTRIBUTES
|
|
#[Route(path, methods)]
|
|
Define HTTP route.
|
|
path: URL pattern with optional parameters
|
|
methods: ['GET'] or ['POST'] (default both)
|
|
|
|
Examples:
|
|
#[Route('/users')]
|
|
#[Route('/users/{id}', methods: ['GET'])]
|
|
#[Route('/api/users', methods: ['POST'])]
|
|
|
|
ROUTE PARAMETERS
|
|
URL segments in braces become $params entries:
|
|
|
|
#[Route('/users/{id}/posts/{post_id}')]
|
|
public static function show(Request $request, array $params = [])
|
|
{
|
|
$user_id = $params['id'];
|
|
$post_id = $params['post_id'];
|
|
}
|
|
|
|
Query parameters also added to $params:
|
|
/users?sort=name
|
|
$params['sort'] === 'name'
|
|
|
|
REQUIRE ATTRIBUTE
|
|
#[Auth(callable, message, redirect, redirect_to)]
|
|
REQUIRED on all routes. Defines access control check.
|
|
callable: 'Class::method()' string to execute
|
|
message: Optional error message
|
|
redirect: Optional URL to redirect on failure (HTTP only)
|
|
redirect_to: Optional ['Controller', 'action'] (HTTP only)
|
|
|
|
All routes MUST have at least one #[Auth] attribute, either on:
|
|
- The route method itself
|
|
- The controller's pre_dispatch() method (applies to all routes)
|
|
- Both (pre_dispatch Require runs first, then route Require)
|
|
|
|
Multiple #[Auth] attributes are supported - all must pass.
|
|
|
|
Permission Method Contract:
|
|
public static function method_name(Request $request, array $params, ...$args): mixed
|
|
|
|
Returns:
|
|
- true or null: Allow access
|
|
- false: Deny access
|
|
- Response: Custom response (overrides default handling)
|
|
|
|
Examples:
|
|
// Public access
|
|
#[Auth('Permission::anybody()')]
|
|
#[Route('/')]
|
|
public static function index(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('Landing');
|
|
}
|
|
|
|
// Authenticated users only
|
|
#[Auth('Permission::authenticated()',
|
|
message: 'Please log in',
|
|
redirect: '/login')]
|
|
#[Route('/dashboard')]
|
|
public static function dashboard(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('Dashboard');
|
|
}
|
|
|
|
// Redirect using controller/action
|
|
#[Auth('Permission::authenticated()',
|
|
message: 'Login required',
|
|
redirect_to: ['Login_Index_Controller', 'show_login'])]
|
|
#[Route('/profile')]
|
|
public static function profile(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('Profile');
|
|
}
|
|
|
|
// Permission with arguments
|
|
#[Auth('Permission::has_role("admin")')]
|
|
#[Route('/admin')]
|
|
public static function admin_panel(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('Admin_Panel');
|
|
}
|
|
|
|
// Multiple requirements
|
|
#[Auth('Permission::authenticated()')]
|
|
#[Auth('Permission::has_permission("edit_users")')]
|
|
#[Route('/users/edit')]
|
|
public static function edit_users(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('User_Edit');
|
|
}
|
|
|
|
// Controller-wide requirement
|
|
class Admin_Controller extends Rsx_Controller_Abstract
|
|
{
|
|
#[Auth('Permission::has_role("admin")',
|
|
message: 'Admin access required',
|
|
redirect: '/')]
|
|
public static function pre_dispatch(Request $request, array $params = [])
|
|
{
|
|
return null;
|
|
}
|
|
|
|
// All routes in this controller require admin role
|
|
#[Route('/admin/users')]
|
|
public static function users(Request $request, array $params = [])
|
|
{
|
|
return rsx_view('Admin_Users');
|
|
}
|
|
}
|
|
|
|
Creating Permission Methods (rsx/permission.php):
|
|
class Permission extends Permission_Abstract
|
|
{
|
|
public static function anybody(Request $request, array $params): mixed
|
|
{
|
|
return true; // Always allow
|
|
}
|
|
|
|
public static function authenticated(Request $request, array $params): mixed
|
|
{
|
|
return Session::is_logged_in();
|
|
}
|
|
|
|
public static function has_role(Request $request, array $params, string $role): mixed
|
|
{
|
|
if (!Session::is_logged_in()) {
|
|
return false;
|
|
}
|
|
return Session::get_user()->has_role($role);
|
|
}
|
|
}
|
|
|
|
AJAX ENDPOINTS AND REQUIRE
|
|
Ajax endpoints also require #[Auth] attributes.
|
|
For Ajax endpoints, redirect parameters are ignored and JSON errors returned:
|
|
|
|
#[Auth('Permission::authenticated()',
|
|
message: 'Login required')]
|
|
#[Ajax_Endpoint]
|
|
public static function get_data(Request $request, array $params = [])
|
|
{
|
|
return ['data' => 'value'];
|
|
}
|
|
|
|
On failure, returns:
|
|
{
|
|
"success": false,
|
|
"error": "Login required",
|
|
"error_type": "permission_denied"
|
|
}
|
|
|
|
HTTP Status: 403 Forbidden
|
|
|
|
API_INTERNAL ATTRIBUTE
|
|
#[Ajax_Endpoint]
|
|
Makes method callable via Ajax.
|
|
Generates JavaScript stub automatically.
|
|
Returns JSON response.
|
|
|
|
PHP:
|
|
#[Ajax_Endpoint]
|
|
public static function search(Request $request, array $params = [])
|
|
{
|
|
$query = $params['query'] ?? '';
|
|
return User::where('name', 'like', "%$query%")->get();
|
|
}
|
|
|
|
JavaScript (auto-generated):
|
|
const results = await User_Controller.search({query: 'john'});
|
|
|
|
JAVASCRIPT STUB GENERATION
|
|
Controllers with Ajax_Endpoint methods get stubs in
|
|
storage/rsx-build/js-stubs/ControllerName.js:
|
|
|
|
class User_Controller {
|
|
static async search(...args) {
|
|
return Ajax.call('User_Controller', 'search', args);
|
|
}
|
|
}
|
|
|
|
Stubs included automatically in bundles.
|
|
|
|
CALLING API METHODS
|
|
From JavaScript:
|
|
// Single argument
|
|
const user = await User_Controller.get_user(123);
|
|
|
|
// Named parameters
|
|
const results = await User_Controller.search({
|
|
query: 'john',
|
|
limit: 10
|
|
});
|
|
|
|
// Multiple arguments
|
|
const data = await User_Controller.process(id, options);
|
|
|
|
From PHP:
|
|
User_Controller::get_user($request, ['id' => 123]);
|
|
|
|
ROUTE RESOLUTION
|
|
PHP:
|
|
$url = Rsx::Route('User_Controller', 'show', ['id' => 5]);
|
|
// Returns: "/users/5"
|
|
|
|
if (Rsx::Route('User_Controller')->is_current()) {
|
|
// Current page is users index
|
|
}
|
|
|
|
JavaScript:
|
|
const url = Rsx.Route('User_Controller', 'show', {id: 5});
|
|
// Returns: "/users/5"
|
|
|
|
PRE_DISPATCH HOOK
|
|
Run before any action in controller:
|
|
|
|
public static function pre_dispatch(Request $request, array $params = [])
|
|
{
|
|
// Check authentication
|
|
if (!RsxAuth::check()) {
|
|
return redirect('/login');
|
|
}
|
|
|
|
// Return null to continue
|
|
return null;
|
|
}
|
|
|
|
Return non-null to override response.
|
|
|
|
RESPONSE TYPES
|
|
Views:
|
|
return rsx_view('View_Name', ['data' => $value]);
|
|
|
|
JSON (Ajax_Endpoint):
|
|
return ['key' => 'value']; // Auto-converted to JSON
|
|
|
|
Redirects:
|
|
return redirect('/path');
|
|
return redirect()->route('route.name');
|
|
|
|
Raw responses:
|
|
return response('content', 200);
|
|
return response()->json(['key' => 'value']);
|
|
|
|
AUTHENTICATION
|
|
Use RsxAuth in pre_dispatch:
|
|
|
|
public static function pre_dispatch(Request $request, array $params = [])
|
|
{
|
|
if (!RsxAuth::check()) {
|
|
if ($request->ajax()) {
|
|
return response()->json(['error' => 'Unauthorized'], 401);
|
|
}
|
|
return redirect('/login');
|
|
}
|
|
return null;
|
|
}
|
|
|
|
ERROR HANDLING
|
|
Throw exceptions for errors:
|
|
|
|
public static function show(Request $request, array $params = [])
|
|
{
|
|
$user = User::find($params['id']);
|
|
if (!$user) {
|
|
throw new NotFoundHttpException('User not found');
|
|
}
|
|
return rsx_view('User_Profile', ['user' => $user]);
|
|
}
|
|
|
|
AJAX VS PAGE REQUESTS
|
|
Detect Ajax requests:
|
|
|
|
if ($request->ajax()) {
|
|
return ['data' => $data]; // JSON
|
|
} else {
|
|
return rsx_view('Page', ['data' => $data]); // HTML
|
|
}
|
|
|
|
EXAMPLES
|
|
// CRUD controller
|
|
class Product_Controller extends Rsx_Controller_Abstract
|
|
{
|
|
#[Route('/products')]
|
|
public static function index(Request $request, array $params = [])
|
|
{
|
|
$products = Product::paginate(20);
|
|
return rsx_view('Product_List', compact('products'));
|
|
}
|
|
|
|
#[Route('/products/{id}')]
|
|
public static function show(Request $request, array $params = [])
|
|
{
|
|
$product = Product::findOrFail($params['id']);
|
|
return rsx_view('Product_Detail', compact('product'));
|
|
}
|
|
|
|
#[Ajax_Endpoint]
|
|
public static function create(Request $request, array $params = [])
|
|
{
|
|
$product = Product::create($params);
|
|
return $product;
|
|
}
|
|
|
|
#[Ajax_Endpoint]
|
|
public static function update(Request $request, array $params = [])
|
|
{
|
|
$product = Product::findOrFail($params['id']);
|
|
$product->update($params);
|
|
return $product;
|
|
}
|
|
|
|
#[Ajax_Endpoint]
|
|
public static function delete(Request $request, array $params = [])
|
|
{
|
|
Product::destroy($params['id']);
|
|
return ['success' => true];
|
|
}
|
|
}
|
|
|
|
// JavaScript usage
|
|
async function create_product(data) {
|
|
const product = await Product_Controller.create(data);
|
|
console.log('Created:', product);
|
|
}
|
|
|
|
async function update_product(id, data) {
|
|
const product = await Product_Controller.update({id, ...data});
|
|
console.log('Updated:', product);
|
|
}
|
|
|
|
ROUTE CACHING
|
|
Routes extracted from manifest and cached.
|
|
Clear cache after adding routes:
|
|
php artisan rsx:clean
|
|
|
|
NAMING CONVENTIONS
|
|
Controllers: Noun_Controller (User_Controller, Product_Controller)
|
|
Actions: verb or verb_noun (index, show, create, update_profile)
|
|
Routes: RESTful patterns (/users, /users/{id})
|
|
|
|
FILE ORGANIZATION
|
|
rsx/app/module/
|
|
├── module_controller.php # Main controller
|
|
├── module_api_controller.php # API endpoints
|
|
└── feature/
|
|
└── module_feature_controller.php
|
|
|
|
TESTING ROUTES
|
|
Use rsx:debug command:
|
|
php artisan rsx:debug /users
|
|
php artisan rsx:debug /api/search --post='{"query":"test"}'
|
|
|
|
TROUBLESHOOTING
|
|
Route not found:
|
|
- Check Route attribute syntax
|
|
- Run php artisan rsx:routes to list all
|
|
- Clear cache: php artisan rsx:clean
|
|
|
|
Missing #[Auth] attribute error:
|
|
- Add #[Auth('Permission::anybody()')] to route method
|
|
- OR add #[Auth] to pre_dispatch() for controller-wide access
|
|
- Rebuild manifest: php artisan rsx:manifest:build
|
|
|
|
Permission denied (403):
|
|
- Check permission method logic returns true
|
|
- Verify Session::is_logged_in() for authenticated routes
|
|
- Add message parameter for clearer errors
|
|
- Check permission method exists in rsx/permission.php
|
|
|
|
JavaScript stub missing:
|
|
- Ensure Ajax_Endpoint attribute present
|
|
- Ensure #[Auth] attribute present on Ajax method
|
|
- Rebuild manifest: php artisan rsx:manifest:build
|
|
- Check storage/rsx-build/js-stubs/
|
|
|
|
Authentication issues:
|
|
- Implement pre_dispatch hook
|
|
- Use Permission::authenticated() in Require
|
|
- Verify session configuration
|
|
|
|
SEE ALSO
|
|
manifest_api(3), bundle_api(3), jqhtml(3), rsx:routes(1)
|
|
|
|
RSX Framework 2025-09-17 CONTROLLER(3) |