Files
rspade_system/app/RSpade/CodeQuality/Rules/PHP/DbTableUsage_CodeQualityRule.php
root 9ebcc359ae Fix code quality violations and enhance ROUTE-EXISTS-01 rule
Implement JQHTML function cache ID system and fix bundle compilation
Implement underscore prefix for system tables
Fix JS syntax linter to support decorators and grant exception to Task system
SPA: Update planning docs and wishlists with remaining features
SPA: Document Navigation API abandonment and future enhancements
Implement SPA browser integration with History API (Phase 1)
Convert contacts view page to SPA action
Convert clients pages to SPA actions and document conversion procedure
SPA: Merge GET parameters and update documentation
Implement SPA route URL generation in JavaScript and PHP
Implement SPA bootstrap controller architecture
Add SPA routing manual page (rsx:man spa)
Add SPA routing documentation to CLAUDE.md
Phase 4 Complete: Client-side SPA routing implementation
Update get_routes() consumers for unified route structure
Complete SPA Phase 3: PHP-side route type detection and is_spa flag
Restore unified routes structure and Manifest_Query class
Refactor route indexing and add SPA infrastructure
Phase 3 Complete: SPA route registration in manifest
Implement SPA Phase 2: Extract router code and test decorators
Rename Jqhtml_Component to Component and complete SPA foundation setup

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-19 17:48:15 +00:00

162 lines
6.9 KiB
PHP
Executable File

<?php
namespace App\RSpade\CodeQuality\Rules\PHP;
use App\RSpade\CodeQuality\Rules\CodeQualityRule_Abstract;
use App\RSpade\CodeQuality\Support\FileSanitizer;
use App\RSpade\Core\Database\ModelHelper;
class DbTableUsage_CodeQualityRule extends CodeQualityRule_Abstract
{
public function get_id(): string
{
return 'PHP-DB-01';
}
public function get_name(): string
{
return 'DB::table() Usage Check';
}
public function get_description(): string
{
return 'Enforces ORM pattern - no direct query builder access via DB::table()';
}
public function get_file_patterns(): array
{
return ['*.php'];
}
public function get_default_severity(): string
{
return 'high';
}
/**
* Check for DB::table() usage and enforce ORM model usage (from line 1814)
* Enforces ORM pattern - no direct query builder access via DB::table()
*/
public function check(string $file_path, string $contents, array $metadata = []): void
{
// Skip vendor directories
if (str_contains($file_path, '/vendor/')) {
return;
}
// Skip migration files - they legitimately use DB::table() for schema operations
if (str_contains($file_path, '/database/migrations/')) {
return;
}
// Skip CodeQuality directory
if (str_contains($file_path, '/CodeQuality/')) {
return;
}
// Skip InspectCommand.php - it documents what the checks do
if (str_contains($file_path, 'InspectCommand.php')) {
return;
}
// Get both original and sanitized content
$original_content = file_get_contents($file_path);
$original_lines = explode("\n", $original_content);
// Get sanitized content with comments removed
$sanitized_data = FileSanitizer::sanitize_php($contents);
$sanitized_lines = $sanitized_data['lines'];
foreach ($sanitized_lines as $line_num => $sanitized_line) {
$line_number = $line_num + 1;
// Skip if the line is empty in sanitized version (was a comment)
if (trim($sanitized_line) === '') {
continue;
}
// Check for DB::table( usage in sanitized content
if (preg_match('/DB\s*::\s*table\s*\(/i', $sanitized_line)) {
// Try to extract table name from the parameter (use sanitized line)
$table_name = null;
if (preg_match('/DB\s*::\s*table\s*\(\s*[\'"]([^\'"]+)[\'"]\s*\)/i', $sanitized_line, $matches)) {
$table_name = $matches[1];
}
// Skip Laravel's internal migrations table
if ($table_name === 'migrations') {
continue;
}
// Skip Laravel's sessions table - managed by framework
if ($table_name === 'sessions') {
continue;
}
// Skip framework internal tables (prefixed with underscore)
// These are low-level system tables managed directly for performance
if (str_starts_with($table_name, '_')) {
continue;
}
// Use original line for display in error message
$original_line = $original_lines[$line_num] ?? $sanitized_line;
// Determine which directory to suggest for model creation
$model_location = str_contains($file_path, '/rsx/') ? './rsx/models' : './app/Models';
// Build resolution message based on whether we found the table name
$resolution = "Direct database table access via DB::table() violates framework architecture principles.\n\n";
if ($table_name) {
// Check if a model exists for this table
$model_exists = false;
$model_class = null;
try {
$model_class = ModelHelper::get_model_by_table($table_name);
$model_exists = true;
} catch (\Exception $e) {
// Model doesn't exist
$model_exists = false;
}
if ($model_exists) {
$resolution .= "RECOMMENDED SOLUTION:\n";
$resolution .= "Use the existing '{$model_class}' model instead of DB::table('{$table_name}').\n";
$resolution .= "Example: {$model_class}::where('column', \$value)->get();\n\n";
} else {
$resolution .= "RECOMMENDED SOLUTION:\n";
$resolution .= "No model detected for table '{$table_name}'. Create a new model class extending Rsx_Model_Abstract in {$model_location}.\n";
$resolution .= "Example model definition:\n";
$resolution .= " class " . ucfirst(str_replace('_', '', ucwords($table_name, '_'))) . " extends Rsx_Model_Abstract {\n";
$resolution .= " protected \$table = '{$table_name}';\n";
$resolution .= " }\n\n";
}
} else {
$resolution .= "RECOMMENDED SOLUTION:\n";
$resolution .= "Create an ORM model extending Rsx_Model_Abstract in {$model_location} for the target table.\n\n";
}
$resolution .= "ALTERNATIVE (for complex reporting queries only):\n";
$resolution .= "If ORM is genuinely inappropriate due to query complexity (e.g., multi-table aggregations, complex reporting):\n";
$resolution .= "- Use DB::select() with raw SQL and prepared parameters for read operations\n";
$resolution .= "- Use DB::statement() with prepared parameters for write operations\n";
$resolution .= "Example: DB::select('SELECT * FROM table WHERE id = ?', [\$id]);\n\n";
$resolution .= "RATIONALE:\n";
$resolution .= "- ORM models provide data integrity, relationships, and business logic encapsulation\n";
$resolution .= "- Query builder (DB::table()) offers no advantages over raw queries for complex operations\n";
$resolution .= "- Consistent use of models maintains architectural coherence";
$this->add_violation(
$file_path,
$line_number,
"DB::table() usage detected. Framework requires ORM models for database access.",
trim($original_line),
$resolution,
'high'
);
}
}
}
}