Génération de règles de validation pour model

J’apprécie de mettre des règles sur les modèles. Mais sous Laravel il n’y a rien de prévu en particulier.

Sous Laravel il est facile d’utiliser les setter :

private $titre;    
    /**
     * mutateur de l'attribut titre
     */
public function setTitreAttribute($value){
	$valid= Validator::make(array('titre'=>$value), array('titre'=>'required|min:3'));
	if ($valid->passes())
		$this->attributes['titre'] = $value;
	else
		throw new \Exception( 'Validation - '.$valid->messages()->first('titre') );
    }

il est très simple d’ajouter pour simplifier une méthode de règles dans notre modèle :

protected static $rules = array(
        'titre' => 'required|max:255',
        'description' => 'required';

Cependant Laravel, a un outil artisan migrate, et on se retrouve désynchronisé entre notre table et notre code.

Donc mon idée a été de générer(actualiser) ces règles a partir des fichiers de migrations. Ces fichiers sont de la forme :

<?php
use Illuminate\Database\Migrations\Migration;

class UpdateUsersTable extends Migration
{
	public function up()
	{
		// Update the users table
		Schema::table('users', function($table){
			$table->softDeletes();
			$table->string('website');
			$table->string('country');
			$table->string('gravatar');
		});
	}

Il suffit donc juste d’ajouter une méthode donnant nos règles :

public function upRules()
	{
		SchemaRule::table('users',function($table)
		{
			$table->add('website','max:178');
			$table->add('country','max:32');
		});
	}
public function upRules()	{
	SchemaRule::create('posts',function($table)
	{
		$table->add('id','required|integer');
		$table->add('user_id','required|integer');
		$table->add('title','required');
	});
}

J’ai gardé volontairement la meme synthaxe que migration/Shema, cette nouvelle méthode en plus, dans la classe de migration, ne gène en rien et est optionnelle.


Php Artisan

Il nous faut donc créer une commande php artisan, php artisan model:rules , pour ce faire nous devons au préalable la déclarer dans /app/start/artisan.php en ajoutant la ligne :
Artisan::add(new RulesCommand);

Puis, créer la commande artisan dans le fichier app/commands/RulesCommand.php

&lt;?php // app:commands/RulesCommand.php

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class SchemaRule
{
	static $command;
    static $path = '/config/models/';

	/**
	 *	creation des regles
	 */
	static public function create( $table, Closure $callback)
	{
		$table =new Table(static::$command, $table, $callback);
		$file= app_path().static::$path;
		$file.=$table->name.'-rules.php';
		file_put_contents($file, static::arrayToString($table-&gt;colums) );
	}

	/**
	 *	mise a jour des regles
	 */
	static public function table( $table, Closure $callback)
	{
		$file= app_path().static::$path.$table.'.rules.php';
		if (!file_exists($file)) static::create($table,$callback);
		$table =new Table(static::$command,$table);
		$table-&gt;colums =static::loadFile($file);
		$callback($table);
		file_put_contents($file, static::arrayToString($table-&gt;colums) );
	}

	public function drop($table)
	{
		unlink(app_path().static::$path.$table.'.rules.php');
	}

	static private function arrayToString(Array $array)
	{
		$ret='';
		foreach($array as $key=&gt;$a){
		    if ($a){
			if (!is_int($key)){
			    if ($key) $ret.="\t'$key'\t=&gt;'$a',\n";
			}
			else
			$ret.="\t'$a',\n";
		    }
		}
		return "&lt;?php\nreturn array(\n".substr($ret,0,-2)."\n);";
	}

	static private function loadFile($file){
		return require_once($file);
	}
}

class Table
{
	public $name;
	public $colums=array();
	static $command;

	public function __construct($command,$table, Closure $callback = null)
	{
		$this-&gt;name = $table;
		static::$command = $command;
		if ( ! is_null($callback)) $callback($this);
	}

	public function add($column,$rule)
	{
		static::$command-&gt;info('          '.$this-&gt;name.'.set '.$column);
		$this-&gt;colums[$column]=$rule;
	}

	public function dropColumns()
	{
		$args=func_get_args();
		foreach ($args as $i=&gt;$arg) $this-&gt;del($arg);
	}

	private function del($column)
	{
		if (array_key_exists($column,$this-&gt;colums))
			unset($this-&gt;colums[$column]);
	}
}

class RulesCommand extends Command {

	protected $name = 'model:rules';
	protected $description = 'Generation de regles pour modeles';

	/**
	 * Execute the console command.
	 * @return void
	 */
	public function fire()
	{
		$this-&gt;comment('=====================================');
		$this-&gt;line('');
		$this-&gt;info('    lister les migrations');
		$this-&gt;info('    generer des regles de validations');
		$this-&gt;info('    dans dossier /app/models/rules/');
		$this-&gt;line('');
		$this-&gt;comment('-------------------------------------');
		$this-&gt;line('');

		$this-&gt;listMigrates();

		$this-&gt;line('');
		$this-&gt;comment('=====================================');
	}

	protected function listMigrates()
	{
                $path= app_path().SchemaRule::$path;
                if (!file_exists($path)) mkdir($path);

		$path= app_path().'/database/migrations/';
		$files= $this-&gt;getMigrationFiles($path);
		SchemaRule::$command=$this;
		foreach($files as $file){
			$this-&gt;comment($file);
			include_once ($path.''.$file.'.php');
			$class=$this-&gt;resolve($file);
			if ($class){
				$this-&gt;info(class_basename($class));
				if (method_exists($class,'upRules'))
					$class-&gt;upRules();
				else
					$this-&gt;error('   pas de rules');
			}
		}
	}

	private function resolve($file)
	{
		$file = implode('_', array_slice(explode('_', $file), 4));
		$class = studly_case($file);
		return new $class;
	}

	private function getMigrationFiles($path)
	{
		$files= glob($path.'*_*.php');
		if ($files === false) return array();

		$files = array_map(function($file)
		{
			return str_replace('.php', '', basename($file));

		}, $files);
		sort($files);
		return $files;
	}

}

Cette commande est un peu longue, désolé mais garder la syntaxe de Shema a un coût.

Lorsque vous l’exécuter, cette commande php artisan model:rules génère (SchemaRule::create) ou actualise (Shema::table) les fichiers de règles liés a ces tables. Fichiers du type:

<?php // app/config/models/posts-rules.php
return array(
	'id'	=>'required|integer',
	'user_id'	=>'required|integer',
	'title'	=>'required'
);

Vous avons donc un model php toujours synchronisé pour les règles avec les tables, en ayant une méthode Conf::get(‘models/posts-rules.title’).

Share Button

Vous devriez aimer...