NAME model_fetch - RSX Ajax ORM with secure model fetching from JavaScript SYNOPSIS Secure, controlled access to ORM models from JavaScript with explicit opt-in DESCRIPTION The RSX model fetch system allows JavaScript to securely access ORM models through an explicit opt-in mechanism. Unlike Laravel's default API routes which expose all model data, RSX requires each model to implement its own fetch() method with authorization and data filtering. Key differences from Laravel: - Laravel: API routes with resource controllers expose all fields - RSX: Each model implements custom fetch() with authorization Benefits: - Explicit security control per model - No mass exposure of sensitive data - Individual authorization checks for each record - Automatic JavaScript stub generation SECURITY MODEL Explicit Opt-In: Models must deliberately implement fetch() with the attribute. No models are fetchable by default. Individual Authorization: Each model controls who can fetch its records. Authorization checked for every single record. Data Filtering: Models can filter sensitive fields before returning data. Complete control over what data JavaScript receives. No Mass Fetching: Framework splits array requests into individual fetch calls. Prevents bulk data extraction without individual authorization. IMPLEMENTING FETCHABLE MODELS Required Components: 1. Override static fetch() method 2. Add #[Ajax_Endpoint_Model_Fetch] attribute 3. Accept exactly one parameter: $id (single ID only) 4. Implement authorization checks 5. Return model object or false Basic Implementation: use Ajax_Endpoint_Model_Fetch; class Product_Model extends Rsx_Model { #[Ajax_Endpoint_Model_Fetch] public static function fetch($id) { // Authorization check if (!RsxAuth::check()) { return false; } // Fetch single record $model = static::find($id); return $model ?: false; } } Advanced Authorization: class Order_Model extends Rsx_Model { #[Ajax_Endpoint_Model_Fetch] public static function fetch($id) { $user = RsxAuth::user(); if (!$user) { return false; } $order = static::find($id); if (!$order) { return false; } // Only allow access to own orders or admin users if ($order->user_id !== $user->id && !$user->is_admin) { return false; } return $order; } } Data Filtering: class User_Model extends Rsx_Model { #[Ajax_Endpoint_Model_Fetch] public static function fetch($id) { if (!RsxAuth::check()) { return false; } $user = static::find($id); if (!$user) { return false; } // Remove sensitive fields unset($user->password_hash); unset($user->remember_token); unset($user->email_verification_token); return $user; } } JAVASCRIPT USAGE Single Record Fetching: // Fetch single record const product = await Product_Model.fetch(1); if (product) { console.log(product.name); console.log(product.price); } // Handle fetch failure const order = await Order_Model.fetch(999); if (!order) { console.log('Order not found or access denied'); } Multiple Record Fetching: // Framework automatically splits array into individual calls const products = await Product_Model.fetch([1, 2, 3]); products.forEach(product => { if (product) { console.log(product.name); } }); // Mixed results (some succeed, some fail authorization) const orders = await Order_Model.fetch([101, 102, 103]); const validOrders = orders.filter(order => order !== false); Error Handling: try { const user = await User_Model.fetch(userId); if (user) { updateUserInterface(user); } else { showAccessDeniedMessage(); } } catch (error) { console.error('Fetch failed:', error); showErrorMessage(); } ARRAY HANDLING Framework Behavior: When JavaScript passes an array to fetch(), the framework: 1. Splits array into individual IDs 2. Calls fetch() once for each ID 3. Collects results maintaining array order 4. Returns array with same length (false for failed fetches) Implementation Rules: - NEVER use is_array($id) checks in fetch() method - Always handle exactly one ID parameter - Framework handles array splitting automatically - Results maintain original array order Example Results: // JavaScript call const results = await Product_Model.fetch([1, 2, 999]); // Results array (999 not found or unauthorized) [ {id: 1, name: "Product A"}, // Successful fetch {id: 2, name: "Product B"}, // Successful fetch false // Failed fetch ] STUB GENERATION Automatic JavaScript Stubs: The framework automatically generates JavaScript stub classes for models with #[Ajax_Endpoint_Model_Fetch] attributes. Stub Class Generation: // Generated stub for Product_Model class Product_Model { static async fetch(id) { // Generated implementation calls PHP fetch() method return await Rsx._internal_api_call('Product_Model', 'fetch', {id}); } } Bundle Integration: Stubs are automatically included in JavaScript bundles when models are discovered in the bundle's include paths. AUTHORIZATION PATTERNS User-Specific Access: class User_Profile_Model extends Rsx_Model { #[Ajax_Endpoint_Model_Fetch] public static function fetch($id) { $current_user = RsxAuth::user(); // Users can only fetch their own profile if (!$current_user || $current_user->id != $id) { return false; } return static::find($id); } } Role-Based Access: class Admin_Report_Model extends Rsx_Model { #[Ajax_Endpoint_Model_Fetch] public static function fetch($id) { $user = RsxAuth::user(); // Only admin users can fetch reports if (!$user || !$user->hasRole('admin')) { return false; } return static::find($id); } } Public Data Access: class Public_Article_Model extends Rsx_Model { #[Ajax_Endpoint_Model_Fetch] public static function fetch($id) { // Public articles can be fetched by anyone $article = static::find($id); // But only return published articles if ($article && $article->status === 'published') { return $article; } return false; } } BASE MODEL PROTECTION Default Behavior: The base Rsx_Model::fetch() method throws an exception with clear instructions on how to implement fetch() in your model. Exception Message: "Model MyModel does not implement fetch() method. To enable Ajax fetching, add #[Ajax_Endpoint_Model_Fetch] attribute and implement static fetch($id) method with authorization checks." Purpose: - Prevents accidental exposure of model data - Forces developers to explicitly handle security - Provides clear implementation guidance - Ensures no models are fetchable by default TESTING FETCH METHODS PHP Testing: // Test authorization $user = User_Model::factory()->create(); RsxAuth::login($user); $product = Product_Model::fetch(1); $this->assertNotFalse($product); RsxAuth::logout(); $product = Product_Model::fetch(1); $this->assertFalse($product); JavaScript Testing (via rsx:debug): php artisan rsx:debug /demo --eval="Product_Model.fetch(1).then(r => console.log(r))" COMMON PATTERNS Soft Delete Handling: public static function fetch($id) { // Only return non-deleted records $model = static::where('id', $id)->whereNull('deleted_at')->first(); return $model ?: false; } Relationship Preloading: public static function fetch($id) { $model = static::with(['category', 'tags'])->find($id); return $model ?: false; } Conditional Field Removal: public static function fetch($id) { $model = static::find($id); if (!$model) return false; $user = RsxAuth::user(); if (!$user || !$user->is_admin) { // Remove admin-only fields for non-admin users unset($model->internal_notes); unset($model->cost_price); } return $model; } TROUBLESHOOTING Model Not Fetchable: - Verify #[Ajax_Endpoint_Model_Fetch] attribute present - Check fetch() method is static and public - Ensure method accepts single $id parameter - Verify model included in bundle manifest Authorization Failures: - Check RsxAuth::check() and RsxAuth::user() values - Verify authorization logic in fetch() method - Test with different user roles and permissions - Use rsx_dump_die() to debug authorization flow JavaScript Stub Missing: - Verify model discovered during manifest build - Check bundle includes model's directory - Ensure bundle compiles without errors - Confirm JavaScript bundle loads in browser Array Handling Issues: - Never use is_array($id) in fetch() method - Framework handles array splitting automatically - Check for typos in model class names - Verify all IDs in array are valid integers SEE ALSO controller - Internal API attribute patterns manifest_api - Model discovery and stub generation coding_standards - Security patterns and authorization