Laravel 4 中相关模型验证的最佳实践?

Posted

技术标签:

【中文标题】Laravel 4 中相关模型验证的最佳实践?【英文标题】:Best Practice for Related Model Validation in Laravel 4? 【发布时间】:2014-02-07 00:41:33 【问题描述】:

我希望能够在开始保存之前验证具有相关模型的创建表单。我正在保存一个拥有许多相关模型的组织。由于我有许多多对多关系,因此我必须先创建主模型,然后通过从输入数据分别创建每个相关模型的费力过程。创建初始组织后,我在组织存储库中调用“createRelated”方法。

public function createRelated($input)

    $this->orgtypes()->attach(1, ['objectstate_id' => ORGANIZATION_STARTED]);
    $this->addresses()->create(array_only($input, ['street1', 'zip', 'city', 'state_code']));
    for ($i=0; $i < 2; $i++) 
     
        if($input['phone_numbers_'.$i.'_number'])
        
            $this->phoneNumbers()->create(['phone_number_type_id' => $input['phone_numbers_'.$i.'_phone_number_type_id'], 'number' => $input['phone_numbers_'.$i.'_number']]);
        
      
    $this->procedures()->attach($input['procedures']);     
    $input['objectstate_id'] = USER_STARTED;
    $input['password'] = $this->encryptPassword($input['password']);
    $this->users()->create(array_only($input, ['first_name', 'last_name', 'email', 'password', 'objectstate_id']));
    $this->profileElements()->create(['type' => 'short_description', 'content' => $input['short_description']]);

在我看来,必须调用每个输入并将其发送到相应的模型并不正确,但这是我在 IRC 上听到的“正确”方式。但现在我开始验证,目前没有为这些模型创建验证,即使输入可能违反验证规则。我很惊讶没有命名约定(我已经看到)可以清楚地识别相关的模型输入,这样就可以在没有像上面那样手动处理的情况下对其进行验证和创建。在我开始上述保存事务之前如何验证相关输入数据的任何想法?

我的组织验证器如下所示:

<?php namespace Acme\Validators\Organizations;

use Acme\Validators\ValidatorAbstract;

class Validator extends ValidatorAbstract 

   /**
    * Validation rules
    */
   protected $rules = array(
       'organization_name' => 'required'
   );

   /**
    * Custom Validation Messages
    */
   protected $messages = array(
   );



<?php namespace Acme\Validators;

use Illuminate\Support\MessageBag as MessageBag;


/*
* This class defines abstract Validator methods
*/

abstract class ValidatorAbstract implements ValidatorInterface 
   protected $validator;

   protected $input;

   protected $errors;

   /**
    * @param array $input
    *
    */
   public function __construct($input = NULL, MessageBag $bag)
   
       $this->input = $input ?: \Input::all();

       $this->validator = \Validator::make($this->input, $this->rules, $this->messages);
       $this->errors = $bag;
   

   /**
    * Run validation on input.
    *
    * @return boolean
    */
   public function passes()
   
       if($this->validator->passes())
       
           return true;
       
       $this->errors = $this->validator->messages();

       return false;
   

   /**
    * Get all errors stored.
    *
    * @return MessageBag
    */
   public function getErrors()
   
       return $this->errors;
   

   /**
    * Add new error.
    *
    * @return MessageBag
    */
   public function addError($key, $message)
   
      return $this->errors->add($key, $message);
   


我在其中引入 Ardent 的 Abstract Repository 类

<?php namespace Acme\Repositories;

// use Eloquent;
use LaravelBook\Ardent\Ardent;

/*
* This class defines Eloquent methods
*/

abstract class EloquentRepositoryAbstract extends Ardent implements RepositoryInterface 

   protected $guarded = [];

   public $timestamps = false;
   public $autoHydrateEntityFromInput = true;    // Ardent hydrates on new entries' validation
   public $forceEntityHydrationFromInput = true; // Ardent hydrates whenever validation is called
