Laravel 智能驗證規則生成器:企業級增強方案
<?php namespace App\ Services\ Validation ; use Illuminate\ Support\ Facades\ DB ;
use Illuminate\ Support\ Facades\ Cache ;
use Illuminate\ Support\ Facades\ Validator ;
use Illuminate\ Support\ Str ;
use Illuminate\ Validation\ Rule ;
use InvalidArgumentException ; class SchemaDrivenValidator
{ protected $config ; protected $schemaCacheKey = 'database_schema_v2' ; protected $connection ; protected $databaseDriver ; protected $customRules = [ ] ; protected $ignoreRules = [ ] ; protected $tableAliases = [ ] ; protected $currentTable ; protected $nestedValidators = [ ] ; public function __construct ( ) { $this -> loadConfig ( ) ; $this -> connection = config ( 'database.default' ) ; $this -> databaseDriver = config ( "database.connections.{ $this -> connection } .driver" ) ; } protected function loadConfig ( ) { $this -> config = config ( 'validation' , [ 'cache_ttl' => 86400 , 'auto_relation' => true , 'strict_types' => false , 'default_string_max' => 255 , 'password_min_length' => 8 , 'image_max_size' => 2048 , 'custom_rules' => [ ] , 'ignore_fields' => [ ] , 'table_aliases' => [ ] , 'type_mappings' => [ 'int' => 'integer' , 'varchar' => 'string' , 'text' => 'string' , 'datetime' => 'date' , 'timestamp' => 'date' , 'json' => 'array' , ] , 'special_field_handlers' => [ 'email' => [ 'email' ] , 'phone' => [ 'phone' ] , 'password' => [ 'min::password_min_length' , 'confirmed' ] , 'image' => [ 'image' , 'mimes:jpg,png,jpeg' , 'max::image_max_size' ] , '_at' => [ 'date' ] , '_image' => [ 'image' , 'mimes:jpg,png,jpeg' , 'max::image_max_size' ] , '_avatar' => [ 'image' , 'mimes:jpg,png,jpeg' , 'max::image_max_size' ] , ] ] ) ; $this -> customRules = $this -> config [ 'custom_rules' ] ?? [ ] ; $this -> ignoreRules = $this -> config [ 'ignore_fields' ] ?? [ ] ; $this -> tableAliases = $this -> config [ 'table_aliases' ] ?? [ ] ; } public function getDatabaseSchema ( bool $forceRefresh = false ) { $cacheKey = $this -> schemaCacheKey . '_' . $this -> connection ; if ( $forceRefresh ) { Cache :: forget ( $cacheKey ) ; } return Cache :: remember ( $cacheKey , $this -> config [ 'cache_ttl' ] , function ( ) { return $this -> fetchDatabaseSchema ( ) ; } ) ; } protected function fetchDatabaseSchema ( ) { switch ( $this -> databaseDriver ) { case 'mysql' : return $this -> getMysqlSchema ( ) ; case 'pgsql' : return $this -> getPostgresSchema ( ) ; case 'sqlite' : return $this -> getSqliteSchema ( ) ; default : throw new InvalidArgumentException ( "Unsupported database driver: { $this -> databaseDriver } " ) ; } } protected function getMysqlSchema ( ) { $databaseName = config ( "database.connections.{ $this -> connection } .database" ) ; $columns = DB :: connection ( $this -> connection ) -> table ( 'information_schema.columns' ) -> select ( 'table_name' , 'column_name' , 'column_comment' , 'data_type' , 'character_maximum_length' , 'is_nullable' , 'column_default' , 'column_type' , 'numeric_precision' , 'numeric_scale' , 'extra' ) -> where ( 'table_schema' , $databaseName ) -> get ( ) ; $indexes = DB :: connection ( $this -> connection ) -> table ( 'information_schema.statistics' ) -> select ( 'table_name' , 'column_name' , 'index_name' , 'non_unique' ) -> where ( 'table_schema' , $databaseName ) -> get ( ) ; $foreignKeys = DB :: connection ( $this -> connection ) -> table ( 'information_schema.key_column_usage' ) -> select ( 'table_name' , 'column_name' , 'referenced_table_name' , 'referenced_column_name' ) -> where ( 'table_schema' , $databaseName ) -> whereNotNull ( 'referenced_table_name' ) -> get ( ) ; return $this -> processSchemaData ( $columns , $indexes , $foreignKeys ) ; } protected function getPostgresSchema ( ) { $schema = config ( "database.connections.{ $this -> connection } .schema" , 'public' ) ; $columns = DB :: connection ( $this -> connection ) -> table ( 'information_schema.columns' ) -> select ( 'table_name' , 'column_name' , 'column_default' , 'is_nullable' , 'data_type' , 'character_maximum_length' , 'numeric_precision' , 'numeri