在任务开始,我们会扩展一下新建的数据库表移植文件。刚建立的移植文件只有两列,手动添加如下:
public function up() { Schema::create('tasks', function (Blueprint $table) { $table->increments('id'); $table->integer('user_id')->index(); $table->string('name'); $table->timestamps(); }); }
再次使用命令进行建表:
php artisan migrate
创建Model:
php artisan make:model Task
这一次我们会在新建的Model里添加一个属性:
<?php namespace App; use IlluminateDatabaseEloquentModel; class Task extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['name']; }
通过fillable,我们声明name属性为mass-assignable。目前理解mass-assignable的意思是允许不同类型的赋值。
Models定义以后,我们现在需要对他们的关系进行声明。
在User中定义和Task的关系:
class User extends Authenticatable { // Other Eloquent Properties... /** * Get all of the tasks for the user. */ public function tasks() { return $this->hasMany(Task::class); } }
在Task中定义和User的关系:
class Task extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['name']; /** * Get the user that owns the task. */ public function user() { return $this->belongsTo(User::class); } }
现在我们使用控制器来组织我们的Router。因为用户认证是常用的功能,因此laravel中已经集成了此功能。命令如下:
php artisan make:auth --views
之后在Router中添加如下行:
Route::auth();
protected $redirectTo = '/tasks';
这样就能自动注册用户授权,并将非法用户重定向至/tasks。
同时我们也需要将重定向在中间件app/Middleware/RedirectIfAuthenticated.php
中定义:
return redirect('/tasks');
在进阶任务中,我们使用Controller进行业务逻辑的设计。在目录app/Http/Controllers下创建一个TaskController的artisan语法如下:
php artisan make:controller TaskController
创建好controller后,就可以在Router里根据url对应controller中的业务逻辑:
Route::get('/tasks', 'TaskController@index'); Route::post('/task', 'TaskController@store'); Route::delete('/task/{task}', 'TaskController@destroy');
使用Controller后我们就可以方便的用中间件对所有Controller里的业务进行用户权限管理,只需要在刚才创建的TaskController中加入:
class TaskController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('auth'); } }
定义视图的方法和基本任务中的一致,只是部分文件存储的路径不同,之后就可以在控制器中定义我们的业务逻辑:
public function index(Request $request) { return view('tasks.index'); }
存储task的方法和基本任务中的一样,只不过现在需要在Controller中定义。
public function store(Request $request) { $this->validate($request, [ 'name' => 'required|max:255', ]);
$request->user()->tasks()->create([
'name' => $request->name,
]);
return redirect('/tasks');
}
可以发现储存时的验证和我们基本任务中router的有所区别。因为Controller类本身就支持validate,因此验证失败后errors变量会自动被压入session中,并且重定向至访问前的页面。
创建任务时,我们可以使用user和task的关系,通过此创建方法,可以自动设置task对应的userId。
这时候,我们的主页面还需要加入展示任务的功能,代码如下:
public function index(Request $request) { $tasks = Task::where('user_id', $request->user()->id)->get(); return view('tasks.index', [ 'tasks' => $tasks, ]); }
这里用where查询获得任务,并将tasks传参给tasks.index
然而数据的逻辑我们还需要通过在Task Model中定义TaskRepository进行管理。因为Model就是针对特定表的操作集合。针对不同的业务逻辑,我们可能会有不同的对表的操作,因此为了方便重用,我们应该将所有的操作注册。
我们在app目录下新建Repositories目录,并创建TaskRepository类:
namespace AppRepositories; use AppUser; use AppTask; class TaskRepository { /** * Get all of the tasks for a given user. * * @param User $user * @return Collection */ public function forUser(User $user) { return Task::where('user_id', $user->id) ->orderBy('created_at', 'asc') ->get(); } }
接下来我们需要将Repository引入我们的Controller:
namespace AppHttpControllers; use AppTask; use AppHttpRequests; use IlluminateHttpRequest; use AppHttpControllersController; use AppRepositoriesTaskRepository; class TaskController extends Controller { /** * The task repository instance. * * @var TaskRepository */ protected $tasks; /** * Create a new controller instance. * * @param TaskRepository $tasks * @return void */ public function __construct(TaskRepository $tasks) { $this->middleware('auth'); $this->tasks = $tasks; } /** * Display a list of all of the user's task. * * @param Request $request * @return Response */ public function index(Request $request) { return view('tasks.index', [ 'tasks' => $this->tasks->forUser($request->user()), ]); } }
数据展示和删除的view视图和基本任务一致,不重复了。
接下来就是和基本任务中有所区别的地方,Router中我们使用Destroy方法:
Route::delete('/task/{task}', 'TaskController@destroy'); /** * Destroy the given task. * * @param Request $request * @param Task $task * @return Response */ public function destroy(Request $request, Task $task) { // }
上面是Router文件,下面是Controller文件中的对应方法。可以看到这里有个隐式传参,{task}会自动传递给$task变量。
由于此次我们引入了用户权限机制,我们担心在删除过程中用户随意传递task_id,因此我们需要建立Policy防止这一情况。Policy可以将授权逻辑进行整合处理。通常一个Policy对应一个Model。我们通过artisan创建app/Policies/TaskPolicy.php文件:
php artisan make:policy TaskPolicy
我们需要在TaskPolicy中声明某方法所需要的权限。如此处,Destroy方法需要确认用户的taskid和此taskid是否对应:
<?php namespace AppPolicies; use AppUser; use AppTask; use IlluminateAuthAccessHandlesAuthorization; class TaskPolicy { use HandlesAuthorization; /** * Determine if the given user can delete the given task. * * @param User $user * @param Task $task * @return bool */ public function destroy(User $user, Task $task) { return $user->id === $task->user_id; } }
之后我们需要将Task Model和TaskPolicy进行关联,在文件app/Providers/AuthServiceProvider.php的$policies变量中声明:
/** * The policy mappings for the application. * * @var array */ protected $policies = [ 'AppTask' => 'AppPoliciesTaskPolicy', ];
这样所有对Task的授权都会交予TaskPolicy。
现在重新回到我们的Controller:
public function destroy(Request $request, Task $task) { $this->authorize('destroy', $task);
$task->delete();
return redirect('/tasks');
}
当前用户会被自动传参,因此不需要额外传递。如果授权失败,将会跳转至403页面。