Fix code quality violations and rename select input components

Move small tasks from wishlist to todo, update npm packages
Replace #[Auth] attributes with manual auth checks and code quality rule
Remove on_jqhtml_ready lifecycle method from framework
Complete ACL system with 100-based role indexing and /dev/acl tester
WIP: ACL system implementation with debug instrumentation
Convert rsx:check JS linting to RPC socket server
Clean up docs and fix $id→$sid in man pages, remove SSR/FPC feature
Reorganize wishlists: priority order, mark sublayouts complete, add email
Update model_fetch docs: mark MVP complete, fix enum docs, reorganize
Comprehensive documentation overhaul: clarity, compression, and critical rules
Convert Contacts/Projects CRUD to Model.fetch() and add fetch_or_null()
Add JS ORM relationship lazy-loading and fetch array handling
Add JS ORM relationship fetching and CRUD documentation
Fix ORM hydration and add IDE resolution for Base_* model stubs
Rename Json_Tree_Component to JS_Tree_Debug_Component and move to framework
Enhance JS ORM infrastructure and add Json_Tree class name badges

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
root
2025-11-23 21:39:43 +00:00
parent 78553d4edf
commit 84ca3dfe42
167 changed files with 7538 additions and 49164 deletions

View File

@@ -1,26 +1,25 @@
<!--
===============================================================================
WARNING: READ-ONLY FRAMEWORK DOCUMENTATION
FRAMEWORK DOCUMENTATION - READ ONLY
===============================================================================
This file contains RSpade framework documentation for AI/LLM assistants and is
maintained by the framework developers. It is marked read-only and will be
REPLACED during framework updates via `php artisan rsx:framework:pull`.
This file: /var/www/html/system/docs/CLAUDE.dist.md (symlinked to ~/.claude/CLAUDE.md)
Your file: /var/www/html/CLAUDE.md
DO NOT modify this file directly. Any changes you make will be lost during the
next framework update.
This file is replaced during `php artisan rsx:framework:pull`. Do not edit it.
For application-specific notes, edit /var/www/html/CLAUDE.md instead.
For application-specific documentation and project context:
- Edit the CLAUDE.md file in your project root
- That file is NOT managed by the framework and persists across updates
DOCUMENTATION SIZE CONSTRAINTS
------------------------------
Claude Code recommends combined CLAUDE.md files under 40kb - already tight for
framework + application documentation. Every line must justify its existence.
This separation ensures:
- Framework documentation stays current with updates
- Your project-specific documentation is preserved
- AI assistants have access to both framework and application context
When editing /var/www/html/CLAUDE.md:
- Terse, not verbose - no filler words or redundant explanations
- Complete, not partial - include all critical information
- Patterns over prose - code examples beat paragraphs
- Reference this file's tone and density as the standard
===============================================================================
-->
# RSpade Framework - AI/LLM Development Guide
@@ -35,7 +34,7 @@ This separation ensures:
**Visual Basic-like development for PHP/Laravel.** Think: VB6 apps → VB6 runtime → Windows = RSX apps → RSpade runtime → Laravel.
**Philosophy**: Modern anti-modernization. While JavaScript fragments into complexity and React demands quarterly paradigm shifts, RSpade asks: "What if coding was easy again?" Business apps need quick builds and easy maintenance, not bleeding-edge architecture.
**Philosophy**: RSpade is rebellion against JavaScript fatigue. Like VB6 made Windows programming accessible, RSpade makes web development simple again. No build steps, no config hell, just code and reload.
**Important**: RSpade is built on Laravel but diverges significantly. Do not assume Laravel patterns work in RSX without verification.
@@ -45,38 +44,32 @@ This separation ensures:
## CRITICAL RULES
### 🔴 RSpade Builds Automatically - NEVER RUN BUILD COMMANDS
### CRITICAL: RSpade Builds Automatically - NEVER RUN BUILD COMMANDS
**RSpade is an INTERPRETED framework** - like Python or PHP, changes are automatically detected and compiled on-the-fly. There is NO manual build step.
**RSpade is INTERPRETED** - changes compile on-the-fly. NO manual build steps.
**ABSOLUTELY FORBIDDEN** (unless explicitly instructed):
- `npm run compile` / `npm run build` - **DO NOT EXIST**
- `bin/publish` - Creates releases for OTHER developers (not for testing YOUR changes)
- `rsx:bundle:compile` - Bundles compile automatically in dev mode
- `rsx:manifest:build` - Manifest rebuilds automatically in dev mode
- ANY command with "build", "compile", or "publish"
**FORBIDDEN** (unless explicitly instructed):
- `npm run compile/build` - Don't exist
- `bin/publish` - For releases, not testing
- `rsx:bundle:compile` / `rsx:manifest:build` - Automatic
- ANY "build", "compile", or "publish" command
**How it works**:
1. Edit JS/SCSS/PHP files
2. Refresh browser
3. Changes are live (< 1 second)
Edit → Save → Refresh browser → Changes live (< 1 second)
**If you find yourself wanting to run build commands**: STOP. You're doing something wrong. Changes are already live.
### 🔴 Framework Updates
### Framework Updates
```bash
php artisan rsx:framework:pull # 5-minute timeout required
php artisan rsx:framework:pull # User-initiated only
```
Updates take 2-5 minutes. Includes code pull, manifest rebuild, bundle recompilation. **For AI: Always use 5-minute timeout.**
Updates take 2-5 minutes. Includes code pull, manifest rebuild, bundle recompilation. Only run when requested by user.
### 🔴 Fail Loud - No Silent Fallbacks
### Fail Loud - No Silent Fallbacks
**ALWAYS fail visibly.** No redundant fallbacks, silent failures, or alternative code paths.
```php
// ❌ CATASTROPHIC
// ❌ WRONG
try { $clean = Sanitizer::sanitize($input); }
catch (Exception $e) { $clean = $input; } // DISASTER
@@ -84,29 +77,9 @@ catch (Exception $e) { $clean = $input; } // DISASTER
$clean = Sanitizer::sanitize($input); // Let it throw
```
**SECURITY-CRITICAL**: If sanitization/validation/auth fails, NEVER continue. Always throw immediately.
**SECURITY-CRITICAL**: Never continue after security failures. Only catch expected failures (file uploads, APIs, user input). Let exceptions bubble to global handler.
**NO BLANKET TRY/CATCH**: Use try/catch only for expected failures (file uploads, external APIs, user input parsing). NEVER wrap database operations or entire functions "just in case".
```php
// ❌ WRONG - Defensive "on error resume"
try {
$user->save();
$result = process_data($user);
return $result;
} catch (Exception $e) {
throw new Exception("Failed: " . $e->getMessage(), 0, $e);
}
// ✅ CORRECT - Let exceptions bubble
$user->save();
$result = process_data($user);
return $result;
```
Exception handlers format errors for different contexts (Ajax JSON, CLI, HTML). Don't wrap exceptions with generic messages - let them bubble to the global handler.
### 🔴 No Defensive Coding
### No Defensive Coding
Core classes ALWAYS exist. Never check.
@@ -115,11 +88,11 @@ Core classes ALWAYS exist. Never check.
// ✅ GOOD: Rsx.Route('Controller::method')
```
### 🔴 Static-First Philosophy
### Static-First Philosophy
Classes are namespacing tools. Use static unless instances needed (models, resources). Avoid dependency injection.
### 🔴 Git Workflow - Framework is READ-ONLY
### Git Workflow - Framework is READ-ONLY
**NEVER modify `/var/www/html/system/`** - It's like node_modules or the Linux kernel.
@@ -129,7 +102,7 @@ Classes are namespacing tools. Use static unless instances needed (models, resou
**Commit discipline**: ONLY commit when explicitly asked. Commits are milestones, not individual changes.
### 🔴 DO NOT RUN `rsx:clean`
### DO NOT RUN `rsx:clean`
**RSpade's cache auto-invalidates on file changes.** Running `rsx:clean` causes 30-60 second rebuilds with zero benefit.
@@ -137,7 +110,7 @@ Classes are namespacing tools. Use static unless instances needed (models, resou
**Correct workflow**: Edit → Save → Reload browser → See changes (< 1 second)
### 🔴 Trust Code Quality Rules
### Trust Code Quality Rules
Each `rsx:check` rule has remediation text that tells AI assistants exactly what to do:
- Some rules say "fix immediately"
@@ -168,12 +141,16 @@ Files sharing a common prefix are a related set. When renaming, maintain the gro
```
frontend_calendar_event.scss
frontend_calendar_event_controller.php
frontend_calendar_event.blade.php
frontend_calendar_event.jqhtml
frontend_calendar_event.js
```
**Critical**: Never create same-name different-case files (e.g., `user.php` and `User.php`).
### Component Naming Pattern
Input components follow: `{Supertype}_{Variant}_{Supertype}` → e.g., `Select_Country_Input`, `Select_State_Input`, `Select_Ajax_Input`
---
## DIRECTORY STRUCTURE
@@ -183,12 +160,15 @@ frontend_calendar_event.js
├── rsx/ # YOUR CODE
│ ├── app/ # Modules
│ ├── models/ # Database models
│ ├── services/ # Background tasks, external integrations
│ ├── public/ # Static files (web-accessible)
│ ├── resource/ # Framework-ignored
│ └── theme/ # Global assets
└── system/ # FRAMEWORK (read-only)
```
**Services**: Extend `Rsx_Service_Abstract` for non-HTTP functionality like scheduled tasks or external system integrations.
### Special Directories (Path-Agnostic)
**`resource/`** - ANY directory named this is framework-ignored. Store helpers, docs, third-party code. Exception: `/rsx/resource/config/` IS processed.
@@ -221,7 +201,12 @@ Merged via `array_merge_deep()`. Common overrides: `development.auto_rename_file
```php
class Frontend_Controller extends Rsx_Controller_Abstract
{
#[Auth('Permission::anybody()')]
public static function pre_dispatch(Request $request, array $params = [])
{
if (!Session::is_logged_in()) return response_unauthorized();
return null;
}
#[Route('/', methods: ['GET'])]
public static function index(Request $request, array $params = [])
{
@@ -232,17 +217,22 @@ class Frontend_Controller extends Rsx_Controller_Abstract
}
```
**Rules**: Only GET/POST allowed. Use `:param` syntax. All routes MUST have `#[Auth]`.
**Rules**: Only GET/POST. Use `:param` syntax. Manual auth checks in pre_dispatch or method body.
### #[Auth] Attribute
### Authentication Pattern
```php
#[Auth('Permission::anybody()')] // Public
#[Auth('Permission::authenticated()')] // Require login
#[Auth('Permission::has_role("admin")')] // Custom
// Controller-wide auth (recommended)
public static function pre_dispatch(Request $request, array $params = []) {
if (!Session::is_logged_in()) return response_unauthorized();
return null;
}
// Public endpoints: add @auth-exempt to class docblock
/** @auth-exempt Public route */
```
**Controller-wide**: Add to `pre_dispatch()`. Multiple attributes = all must pass.
**Code quality**: PHP-AUTH-01 rule verifies auth checks exist. Use `@auth-exempt` for public routes.
### Type-Safe URLs
@@ -283,17 +273,19 @@ Client-side routing for authenticated application areas. One PHP bootstrap contr
### SPA Components
**1. PHP Bootstrap Controller (ONE per feature/bundle)**
**1. PHP Bootstrap Controller** - ONE per module with auth in pre_dispatch
```php
class Frontend_Spa_Controller extends Rsx_Controller_Abstract {
#[SPA]
#[Auth('Permission::authenticated()')]
public static function index(Request $request, array $params = []) {
return rsx_view(SPA);
}
public static function pre_dispatch(Request $request, array $params = []) {
if (!Session::is_logged_in()) return response_unauthorized();
return null;
}
#[SPA]
public static function index(Request $request, array $params = []) {
return rsx_view(SPA);
}
```
**CRITICAL**: One #[SPA] per feature/bundle (e.g., `/app/frontend`, `/app/root`, `/app/login`). Bundles separate features to save bandwidth, reduce processing time, and segregate confidential code (e.g., root admin from unauthorized users). The #[SPA] bootstrap performs server-side auth checks with failure/redirect before loading client-side actions. Typically one #[SPA] per feature at `rsx/app/(feature)/(feature)_spa_controller::index`.
One #[SPA] per module at `rsx/app/(module)/(module)_spa_controller::index`. Segregates code by permission level.
**2. JavaScript Actions (MANY)**
```javascript
@@ -350,12 +342,17 @@ class Contacts_View_Action extends Spa_Action {
### File Organization
Pattern: `/rsx/app/(module)/(feature)/`
- **Module**: Major functionality (login, frontend, root)
- **Feature**: Screen within module (contacts, reports, invoices)
- **Submodule**: Feature grouping (settings), often with sublayouts
```
/rsx/app/frontend/
/rsx/app/frontend/ # Module
├── Frontend_Spa_Controller.php # Single SPA bootstrap
├── Frontend_Layout.js
├── Frontend_Layout.jqhtml
└── contacts/
└── contacts/ # Feature
├── frontend_contacts_controller.php # Ajax endpoints only
├── Contacts_Index_Action.js # /contacts
├── Contacts_Index_Action.jqhtml
@@ -366,6 +363,10 @@ class Contacts_View_Action extends Spa_Action {
**Use SPA for:** Authenticated areas, dashboards, admin panels
**Avoid for:** Public pages (SEO needed), simple static pages
### Sublayouts
**Sublayouts** are `Spa_Layout` classes for nested persistent UI (e.g., settings sidebar). Use multiple `@layout` decorators - first is outermost: `@layout('Frontend_Spa_Layout')` then `@layout('Settings_Layout')`. Each must have `$sid="content"`. Layouts persist when unchanged; only differing parts recreated. All receive `on_action(url, action_name, args)` with final action info.
Details: `php artisan rsx:man spa`
### View Action Pattern (Loading Data)
@@ -396,149 +397,47 @@ Template uses three states: `<Loading_Spinner>` → `<Universal_Error_Page_Compo
## CONVERTING BLADE PAGES TO SPA ACTIONS
**7-step procedure to convert server-side pages to client-side SPA:**
**1. Create Action Files**
```bash
# In feature directory
touch Feature_Index_Action.js Feature_Index_Action.jqhtml
```
**2. Create Action Class (.js)**
```javascript
@route('/path')
@layout('Frontend_Spa_Layout')
@spa('Frontend_Spa_Controller::index')
@title('Page Title')
class Feature_Index_Action extends Spa_Action {
full_width = true; // For DataGrid pages
async on_load() {
this.data.items = await Feature_Controller.fetch_items();
}
}
```
**3. Convert Template (.jqhtml)**
**Blade → jqhtml syntax:**
- `{{ $var }}``<%= this.data.var %>`
- `{!! $html !!}``<%!= this.data.html %>`
- `@if($cond)``<% if (this.data.cond) { %>`
- `@foreach($items as $item)``<% for (let item of this.data.items) { %>`
- `@endforeach``<% } %>`
- `{{-- comment --}}``<%-- comment --%>`
- `{{ Rsx::Route('Class') }}``<%= Rsx.Route('Class') %>`
```jqhtml
<Define:Feature_Index_Action>
<Page>
<Page_Header><Page_Title>Title</Page_Title></Page_Header>
<% for (let item of this.data.items) { %>
<div><%= item.name %></div>
<% } %>
</Page>
</Define:Feature_Index_Action>
```
**4. Update Controller - Remove server-side route entirely:**
```php
// Remove #[Route] method completely. Add Ajax endpoints:
#[Ajax_Endpoint]
public static function fetch_items(Request $request, array $params = []) {
return ['items' => Feature_Model::all()];
}
```
**CRITICAL**: Do NOT add `#[SPA]` to feature controllers. The `#[SPA]` attribute only exists in the bootstrap controller (e.g., `Frontend_Spa_Controller::index`). Feature controllers should only contain `#[Ajax_Endpoint]` methods for data fetching.
**5. Update Route References**
**Search entire codebase for old route references:**
```bash
grep -r "Feature_Controller::method" rsx/app/
```
Find/replace in all files:
- `Rsx::Route('Feature_Controller::method')``Rsx::Route('Feature_Action')`
- `Rsx.Route('Feature_Controller::method')``Rsx.Route('Feature_Action')`
- Hardcoded `/feature/method/123``Rsx.Route('Feature_Action', 123)`
**Check:** DataGrids, dashboards, save endpoints, navigation, breadcrumbs
**6. Archive Old Files**
```bash
mkdir -p rsx/resource/archive/frontend/feature/
mv feature_index.blade.php rsx/resource/archive/frontend/feature/
```
**7. Test**
```bash
php artisan rsx:debug /path
```
Verify: No JS errors, page renders, data loads.
**Common Patterns:**
**DataGrid (no data loading):**
```javascript
class Items_Index_Action extends Spa_Action {
full_width = true;
async on_load() {} // DataGrid loads own data
}
```
**Detail page (load data):**
```javascript
@route('/items/:id')
class Items_View_Action extends Spa_Action {
async on_load() {
this.data.item = await Items_Controller.get({id: this.args.id});
}
}
```
For converting server-side Blade pages to client-side SPA actions, see `php artisan rsx:man blade_to_spa`.
The process involves creating Action classes with @route decorators and converting templates from Blade to jqhtml syntax.
---
## BLADE & VIEWS
**Note**: SPA pages are the preferred standard. Use Blade only for SEO-critical public pages or authentication flows.
```blade
@rsx_id('Frontend_Index') {{-- Every view starts with this --}}
<body class="{{ rsx_body_class() }}"> {{-- Adds view class --}}
@rsx_include('Component_Name') {{-- Include by name, not path --}}
```
**NO inline styles, scripts, or event handlers** in Blade views:
- No `<style>` tags
- No `<script>` tags
- No inline event handlers: `onclick=""`, `onchange=""`, etc. are **forbidden**
- Use companion `.scss` and `.js` files instead
- Exception: jqhtml templates may use `@click` directive syntax (jqhtml-specific feature)
**Why no inline handlers**:
- Violates separation of concerns (HTML structure vs behavior)
- Makes code harder to maintain and test
- `rsx:check` will flag inline event handlers and require refactoring
**Correct pattern**:
```php
// Blade view - NO event handlers
<button id="submit-btn" class="btn btn-primary">Submit</button>
// Companion .js file
$('#submit-btn').click(() => {
// Handle click
});
```
**NO inline styles, scripts, or event handlers** - Use companion `.scss` and `.js` files.
**jqhtml components** work fully in Blade (no slots).
### SCSS Pairing
For page/component-specific styles, wrap all rules in a class matching the component name:
```scss
/* frontend_index.scss - Same directory as view */
.Frontend_Index { /* Matches @rsx_id */
/* my_component.scss - Scoped to component */
.My_Component { /* Matches component class or @rsx_id */
.content { padding: 20px; }
/* All component-specific rules here */
}
```
**Convention**: Each Blade view, jqhtml action, or component automatically gets its name as a class on its root element, enabling scoped styling.
### JavaScript for Blade Pages
Unlike SPA actions (which use component lifecycle), Blade pages use static `on_app_ready()` with a page guard:
```javascript
class My_Page { // Matches @rsx_id('My_Page')
static on_app_ready() {
if (!$('.My_Page').exists()) return; // Guard required - fires for ALL pages in bundle
// Page code here
}
}
```
@@ -565,27 +464,13 @@ Use when data doesn't belong in DOM attributes. Multiple calls merge together.
## JAVASCRIPT
### Auto-Initialization
```javascript
class Frontend_Index {
static async on_app_ready() {
// DOM ready
}
static async on_jqhtml_ready() {
// Components ready
}
}
```
**CRITICAL**: JavaScript only executes when bundle rendered.
**CRITICAL**: JavaScript only executes when bundle rendered. See "JavaScript for Blade Pages" in BLADE & VIEWS section for the `on_app_ready()` pattern.
---
## BUNDLE SYSTEM
**One bundle per page required.**
**One bundle per page required.** Compiles JS/CSS automatically on request - no manual build steps.
```php
class Frontend_Bundle extends Rsx_Bundle_Abstract
@@ -599,17 +484,12 @@ class Frontend_Bundle extends Rsx_Bundle_Abstract
'rsx/theme/variables.scss', // Order matters
'rsx/app/frontend', // Directory
'rsx/models', // For JS stubs
'/public/vendor/css/core.css', // Public directory asset (filemtime cache-busting)
],
];
}
}
```
Bundles support `/public/` prefix for including static assets from public directories with automatic cache-busting.
Auto-compiles on page reload in development.
```blade
<head>
{!! Frontend_Bundle::render() !!}
@@ -626,7 +506,7 @@ For mechanical thinkers who see structure, not visuals. Write `<User_Card>` not
### Template Syntax
**🔴 CRITICAL: `<Define>` IS the element, not a wrapper**
**CRITICAL: `<Define>` IS the element, not a wrapper**
```jqhtml
<!-- ✅ CORRECT - Define becomes button -->
@@ -648,17 +528,51 @@ Example: `<input <% if (this.args.required) { %>required="required"<% } %> />`
**Event Handlers**: `@click=this.method` (unquoted) - Methods defined inline or in companion .js
**Validation**: `<% if (!this.args.required) throw new Error('Missing arg'); %>` - Fail loud in template
### 🔴 State Management Rules (ENFORCED)
### Simple Components (No JS File Needed)
For simple components without external data or complex state, write JS directly in the template:
```jqhtml
<Define:CSV_Renderer>
<%
// Validate input
if (!this.args.csv_data) throw new Error('csv_data required');
// Parse CSV
const rows = this.args.csv_data.split('\n').map(r => r.split(','));
// Define click handler inline
this.toggle = () => { this.args.expanded = !this.args.expanded; this.render(); };
%>
<table>
<% for (let row of rows) { %>
<tr>
<% for (let cell of row) { %>
<td><%= cell %></td>
<% } %>
</tr>
<% } %>
</table>
<button @click=this.toggle>Toggle View</button>
</Define:CSV_Renderer>
```
**When to use inline JS**: Simple data transformations, conditionals, loops, basic event handlers
**When to create .js file**: External data loading, complex state management, multiple methods, or when JS overwhelms the template (should look mostly like HTML with some logic, not JS with some HTML)
### State Management Rules (ENFORCED)
**Quick Guide:**
- Loading from API? → Use `this.data` in `on_load()`
- Need reload with different params? → Modify `this.args`, call `reload()`
- UI state (toggles, selections)? → Use `this.state`
**this.args** - Component arguments (read-only in on_load(), modifiable elsewhere)
**this.data** - Ajax-loaded data (writable ONLY in on_create() and on_load())
**this.state** - Arbitrary component state (modifiable anytime)
**Quick Guide:**
- Loading from API? Use `this.data` in `on_load()`
- Need to reload with new params? Modify `this.args`, call `reload()`
- UI state (toggles, counters, etc)? Use `this.state`
```javascript
// WITH Ajax data
class Users_List extends Component {
@@ -739,17 +653,27 @@ async on_load() {
### Component Access
**this.$sid(name)** → jQuery object (for DOM):
```javascript
this.$sid('button').on('click', ...);
```
**$sid** attribute = "scoped ID" - unique within component instance
**this.sid(name)** → Component instance (for methods):
```javascript
const comp = this.sid('child'); // ✅ Returns component
await comp.reload();
From within component methods:
- **this.$** → jQuery selector for the component element itself
- **this.$sid(name)** → jQuery selector for child element with `$sid="name"`
- **this.sid(name)** → Component instance of child (or null if not a component)
- **$(selector).component()** → Get component instance from jQuery element
- **`await $(selector).component().ready()`** → Await component initialization. Rarely needed - `on_ready()` auto-waits for children created during render. Use for dynamically created components or Blade page JS interaction.
const comp = this.sid('child').component(); // ❌ WRONG
### Dynamic Component Creation
To dynamically create/replace a component in JavaScript:
```javascript
// Destroys existing component (if any) and creates new one in its place
$(selector).component('Component_Name', { arg1: value1, arg2: value2 });
// Example: render a component into a container
this.$sid('result_container').component('My_Component', {
data: myData,
some_option: true
});
```
### Incremental Scaffolding
@@ -763,18 +687,17 @@ const comp = this.sid('child').component(); // ❌ WRONG
</Dashboard>
```
### Common Pitfalls
### Key Pitfalls (ABSOLUTE RULES)
1. `<Define>` IS the element - use `tag=""` attribute
2. `this.data` starts empty `{}`, set defaults in on_create()
3. ONLY modify `this.data` in on_create() and on_load() (enforced)
4. on_load() can ONLY access this.args and this.data (enforced)
5. Use `this.state = {}` in on_create() for component state (not loaded from Ajax)
6. Use this.args for reload parameters, reload() to re-fetch
7. Use `Controller.method()` not `$.ajax()`
8. Blade components self-closing only
9. `on_create/render/stop` must be sync
10. Use this.sid() for components, NOT this.sid().component()
2. `this.data` starts empty `{}` - MUST set defaults in `on_create()`
3. ONLY modify `this.data` in `on_create()` and `on_load()` (enforced by framework)
4. `on_load()` can ONLY access `this.args` and `this.data` (no DOM, no `this.state`)
5. Use `this.state = {}` in `on_create()` for UI state (not from Ajax)
6. Use `this.args` for reload parameters, call `reload()` to re-fetch
7. Use `Controller.method()` not `$.ajax()` - PHP methods with #[Ajax_Endpoint] auto-callable from JS
8. `on_create/render/stop` must be sync
9. `this.sid()` returns component instance, `$(selector).component()` converts jQuery to component
### Bundle Integration Required
@@ -891,27 +814,16 @@ Details: `php artisan rsx:man modals`
## JQUERY EXTENSIONS
RSpade extends jQuery with utility methods:
| Method | Purpose |
|--------|---------|
| `.exists()` | Check element exists (instead of `.length > 0`) |
| `.shallowFind(selector)` | Find children without nested component interference |
| `.closest_sibling(selector)` | Search within ancestor hierarchy |
| `.checkValidity()` | Form validation helper |
| `.click()` | Auto-prevents default |
| `.click_allow_default()` | Native click behavior |
**Element existence**: `$('.element').exists()` instead of `.length > 0`
**Component traversal**: `this.$.shallowFind('.Widget')` - Finds child elements matching selector that don't have another element of the same class as a parent between them and the component. Prevents selecting widgets from nested child components.
```javascript
// Use case: Finding form widgets without selecting nested widgets
this.$.shallowFind('.Form_Field').each(function() {
// Only processes fields directly in this form,
// not fields in nested sub-forms
});
```
**Sibling component lookup**: `$('.element').closest_sibling('.Widget')` - Searches for elements within progressively higher ancestors. Like `.closest()` but searches within ancestors instead of matching them. Stops at body tag. Useful for component-to-component communication.
**Form validation**: `$('form').checkValidity()` instead of `$('form')[0].checkValidity()`
**Click override**: `.click()` automatically calls `e.preventDefault()`. Use `.click_allow_default()` for native behavior.
For complete details: `php artisan rsx:man jquery`
Details: `php artisan rsx:man jquery`
---
@@ -931,7 +843,7 @@ User_Model::create(['email' => $email]);
### Enums
**🔴 Read `php artisan rsx:man enum` for complete documentation before implementing.**
**CRITICAL: Read `php artisan rsx:man enum` for complete documentation before implementing.**
Integer-backed enums with model-level mapping to constants, labels, and custom properties.
@@ -953,6 +865,32 @@ echo $project->status_badge; // "bg-success" (custom property)
**Migration:** Use BIGINT for enum columns, TINYINT(1) for booleans. Run `rsx:migrate:document_models` after adding enums.
### Model Fetch
```php
#[Ajax_Endpoint_Model_Fetch]
public static function fetch($id)
{
if (!Session::is_logged_in()) return false;
return static::find($id);
}
```
```javascript
const project = await Project_Model.fetch(1); // Throws if not found
const maybe = await Project_Model.fetch_or_null(999); // Returns null if not found
console.log(project.status_label); // Enum properties populated
console.log(Project_Model.STATUS_ACTIVE); // Static enum constants
// Lazy relationships (requires #[Ajax_Endpoint_Model_Fetch] on relationship method)
const client = await project.client(); // belongsTo → Model or null
const tasks = await project.tasks(); // hasMany → Model[]
```
**Security**: Both `fetch()` and relationships require `#[Ajax_Endpoint_Model_Fetch]` attribute. Related models must also implement `fetch()` with this attribute.
Details: `php artisan rsx:man model_fetch`
### Migrations
**Forward-only, no rollbacks.**
@@ -1003,7 +941,18 @@ public static function method(Request $request, array $params = []) {
}
```
**Call:** `await Controller.method({param: value})`
**PHP→JS Auto-mapping:**
```php
// PHP: My_Controller class
#[Ajax_Endpoint]
public static function save(Request $request, array $params = []) {
return ['id' => 123];
}
// JS: Automatically callable
const result = await My_Controller.save({name: 'Test'});
console.log(result.id); // 123
```
### Error Responses
@@ -1052,31 +1001,17 @@ Details: `php artisan rsx:man storage`
---
### Model Fetch
```php
#[Ajax_Endpoint_Model_Fetch]
public static function fetch($id)
{
if (!RsxAuth::check()) return false;
return static::find($id);
}
```
```javascript
const user = await User_Model.fetch(1);
```
---
## AUTHENTICATION
**Always use RsxAuth**, never Laravel Auth or $_SESSION.
**Always use Session** - Static methods only. Never Laravel Auth or $_SESSION.
```php
RsxAuth::check(); // Is authenticated
RsxAuth::user(); // User model
RsxAuth::id(); // User ID
Session::is_logged_in(); // Returns true if user logged in
Session::get_user(); // Returns user model or null
Session::get_user_id(); // Returns user ID or null
Session::get_site(); // Returns site model
Session::get_site_id(); // Returns current site ID
Session::get_session_id(); // Returns session ID
```
Sessions persist 365 days. Never implement "Remember Me".
@@ -1159,6 +1094,8 @@ Use for "impossible" conditions that indicate broken assumptions.
**Professional UI**: Hover effects ONLY on buttons, links, form fields. Static elements remain static.
**z-index**: Bootstrap defaults + 1100 (modal children), 1200 (flash alerts), 9000+ (system). Details: `rsx:man zindex`
Run `rsx:check` before commits. Enforces naming, prohibits animations on non-actionable elements.
---
@@ -1184,11 +1121,11 @@ class Main extends Main_Abstract
2. **Static by default** - Unless instances needed
3. **Path-agnostic** - Reference by name
4. **Bundles required** - For JavaScript
5. **Use RsxAuth** - Never Laravel Auth
5. **Use Session** - Never Laravel Auth
6. **No mass assignment** - Explicit only
7. **Forward migrations** - No rollbacks
8. **Don't run rsx:clean** - Cache auto-invalidates
9. **All routes need #[Auth]** - No exceptions
9. **All routes need auth checks** - In pre_dispatch() or method body (@auth-exempt for public)
---