Implement BEM-style enum naming and fetch() anti-aliasing policy
🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -487,6 +487,7 @@ Only the following rules are approved for manifest-time execution:
|
||||
- **JQHTML-INLINE-01** (JqhtmlInlineScriptRule): Prevents inline scripts/styles in Jqhtml template files (critical architecture violation)
|
||||
- **PHP-SPA-01** (SpaAttributeMisuseRule): Prevents combining #[SPA] with #[Route] attributes (critical architecture misunderstanding)
|
||||
- **MANIFEST-INST-01** (InstanceMethodsRule): Enforces static-only classes unless Instantiatable (framework convention)
|
||||
- **PHP-ALIAS-01** (FieldAliasingRule): Prevents field name shortenings in fetch() and Ajax endpoints (anti-aliasing policy)
|
||||
|
||||
All other rules should return `false` from `is_called_during_manifest_scan()`.
|
||||
|
||||
|
||||
@@ -5,33 +5,34 @@ namespace App\RSpade\CodeQuality\Rules\PHP;
|
||||
use App\RSpade\CodeQuality\Rules\CodeQualityRule_Abstract;
|
||||
|
||||
/**
|
||||
* FieldAliasingRule - Detects confusing field name shortenings
|
||||
* FieldAliasingRule - Enforces fetch() anti-aliasing policy
|
||||
*
|
||||
* This rule catches cases where a field name is SHORTENED by dropping parts,
|
||||
* which creates confusion about what the field actually represents.
|
||||
* fetch() exists for SECURITY (removing private data), not aliasing.
|
||||
*
|
||||
* VIOLATION - Dropping parts from a name (confusing):
|
||||
* 'type_label' => $contact->type_id_label, // Dropped "id" - what happened to it?
|
||||
* 'client_label' => $record->client_id_label, // Dropped "id" - confusing
|
||||
* 'name' => $user->display_name, // Dropped "display" - loses context
|
||||
* VALID PATTERNS:
|
||||
* 1. Model method with MATCHING name:
|
||||
* 'full_name' => $model->full_name()
|
||||
* 'unread_count' => $this->unread_count()
|
||||
*
|
||||
* ALLOWED - Renaming to a completely different concept:
|
||||
* 'value' => $client->id, // "value" is a UI concept, not a shortened "id"
|
||||
* 'label' => $client->name, // "label" is a UI concept, not a shortened "name"
|
||||
* 'client_id' => $client->id, // Adding context, not dropping it
|
||||
* 'id' => $client->id, // Same name - no aliasing
|
||||
* 2. Conditional with matching property/method or literals:
|
||||
* 'foo' => $condition ? $model->foo : null
|
||||
* 'secret' => $user->is_admin ? $model->secret : '[REDACTED]'
|
||||
*
|
||||
* ALLOWED - Transformations:
|
||||
* 'type_id_label_upper' => strtoupper($contact->type_id_label),
|
||||
* INVALID PATTERNS:
|
||||
* 1. Any property alias (key != property):
|
||||
* 'type_label' => $model->type_id__label // BAD
|
||||
*
|
||||
* The detection logic: Flag when the key's underscore-separated parts are a
|
||||
* PROPER SUBSET of the source property's parts (all key parts exist in source,
|
||||
* but source has additional parts). This catches "dropping parts" without
|
||||
* flagging legitimate renames to different concepts.
|
||||
* 2. Method call with mismatched name:
|
||||
* 'addr' => $model->formatted_address() // BAD - name must match
|
||||
*
|
||||
* 3. Redundant explicit assignments (unnecessary):
|
||||
* 'id' => $model->id // Already in toArray()
|
||||
*
|
||||
* Applies to:
|
||||
* - Controller methods with #[Ajax_Endpoint] attribute
|
||||
* - Model fetch() methods with #[Ajax_Endpoint_Model_Fetch] attribute
|
||||
*
|
||||
* NOT checked (controllers are an escape hatch for custom responses):
|
||||
* - Controller methods with #[Ajax_Endpoint] attribute
|
||||
*/
|
||||
class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
{
|
||||
@@ -48,7 +49,7 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
*/
|
||||
public function get_name(): string
|
||||
{
|
||||
return 'Field Aliasing Prohibition';
|
||||
return 'Fetch Anti-Aliasing Policy';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -56,7 +57,7 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
*/
|
||||
public function get_description(): string
|
||||
{
|
||||
return 'Prohibits renaming fields during serialization - field names must be consistent across all application layers';
|
||||
return 'Enforces fetch() anti-aliasing policy - fetch() is for security, not aliasing';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,7 +73,7 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
*/
|
||||
public function is_called_during_manifest_scan(): bool
|
||||
{
|
||||
return false; // Only run during rsx:check
|
||||
return true; // Immediate feedback on aliasing violations
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,21 +102,16 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine file type and get relevant methods
|
||||
// Only check models - controllers are an escape hatch for custom responses
|
||||
$extends = $metadata['extends'] ?? null;
|
||||
$methods_to_check = [];
|
||||
|
||||
if ($extends === 'Rsx_Controller_Abstract') {
|
||||
// Controller - check methods with #[Ajax_Endpoint]
|
||||
$methods_to_check = $this->get_ajax_endpoint_methods($metadata);
|
||||
} elseif ($extends === 'Rsx_Model_Abstract') {
|
||||
// Model - check fetch() method with #[Ajax_Endpoint_Model_Fetch]
|
||||
$methods_to_check = $this->get_model_fetch_methods($metadata);
|
||||
} else {
|
||||
// Not a controller or model we care about
|
||||
if ($extends !== 'Rsx_Model_Abstract') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check fetch() method with #[Ajax_Endpoint_Model_Fetch]
|
||||
$methods_to_check = $this->get_model_fetch_methods($metadata);
|
||||
|
||||
if (empty($methods_to_check)) {
|
||||
return;
|
||||
}
|
||||
@@ -138,29 +134,6 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get methods with #[Ajax_Endpoint] attribute from controller
|
||||
*/
|
||||
private function get_ajax_endpoint_methods(array $metadata): array
|
||||
{
|
||||
$methods = $metadata['public_static_methods'] ?? [];
|
||||
$result = [];
|
||||
|
||||
foreach ($methods as $method_name => $method_info) {
|
||||
$attributes = $method_info['attributes'] ?? [];
|
||||
|
||||
foreach ($attributes as $attr_name => $attr_data) {
|
||||
$short_name = basename(str_replace('\\', '/', $attr_name));
|
||||
if ($short_name === 'Ajax_Endpoint') {
|
||||
$result[$method_name] = $method_info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get fetch() methods with #[Ajax_Endpoint_Model_Fetch] attribute from model
|
||||
*/
|
||||
@@ -196,54 +169,82 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
$original_lines = explode("\n", $original_contents);
|
||||
|
||||
foreach ($lines as $offset => $line) {
|
||||
// Pattern: 'key' => $var->property or 'key' => $var['property']
|
||||
// We need to detect when key != property (with no transformation)
|
||||
$actual_line_num = $method_start_line + $offset;
|
||||
|
||||
// Match: 'key_name' => $something->property_name
|
||||
// or: 'key_name' => $something['property_name']
|
||||
// Without function wrapping
|
||||
// Check for line-level exception
|
||||
if ($this->line_has_exception($original_lines, $actual_line_num)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pattern for object property access: 'key' => $var->prop
|
||||
if (preg_match("/['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]\\s*=>\\s*\\$[a-zA-Z_][a-zA-Z0-9_]*->([a-zA-Z_][a-zA-Z0-9_]*)\\s*[,\\]\\)]/", $line, $matches)) {
|
||||
$key = $matches[1];
|
||||
$property = $matches[2];
|
||||
// Pattern: 'key' => ...
|
||||
// We need to analyze what's on the right side
|
||||
if (!preg_match("/['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]\\s*=>/", $line, $key_match)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($this->is_problematic_alias($key, $property) && !$this->has_transformation($line, $matches[0])) {
|
||||
// Check for line-level exception
|
||||
$actual_line_num = $method_start_line + $offset;
|
||||
if ($this->line_has_exception($original_lines, $actual_line_num)) {
|
||||
continue;
|
||||
}
|
||||
$key = $key_match[1];
|
||||
|
||||
// Get the value part (everything after =>)
|
||||
$arrow_pos = strpos($line, '=>');
|
||||
if ($arrow_pos === false) {
|
||||
continue;
|
||||
}
|
||||
$value_part = trim(substr($line, $arrow_pos + 2));
|
||||
|
||||
// Check for ternary operator
|
||||
if ($this->is_ternary_expression($value_part)) {
|
||||
$this->check_ternary($file_path, $actual_line_num, $line, $key, $value_part);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check for method call: $var->method() or $this->method()
|
||||
if (preg_match('/\$([a-zA-Z_][a-zA-Z0-9_]*)->([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/', $value_part, $method_match)) {
|
||||
$method_called = $method_match[2];
|
||||
|
||||
if ($key !== $method_called) {
|
||||
$this->add_violation(
|
||||
$file_path,
|
||||
$actual_line_num,
|
||||
"Field name shortened by dropping parts: '{$key}' is missing parts from '{$property}'",
|
||||
"Method call key must match method name: '{$key}' != '{$method_called}()'",
|
||||
trim($line),
|
||||
$this->build_suggestion($key, $property),
|
||||
$this->build_method_mismatch_suggestion($key, $method_called),
|
||||
'high'
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Pattern for array access: 'key' => $var['prop']
|
||||
if (preg_match("/['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]\\s*=>\\s*\\$[a-zA-Z_][a-zA-Z0-9_]*\\[['\"]([a-zA-Z_][a-zA-Z0-9_]*)['\"]\\]\\s*[,\\]\\)]/", $line, $matches)) {
|
||||
$key = $matches[1];
|
||||
$property = $matches[2];
|
||||
// Check for property access: $var->property or $var['property']
|
||||
$property = null;
|
||||
|
||||
if ($this->is_problematic_alias($key, $property) && !$this->has_transformation($line, $matches[0])) {
|
||||
// Check for line-level exception
|
||||
$actual_line_num = $method_start_line + $offset;
|
||||
if ($this->line_has_exception($original_lines, $actual_line_num)) {
|
||||
continue;
|
||||
}
|
||||
// Object property: $var->prop
|
||||
if (preg_match('/\$([a-zA-Z_][a-zA-Z0-9_]*)->([a-zA-Z_][a-zA-Z0-9_]*)(?:\s*[,;\]\)]|$)/', $value_part, $prop_match)) {
|
||||
$property = $prop_match[2];
|
||||
}
|
||||
// Array access: $var['prop']
|
||||
elseif (preg_match('/\$([a-zA-Z_][a-zA-Z0-9_]*)\[[\'"]([a-zA-Z_][a-zA-Z0-9_]*)[\'"]\]/', $value_part, $arr_match)) {
|
||||
$property = $arr_match[2];
|
||||
}
|
||||
|
||||
if ($property !== null) {
|
||||
if ($key === $property) {
|
||||
// Redundant assignment - already in toArray()
|
||||
$this->add_violation(
|
||||
$file_path,
|
||||
$actual_line_num,
|
||||
"Field name shortened by dropping parts: '{$key}' is missing parts from '{$property}'",
|
||||
"Redundant assignment: '{$key}' is already included by toArray()",
|
||||
trim($line),
|
||||
$this->build_suggestion($key, $property),
|
||||
$this->build_redundant_suggestion($key),
|
||||
'medium'
|
||||
);
|
||||
} else {
|
||||
// Aliasing - key != property
|
||||
$this->add_violation(
|
||||
$file_path,
|
||||
$actual_line_num,
|
||||
"Field aliasing prohibited: '{$key}' != '{$property}'",
|
||||
trim($line),
|
||||
$this->build_alias_suggestion($key, $property),
|
||||
'high'
|
||||
);
|
||||
}
|
||||
@@ -252,85 +253,112 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the key is a problematic alias of the property
|
||||
*
|
||||
* Returns true if the key is a PROPER SUBSET of the property's parts,
|
||||
* meaning all parts of the key exist in the property, but the property
|
||||
* has additional parts that were dropped.
|
||||
*
|
||||
* Examples:
|
||||
* 'type_label', 'type_id_label' -> true (dropped "id")
|
||||
* 'name', 'display_name' -> true (dropped "display")
|
||||
* 'value', 'id' -> false (different concept)
|
||||
* 'client_id', 'id' -> false (adding context)
|
||||
* 'id', 'id' -> false (same name)
|
||||
* Check if expression contains a ternary operator (not inside a string)
|
||||
*/
|
||||
private function is_problematic_alias(string $key, string $property): bool
|
||||
private function is_ternary_expression(string $value): bool
|
||||
{
|
||||
// Same name is never a violation
|
||||
if ($key === $property) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Split by underscores
|
||||
$key_parts = explode('_', strtolower($key));
|
||||
$property_parts = explode('_', strtolower($property));
|
||||
|
||||
// Check if ALL key parts exist in the property parts
|
||||
foreach ($key_parts as $key_part) {
|
||||
if (!in_array($key_part, $property_parts)) {
|
||||
// Key has a part that doesn't exist in property
|
||||
// This means it's a rename to a different concept, not a shortening
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// At this point, all key parts exist in property parts
|
||||
// Check if property has additional parts (making key a proper subset)
|
||||
if (count($property_parts) > count($key_parts)) {
|
||||
// Property has more parts than key - parts were dropped
|
||||
return true;
|
||||
}
|
||||
|
||||
// Same number of parts (just reordered?) - not a violation
|
||||
return false;
|
||||
// Remove string contents to avoid false positives
|
||||
$no_strings = preg_replace('/(["\'])(?:[^\\\\]|\\\\.)*?\1/', '', $value);
|
||||
return str_contains($no_strings, '?') && str_contains($no_strings, ':');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value has a transformation applied (function call wrapping it)
|
||||
* Check ternary expression for valid patterns
|
||||
*/
|
||||
private function has_transformation(string $line, string $matched_portion): bool
|
||||
private function check_ternary(string $file_path, int $line_num, string $line, string $key, string $value_part): void
|
||||
{
|
||||
// Find where the matched portion starts in the line
|
||||
$pos = strpos($line, $matched_portion);
|
||||
if ($pos === false) {
|
||||
return false;
|
||||
// Extract the true and false branches
|
||||
// This is simplified - a full parser would be needed for nested ternaries
|
||||
$no_strings = preg_replace('/(["\'])(?:[^\\\\]|\\\\.)*?\1/', '""', $value_part);
|
||||
|
||||
// Find the ? and : positions
|
||||
$q_pos = strpos($no_strings, '?');
|
||||
$c_pos = strpos($no_strings, ':');
|
||||
|
||||
if ($q_pos === false || $c_pos === false || $c_pos < $q_pos) {
|
||||
return; // Can't parse
|
||||
}
|
||||
|
||||
// Get everything after '=>' and before the matched value
|
||||
if (preg_match("/=>\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\(/", $line, $fn_match)) {
|
||||
// There's a function call before the value
|
||||
$true_branch = trim(substr($value_part, $q_pos + 1, $c_pos - $q_pos - 1));
|
||||
$false_branch = trim(substr($value_part, $c_pos + 1));
|
||||
|
||||
// Remove trailing punctuation from false branch
|
||||
$false_branch = rtrim($false_branch, ',;)');
|
||||
|
||||
// Check each branch - must be either:
|
||||
// 1. A literal (string, number, null, true, false)
|
||||
// 2. A property/method access with matching key name
|
||||
|
||||
$true_valid = $this->is_valid_ternary_branch($key, $true_branch);
|
||||
$false_valid = $this->is_valid_ternary_branch($key, $false_branch);
|
||||
|
||||
if (!$true_valid || !$false_valid) {
|
||||
$this->add_violation(
|
||||
$file_path,
|
||||
$line_num,
|
||||
"Ternary branches must use matching property/method name or literals",
|
||||
trim($line),
|
||||
$this->build_ternary_suggestion($key),
|
||||
'high'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a ternary branch is valid
|
||||
*/
|
||||
private function is_valid_ternary_branch(string $key, string $branch): bool
|
||||
{
|
||||
$branch = trim($branch);
|
||||
|
||||
// Literal values are always valid
|
||||
if ($this->is_literal($branch)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for method chaining or casting
|
||||
if (preg_match("/=>\\s*\\([^)]+\\)\\s*\\$/", $line)) {
|
||||
// Cast like (string)$var->prop
|
||||
// Method call: $var->method() - method must match key
|
||||
if (preg_match('/\$[a-zA-Z_][a-zA-Z0-9_]*->([a-zA-Z_][a-zA-Z0-9_]*)\s*\(/', $branch, $m)) {
|
||||
return $m[1] === $key;
|
||||
}
|
||||
|
||||
// Property access: $var->prop - prop must match key
|
||||
if (preg_match('/\$[a-zA-Z_][a-zA-Z0-9_]*->([a-zA-Z_][a-zA-Z0-9_]*)(?:\s*$|[^(])/', $branch, $m)) {
|
||||
return $m[1] === $key;
|
||||
}
|
||||
|
||||
// Array access: $var['prop'] - prop must match key
|
||||
if (preg_match('/\$[a-zA-Z_][a-zA-Z0-9_]*\[[\'"]([a-zA-Z_][a-zA-Z0-9_]*)[\'"]\]/', $branch, $m)) {
|
||||
return $m[1] === $key;
|
||||
}
|
||||
|
||||
// Other expressions (function calls, etc.) - can't validate easily, allow
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if value is a literal
|
||||
*/
|
||||
private function is_literal(string $value): bool
|
||||
{
|
||||
$value = trim($value);
|
||||
|
||||
// null, true, false
|
||||
if (in_array(strtolower($value), ['null', 'true', 'false'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for string concatenation
|
||||
if (preg_match("/=>\\s*['\"].*['\"]\\s*\\.\\s*\\$/", $line) || preg_match("/\\$[^,]+\\.\\s*['\"]/", $line)) {
|
||||
// Number
|
||||
if (is_numeric($value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for ternary operator
|
||||
if (strpos($line, '?') !== false && strpos($line, ':') !== false) {
|
||||
// String literal
|
||||
if (preg_match('/^(["\']).*\1$/', $value)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for null coalescing
|
||||
if (strpos($line, '??') !== false) {
|
||||
// Empty array
|
||||
if ($value === '[]') {
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -413,33 +441,100 @@ class FieldAliasing_CodeQualityRule extends CodeQualityRule_Abstract
|
||||
}
|
||||
|
||||
/**
|
||||
* Build suggestion for fixing the violation
|
||||
* Build suggestion for method name mismatch
|
||||
*/
|
||||
private function build_suggestion(string $key, string $property): string
|
||||
private function build_method_mismatch_suggestion(string $key, string $method): string
|
||||
{
|
||||
$suggestions = [];
|
||||
$suggestions[] = "PROBLEM: Field name shortened by dropping parts.";
|
||||
$suggestions[] = "";
|
||||
$suggestions[] = "The key '{$key}' contains only some parts of '{$property}'.";
|
||||
$suggestions[] = "This is confusing because it obscures what was removed.";
|
||||
$suggestions[] = "";
|
||||
$suggestions[] = "FIX: Use the full property name:";
|
||||
$suggestions[] = "";
|
||||
$suggestions[] = " // WRONG - parts dropped, confusing";
|
||||
$suggestions[] = " '{$key}' => \$model->{$property},";
|
||||
$suggestions[] = "";
|
||||
$suggestions[] = " // CORRECT - full name preserved";
|
||||
$suggestions[] = " '{$property}' => \$model->{$property},";
|
||||
$suggestions[] = "";
|
||||
$suggestions[] = "NOTE: Renaming to a DIFFERENT concept is allowed:";
|
||||
$suggestions[] = "";
|
||||
$suggestions[] = " // OK - 'value'/'label' are UI concepts, not shortenings";
|
||||
$suggestions[] = " 'value' => \$model->id,";
|
||||
$suggestions[] = " 'label' => \$model->name,";
|
||||
$suggestions[] = "";
|
||||
$suggestions[] = " // OK - adding context, not dropping it";
|
||||
$suggestions[] = " 'client_id' => \$client->id,";
|
||||
return implode("\n", [
|
||||
"PROBLEM: Method call key doesn't match method name.",
|
||||
"",
|
||||
"fetch() anti-aliasing policy requires method keys to match method names.",
|
||||
"This ensures a single source of truth and consistent naming across PHP/JS.",
|
||||
"",
|
||||
"FIX: Use the method name as the key:",
|
||||
"",
|
||||
" // WRONG",
|
||||
" '{$key}' => \$model->{$method}(),",
|
||||
"",
|
||||
" // CORRECT",
|
||||
" '{$method}' => \$model->{$method}(),",
|
||||
"",
|
||||
"See: php artisan rsx:man model_fetch",
|
||||
]);
|
||||
}
|
||||
|
||||
return implode("\n", $suggestions);
|
||||
/**
|
||||
* Build suggestion for redundant assignment
|
||||
*/
|
||||
private function build_redundant_suggestion(string $key): string
|
||||
{
|
||||
return implode("\n", [
|
||||
"PROBLEM: Redundant explicit assignment.",
|
||||
"",
|
||||
"This field is already included automatically by toArray().",
|
||||
"Explicit assignment is unnecessary and adds maintenance burden.",
|
||||
"",
|
||||
"FIX: Remove this line - the field is already in the output.",
|
||||
"",
|
||||
" // UNNECESSARY - remove this line",
|
||||
" '{$key}' => \$model->{$key},",
|
||||
"",
|
||||
"toArray() automatically includes all model fields, enum properties,",
|
||||
"and the __MODEL marker for JavaScript hydration.",
|
||||
"",
|
||||
"See: php artisan rsx:man model_fetch",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build suggestion for property aliasing
|
||||
*/
|
||||
private function build_alias_suggestion(string $key, string $property): string
|
||||
{
|
||||
return implode("\n", [
|
||||
"PROBLEM: Field aliasing is prohibited.",
|
||||
"",
|
||||
"fetch() exists for SECURITY (removing private data), not aliasing.",
|
||||
"Aliasing breaks grep searches and obscures data sources.",
|
||||
"",
|
||||
"OPTIONS:",
|
||||
"",
|
||||
"1. Use the original property name:",
|
||||
" '{$property}' => \$model->{$property},",
|
||||
"",
|
||||
"2. If this is a computed value, create a model method:",
|
||||
" // In model:",
|
||||
" public function {$key}() { return ...; }",
|
||||
"",
|
||||
" // In fetch:",
|
||||
" '{$key}' => \$model->{$key}(),",
|
||||
"",
|
||||
"3. If this is an enum property, use the full BEM-style name:",
|
||||
" // Instead of 'type_label', use 'type_id__label'",
|
||||
"",
|
||||
"See: php artisan rsx:man model_fetch",
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build suggestion for ternary violations
|
||||
*/
|
||||
private function build_ternary_suggestion(string $key): string
|
||||
{
|
||||
return implode("\n", [
|
||||
"PROBLEM: Ternary branches must use matching names or literals.",
|
||||
"",
|
||||
"Conditional assignments in fetch() are allowed, but both branches",
|
||||
"must use the same property/method name as the key, or be literals.",
|
||||
"",
|
||||
"VALID patterns:",
|
||||
" '{$key}' => \$condition ? \$model->{$key} : null,",
|
||||
" '{$key}' => \$model->can_see() ? \$model->{$key} : '[HIDDEN]',",
|
||||
"",
|
||||
"INVALID patterns:",
|
||||
" '{$key}' => \$condition ? \$model->other_field : null,",
|
||||
"",
|
||||
"See: php artisan rsx:man model_fetch",
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,16 +167,15 @@ class Document_Models_Command extends FrameworkDeveloperCommand
|
||||
|
||||
if (property_exists($className, 'enums') && !empty($className::$enums)) {
|
||||
foreach ($className::$enums as $columnName => $enumDefinitions) {
|
||||
// Add enum accessor properties (instance properties for current value)
|
||||
$enumProperties[] = " * @property-read string \${$columnName}_label";
|
||||
$enumProperties[] = " * @property-read string \${$columnName}_constant";
|
||||
$enumProperties[] = " * @property-read array \${$columnName}_enum_val";
|
||||
// Add enum accessor properties (BEM-style: field__property)
|
||||
$enumProperties[] = " * @property-read string \${$columnName}__label";
|
||||
$enumProperties[] = " * @property-read string \${$columnName}__constant";
|
||||
|
||||
// Add enum static methods (mirrored in JavaScript stubs)
|
||||
$enumMethods[] = " * @method static array {$columnName}_enum_val() Get all enum definitions with full metadata";
|
||||
$enumMethods[] = " * @method static array {$columnName}_enum_select() Get selectable items for dropdowns";
|
||||
$enumMethods[] = " * @method static array {$columnName}_enum_labels() Get simple id => label map";
|
||||
$enumMethods[] = " * @method static array {$columnName}_enum_ids() Get array of all valid enum IDs";
|
||||
// Add enum static methods (BEM-style, mirrored in JavaScript stubs)
|
||||
$enumMethods[] = " * @method static array {$columnName}__enum() Get all enum definitions with full metadata";
|
||||
$enumMethods[] = " * @method static array {$columnName}__enum_select() Get selectable items for dropdowns";
|
||||
$enumMethods[] = " * @method static array {$columnName}__enum_labels() Get simple id => label map";
|
||||
$enumMethods[] = " * @method static array {$columnName}__enum_ids() Get array of all valid enum IDs";
|
||||
|
||||
// Generate constants for each enum value
|
||||
foreach ($enumDefinitions as $value => $definition) {
|
||||
|
||||
@@ -33,7 +33,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:53
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: _api_keys
|
||||
*
|
||||
* @property int $id
|
||||
@@ -53,7 +53,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Api_Key_Model extends Rsx_System_Model_Abstract
|
||||
{
|
||||
{
|
||||
protected $table = '_api_keys';
|
||||
|
||||
public static $enums = [];
|
||||
|
||||
@@ -402,10 +402,18 @@ class Database_BundleIntegration extends BundleIntegration_Abstract
|
||||
$content .= "\n";
|
||||
}
|
||||
|
||||
// Generate enum value getter with Proxy for maintaining order
|
||||
$content .= " static __{$column}_enum_val = null;\n";
|
||||
$content .= " static {$column}_enum_val(enum_value) {\n";
|
||||
$content .= " if (!this.__{$column}_enum_val) {\n";
|
||||
// Generate enum getter with Proxy for maintaining order (BEM-style: field__enum)
|
||||
$content .= " /**\n";
|
||||
$content .= " * Get enum metadata for {$column}.\n";
|
||||
$content .= " * @param {number} [enum_value] - If provided, returns metadata for that ID (or null + console.error if invalid)\n";
|
||||
$content .= " * @returns {Object} All enum definitions keyed by ID, or single enum's metadata if enum_value provided\n";
|
||||
$content .= " * @example\n";
|
||||
$content .= " * // Get all: Model.{$column}__enum()\n";
|
||||
$content .= " * // Get one: Model.{$column}__enum(Model.CONSTANT_NAME).property\n";
|
||||
$content .= " */\n";
|
||||
$content .= " static __{$column}__enum = null;\n";
|
||||
$content .= " static {$column}__enum(enum_value) {\n";
|
||||
$content .= " if (!this.__{$column}__enum) {\n";
|
||||
$content .= " const data = {};\n";
|
||||
$content .= " const order = [];\n";
|
||||
|
||||
@@ -418,7 +426,7 @@ class Database_BundleIntegration extends BundleIntegration_Abstract
|
||||
}
|
||||
|
||||
$content .= " // Cache Proxy that maintains sort order for enumeration\n";
|
||||
$content .= " this.__{$column}_enum_val = new Proxy(data, {\n";
|
||||
$content .= " this.__{$column}__enum = new Proxy(data, {\n";
|
||||
$content .= " ownKeys() {\n";
|
||||
$content .= " return order.map(String);\n";
|
||||
$content .= " },\n";
|
||||
@@ -434,19 +442,23 @@ class Database_BundleIntegration extends BundleIntegration_Abstract
|
||||
$content .= " });\n";
|
||||
$content .= " }\n";
|
||||
$content .= " if (enum_value !== undefined) {\n";
|
||||
$content .= " const result = this.__{$column}_enum_val[enum_value];\n";
|
||||
$content .= " const result = this.__{$column}__enum[enum_value];\n";
|
||||
$content .= " if (!result) {\n";
|
||||
$content .= " console.error(`Invalid enum value '\${enum_value}' for {$column}`);\n";
|
||||
$content .= " return null;\n";
|
||||
$content .= " }\n";
|
||||
$content .= " return result;\n";
|
||||
$content .= " }\n";
|
||||
$content .= " return this.__{$column}_enum_val;\n";
|
||||
$content .= " return this.__{$column}__enum;\n";
|
||||
$content .= " }\n\n";
|
||||
|
||||
// Generate enum_select() - Selectable items for dropdowns (respects selectable: false)
|
||||
$content .= " static {$column}_enum_select() {\n";
|
||||
$content .= " const fullData = this.{$column}_enum_val();\n";
|
||||
$content .= " /**\n";
|
||||
$content .= " * Get selectable options for {$column} dropdowns (excludes selectable:false items).\n";
|
||||
$content .= " * @returns {Object} {id: label} pairs for dropdown options, sorted by 'order' property\n";
|
||||
$content .= " */\n";
|
||||
$content .= " static {$column}__enum_select() {\n";
|
||||
$content .= " const fullData = this.{$column}__enum();\n";
|
||||
$content .= " const data = {};\n";
|
||||
$content .= " const order = [];\n";
|
||||
$content .= " \n";
|
||||
@@ -477,7 +489,11 @@ class Database_BundleIntegration extends BundleIntegration_Abstract
|
||||
$content .= " }\n\n";
|
||||
|
||||
// Generate enum_labels() - Simple id => label map (all items, ignores selectable)
|
||||
$content .= " static {$column}_enum_labels() {\n";
|
||||
$content .= " /**\n";
|
||||
$content .= " * Get all {$column} labels (includes non-selectable items).\n";
|
||||
$content .= " * @returns {Object} {id: label} pairs for all enum values\n";
|
||||
$content .= " */\n";
|
||||
$content .= " static {$column}__enum_labels() {\n";
|
||||
$content .= " const values = {};\n";
|
||||
foreach ($enum_values as $value => $props) {
|
||||
if (isset($props['label'])) {
|
||||
@@ -490,7 +506,11 @@ class Database_BundleIntegration extends BundleIntegration_Abstract
|
||||
$content .= " }\n\n";
|
||||
|
||||
// Generate enum_ids() - Array of all valid enum IDs
|
||||
$content .= " static {$column}_enum_ids() {\n";
|
||||
$content .= " /**\n";
|
||||
$content .= " * Get all valid {$column} IDs.\n";
|
||||
$content .= " * @returns {number[]} Array of all enum IDs\n";
|
||||
$content .= " */\n";
|
||||
$content .= " static {$column}__enum_ids() {\n";
|
||||
$content .= " return [";
|
||||
$ids = array_keys($enum_values);
|
||||
$content .= implode(', ', array_map('json_encode', $ids));
|
||||
|
||||
@@ -37,16 +37,16 @@ use RuntimeException;
|
||||
* ]
|
||||
* ];
|
||||
*
|
||||
* This provides magic properties and methods:
|
||||
* - $model->status_label - Get label for current enum value
|
||||
* - $model->status_constant - Get constant name for current value
|
||||
* - $model->status_enum_val - Get all properties for current value
|
||||
* This provides magic properties and methods (BEM-style double underscore):
|
||||
* - $model->status__label - Get label for current enum value
|
||||
* - $model->status__constant - Get constant name for current value
|
||||
* - $model->status__badge - Get any custom property for current value
|
||||
*
|
||||
* Static methods (available in both PHP and JavaScript):
|
||||
* - Model::status_enum_val() - Get all enum definitions with full metadata
|
||||
* - Model::status_enum_select() - Get selectable items for dropdowns (respects selectable: false)
|
||||
* - Model::status_enum_labels() - Get simple id => label lookup map
|
||||
* - Model::status_enum_ids() - Get array of all valid enum IDs
|
||||
* - Model::status__enum() - Get all enum definitions with full metadata
|
||||
* - Model::status__enum_select() - Get selectable items for dropdowns (respects selectable: false)
|
||||
* - Model::status__enum_labels() - Get simple id => label lookup map
|
||||
* - Model::status__enum_ids() - Get array of all valid enum IDs
|
||||
*/
|
||||
#[Monoprogenic]
|
||||
#[Instantiatable]
|
||||
@@ -86,11 +86,11 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
/**
|
||||
* Private helper to resolve enum magic properties and methods
|
||||
*
|
||||
* Handles (these are mirrored in JavaScript stubs):
|
||||
* - field_enum_val() - Returns all enum definitions with full metadata
|
||||
* - field_enum_select() - Returns selectable items for dropdowns
|
||||
* - field_enum_labels() - Returns simple id => label map
|
||||
* - field_enum_ids() - Returns array of all valid enum IDs
|
||||
* Handles (these are mirrored in JavaScript stubs, BEM-style double underscore):
|
||||
* - field__enum() - Returns all enum definitions with full metadata
|
||||
* - field__enum_select() - Returns selectable items for dropdowns
|
||||
* - field__enum_labels() - Returns simple id => label map
|
||||
* - field__enum_ids() - Returns array of all valid enum IDs
|
||||
*
|
||||
* @param string $key The property/method being accessed
|
||||
* @param mixed $value Optional value for filtering selectable items
|
||||
@@ -119,13 +119,13 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
return $keyA <=> $keyB;
|
||||
});
|
||||
|
||||
// field_enum_val() - All enum definitions with full metadata
|
||||
if ($key == $column . '_enum_val') {
|
||||
// field__enum() - All enum definitions with full metadata
|
||||
if ($key == $column . '__enum') {
|
||||
return $sorted_config;
|
||||
}
|
||||
|
||||
// field_enum_select() - Selectable items for dropdowns (respects selectable: false)
|
||||
if ($key == $column . '_enum_select') {
|
||||
// field__enum_select() - Selectable items for dropdowns (respects selectable: false)
|
||||
if ($key == $column . '__enum_select') {
|
||||
$return = [];
|
||||
|
||||
foreach ($sorted_config as $k => $v) {
|
||||
@@ -140,8 +140,8 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
return $return;
|
||||
}
|
||||
|
||||
// field_enum_labels() - Simple id => label map (all items, ignores selectable)
|
||||
if ($key == $column . '_enum_labels') {
|
||||
// field__enum_labels() - Simple id => label map (all items, ignores selectable)
|
||||
if ($key == $column . '__enum_labels') {
|
||||
$return = [];
|
||||
foreach ($sorted_config as $k => $v) {
|
||||
if (isset($v['label'])) {
|
||||
@@ -151,8 +151,8 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
return $return;
|
||||
}
|
||||
|
||||
// field_enum_ids() - Array of all valid enum IDs
|
||||
if ($key == $column . '_enum_ids') {
|
||||
// field__enum_ids() - Array of all valid enum IDs
|
||||
if ($key == $column . '__enum_ids') {
|
||||
return array_keys($sorted_config);
|
||||
}
|
||||
}
|
||||
@@ -164,39 +164,30 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
/**
|
||||
* Magic getter for enum properties
|
||||
*
|
||||
* Provides access to:
|
||||
* - field_label - Label for current enum value
|
||||
* - field_constant - Constant name for current value
|
||||
* - field_enum_val - All properties for current value
|
||||
* - field_enum, field_enum_select, field_enum_ids - Via _get_static_magic
|
||||
* Uses BEM-style double underscore to separate field from property:
|
||||
* - field__label - Label for current enum value
|
||||
* - field__constant - Constant name for current value
|
||||
* - field__badge - Any custom property for current value
|
||||
* - field__enum(), field__enum_select(), etc. - Via _get_static_magic
|
||||
*
|
||||
* @param string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($key)
|
||||
{
|
||||
// Check for enum lookup functions: _enum, _enum_select, _enum_ids
|
||||
// Check for enum lookup functions: __enum, __enum_select, __enum_ids
|
||||
$static_call = self::_get_static_magic($key);
|
||||
if ($static_call !== null) {
|
||||
return $static_call;
|
||||
}
|
||||
|
||||
// Look up enum properties related to current column value
|
||||
// Look up enum properties related to current column value (BEM-style: field__property)
|
||||
if (!empty(static::$enums)) {
|
||||
foreach (static::$enums as $column => $enum_config) {
|
||||
// $object->field_enum_val returns all properties for current value
|
||||
if ($key == $column . '_enum_val') {
|
||||
$current_value = $this->$column;
|
||||
|
||||
return isset(static::$enums[$column][$current_value])
|
||||
? static::$enums[$column][$current_value]
|
||||
: null;
|
||||
}
|
||||
|
||||
// Look for specific enum property (e.g., field_label, field_constant)
|
||||
// Look for specific enum property (e.g., field__label, field__constant)
|
||||
foreach ($enum_config as $enum_val => $enum_properties) {
|
||||
foreach ($enum_properties as $prop_name => $prop_value) {
|
||||
if ($key == $column . '_' . $prop_name && $this->$column == $enum_val) {
|
||||
if ($key == $column . '__' . $prop_name && $this->$column == $enum_val) {
|
||||
return $prop_value;
|
||||
}
|
||||
}
|
||||
@@ -219,18 +210,13 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
*/
|
||||
public function __isset($key)
|
||||
{
|
||||
// Check for enum magic properties
|
||||
// Check for enum magic properties (BEM-style: field__property)
|
||||
if (!empty(static::$enums)) {
|
||||
foreach (static::$enums as $column => $enum_config) {
|
||||
// field_enum_val
|
||||
if ($key == $column . '_enum_val') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// field_label, field_constant, field_* (any custom enum property)
|
||||
// field__label, field__constant, field__* (any custom enum property)
|
||||
foreach ($enum_config as $enum_val => $enum_properties) {
|
||||
foreach ($enum_properties as $prop_name => $prop_value) {
|
||||
if ($key == $column . '_' . $prop_name) {
|
||||
if ($key == $column . '__' . $prop_name) {
|
||||
// Property exists if current column value matches this enum value
|
||||
if ($this->$column == $enum_val) {
|
||||
return true;
|
||||
@@ -247,11 +233,11 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
/**
|
||||
* Magic static method handler for enum methods
|
||||
*
|
||||
* Provides static access to (mirrored in JavaScript stubs):
|
||||
* - Model::field_enum_val() - All enum definitions with full metadata
|
||||
* - Model::field_enum_select() - Selectable items for dropdowns
|
||||
* - Model::field_enum_labels() - Simple id => label map
|
||||
* - Model::field_enum_ids() - Array of all valid enum IDs
|
||||
* Provides static access to (mirrored in JavaScript stubs, BEM-style):
|
||||
* - Model::field__enum() - All enum definitions with full metadata
|
||||
* - Model::field__enum_select() - Selectable items for dropdowns
|
||||
* - Model::field__enum_labels() - Simple id => label map
|
||||
* - Model::field__enum_ids() - Array of all valid enum IDs
|
||||
*
|
||||
* @param string $key
|
||||
* @param array $args
|
||||
@@ -318,12 +304,12 @@ abstract class Rsx_Model_Abstract extends Model
|
||||
}
|
||||
}
|
||||
|
||||
// Add enum field extra data - ALL properties, not just label and constant
|
||||
// Add enum field extra data - ALL properties (BEM-style: field__property)
|
||||
foreach (static::$enums as $column => $definitions) {
|
||||
if (isset($this->$column) && isset($definitions[$this->$column])) {
|
||||
foreach ($definitions[$this->$column] as $prop => $value) {
|
||||
// Add all enum properties to the export
|
||||
$array[$column . '_' . $prop] = $value;
|
||||
$array[$column . '__' . $prop] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ use App\RSpade\Core\Files\File_Storage_Model;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:54
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: _file_attachments
|
||||
*
|
||||
* @property int $id
|
||||
@@ -59,19 +59,18 @@ use App\RSpade\Core\Files\File_Storage_Model;
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
*
|
||||
* @property-read string $file_type_id_label
|
||||
* @property-read string $file_type_id_constant
|
||||
* @property-read array $file_type_id_enum_val
|
||||
* @property-read string $file_type_id__label
|
||||
* @property-read string $file_type_id__constant
|
||||
*
|
||||
* @method static array file_type_id_enum_val() Get all enum definitions with full metadata
|
||||
* @method static array file_type_id_enum_select() Get selectable items for dropdowns
|
||||
* @method static array file_type_id_enum_labels() Get simple id => label map
|
||||
* @method static array file_type_id_enum_ids() Get array of all valid enum IDs
|
||||
* @method static array file_type_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array file_type_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array file_type_id__enum_labels() Get simple id => label map
|
||||
* @method static array file_type_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class File_Attachment_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
@@ -83,6 +82,7 @@ class File_Attachment_Model extends Rsx_Site_Model_Abstract
|
||||
const FILE_TYPE_DOCUMENT = 6;
|
||||
const FILE_TYPE_OTHER = 7;
|
||||
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
@@ -16,7 +16,7 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:54
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: _file_storage
|
||||
*
|
||||
* @property int $id
|
||||
@@ -30,7 +30,7 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class File_Storage_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
// Required static properties from parent abstract class
|
||||
public static $enums = [];
|
||||
public static $rel = [];
|
||||
|
||||
@@ -14,7 +14,7 @@ use App\RSpade\Core\Models\Region_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:54
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: countries
|
||||
*
|
||||
* @property int $id
|
||||
@@ -32,7 +32,7 @@ use App\RSpade\Core\Models\Region_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Country_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
public static $enums = [];
|
||||
|
||||
protected $table = 'countries';
|
||||
|
||||
@@ -12,7 +12,7 @@ use App\RSpade\Core\Database\Models\Rsx_System_Model_Abstract;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:54
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: ip_addresses
|
||||
*
|
||||
* @property int $id
|
||||
@@ -30,7 +30,7 @@ use App\RSpade\Core\Database\Models\Rsx_System_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Ip_Address_Model extends Rsx_System_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* Enum field definitions
|
||||
* @var array
|
||||
|
||||
@@ -24,7 +24,7 @@ use App\RSpade\Core\Session\Session;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:54
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: login_users
|
||||
*
|
||||
* @property int $id
|
||||
@@ -40,21 +40,19 @@ use App\RSpade\Core\Session\Session;
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
*
|
||||
* @property-read string $status_id_label
|
||||
* @property-read string $status_id_constant
|
||||
* @property-read array $status_id_enum_val
|
||||
* @property-read string $is_verified_label
|
||||
* @property-read string $is_verified_constant
|
||||
* @property-read array $is_verified_enum_val
|
||||
* @property-read string $status_id__label
|
||||
* @property-read string $status_id__constant
|
||||
* @property-read string $is_verified__label
|
||||
* @property-read string $is_verified__constant
|
||||
*
|
||||
* @method static array status_id_enum_val() Get all enum definitions with full metadata
|
||||
* @method static array status_id_enum_select() Get selectable items for dropdowns
|
||||
* @method static array status_id_enum_labels() Get simple id => label map
|
||||
* @method static array status_id_enum_ids() Get array of all valid enum IDs
|
||||
* @method static array is_verified_enum_val() Get all enum definitions with full metadata
|
||||
* @method static array is_verified_enum_select() Get selectable items for dropdowns
|
||||
* @method static array is_verified_enum_labels() Get simple id => label map
|
||||
* @method static array is_verified_enum_ids() Get array of all valid enum IDs
|
||||
* @method static array status_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array status_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array status_id__enum_labels() Get simple id => label map
|
||||
* @method static array status_id__enum_ids() Get array of all valid enum IDs
|
||||
* @method static array is_verified__enum() Get all enum definitions with full metadata
|
||||
* @method static array is_verified__enum_select() Get selectable items for dropdowns
|
||||
* @method static array is_verified__enum_labels() Get simple id => label map
|
||||
* @method static array is_verified__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
@@ -62,7 +60,7 @@ class Login_User_Model extends Rsx_Model_Abstract implements
|
||||
\Illuminate\Contracts\Auth\Authenticatable,
|
||||
\Illuminate\Contracts\Auth\Access\Authorizable,
|
||||
\Illuminate\Contracts\Auth\CanResetPassword
|
||||
{
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,7 @@ use App\RSpade\Core\Models\Country_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:54
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: regions
|
||||
*
|
||||
* @property int $id
|
||||
@@ -31,7 +31,7 @@ use App\RSpade\Core\Models\Country_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Region_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
public static $enums = [];
|
||||
|
||||
protected $table = 'regions';
|
||||
|
||||
@@ -14,7 +14,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: sites
|
||||
*
|
||||
* @property int $id
|
||||
@@ -31,7 +31,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Site_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,7 +12,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: user_invites
|
||||
*
|
||||
* @property int $id
|
||||
@@ -28,7 +28,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Invite_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* Enum field definitions
|
||||
* @var array
|
||||
|
||||
@@ -25,7 +25,7 @@ use App\RSpade\Core\Models\User_Profile_Model;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: users
|
||||
*
|
||||
* @property int $id
|
||||
@@ -48,19 +48,18 @@ use App\RSpade\Core\Models\User_Profile_Model;
|
||||
* @property string $invite_accepted_at
|
||||
* @property string $invite_expires_at
|
||||
*
|
||||
* @property-read string $role_id_label
|
||||
* @property-read string $role_id_constant
|
||||
* @property-read array $role_id_enum_val
|
||||
* @property-read string $role_id__label
|
||||
* @property-read string $role_id__constant
|
||||
*
|
||||
* @method static array role_id_enum_val() Get all enum definitions with full metadata
|
||||
* @method static array role_id_enum_select() Get selectable items for dropdowns
|
||||
* @method static array role_id_enum_labels() Get simple id => label map
|
||||
* @method static array role_id_enum_ids() Get array of all valid enum IDs
|
||||
* @method static array role_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array role_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array role_id__enum_labels() Get simple id => label map
|
||||
* @method static array role_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
@@ -73,6 +72,7 @@ class User_Model extends Rsx_Site_Model_Abstract
|
||||
const ROLE_VIEWER = 700;
|
||||
const ROLE_DISABLED = 800;
|
||||
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
@@ -7,7 +7,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: user_permissions
|
||||
*
|
||||
* @property int $id
|
||||
@@ -22,7 +22,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Permission_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
protected $table = 'user_permissions';
|
||||
protected $fillable = []; // No mass assignment - always explicit
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: user_profiles
|
||||
*
|
||||
* @property int $id
|
||||
@@ -51,7 +51,7 @@ use App\RSpade\Core\Models\User_Model;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Profile_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* The table associated with the model
|
||||
*
|
||||
|
||||
@@ -13,7 +13,7 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: user_verifications
|
||||
*
|
||||
* @property int $id
|
||||
@@ -27,19 +27,18 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
* @property int $created_by
|
||||
* @property int $updated_by
|
||||
*
|
||||
* @property-read string $verification_type_id_label
|
||||
* @property-read string $verification_type_id_constant
|
||||
* @property-read array $verification_type_id_enum_val
|
||||
* @property-read string $verification_type_id__label
|
||||
* @property-read string $verification_type_id__constant
|
||||
*
|
||||
* @method static array verification_type_id_enum_val() Get all enum definitions with full metadata
|
||||
* @method static array verification_type_id_enum_select() Get selectable items for dropdowns
|
||||
* @method static array verification_type_id_enum_labels() Get simple id => label map
|
||||
* @method static array verification_type_id_enum_ids() Get array of all valid enum IDs
|
||||
* @method static array verification_type_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array verification_type_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array verification_type_id__enum_labels() Get simple id => label map
|
||||
* @method static array verification_type_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class User_Verification_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
@@ -48,6 +47,7 @@ class User_Verification_Model extends Rsx_Model_Abstract
|
||||
const VERIFICATION_TYPE_EMAIL_RECOVERY = 3;
|
||||
const VERIFICATION_TYPE_SMS_RECOVERY = 4;
|
||||
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
@@ -17,7 +17,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: _search_indexes
|
||||
*
|
||||
* @property int $id
|
||||
@@ -37,7 +37,7 @@ use App\RSpade\Core\Database\Models\Rsx_Site_Model_Abstract;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Search_Index_Model extends Rsx_Site_Model_Abstract
|
||||
{
|
||||
{
|
||||
// Required static properties from parent abstract class
|
||||
public static $enums = [];
|
||||
public static $rel = [];
|
||||
|
||||
@@ -41,7 +41,7 @@ use App\RSpade\Core\Session\User_Agent;
|
||||
*/
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: _sessions
|
||||
*
|
||||
* @property int $id
|
||||
@@ -63,7 +63,7 @@ use App\RSpade\Core\Session\User_Agent;
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Session extends Rsx_System_Model_Abstract
|
||||
{
|
||||
{
|
||||
// Enum definitions (required by abstract parent)
|
||||
public static $enums = [];
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
|
||||
/**
|
||||
* _AUTO_GENERATED_ Database type hints - do not edit manually
|
||||
* Generated on: 2025-12-25 20:57:55
|
||||
* Generated on: 2025-12-26 01:29:30
|
||||
* Table: _flash_alerts
|
||||
*
|
||||
* @property int $id
|
||||
@@ -18,19 +18,18 @@ use App\RSpade\Core\Database\Models\Rsx_Model_Abstract;
|
||||
* @property int $updated_by
|
||||
* @property string $updated_at
|
||||
*
|
||||
* @property-read string $type_id_label
|
||||
* @property-read string $type_id_constant
|
||||
* @property-read array $type_id_enum_val
|
||||
* @property-read string $type_id__label
|
||||
* @property-read string $type_id__constant
|
||||
*
|
||||
* @method static array type_id_enum_val() Get all enum definitions with full metadata
|
||||
* @method static array type_id_enum_select() Get selectable items for dropdowns
|
||||
* @method static array type_id_enum_labels() Get simple id => label map
|
||||
* @method static array type_id_enum_ids() Get array of all valid enum IDs
|
||||
* @method static array type_id__enum() Get all enum definitions with full metadata
|
||||
* @method static array type_id__enum_select() Get selectable items for dropdowns
|
||||
* @method static array type_id__enum_labels() Get simple id => label map
|
||||
* @method static array type_id__enum_ids() Get array of all valid enum IDs
|
||||
*
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class Flash_Alert_Model extends Rsx_Model_Abstract
|
||||
{
|
||||
{
|
||||
/**
|
||||
* _AUTO_GENERATED_ Enum constants
|
||||
*/
|
||||
@@ -39,6 +38,7 @@ class Flash_Alert_Model extends Rsx_Model_Abstract
|
||||
const TYPE_INFO = 3;
|
||||
const TYPE_WARNING = 4;
|
||||
|
||||
|
||||
/** __AUTO_GENERATED: */
|
||||
|
||||
/** __/AUTO_GENERATED */
|
||||
|
||||
@@ -13,8 +13,16 @@ SYNOPSIS
|
||||
DESCRIPTION
|
||||
The enum system provides a powerful way to define predefined values for database
|
||||
fields with associated metadata. It automatically generates constants, magic
|
||||
properties, helper methods, and JavaScript equivalents for both PHP and JavaScript
|
||||
code.
|
||||
properties, helper methods, and JavaScript equivalents.
|
||||
|
||||
BEM-STYLE NAMING: All enum magic properties and methods use double underscore
|
||||
to clearly separate field name from property/method name:
|
||||
|
||||
$user->role_id__label (not role_id_label)
|
||||
User_Model::role_id__enum() (not role_id_enum_val)
|
||||
|
||||
This makes it immediately clear when accessing generated enum properties vs
|
||||
regular model attributes, and enables reliable grep searches.
|
||||
|
||||
DEFINING ENUMS
|
||||
|
||||
@@ -56,63 +64,67 @@ DEFINING ENUMS
|
||||
|
||||
PHP MAGIC PROPERTIES (Instance)
|
||||
|
||||
For a model instance with an enum field, these properties are automatically available:
|
||||
For a model instance with an enum field, these properties are automatically
|
||||
available using BEM-style double underscore:
|
||||
|
||||
field_label Returns the label for the current value
|
||||
$user->status_id = 1;
|
||||
echo $user->status_label; // "Active"
|
||||
field__label Returns the label for the current value
|
||||
$user->status_id = 1;
|
||||
echo $user->status_id__label; // "Active"
|
||||
|
||||
field_constant Returns the constant name for the current value
|
||||
echo $user->status_constant; // "STATUS_ACTIVE"
|
||||
field__constant Returns the constant name for the current value
|
||||
echo $user->status_id__constant; // "STATUS_ACTIVE"
|
||||
|
||||
field_enum_val Returns ALL properties for the current value
|
||||
$props = $user->status_enum_val;
|
||||
// ['constant' => 'STATUS_ACTIVE', 'label' => 'Active', ...]
|
||||
|
||||
field_[property] Returns any custom property for the current value
|
||||
echo $user->status_badge; // "bg-success"
|
||||
echo $user->status_visible_frontend; // true
|
||||
field__[property] Returns any custom property for the current value
|
||||
echo $user->status_id__badge; // "bg-success"
|
||||
echo $user->status_id__visible_frontend; // true
|
||||
|
||||
STATIC METHODS (PHP and JavaScript)
|
||||
|
||||
These four methods are available as static methods on model classes in both PHP
|
||||
and JavaScript. The JavaScript stubs are auto-generated to mirror PHP behavior.
|
||||
These methods are available as static methods on model classes in both PHP
|
||||
and JavaScript. Uses BEM-style double underscore naming.
|
||||
|
||||
Model::field_enum_val()
|
||||
Model::field__enum()
|
||||
Returns all enum definitions for a field with full metadata:
|
||||
|
||||
$statuses = User_Model::status_id_enum_val();
|
||||
$statuses = User_Model::status_id__enum();
|
||||
// [1 => ['constant' => 'STATUS_ACTIVE', 'label' => 'Active', ...], ...]
|
||||
|
||||
// JavaScript equivalent:
|
||||
const statuses = User_Model.status_id_enum_val();
|
||||
const statuses = User_Model.status_id__enum();
|
||||
|
||||
Model::field_enum_select()
|
||||
Model::field__enum(id) [JavaScript only]
|
||||
Returns single enum's metadata by ID, or null + console.error if invalid:
|
||||
|
||||
User_Model.status_id__enum(User_Model.STATUS_ACTIVE).badge // "bg-success"
|
||||
User_Model.status_id__enum(1).selectable // true
|
||||
User_Model.status_id__enum(999) // null + console.error
|
||||
|
||||
Model::field__enum_select()
|
||||
Returns selectable items for dropdowns (respects 'selectable' and 'order'):
|
||||
|
||||
$options = User_Model::status_id_enum_select();
|
||||
$options = User_Model::status_id__enum_select();
|
||||
// [1 => 'Active', 2 => 'Inactive'] (excludes selectable: false items)
|
||||
|
||||
// JavaScript equivalent:
|
||||
const options = User_Model.status_id_enum_select();
|
||||
const options = User_Model.status_id__enum_select();
|
||||
|
||||
Model::field_enum_labels()
|
||||
Model::field__enum_labels()
|
||||
Returns simple id => label map (all items, ignores selectable flag):
|
||||
|
||||
$labels = User_Model::status_id_enum_labels();
|
||||
$labels = User_Model::status_id__enum_labels();
|
||||
// [1 => 'Active', 2 => 'Inactive', 3 => 'Archived']
|
||||
|
||||
// JavaScript equivalent:
|
||||
const labels = User_Model.status_id_enum_labels();
|
||||
const labels = User_Model.status_id__enum_labels();
|
||||
|
||||
Model::field_enum_ids()
|
||||
Model::field__enum_ids()
|
||||
Returns array of all valid enum IDs:
|
||||
|
||||
$ids = User_Model::status_id_enum_ids();
|
||||
$ids = User_Model::status_id__enum_ids();
|
||||
// [1, 2, 3]
|
||||
|
||||
// JavaScript equivalent:
|
||||
const ids = User_Model.status_id_enum_ids();
|
||||
const ids = User_Model.status_id__enum_ids();
|
||||
|
||||
PHP CONSTANTS
|
||||
|
||||
@@ -138,32 +150,61 @@ JAVASCRIPT ACCESS
|
||||
Project_Model.STATUS_ACTIVE // 2
|
||||
Project_Model.STATUS_PLANNING // 1
|
||||
|
||||
Static Methods (same as PHP - see STATIC METHODS section above)
|
||||
Project_Model.status_enum_val() // Full enum definitions with metadata
|
||||
Project_Model.status_enum_select() // Selectable items for dropdowns
|
||||
Project_Model.status_enum_labels() // Simple id => label map
|
||||
Project_Model.status_enum_ids() // Array of valid IDs
|
||||
Static Methods (BEM-style double underscore)
|
||||
Project_Model.status__enum() // Full enum definitions with metadata
|
||||
Project_Model.status__enum(id) // Single enum metadata by ID
|
||||
Project_Model.status__enum_select() // Selectable items for dropdowns
|
||||
Project_Model.status__enum_labels() // Simple id => label map
|
||||
Project_Model.status__enum_ids() // Array of valid IDs
|
||||
|
||||
Instance Properties (after fetch)
|
||||
Instance Properties (after fetch, BEM-style)
|
||||
const project = await Project_Model.fetch(1);
|
||||
project.status // 2 (raw value)
|
||||
project.status_label // "Active"
|
||||
project.status_badge // "bg-success"
|
||||
project.status // 2 (raw value)
|
||||
project.status__label // "Active"
|
||||
project.status__badge // "bg-success"
|
||||
|
||||
AJAX/JSON EXPORT
|
||||
|
||||
When models are converted to arrays/JSON, enum properties are automatically included:
|
||||
When models are converted to arrays/JSON, enum properties are automatically
|
||||
included using BEM-style naming:
|
||||
|
||||
$user->toArray() returns:
|
||||
[
|
||||
'id' => 1,
|
||||
'status_id' => 1,
|
||||
'status_id_label' => 'Active', // Added automatically
|
||||
'status_id_constant' => 'STATUS_ACTIVE', // Added automatically
|
||||
'status_id_badge' => 'bg-success', // Custom properties too
|
||||
'status_id__label' => 'Active', // Added automatically
|
||||
'status_id__constant' => 'STATUS_ACTIVE', // Added automatically
|
||||
'status_id__badge' => 'bg-success', // Custom properties too
|
||||
// ... all enum properties for current value
|
||||
]
|
||||
|
||||
ANTI-ALIASING POLICY
|
||||
|
||||
RSpade considers aliasing an anti-pattern. The BEM-style naming exists
|
||||
specifically to make enum properties grepable and self-documenting.
|
||||
|
||||
WRONG - Aliasing in fetch():
|
||||
public static function fetch($id) {
|
||||
$data = parent::fetch($id);
|
||||
$data['type_label'] = $contact->type_id__label; // Alias - BAD
|
||||
$data['type_icon'] = $contact->type_id__icon; // Alias - BAD
|
||||
return $data;
|
||||
}
|
||||
|
||||
RIGHT - Use full BEM-style names:
|
||||
// In JavaScript, use the automatic property names:
|
||||
contact.type_id__label
|
||||
contact.type_id__icon
|
||||
|
||||
Why aliasing is harmful:
|
||||
1. Makes grep searches unreliable (can't find all usages of type_id__label)
|
||||
2. Adds no value (we're not paying by the byte in source code)
|
||||
3. Creates maintenance burden (two names for the same thing)
|
||||
4. Obscures the data source (is 'type_label' a DB column or computed?)
|
||||
|
||||
The fetch() function's purpose is SECURITY - removing private data that
|
||||
the current user shouldn't see. It is not for aliasing or adding data.
|
||||
|
||||
ADVANCED FEATURES
|
||||
|
||||
Ordering
|
||||
@@ -174,10 +215,11 @@ ADVANCED FEATURES
|
||||
1 => ['label' => 'High', 'order' => 1],
|
||||
2 => ['label' => 'Medium', 'order' => 2],
|
||||
]
|
||||
// enum_select() returns: [1 => 'High', 2 => 'Medium', 3 => 'Low']
|
||||
// __enum_select() returns: [1 => 'High', 2 => 'Medium', 3 => 'Low']
|
||||
|
||||
Selective Options
|
||||
Use 'selectable' => false to hide options from dropdowns while keeping them valid:
|
||||
Use 'selectable' => false to hide options from dropdowns while keeping
|
||||
them valid:
|
||||
|
||||
3 => [
|
||||
'constant' => 'STATUS_ARCHIVED',
|
||||
@@ -197,18 +239,18 @@ PRACTICAL APPLICATIONS
|
||||
Populating Select Boxes
|
||||
<!-- Blade template -->
|
||||
<select name="status_id">
|
||||
@foreach(User_Model::status_id_enum_select() as $id => $label)
|
||||
@foreach(User_Model::status_id__enum_select() as $id => $label)
|
||||
<option value="{{ $id }}">{{ $label }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
|
||||
Dynamic CSS Classes
|
||||
<span class="badge {{ $auction->auction_status_badge }}">
|
||||
{{ $auction->auction_status_label }}
|
||||
<span class="badge {{ $auction->auction_status__badge }}">
|
||||
{{ $auction->auction_status__label }}
|
||||
</span>
|
||||
|
||||
Business Logic Flags
|
||||
if ($auction->auction_status_can_bid) {
|
||||
if ($auction->auction_status__can_bid) {
|
||||
// Show bidding interface
|
||||
}
|
||||
|
||||
@@ -223,7 +265,7 @@ PRACTICAL APPLICATIONS
|
||||
]
|
||||
|
||||
// Check permissions
|
||||
if (in_array('users.create', $user->role_permissions)) {
|
||||
if (in_array('users.create', $user->role__permissions)) {
|
||||
// User can create users
|
||||
}
|
||||
|
||||
@@ -255,6 +297,7 @@ BEST PRACTICES
|
||||
5. Keep enum values immutable - add new values, don't change existing
|
||||
6. Document custom properties in your model
|
||||
7. Run rsx:migrate:document_models after adding enums
|
||||
8. NEVER alias enum properties - use full BEM-style names
|
||||
|
||||
EXAMPLE IMPLEMENTATION
|
||||
|
||||
@@ -312,15 +355,15 @@ EXAMPLE IMPLEMENTATION
|
||||
|
||||
// Usage in controller
|
||||
if ($project->status === Project_Model::STATUS_IN_PROGRESS) {
|
||||
if ($project->priority_days < 3) {
|
||||
if ($project->priority__days < 3) {
|
||||
// Escalate critical project
|
||||
}
|
||||
}
|
||||
|
||||
// Usage in Blade view
|
||||
<div class="{{ $project->status_badge }}">
|
||||
{{ $project->status_label }}
|
||||
@if($project->status_can_edit)
|
||||
<div class="{{ $project->status__badge }}">
|
||||
{{ $project->status__label }}
|
||||
@if($project->status__can_edit)
|
||||
<button>Edit</button>
|
||||
@endif
|
||||
</div>
|
||||
@@ -330,4 +373,4 @@ SEE ALSO
|
||||
php artisan rsx:man model_fetch - Model fetching from JavaScript
|
||||
php artisan rsx:man models - RSX model system overview
|
||||
|
||||
RSpade 1.0 September 2025 ENUMS(7)
|
||||
RSpade 1.0 December 2025 ENUMS(7)
|
||||
|
||||
@@ -774,6 +774,16 @@ INSTANTIATION METHODS
|
||||
append: true, // Append instead of replace
|
||||
});
|
||||
|
||||
Replacing Existing Components:
|
||||
When called on an element with an existing component, .component()
|
||||
destroys the old component and creates a new one. Class preservation:
|
||||
- Removed: PascalCase component names (capital start, no __)
|
||||
- Preserved: Utility classes (text-muted), BEM child classes (Parent__child)
|
||||
- Preserved: All HTML attributes
|
||||
|
||||
This allows parent components to add BEM-style classes for targeting
|
||||
(e.g., Parent__slot) that survive child component replacement.
|
||||
|
||||
Client-side (HTML attributes):
|
||||
<div data-component="User_Card"
|
||||
data-component-data='{"name": "John"}'>
|
||||
|
||||
@@ -29,7 +29,7 @@ STATUS (as of 2025-11-23)
|
||||
Implemented:
|
||||
- fetch() and fetch_or_null() methods
|
||||
- Lazy relationship loading (belongsTo, hasMany, morphTo, etc.)
|
||||
- Enum properties on instances ({column}_{field} pattern)
|
||||
- Enum properties on instances (BEM-style {column}__{field} pattern)
|
||||
- Static enum constants and accessor methods
|
||||
- Automatic model hydration from Ajax responses
|
||||
- JavaScript class hierarchy with Base_* stubs
|
||||
@@ -140,31 +140,34 @@ IMPLEMENTING FETCHABLE MODELS
|
||||
}
|
||||
}
|
||||
|
||||
Augmented Array Return (recommended for CRUD pages):
|
||||
class Client_Model extends Rsx_Model
|
||||
Augmented Array Return (for model method outputs only):
|
||||
class Contact_Model extends Rsx_Model
|
||||
{
|
||||
// Model methods that produce NEW computed values
|
||||
public function full_name(): string
|
||||
{
|
||||
return trim($this->first_name . ' ' . $this->last_name);
|
||||
}
|
||||
|
||||
public function mailto_link(): string
|
||||
{
|
||||
return '<a href="mailto:' . e($this->email) . '">' . e($this->email) . '</a>';
|
||||
}
|
||||
|
||||
#[Ajax_Endpoint_Model_Fetch]
|
||||
public static function fetch($id)
|
||||
{
|
||||
$client = static::withTrashed()->find($id);
|
||||
if (!$client) {
|
||||
$contact = static::withTrashed()->find($id);
|
||||
if (!$contact) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Start with model's toArray() to get __MODEL and base data
|
||||
$data = $client->toArray();
|
||||
$data = $contact->toArray();
|
||||
|
||||
// Augment with computed/formatted fields
|
||||
$data['status_label'] = ucfirst($client->status);
|
||||
$data['status_badge'] = match($client->status) {
|
||||
'active' => 'bg-success',
|
||||
'inactive' => 'bg-secondary',
|
||||
default => 'bg-warning'
|
||||
};
|
||||
$data['created_at_formatted'] = $client->created_at->format('M d, Y');
|
||||
$data['created_at_human'] = $client->created_at->diffForHumans();
|
||||
$data['region_name'] = $client->region_name();
|
||||
$data['country_name'] = $client->country_name();
|
||||
// Augment ONLY with outputs from defined model methods
|
||||
$data['full_name'] = $contact->full_name();
|
||||
$data['mailto_link'] = $contact->mailto_link();
|
||||
|
||||
return $data;
|
||||
}
|
||||
@@ -172,7 +175,6 @@ IMPLEMENTING FETCHABLE MODELS
|
||||
|
||||
IMPORTANT: Always start with $model->toArray() when augmenting data.
|
||||
This preserves the __MODEL property needed for JavaScript hydration.
|
||||
The result is a hydrated model instance with your extra fields added.
|
||||
|
||||
DO NOT manually construct the return array like this (outdated pattern):
|
||||
return [
|
||||
@@ -182,6 +184,87 @@ IMPLEMENTING FETCHABLE MODELS
|
||||
];
|
||||
This loses __MODEL and returns a plain object instead of a model instance.
|
||||
|
||||
FETCH() IS FOR SECURITY, NOT ALIASING
|
||||
|
||||
The fetch() method exists for ONE purpose: security filtering - removing private
|
||||
data that the current user shouldn't see. It is NOT for:
|
||||
- Renaming fields (aliasing)
|
||||
- Formatting dates (use Rsx_Date/Rsx_Time on client)
|
||||
- Adding computed properties that should be in enums
|
||||
- Reshaping data for frontend convenience
|
||||
|
||||
ANTI-ALIASING POLICY
|
||||
|
||||
RSpade considers aliasing in fetch() an anti-pattern. Enum magic properties use
|
||||
BEM-style double underscore naming specifically to be grepable and self-documenting.
|
||||
|
||||
Why aliasing is harmful:
|
||||
1. Makes grep searches unreliable (can't find all usages of type_id__label)
|
||||
2. Adds no value (we're not paying by the byte in source code)
|
||||
3. Creates maintenance burden (two names for the same thing)
|
||||
4. Obscures the data source (is 'type_label' a DB column or computed?)
|
||||
|
||||
VALID PATTERNS IN FETCH()
|
||||
|
||||
1. Security removal (the primary purpose):
|
||||
unset($data['password_hash']);
|
||||
unset($data['api_secret']);
|
||||
|
||||
2. Model method with MATCHING name:
|
||||
$data['full_name'] = $model->full_name();
|
||||
$data['formatted_address'] = $model->formatted_address();
|
||||
$data['unread_count'] = $model->unread_count();
|
||||
|
||||
The key MUST match the method name. This ensures:
|
||||
- Single source of truth (method defines the computation)
|
||||
- Same name in PHP and JavaScript
|
||||
- Grepable across codebase
|
||||
|
||||
3. Conditional with matching property or method:
|
||||
$data['foo'] = $condition ? $model->foo : null;
|
||||
$data['jazz'] = $model->jazz_allowed() ? $model->jazz : 'Not permitted';
|
||||
$data['secret'] = $user->is_admin ? $model->secret : '[REDACTED]';
|
||||
|
||||
Both sides of the ternary must use matching property/method names
|
||||
or be literal values. This allows conditional defaults or permission-
|
||||
based field masking.
|
||||
|
||||
INVALID PATTERNS (Violations)
|
||||
|
||||
1. Aliasing enum properties:
|
||||
$data['type_label'] = $model->type_id__label; // BAD - use full name
|
||||
$data['status_badge'] = $model->status_id__badge; // BAD - use full name
|
||||
|
||||
2. Date formatting:
|
||||
$data['created_formatted'] = $model->created_at->format('M d'); // BAD
|
||||
$data['updated_human'] = $model->updated_at->diffForHumans(); // BAD
|
||||
// Dates are YYYY-mm-dd, datetimes are ISO UTC - format on client!
|
||||
|
||||
3. Relationship plucking without method:
|
||||
$data['client_name'] = $model->client->name; // BAD - make a method
|
||||
|
||||
4. Inline computations:
|
||||
$data['is_owner'] = $model->user_id === Session::get_user_id(); // BAD
|
||||
// Make a method: $data['is_owner'] = $model->is_owner();
|
||||
|
||||
5. Redundant explicit assignments:
|
||||
$data['id'] = $model->id; // UNNECESSARY - already in toArray()
|
||||
$data['name'] = $model->name; // UNNECESSARY - already in toArray()
|
||||
|
||||
6. Mismatched method names:
|
||||
$data['addr'] = $model->formatted_address(); // BAD - name must match
|
||||
// CORRECT: $data['formatted_address'] = $model->formatted_address();
|
||||
|
||||
CLIENT-SIDE PATTERNS
|
||||
|
||||
Use full BEM-style names in JavaScript:
|
||||
contact.type_id__label
|
||||
contact.type_id__icon
|
||||
|
||||
Format dates on client:
|
||||
Rsx_Date.format(contact.created_at) // "Dec 24, 2025"
|
||||
Rsx_Time.relative(contact.updated_at) // "2 hours ago"
|
||||
|
||||
JAVASCRIPT USAGE
|
||||
Single Record Fetching:
|
||||
// fetch() throws if record not found - no need to check for null/false
|
||||
@@ -205,13 +288,13 @@ JAVASCRIPT USAGE
|
||||
- fetch() - View/edit pages where record MUST exist (throws on not found)
|
||||
- fetch_or_null() - Optional lookups where missing is valid (returns null)
|
||||
|
||||
Enum Properties on Instances:
|
||||
Enum Properties on Instances (BEM-style double underscore):
|
||||
const project = await Project_Model.fetch(1);
|
||||
|
||||
// All enum helper properties from PHP are available
|
||||
console.log(project.status_id); // 2 (raw value)
|
||||
console.log(project.status_id_label); // "Active"
|
||||
console.log(project.status_id_badge); // "bg-success"
|
||||
console.log(project.status_id); // 2 (raw value)
|
||||
console.log(project.status_id__label); // "Active"
|
||||
console.log(project.status_id__badge); // "bg-success"
|
||||
|
||||
Static Enum Constants:
|
||||
// Constants available on the class
|
||||
@@ -220,13 +303,17 @@ JAVASCRIPT USAGE
|
||||
}
|
||||
|
||||
// Get all enum values for dropdowns
|
||||
const statusOptions = Project_Model.status_id_enum_select();
|
||||
const statusOptions = Project_Model.status_id__enum_select();
|
||||
// {1: "Planning", 2: "Active", 3: "On Hold", ...}
|
||||
|
||||
// Get full enum config
|
||||
const statusConfig = Project_Model.status_id_enum_val();
|
||||
// Get full enum config with all metadata
|
||||
const statusConfig = Project_Model.status_id__enum();
|
||||
// {1: {label: "Planning", badge: "bg-info"}, ...}
|
||||
|
||||
// Get single enum's metadata by ID
|
||||
const activeConfig = Project_Model.status_id__enum(Project_Model.STATUS_ACTIVE);
|
||||
// {label: "Active", badge: "bg-success", ...}
|
||||
|
||||
Error Handling:
|
||||
// In SPA actions, errors bubble up to Universal_Error_Page_Component
|
||||
// No try/catch needed - just call fetch() and use the result
|
||||
@@ -266,9 +353,12 @@ JAVASCRIPT CLASS ARCHITECTURE
|
||||
static STATUS_PLANNING = 1;
|
||||
static STATUS_ACTIVE = 2;
|
||||
|
||||
// Enum accessor methods
|
||||
static status_enum_val() { ... } // Full enum config
|
||||
static status_enum_select() { ... } // For dropdown population
|
||||
// Enum accessor methods (BEM-style double underscore)
|
||||
static status__enum() { ... } // Full enum config (all values)
|
||||
static status__enum(id) { ... } // Single enum's metadata by ID
|
||||
static status__enum_select() { ... } // For dropdown population
|
||||
static status__enum_labels() { ... } // Simple id => label map
|
||||
static status__enum_ids() { ... } // Array of valid IDs
|
||||
|
||||
// Relationship discovery
|
||||
static get_relationships() { ... } // Returns array of names
|
||||
@@ -618,7 +708,7 @@ LAZY RELATIONSHIPS
|
||||
ENUM PROPERTIES
|
||||
Enum values are exposed as properties on fetched model instances, mirroring
|
||||
the PHP magic property behavior. Each custom field defined in the enum
|
||||
becomes a property named {column}_{field}.
|
||||
becomes a property using BEM-style naming: {column}__{field} (double underscore).
|
||||
|
||||
PHP Enum Definition:
|
||||
public static $enums = [
|
||||
@@ -629,19 +719,19 @@ ENUM PROPERTIES
|
||||
],
|
||||
];
|
||||
|
||||
Resulting JavaScript Instance Properties:
|
||||
Resulting JavaScript Instance Properties (BEM-style double underscore):
|
||||
const project = await Project_Model.fetch(123);
|
||||
|
||||
// Raw enum value
|
||||
project.status_id // 2
|
||||
project.status_id // 2
|
||||
|
||||
// Auto-generated properties from enum definition
|
||||
project.status_id_label // "Active"
|
||||
project.status_id_badge // "bg-success"
|
||||
project.status_id__label // "Active"
|
||||
project.status_id__badge // "bg-success"
|
||||
|
||||
// All custom fields become properties
|
||||
// If enum had 'button_class' => 'btn-success':
|
||||
project.status_id_button_class // "btn-success"
|
||||
project.status_id__button_class // "btn-success"
|
||||
|
||||
Static Enum Constants:
|
||||
// Constants available on the class (from 'constant' field)
|
||||
@@ -649,14 +739,26 @@ ENUM PROPERTIES
|
||||
console.log('Project is active');
|
||||
}
|
||||
|
||||
Static Enum Methods:
|
||||
Static Enum Methods (BEM-style double underscore):
|
||||
// Get full enum config (all values with metadata)
|
||||
const statusConfig = Project_Model.status_id__enum();
|
||||
// {1: {label: "Planning", badge: "bg-info"}, 2: {...}, ...}
|
||||
|
||||
// Get single enum's metadata by ID
|
||||
const activeConfig = Project_Model.status_id__enum(Project_Model.STATUS_ACTIVE);
|
||||
// {label: "Active", badge: "bg-success", ...}
|
||||
|
||||
// Get enum values for dropdown population (id => label)
|
||||
const statusOptions = Project_Model.status_id_enum_select();
|
||||
const statusOptions = Project_Model.status_id__enum_select();
|
||||
// {1: "Planning", 2: "Active", 3: "On Hold"}
|
||||
|
||||
// Get full enum config (id => all fields)
|
||||
const statusConfig = Project_Model.status_id_enum_val();
|
||||
// {1: {label: "Planning", badge: "bg-info"}, 2: {...}, ...}
|
||||
// Get simple id => label map (all items)
|
||||
const labels = Project_Model.status_id__enum_labels();
|
||||
// {1: "Planning", 2: "Active", 3: "On Hold"}
|
||||
|
||||
// Get array of valid IDs
|
||||
const ids = Project_Model.status_id__enum_ids();
|
||||
// [1, 2, 3]
|
||||
|
||||
MODEL CONSTANTS
|
||||
All public constants defined on a PHP model are automatically exported to
|
||||
|
||||
298
app/RSpade/upstream_changes/bem_enum_naming_12_26.txt
Executable file
298
app/RSpade/upstream_changes/bem_enum_naming_12_26.txt
Executable file
@@ -0,0 +1,298 @@
|
||||
BEM-STYLE ENUM MAGIC PROPERTY NAMING - MIGRATION GUIDE
|
||||
Date: 2025-12-26
|
||||
|
||||
SUMMARY
|
||||
Enum magic properties and methods now use BEM-style double underscore naming
|
||||
to separate field names from property/method names. This makes enum properties
|
||||
immediately distinguishable from regular model attributes and enables reliable
|
||||
grep searches across the codebase.
|
||||
|
||||
Old: $model->status_id_label, Model::status_id_enum_val()
|
||||
New: $model->status_id__label, Model::status_id__enum()
|
||||
|
||||
AFFECTED FILES
|
||||
All files that access enum magic properties or static enum methods:
|
||||
- PHP files using $model->field_property magic properties
|
||||
- PHP files using Model::field_enum_val(), field_enum_select(), etc.
|
||||
- JavaScript files using model.field_property instance properties
|
||||
- JavaScript files using Model.field_enum_val(), field_enum_select(), etc.
|
||||
- Blade templates with enum property access
|
||||
- jqhtml templates with enum property access
|
||||
|
||||
CHANGES REQUIRED
|
||||
|
||||
1. Instance Magic Properties (PHP and JavaScript)
|
||||
Change single underscore between field and property to double underscore.
|
||||
|
||||
BEFORE:
|
||||
$project->status_id_label
|
||||
$project->status_id_badge
|
||||
$user->role_id_permissions
|
||||
project.status_id_label
|
||||
user.role_id_badge
|
||||
|
||||
AFTER:
|
||||
$project->status_id__label
|
||||
$project->status_id__badge
|
||||
$user->role_id__permissions
|
||||
project.status_id__label
|
||||
user.role_id__badge
|
||||
|
||||
2. Static Enum Methods (PHP)
|
||||
Change single underscore to double underscore before method suffix.
|
||||
|
||||
BEFORE:
|
||||
User_Model::role_id_enum_val()
|
||||
User_Model::role_id_enum_select()
|
||||
User_Model::role_id_enum_labels()
|
||||
User_Model::role_id_enum_ids()
|
||||
|
||||
AFTER:
|
||||
User_Model::role_id__enum()
|
||||
User_Model::role_id__enum_select()
|
||||
User_Model::role_id__enum_labels()
|
||||
User_Model::role_id__enum_ids()
|
||||
|
||||
3. Static Enum Methods (JavaScript)
|
||||
Change single underscore to double underscore before method suffix.
|
||||
Note: field_enum_val() is renamed to field__enum().
|
||||
|
||||
BEFORE:
|
||||
Project_Model.status_id_enum_val()
|
||||
Project_Model.status_id_enum_val(Project_Model.STATUS_ACTIVE)
|
||||
Project_Model.status_id_enum_select()
|
||||
Project_Model.status_id_enum_labels()
|
||||
Project_Model.status_id_enum_ids()
|
||||
|
||||
AFTER:
|
||||
Project_Model.status_id__enum()
|
||||
Project_Model.status_id__enum(Project_Model.STATUS_ACTIVE)
|
||||
Project_Model.status_id__enum_select()
|
||||
Project_Model.status_id__enum_labels()
|
||||
Project_Model.status_id__enum_ids()
|
||||
|
||||
4. Model Docblocks
|
||||
Run the document_models command to regenerate docblocks with new naming.
|
||||
|
||||
php artisan rsx:migrate:document_models
|
||||
|
||||
This updates @property-read and @method annotations in model files.
|
||||
|
||||
5. Remove Instance field_enum_val Property Access (REMOVED FEATURE)
|
||||
The instance property $model->field_enum_val has been removed entirely.
|
||||
Use the static method instead: Model::field__enum($model->field)
|
||||
|
||||
BEFORE:
|
||||
$current_enum = $project->status_id_enum_val;
|
||||
$badge = $current_enum['badge'];
|
||||
|
||||
AFTER:
|
||||
$badge = $project->status_id__badge;
|
||||
// Or for full metadata:
|
||||
$current_enum = Project_Model::status_id__enum($project->status_id);
|
||||
|
||||
SEARCH AND REPLACE PATTERNS
|
||||
|
||||
Use these regex patterns to find occurrences that need updating:
|
||||
|
||||
PHP instance properties:
|
||||
Search: \$\w+->\w+_(?:label|badge|constant|icon|[a-z_]+)(?!\w)
|
||||
(Review each match - only change if it's an enum property)
|
||||
|
||||
PHP static methods:
|
||||
Search: ::\w+_enum_val\(
|
||||
Replace: ::$1__enum(
|
||||
|
||||
Search: ::\w+_enum_select\(
|
||||
Replace: ::$1__enum_select(
|
||||
|
||||
Search: ::\w+_enum_labels\(
|
||||
Replace: ::$1__enum_labels(
|
||||
|
||||
Search: ::\w+_enum_ids\(
|
||||
Replace: ::$1__enum_ids(
|
||||
|
||||
JavaScript instance properties:
|
||||
Search: \.\w+_(?:label|badge|constant|icon|[a-z_]+)(?!\w|_)
|
||||
(Review each match - only change if it's an enum property)
|
||||
|
||||
JavaScript static methods:
|
||||
Search: \.\w+_enum_val\(
|
||||
Replace: .$1__enum(
|
||||
|
||||
FETCH() ANTI-ALIASING POLICY (ENFORCED BY PHP-ALIAS-01)
|
||||
|
||||
fetch() exists for SECURITY (removing private data), not aliasing.
|
||||
The PHP-ALIAS-01 code quality rule now enforces this at manifest-time.
|
||||
|
||||
VALID PATTERNS IN FETCH()
|
||||
|
||||
1. Security removal (the primary purpose of fetch):
|
||||
|
||||
$data = $model->toArray();
|
||||
unset($data['password_hash']);
|
||||
unset($data['api_secret']);
|
||||
return $data;
|
||||
|
||||
2. Model method with MATCHING name:
|
||||
|
||||
// Method name MUST match the key
|
||||
$data['full_name'] = $model->full_name();
|
||||
$data['formatted_address'] = $model->formatted_address();
|
||||
$data['unread_count'] = $model->unread_count();
|
||||
|
||||
// The method must be defined on the model:
|
||||
public function full_name(): string {
|
||||
return trim($this->first_name . ' ' . $this->last_name);
|
||||
}
|
||||
|
||||
3. Conditional with matching property/method or literals:
|
||||
|
||||
// Both ternary branches must use matching name or be literals
|
||||
$data['secret'] = $user->is_admin ? $model->secret : null;
|
||||
$data['jazz'] = $model->jazz_allowed() ? $model->jazz : 'Not permitted';
|
||||
$data['notes'] = $can_view ? $model->notes : '[REDACTED]';
|
||||
|
||||
INVALID PATTERNS (Violations)
|
||||
|
||||
1. Enum property aliasing:
|
||||
|
||||
WRONG:
|
||||
$data['type_label'] = $model->type_id__label;
|
||||
$data['status_badge'] = $model->status_id__badge;
|
||||
|
||||
RIGHT:
|
||||
// Don't add these - they're already in toArray() automatically
|
||||
// Access them in JavaScript with full BEM-style names:
|
||||
contact.type_id__label
|
||||
contact.status_id__badge
|
||||
|
||||
2. Date formatting:
|
||||
|
||||
WRONG:
|
||||
$data['created_formatted'] = $model->created_at->format('M d, Y');
|
||||
$data['updated_human'] = $model->updated_at->diffForHumans();
|
||||
|
||||
RIGHT:
|
||||
// Dates are always YYYY-mm-dd, datetimes are ISO UTC
|
||||
// Format on client with Rsx_Date/Rsx_Time:
|
||||
Rsx_Date.format(contact.created_at)
|
||||
Rsx_Time.relative(contact.updated_at)
|
||||
|
||||
3. Relationship plucking without method:
|
||||
|
||||
WRONG:
|
||||
$data['client_name'] = $model->client->name;
|
||||
|
||||
RIGHT:
|
||||
// Create a model method:
|
||||
public function client_name(): ?string {
|
||||
return $this->client?->name;
|
||||
}
|
||||
// Then in fetch:
|
||||
$data['client_name'] = $model->client_name();
|
||||
|
||||
4. Inline computations:
|
||||
|
||||
WRONG:
|
||||
$data['is_owner'] = $model->user_id === Session::get_user_id();
|
||||
|
||||
RIGHT:
|
||||
// Create a model method:
|
||||
public function is_owner(): bool {
|
||||
return $this->user_id === Session::get_user_id();
|
||||
}
|
||||
// Then in fetch:
|
||||
$data['is_owner'] = $model->is_owner();
|
||||
|
||||
5. Redundant explicit assignments:
|
||||
|
||||
WRONG:
|
||||
$data['id'] = $model->id;
|
||||
$data['name'] = $model->name;
|
||||
|
||||
RIGHT:
|
||||
// Remove these lines - toArray() already includes all fields
|
||||
|
||||
6. Mismatched method names:
|
||||
|
||||
WRONG:
|
||||
$data['addr'] = $model->formatted_address();
|
||||
|
||||
RIGHT:
|
||||
$data['formatted_address'] = $model->formatted_address();
|
||||
|
||||
REFACTORING EXISTING CODE
|
||||
|
||||
If you have fetch() methods with aliasing, refactor as follows:
|
||||
|
||||
Step 1: Identify the alias type
|
||||
|
||||
- Enum property alias → Remove it (automatic via toArray)
|
||||
- Date formatting → Remove it (format on client)
|
||||
- Computed value → Extract to model method
|
||||
- Redundant assignment → Remove it
|
||||
|
||||
Step 2: For computed values, create model methods
|
||||
|
||||
BEFORE (in fetch):
|
||||
$data['display_name'] = $model->first_name . ' ' . $model->last_name;
|
||||
$data['is_expired'] = $model->expires_at < now();
|
||||
|
||||
AFTER (in model):
|
||||
public function display_name(): string {
|
||||
return $this->first_name . ' ' . $this->last_name;
|
||||
}
|
||||
|
||||
public function is_expired(): bool {
|
||||
return $this->expires_at < now();
|
||||
}
|
||||
|
||||
AFTER (in fetch):
|
||||
$data['display_name'] = $model->display_name();
|
||||
$data['is_expired'] = $model->is_expired();
|
||||
|
||||
Step 3: Update JavaScript to use proper names
|
||||
|
||||
BEFORE:
|
||||
contact.type_label
|
||||
project.created_formatted
|
||||
|
||||
AFTER:
|
||||
contact.type_id__label
|
||||
Rsx_Date.format(project.created_at)
|
||||
|
||||
ESCAPE HATCH: CONTROLLER AJAX ENDPOINTS
|
||||
|
||||
If you truly need custom response formatting that doesn't fit the model
|
||||
serialization pattern, use a controller Ajax endpoint instead. Controllers
|
||||
are NOT checked by PHP-ALIAS-01 - they are the escape hatch for custom
|
||||
responses like reference data formatting:
|
||||
|
||||
// This is fine in a controller:
|
||||
#[Ajax_Endpoint]
|
||||
public static function get_options(...) {
|
||||
return Model::all()->map(fn($m) => [
|
||||
'value' => $m->id,
|
||||
'label' => $m->name,
|
||||
]);
|
||||
}
|
||||
|
||||
VERIFICATION
|
||||
|
||||
1. Search for old patterns:
|
||||
grep -r "_enum_val\(" rsx/
|
||||
grep -r "_enum_select\(" rsx/
|
||||
grep -r "_label[^_]" rsx/ # Review matches for enum properties
|
||||
|
||||
2. Verify bundle compiles without errors:
|
||||
php artisan rsx:bundle:compile --all
|
||||
|
||||
3. Run the application and verify:
|
||||
- Dropdowns populate correctly (using __enum_select())
|
||||
- Labels display correctly (using __label properties)
|
||||
- Badge classes apply correctly (using __badge properties)
|
||||
|
||||
REFERENCE
|
||||
php artisan rsx:man enum - Complete enum documentation
|
||||
php artisan rsx:man model_fetch - Model fetch and anti-aliasing policy
|
||||
@@ -702,6 +702,8 @@ this.$sid('result_container').component('My_Component', {
|
||||
});
|
||||
```
|
||||
|
||||
**Class preservation**: Only PascalCase component names (capital start, no `__`) are replaced. Utility classes (`text-muted`), BEM child classes (`Parent__child`), and all attributes are preserved.
|
||||
|
||||
### Incremental Scaffolding
|
||||
|
||||
**Undefined components work immediately** - they render as div with the component name as a class.
|
||||
@@ -931,7 +933,7 @@ User_Model::create(['email' => $email]);
|
||||
|
||||
**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.
|
||||
Integer-backed enums with model-level mapping to constants, labels, and custom properties. Uses BEM-style double underscore naming for magic properties.
|
||||
|
||||
```php
|
||||
class Project_Model extends Rsx_Model_Abstract {
|
||||
@@ -943,15 +945,30 @@ class Project_Model extends Rsx_Model_Abstract {
|
||||
];
|
||||
}
|
||||
|
||||
// Usage
|
||||
// Usage - BEM-style: field__property (double underscore)
|
||||
$project->status_id = Project_Model::STATUS_ACTIVE;
|
||||
echo $project->status_label; // "Active"
|
||||
echo $project->status_badge; // "bg-success" (custom property)
|
||||
echo $project->status_id__label; // "Active"
|
||||
echo $project->status_id__badge; // "bg-success" (custom property)
|
||||
```
|
||||
|
||||
**Special properties**:
|
||||
- `order` - Sort position in dropdowns (default: 0, lower first)
|
||||
- `selectable` - Include in dropdown options (default: true). Non-selectable items excluded from `field_enum_select()` but still shown if current value.
|
||||
- `selectable` - Include in dropdown options (default: true). Non-selectable items excluded from `field__enum_select()` but still shown if current value.
|
||||
|
||||
**JavaScript static methods** (BEM-style double underscore):
|
||||
```js
|
||||
// Get all enum data
|
||||
Project_Model.status_id__enum()
|
||||
|
||||
// Get specific enum's metadata by ID
|
||||
Project_Model.status_id__enum(Project_Model.STATUS_ACTIVE).badge // "bg-success"
|
||||
Project_Model.status_id__enum(2).selectable // false
|
||||
|
||||
// Other methods
|
||||
Project_Model.status_id__enum_select() // For dropdowns
|
||||
Project_Model.status_id__enum_labels() // id => label map
|
||||
Project_Model.status_id__enum_ids() // Array of valid IDs
|
||||
```
|
||||
|
||||
**Migration:** Use BIGINT for enum columns, TINYINT(1) for booleans. Run `rsx:migrate:document_models` after adding enums.
|
||||
|
||||
@@ -969,7 +986,7 @@ public static function fetch($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.status_id__label); // Enum properties (BEM-style)
|
||||
console.log(Project_Model.STATUS_ACTIVE); // Static enum constants
|
||||
|
||||
// Lazy relationships (requires #[Ajax_Endpoint_Model_Fetch] on relationship method)
|
||||
@@ -979,6 +996,8 @@ 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.
|
||||
|
||||
**fetch() is for SECURITY, not aliasing**: The `fetch()` method exists to remove private data users shouldn't see. NEVER alias enum properties (e.g., `type_label` instead of `type_id__label`) or format dates server-side. Use the full BEM-style names and format dates on client with `Rsx_Date`/`Rsx_Time`.
|
||||
|
||||
Details: `php artisan rsx:man model_fetch`
|
||||
|
||||
### Migrations
|
||||
|
||||
57
node_modules/.package-lock.json
generated
vendored
57
node_modules/.package-lock.json
generated
vendored
@@ -2211,9 +2211,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/core": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/core/-/core-2.3.30.tgz",
|
||||
"integrity": "sha512-6VuZCo4aCr9Qk5LhOnG0Fv6GE2Z/mUfXnSwE5iSk3v+i7bE4IgEMrQVGsndaJtHHLRRnB2n+Aed2W3H5eeV9Fg==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/core/-/core-2.3.31.tgz",
|
||||
"integrity": "sha512-VbTAbFF8QVOljNjf+1OZ4cFTE3NaeNzyMidqrooYSsHZvb6Ja5NIMDft+M4FxeidrMoRIwa7QN09XgiJWBVNRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
@@ -2237,9 +2237,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/parser": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/parser/-/parser-2.3.30.tgz",
|
||||
"integrity": "sha512-UHiGZ0bueaOGtSIQahitzc+1mJ/pibYZgYUOf6gc3a788Gq37lRA5IuyOKtoe7YYPQjJCyH43joF+Qv4bNBXDA==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/parser/-/parser-2.3.31.tgz",
|
||||
"integrity": "sha512-ILV1onWn+rMdwaPd6DYIPM0dj2aUExTJ4ww4c0/+h3Zk50gnxMJQc6fOirDrTB1nWwKtY19yFDMPFYnurOJ2wA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jest": "^29.5.11",
|
||||
@@ -2277,9 +2277,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/ssr": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.30.tgz",
|
||||
"integrity": "sha512-gNpwsWkeqT8TEGzvi6vccjhtFvT28b3NOHiqNSpgGUHgkMupHU4oqEi/QDNhEeU87kNVvqEhTsEIqAXX07Wt3Q==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.31.tgz",
|
||||
"integrity": "sha512-EpZ597l/3MEgpvAJTqcZ81cVTJwYxJzs8BLXAdbnQY+ySeOYQL8ot31IV3hdS8b6FnYezzaHN+jSBtqZZsAYnQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jquery": "^3.7.1",
|
||||
@@ -2373,9 +2373,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/vscode-extension": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.30.tgz",
|
||||
"integrity": "sha512-+l2kI1Uj/kSCeM1bzCHbEtsRE+X6VpxQpw7wfrExqMKYvrzRmU6yiQADHuc85CFg8F2HF+7d7XA9zvgj8cOXcg==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.31.tgz",
|
||||
"integrity": "sha512-aQoKAxLz1ziuJ6I2EfFXxUBvPEsDxirR2q/6VwEzYqfVTHdKiw6M2Sk25Nhm/lrg5dNLuiKa+snRodV8yEOWpQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"vscode": "^1.74.0"
|
||||
@@ -2574,26 +2574,6 @@
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@parcel/watcher-linux-x64-musl": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz",
|
||||
"integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
}
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
@@ -2745,19 +2725,6 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.54.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.54.0.tgz",
|
||||
"integrity": "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@sinclair/typebox": {
|
||||
"version": "0.27.8",
|
||||
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz",
|
||||
|
||||
11
node_modules/@jqhtml/core/dist/index.cjs
generated
vendored
11
node_modules/@jqhtml/core/dist/index.cjs
generated
vendored
@@ -4698,13 +4698,16 @@ function init_jquery_plugin(jQuery) {
|
||||
catch (error) {
|
||||
console.warn('[JQHTML] Error stopping existing component during replacement:', error);
|
||||
}
|
||||
// Remove component classes (any class starting with capital letter)
|
||||
// Remove component classes (any class starting with capital letter, except BEM classes)
|
||||
const classes = element.attr('class');
|
||||
if (classes) {
|
||||
const classList = classes.split(/\s+/);
|
||||
const nonComponentClasses = classList.filter((cls) => {
|
||||
// Keep class if it doesn't start with capital letter
|
||||
return !cls || cls[0] !== cls[0].toUpperCase() || cls[0] === cls[0].toLowerCase();
|
||||
// Keep class if:
|
||||
// 1. It's empty
|
||||
// 2. It doesn't start with a capital letter
|
||||
// 3. It's a BEM-style class (contains __) - these persist across reinitialization
|
||||
return !cls || cls[0] !== cls[0].toUpperCase() || cls[0] === cls[0].toLowerCase() || cls.includes('__');
|
||||
});
|
||||
element.attr('class', nonComponentClasses.join(' '));
|
||||
}
|
||||
@@ -4981,7 +4984,7 @@ function init(jQuery) {
|
||||
}
|
||||
}
|
||||
// Version - will be replaced during build with actual version from package.json
|
||||
const version = '2.3.30';
|
||||
const version = '2.3.31';
|
||||
// Default export with all functionality
|
||||
const jqhtml = {
|
||||
// Core
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/index.cjs.map
generated
vendored
2
node_modules/@jqhtml/core/dist/index.cjs.map
generated
vendored
File diff suppressed because one or more lines are too long
11
node_modules/@jqhtml/core/dist/index.js
generated
vendored
11
node_modules/@jqhtml/core/dist/index.js
generated
vendored
@@ -4694,13 +4694,16 @@ function init_jquery_plugin(jQuery) {
|
||||
catch (error) {
|
||||
console.warn('[JQHTML] Error stopping existing component during replacement:', error);
|
||||
}
|
||||
// Remove component classes (any class starting with capital letter)
|
||||
// Remove component classes (any class starting with capital letter, except BEM classes)
|
||||
const classes = element.attr('class');
|
||||
if (classes) {
|
||||
const classList = classes.split(/\s+/);
|
||||
const nonComponentClasses = classList.filter((cls) => {
|
||||
// Keep class if it doesn't start with capital letter
|
||||
return !cls || cls[0] !== cls[0].toUpperCase() || cls[0] === cls[0].toLowerCase();
|
||||
// Keep class if:
|
||||
// 1. It's empty
|
||||
// 2. It doesn't start with a capital letter
|
||||
// 3. It's a BEM-style class (contains __) - these persist across reinitialization
|
||||
return !cls || cls[0] !== cls[0].toUpperCase() || cls[0] === cls[0].toLowerCase() || cls.includes('__');
|
||||
});
|
||||
element.attr('class', nonComponentClasses.join(' '));
|
||||
}
|
||||
@@ -4977,7 +4980,7 @@ function init(jQuery) {
|
||||
}
|
||||
}
|
||||
// Version - will be replaced during build with actual version from package.json
|
||||
const version = '2.3.30';
|
||||
const version = '2.3.31';
|
||||
// Default export with all functionality
|
||||
const jqhtml = {
|
||||
// Core
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/index.js.map
generated
vendored
2
node_modules/@jqhtml/core/dist/index.js.map
generated
vendored
File diff suppressed because one or more lines are too long
13
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js
generated
vendored
13
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js
generated
vendored
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* JQHTML Core v2.3.30
|
||||
* JQHTML Core v2.3.31
|
||||
* (c) 2025 JQHTML Team
|
||||
* Released under the MIT License
|
||||
*/
|
||||
@@ -4699,13 +4699,16 @@ function init_jquery_plugin(jQuery) {
|
||||
catch (error) {
|
||||
console.warn('[JQHTML] Error stopping existing component during replacement:', error);
|
||||
}
|
||||
// Remove component classes (any class starting with capital letter)
|
||||
// Remove component classes (any class starting with capital letter, except BEM classes)
|
||||
const classes = element.attr('class');
|
||||
if (classes) {
|
||||
const classList = classes.split(/\s+/);
|
||||
const nonComponentClasses = classList.filter((cls) => {
|
||||
// Keep class if it doesn't start with capital letter
|
||||
return !cls || cls[0] !== cls[0].toUpperCase() || cls[0] === cls[0].toLowerCase();
|
||||
// Keep class if:
|
||||
// 1. It's empty
|
||||
// 2. It doesn't start with a capital letter
|
||||
// 3. It's a BEM-style class (contains __) - these persist across reinitialization
|
||||
return !cls || cls[0] !== cls[0].toUpperCase() || cls[0] === cls[0].toLowerCase() || cls.includes('__');
|
||||
});
|
||||
element.attr('class', nonComponentClasses.join(' '));
|
||||
}
|
||||
@@ -4982,7 +4985,7 @@ function init(jQuery) {
|
||||
}
|
||||
}
|
||||
// Version - will be replaced during build with actual version from package.json
|
||||
const version = '2.3.30';
|
||||
const version = '2.3.31';
|
||||
// Default export with all functionality
|
||||
const jqhtml = {
|
||||
// Core
|
||||
|
||||
2
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js.map
generated
vendored
2
node_modules/@jqhtml/core/dist/jqhtml-core.esm.js.map
generated
vendored
File diff suppressed because one or more lines are too long
2
node_modules/@jqhtml/core/dist/jquery-plugin.d.ts.map
generated
vendored
2
node_modules/@jqhtml/core/dist/jquery-plugin.d.ts.map
generated
vendored
@@ -1 +1 @@
|
||||
{"version":3,"file":"jquery-plugin.d.ts","sourceRoot":"","sources":["../src/jquery-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAQpE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd;;WAEG;QACH,SAAS,IAAI,gBAAgB,GAAG,IAAI,CAAC;QACrC,SAAS,CAAC,cAAc,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;QAC9F,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;QAE/E;;;;;;;WAOG;QACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;KACvC;CACF;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CA0apD"}
|
||||
{"version":3,"file":"jquery-plugin.d.ts","sourceRoot":"","sources":["../src/jquery-plugin.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAQpE,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd;;WAEG;QACH,SAAS,IAAI,gBAAgB,GAAG,IAAI,CAAC;QACrC,SAAS,CAAC,cAAc,EAAE,oBAAoB,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;QAC9F,SAAS,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,gBAAgB,CAAC;QAE/E;;;;;;;WAOG;QACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;KACvC;CACF;AAGD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,GAAG,GAAG,IAAI,CA6apD"}
|
||||
2
node_modules/@jqhtml/core/package.json
generated
vendored
2
node_modules/@jqhtml/core/package.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jqhtml/core",
|
||||
"version": "2.3.30",
|
||||
"version": "2.3.31",
|
||||
"description": "Core runtime library for JQHTML",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
|
||||
2
node_modules/@jqhtml/parser/dist/codegen.js
generated
vendored
2
node_modules/@jqhtml/parser/dist/codegen.js
generated
vendored
@@ -1377,7 +1377,7 @@ export class CodeGenerator {
|
||||
for (const [name, component] of this.components) {
|
||||
code += `// Component: ${name}\n`;
|
||||
code += `jqhtml_components.set('${name}', {\n`;
|
||||
code += ` _jqhtml_version: '2.3.30',\n`; // Version will be replaced during build
|
||||
code += ` _jqhtml_version: '2.3.31',\n`; // Version will be replaced during build
|
||||
code += ` name: '${name}',\n`;
|
||||
code += ` tag: '${component.tagName}',\n`;
|
||||
code += ` defaultAttributes: ${this.serializeAttributeObject(component.defaultAttributes)},\n`;
|
||||
|
||||
2
node_modules/@jqhtml/parser/package.json
generated
vendored
2
node_modules/@jqhtml/parser/package.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jqhtml/parser",
|
||||
"version": "2.3.30",
|
||||
"version": "2.3.31",
|
||||
"description": "JQHTML template parser - converts templates to JavaScript",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
2
node_modules/@jqhtml/ssr/package.json
generated
vendored
2
node_modules/@jqhtml/ssr/package.json
generated
vendored
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@jqhtml/ssr",
|
||||
"version": "2.3.30",
|
||||
"version": "2.3.31",
|
||||
"description": "Server-Side Rendering for JQHTML components - renders components to HTML for SEO",
|
||||
"main": "src/index.js",
|
||||
"bin": {
|
||||
|
||||
2
node_modules/@jqhtml/vscode-extension/.version
generated
vendored
2
node_modules/@jqhtml/vscode-extension/.version
generated
vendored
@@ -1 +1 @@
|
||||
2.3.30
|
||||
2.3.31
|
||||
|
||||
BIN
node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.30.vsix → node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.31.vsix
generated
vendored
Normal file → Executable file
BIN
node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.30.vsix → node_modules/@jqhtml/vscode-extension/jqhtml-vscode-extension-2.3.31.vsix
generated
vendored
Normal file → Executable file
Binary file not shown.
2
node_modules/@jqhtml/vscode-extension/package.json
generated
vendored
2
node_modules/@jqhtml/vscode-extension/package.json
generated
vendored
@@ -2,7 +2,7 @@
|
||||
"name": "@jqhtml/vscode-extension",
|
||||
"displayName": "JQHTML",
|
||||
"description": "Syntax highlighting and language support for JQHTML template files",
|
||||
"version": "2.3.30",
|
||||
"version": "2.3.31",
|
||||
"publisher": "jqhtml",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
|
||||
21
node_modules/@parcel/watcher-linux-x64-musl/LICENSE
generated
vendored
21
node_modules/@parcel/watcher-linux-x64-musl/LICENSE
generated
vendored
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-present Devon Govett
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
1
node_modules/@parcel/watcher-linux-x64-musl/README.md
generated
vendored
1
node_modules/@parcel/watcher-linux-x64-musl/README.md
generated
vendored
@@ -1 +0,0 @@
|
||||
This is the linux-x64-musl build of @parcel/watcher. See https://github.com/parcel-bundler/watcher for details.
|
||||
33
node_modules/@parcel/watcher-linux-x64-musl/package.json
generated
vendored
33
node_modules/@parcel/watcher-linux-x64-musl/package.json
generated
vendored
@@ -1,33 +0,0 @@
|
||||
{
|
||||
"name": "@parcel/watcher-linux-x64-musl",
|
||||
"version": "2.5.1",
|
||||
"main": "watcher.node",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/parcel-bundler/watcher.git"
|
||||
},
|
||||
"description": "A native C++ Node module for querying and subscribing to filesystem events. Used by Parcel 2.",
|
||||
"license": "MIT",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/parcel"
|
||||
},
|
||||
"files": [
|
||||
"watcher.node"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
},
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"libc": [
|
||||
"musl"
|
||||
]
|
||||
}
|
||||
BIN
node_modules/@parcel/watcher-linux-x64-musl/watcher.node
generated
vendored
BIN
node_modules/@parcel/watcher-linux-x64-musl/watcher.node
generated
vendored
Binary file not shown.
3
node_modules/@rollup/rollup-linux-x64-musl/README.md
generated
vendored
3
node_modules/@rollup/rollup-linux-x64-musl/README.md
generated
vendored
@@ -1,3 +0,0 @@
|
||||
# `@rollup/rollup-linux-x64-musl`
|
||||
|
||||
This is the **x86_64-unknown-linux-musl** binary for `rollup`
|
||||
25
node_modules/@rollup/rollup-linux-x64-musl/package.json
generated
vendored
25
node_modules/@rollup/rollup-linux-x64-musl/package.json
generated
vendored
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "@rollup/rollup-linux-x64-musl",
|
||||
"version": "4.54.0",
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"files": [
|
||||
"rollup.linux-x64-musl.node"
|
||||
],
|
||||
"description": "Native bindings for Rollup",
|
||||
"author": "Lukas Taegert-Atkinson",
|
||||
"homepage": "https://rollupjs.org/",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/rollup/rollup.git"
|
||||
},
|
||||
"libc": [
|
||||
"musl"
|
||||
],
|
||||
"main": "./rollup.linux-x64-musl.node"
|
||||
}
|
||||
BIN
node_modules/@rollup/rollup-linux-x64-musl/rollup.linux-x64-musl.node
generated
vendored
BIN
node_modules/@rollup/rollup-linux-x64-musl/rollup.linux-x64-musl.node
generated
vendored
Binary file not shown.
24
package-lock.json
generated
24
package-lock.json
generated
@@ -2660,9 +2660,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/core": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/core/-/core-2.3.30.tgz",
|
||||
"integrity": "sha512-6VuZCo4aCr9Qk5LhOnG0Fv6GE2Z/mUfXnSwE5iSk3v+i7bE4IgEMrQVGsndaJtHHLRRnB2n+Aed2W3H5eeV9Fg==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/core/-/core-2.3.31.tgz",
|
||||
"integrity": "sha512-VbTAbFF8QVOljNjf+1OZ4cFTE3NaeNzyMidqrooYSsHZvb6Ja5NIMDft+M4FxeidrMoRIwa7QN09XgiJWBVNRg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
@@ -2686,9 +2686,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/parser": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/parser/-/parser-2.3.30.tgz",
|
||||
"integrity": "sha512-UHiGZ0bueaOGtSIQahitzc+1mJ/pibYZgYUOf6gc3a788Gq37lRA5IuyOKtoe7YYPQjJCyH43joF+Qv4bNBXDA==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/parser/-/parser-2.3.31.tgz",
|
||||
"integrity": "sha512-ILV1onWn+rMdwaPd6DYIPM0dj2aUExTJ4ww4c0/+h3Zk50gnxMJQc6fOirDrTB1nWwKtY19yFDMPFYnurOJ2wA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/jest": "^29.5.11",
|
||||
@@ -2726,9 +2726,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/ssr": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.30.tgz",
|
||||
"integrity": "sha512-gNpwsWkeqT8TEGzvi6vccjhtFvT28b3NOHiqNSpgGUHgkMupHU4oqEi/QDNhEeU87kNVvqEhTsEIqAXX07Wt3Q==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/ssr/-/ssr-2.3.31.tgz",
|
||||
"integrity": "sha512-EpZ597l/3MEgpvAJTqcZ81cVTJwYxJzs8BLXAdbnQY+ySeOYQL8ot31IV3hdS8b6FnYezzaHN+jSBtqZZsAYnQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"jquery": "^3.7.1",
|
||||
@@ -2822,9 +2822,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@jqhtml/vscode-extension": {
|
||||
"version": "2.3.30",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.30.tgz",
|
||||
"integrity": "sha512-+l2kI1Uj/kSCeM1bzCHbEtsRE+X6VpxQpw7wfrExqMKYvrzRmU6yiQADHuc85CFg8F2HF+7d7XA9zvgj8cOXcg==",
|
||||
"version": "2.3.31",
|
||||
"resolved": "http://privatenpm.hanson.xyz/@jqhtml/vscode-extension/-/vscode-extension-2.3.31.tgz",
|
||||
"integrity": "sha512-aQoKAxLz1ziuJ6I2EfFXxUBvPEsDxirR2q/6VwEzYqfVTHdKiw6M2Sk25Nhm/lrg5dNLuiKa+snRodV8yEOWpQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"vscode": "^1.74.0"
|
||||
|
||||
Reference in New Issue
Block a user