…

我的控制器:

<?php

use \Acme\Repositories\Organizations\OrganizationRepositoryInterface;
use \Acme\Validators\Organizations\Validator;
use \Acme\Validators\Users\EditValidator as UsersValidator;

class OrganizationsController extends BaseController 

   /**
    * Organization Repository
    *
    * @var repository
    * @var validator
    * @var usersValidator
    */
   protected $repository;
   protected $validator;
   protected $usersValidator;

   public function __construct(OrganizationRepositoryInterface $repository, 
                               Validator $validator,
                               UsersValidator $usersValidator)
   
       $this->repository = $repository;
       $this->validator = $validator;
       $this->users_validator = $usersValidator;
   

[…] Vanilla 雄辩风格的存储方法:

   public function store()
   
       $input = Input::all();
       $opasses = $this->validator->passes();
       $ppasses = $this->repository->validateProcedure($input);
       $upasses = $this->users_validator->passes();
       if($opasses && $ppasses['status'] && $upasses)
       
           $new_organization = $this->repository->create(['organization_name' => $input['organization_name']]);  
           if($input['logo_url'])
           
               $new_organization->processImage($input, Request::root());             
           
           if($new_organization->saveRelated($input, 'create'))
           
               return Redirect::route('home')
                   ->with('message', 'Organization Created.');               
           
           else
           
               return Redirect::route('organizations.create')
                   ->withInput()
                   ->with('message', 'There were errors in the creation of this Organization');              
           
       
       return Redirect::route('organizations.create')
           ->withInput()
           ->withErrors(array_merge($this->validator->getErrors()->toArray(), $this->users_validator->getErrors()->toArray(), $ppasses))
           ->with('message', 'There were validation errors.');

   

这一切似乎都过于复杂和脆弱。

【问题讨论】:

【参考方案1】:

我认为最艰难的决定是在调用验证方法之前将验证规则放在哪里。这是一个有用的链接:http://laravel.com/docs/validation#available-validation-rules。

这是我的做法,但我仍在决定将验证规则/错误消息放在哪里。

Laravel 4 的内置验证类有一种方法可以让验证变得非常简单:

Validator::make($input, $rules=array(), $errors=array())

(第三个参数可选)

在您的情况下,在您调用 create 方法之前(假设用于保存元素),您可以这样做:

$rules = array(
    "first_name" : "required",
    "second_name" : "required",
     and so on...
);

我给你的链接会给你可能的规则。最后,您还可以像这样创建自己的自定义错误消息:

$messages = array(
    "required" => "This :attribute field is required"
    and so on...
);

:attribute 将自动填写验证失败的字段。

然后,要验证您只需传入这些数组并调用 pass() 方法或 failed() 方法:

Validator::make($input, $rules, $messages)-&gt;passes() (如果通过则返回 true,如果失败则返回 false) 在我看来,这样做会更干净:

$validation = Validator::make($input, $rules, $messages);
if ($validation->passes()) 
      // save data

作为奖励,您可以访问此数组中的错误消息:

$errors = $validation->messages();

所以,我希望这能回答你的问题。我认为最大的问题是将这些验证规则放在哪里,因为我认为还没有约定。如果您有任何见解和意见,请分享。谢谢。

【讨论】:

嗯,我目前有一个我认为不好的解决方案。我正在使用存储库模式和验证作为服务。 是的,我认为这与您原来的帖子完全不同,因为您现在询问的是应用程序架构的问题,而不是如何验证输入。

以上是关于Laravel 4 中相关模型验证的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 中批量更新关系的最佳实践

Laravel 数据库日志 - 最佳实践是啥

为同一 API 混合多个身份验证模型是最佳实践吗? [关闭]

Angular、Laravel 4 和 MySQL 数据库最佳实践

在 Angular 2/4/6 应用程序中管理状态的最佳实践

MVC 验证 - 使用服务层保持 DRY - 最佳实践是啥?