Files
rspade_system/app/RSpade/Modules/Model_ManifestSupport.php
root f6fac6c4bc Fix bin/publish: copy docs.dist from project root
Fix bin/publish: use correct .env path for rspade_system
Fix bin/publish script: prevent grep exit code 1 from terminating script

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 02:08:33 +00:00

171 lines
5.9 KiB
PHP
Executable File

<?php
namespace App\RSpade\Modules;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use ReflectionClass;
use App\RSpade\Core\Cache\RsxCache;
use App\RSpade\Core\Manifest\Manifest;
use App\RSpade\Core\Manifest\ManifestSupport_Abstract;
/**
* Support module for extracting database metadata for Rsx_Model_Abstract classes
* This runs after the primary manifest is built to add model metadata
*/
class Model_ManifestSupport extends ManifestSupport_Abstract
{
/**
* Process the manifest and add model database metadata
*
* @param array &$manifest_data Reference to the manifest data array
* @return void
*/
public static function process(array &$manifest_data): void
{
// Initialize models key if it doesn't exist
if (!isset($manifest_data['data']['models'])) {
$manifest_data['data']['models'] = [];
}
// All PHP files should already be loaded in Phase 3 of manifest processing
// Get all classes extending Rsx_Model_Abstract
$model_entries = Manifest::php_get_extending('Rsx_Model_Abstract');
foreach ($model_entries as $model_entry) {
if (!isset($model_entry['fqcn'])) {
continue;
}
$fqcn = $model_entry['fqcn'];
$class_name = $model_entry['class'] ?? '';
$cachekey = 'Model_ManifestSupport_' . $model_entry['file'] . '__' . $model_entry['hash'];
$cache = RsxCache::get_persistent($cachekey);
if (!empty($cache)) {
$manifest_data['data']['models'][$class_name] = $cache;
}
include_once($model_entry['file']);
// Check if class is abstract
if (\App\RSpade\Core\Manifest\Manifest::php_is_abstract($fqcn)) {
// Skip abstract classes
continue;
}
// Instantiate the model to get table name
$model_instance = new $fqcn();
$table_name = $model_instance->getTable();
// Check if table exists
if (!Schema::hasTable($table_name)) {
// This is acceptable, maybe migrations didnt run or migrations are broken. Just skip adding
// the database metadata to the manifest.
continue;
}
// Get column information
$columns = [];
// Use SHOW COLUMNS to get column information
$column_results = DB::select("SHOW COLUMNS FROM `{$table_name}`");
foreach ($column_results as $column) {
$columns[$column->Field] = [
'type' => static::__parse_column_type($column->Type),
'nullable' => ($column->Null === 'YES'),
'key' => $column->Key,
'default' => $column->Default,
'extra' => $column->Extra,
];
}
// Look up the file's metadata to get public_static_methods extracted during reflection
$file_path = $model_entry['file'] ?? '';
$public_static_methods = [];
// Find the public_static_methods data from the files array
if ($file_path && isset($manifest_data['data']['files'][$file_path])) {
$file_metadata = $manifest_data['data']['files'][$file_path];
$public_static_methods = $file_metadata['public_static_methods'] ?? [];
}
// Store model metadata with database info AND preserved public_static_methods
$full_data = [
'fqcn' => $fqcn,
'file' => $file_path,
'table' => $table_name,
'columns' => $columns,
'public_static_methods' => $public_static_methods, // Include the public static methods
'class' => $class_name, // Ajax_Endpoint_Controller expects this
];
$manifest_data['data']['models'][$class_name] = $full_data;
RsxCache::set_persistent($cachekey, $full_data);
}
}
/**
* Parse MySQL column type to a simpler format
*
* CRITICAL: TINYINT(1) is treated as boolean (common MySQL boolean convention)
* All other TINYINT sizes are treated as integers
*
* @param string $type MySQL column type (e.g., "varchar(255)", "tinyint(1)")
* @return string Simplified type
*/
protected static function __parse_column_type(string $type): string
{
// Special case: tinyint(1) is boolean
if (preg_match('/^tinyint\(1\)/i', $type)) {
return 'boolean';
}
// Extract base type without parameters
if (preg_match('/^([a-z]+)/', $type, $matches)) {
$base_type = $matches[1];
// Map MySQL types to simplified types
$type_map = [
'int' => 'integer',
'bigint' => 'integer',
'tinyint' => 'integer', // tinyint(1) handled above, others are integers
'smallint' => 'integer',
'mediumint' => 'integer',
'varchar' => 'string',
'char' => 'string',
'text' => 'text',
'mediumtext' => 'text',
'longtext' => 'text',
'datetime' => 'datetime',
'timestamp' => 'datetime',
'date' => 'date',
'time' => 'time',
'decimal' => 'decimal',
'float' => 'float',
'double' => 'double',
'boolean' => 'boolean',
'json' => 'json',
'enum' => 'enum',
];
return $type_map[$base_type] ?? $base_type;
}
return $type;
}
/**
* Get the name of this support module
*
* @return string
*/
public static function get_name(): string
{
return 'Model Database Metadata';
}
}