【问题标题】:How would I go about refactoring this controller by extracting some of the code out?我将如何通过提取一些代码来重构这个控制器?
【发布时间】:2013-10-19 08:05:19
【问题描述】:
public function store()
{
  $input = Input::all();
  $validator = User::validate($input);

  if(!$validator->passes()) {
    $notification['danger'] = 'There were validation errors!';
    return Redirect::route('user.create')->withInput()->withErrors($validator)->with('notification', $notification);
  }

  $input['password'] = Hash::make($input['password']);
  $user = $this->user->create($input);
  $role = $this->role->find($input['role_id']);
  $user->roles()->save($role);

  $notification['success'] = "User $user->email sucessfuly created.";
  return Redirect::route('user.index')->with('notification', $notification);    
}

所以我读了很多关于架构的文章,虽然我意识到这不是“好”的做事方式,但我想不出很多解决方案。

将其提取到 UserRepository UserFormValidator 等一堆类中听起来像是过度工程,特别是在我的情况下,这是一个相当小的项目,只会持续几周。

我更感兴趣的是如何将这个业务逻辑提取到我的用户模型中。在我看来,通过关系关联其他模型无论如何都是模型的关注点。我当前的模型代码只设置了 hasMany()、belongsTo() 等关系以及 $filleable、$hidden 属性。

无论如何,我愿意接受建议。

【问题讨论】:

    标签: php laravel refactoring laravel-4


    【解决方案1】:

    在存储库之前,这是我用来创建和验证模型的方式:

    public function store()
    {
        $user = new User(Input::all());
    
        $user->password = Input::get('password');
    
        if( !$user->save() ) {
            return Redirect::back()
                    ->withInput()
                    ->withErrors($user->errors);
        }
    
        return Redirect::route('users.index')
            ->with('successMessages', 'User "'.$user->first_name.'" created.');
    }
    

    此代码由

    提供

    基础模型:

    use Illuminate\Auth\UserInterface;
    use Illuminate\Auth\Reminders\RemindableInterface;
    
    class BaseModel extends Eloquent {
    
        public $errors;
    
        public function __construct(array $attributes = array()) {
    
            parent::__construct($attributes);
    
        }
    
        public static function boot() {
            parent::boot();
    
            static::saving(function ($data) {
                return $data->validate();
            });
        }
    
        public function validate() {
    
            $validation = Validator::make($this->attributes, $this->rules);
    
            if($validation->passes()) return true;
    
            $this->errors = $validation->messages();
    
            return false;
    
        }
    
    }
    

    通过在启动时创建保存事件,您可以验证模型并在 save() 上返回 true 或 false;

    用户模型:

    use Illuminate\Auth\UserInterface;
    use Illuminate\Auth\Reminders\RemindableInterface;
    
    class User extends BaseModel implements UserInterface, RemindableInterface {
        protected $table = 'users';
    
        public $guarded =   [   
                                'password',
                                'password_confirmation',
                            ];
    
        public $rules = array(
                                'first_name' => 'required|min:3',
                                'last_name' => 'required|min:3',
                                'email' => 'required|min:6',
                            );
    
        protected $hidden = array('password');
    
        public function setPasswordAttribute($string) 
        {
    
            $this->attributes['password'] = Hash::make($string);
        }
    
    }
    

    还有一些代码在我的layout.blade.php:

    @if( Session::has('errors') )
        You have some errors:
    
        @foreach( Session::get('errors')->all() as $error )
            <div class="alert alert-block alert-error fade in">
                <button data-dismiss="alert" class="close" type="button">×</button>
                <p>{{ $error->message }}</p>
            </div>
        @endforeach
    @endif
    
    @if( isset($successMessage) ) 
        <div class="alert alert-block alert-success fade in">
            <button data-dismiss="alert" class="close" type="button">×</button>
            <p>{{ $successMessage }}</p>
        </div>
    @endif
    
    @if( isset($errorMessage) ) 
        <div class="alert alert-block alert-error fade in">
            <button data-dismiss="alert" class="close" type="button">×</button>
            <p>{{ $errorMessage }}</p>
        </div>
    @endif
    

    其实这个blade代码并没有那么大,它是由一些blade _partials和一个Template helper类提供的,看起来更像这样:

    {{ Template::notifications('error', Session::get('errors')) }}
    {{ isset($successMessage) ? Template::notify('success', $successMessage) : '' }}
    {{ isset($errorMessage) ? Template::notify('error', $errorMessage) : '' }}
    

    使用验证,您可以节省一些行,并且永远不需要记住再次验证您的输入。

    【讨论】:

    • 这就是所谓的自我验证模型做事方式吧?这实际上是我考虑采用的方法之一。既然您了解存储库,您认为它们在任何规模的项目中都“值得”吗?我目前正在为客户开发第一个原型/最小可行产品,我不确定整个 SOLID/单元测试/设计模式包在“所有”情况下是否现实。
    【解决方案2】:

    因为这是一个小项目,并且您正在考虑将代码移动到用户模型中,所以这里有一段可以重构的代码:

    代替:

      $role = $this->role->find($input['role_id']);
      $user->roles()->save($role);
    

    写:

           $role = $this->role->find($input['role_id']);
           $user->addRole($role);
    

    在您的用户模型中:

    class User extends Eloquent
    {
       public function addRole($role)
       {
              if(!is_null($role) and is_object($role) and $role->id > 0)
              {
                  return    $this->roles()->save($role);
              }
              else
              {
                   throw new RoleNotFoundException($role);
              }
       }
    }
    

    然后在你的 global.php 文件中为这种类型的异常定义一个错误处理程序:

    App::error(function(RoleNotFoundException $exception)
    {
        // Handle the exception...
        return Response::make('Error! ' . $exception->getCode());
    });
    

    这将使您的代码更具可读性和健壮性,并且在实现方法时使您无需记住 Laravel 的细节,这就是您将它们包装在 User 模型中的原因。在这个例子中它相当简单,但这种方法可以在更复杂的场景中为您省去很多麻烦。

    【讨论】:

    • Laravel 上的 Try/Catch 不起作用,这不是它的工作方式。看看这个答案以更好地理解它:stackoverflow.com/questions/19360215/….
    • 谢谢,安东尼奥,我不知道 Try/Catch 根本不起作用。实际上,如果没有它,代码看起来会更干净——也许这就是在单独的文件中定义错误处理程序的全部意义所在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-06
    • 2011-06-11
    • 1970-01-01
    • 2011-09-24
    • 2017-02-25
    • 1970-01-01
    • 2023-04-08
    相关资源
    最近更新 更多