我有三个模型。我想避免用户可以更改属于其他用户的Todo的待办事项。
class User extends Authenticatable
{
public function todolists()
{
return $this->hasMany('App\Todolist');
}
public function todos()
{
return $this->hasManyThrough('App\Todo', 'App\Todolist');
}
}
class Todolist extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
public function todos()
{
return $this->hasMany('App\Todo');
}
}
class Todo extends Model
{
protected $casts = [
'completed' => 'boolean',
];
public function todolist()
{
return $this->belongsTo('App\Todolist');
}
}
为避免用户可以查看其他用户的托儿和待办事项,我实现了以下内容:
public function getTodosForTodolist(Todolist $todolist)
{
if (Auth::user()->id == $todolist->user_id) {
$todos = Todo::where('todolist_id', $todolist->id )->get();
return view('todo/index', ['todos' => $todos);
}
else {
abort(403, 'Unauthorized action.');
}
}
下一步是防止用户可以编辑其他用户的待办事项。当前在TodoController中,我只有以下内容:
public function edit(Todo $todo)
{
if (Auth::user()->todos->id == $todo->todolist->id) {
return view('todo/edit', ['todo' => $todo]);
}
}
这会出现以下错误:
此集合实例上不存在属性[id]。
错误是因为当前用户有多个待办事项。所以我如下更改了代码。
public function edit(Todo $todo)
{
if (Auth::user()->todos->first()->id == $todo->todolist->id) {
return view('todo/edit', ['todo' => $todo]);
}
abort('403', 'Unauthorized action.');
}
这可行,但是这样做实在是非常错误的。
哪种更好的方法来实现用户可以查看/编辑/删除属于其他用户的项目?
答案 0 :(得分:2)
我建议您将policies用于Todo和TodoList模型,并使用范围将dodo限制为一个用户,以防止应用程序中重复代码。
class ToDoListPolicy
{
public function view(User $user, TodoList $post)
{
return $user->id === $todolist->user_id;
}
}
class ToDoPolicy
{
public function edit(User $user, Todo $toDo)
{
$toDo->loadMissing('todolist');
return $user->id === $toDo->todolist->user_id;
}
}
在您的 AuthServiceProvider.php
中注册它们class AuthServiceProvider extends ServiceProvider
{
protected $policies = [
TodoList::class => ToDoListPolicy::class,
Todo::class => ToDoPolicy::class
];
}
然后在您的操作中使用它们:
public function getTodosForTodolist(Todolist $toDoList)
{
$this->authorize('view', $toDoList);
$toDoList->loadMissing('todos');
return view('todo.index', ['todos' => $toDoList->todos);
}
class ToDoController extends Controller
{
public function edit(Todo $toDo)
{
$this->authorize('edit', $toDo);
return view('todo.edit', compact('toDo'));
}
}
以及将查询限制为特定用户的范围:
class Todo extends Model {
// ...
public function scopeByUser(Builder $query, ?User $user = null)
{
if (! $user) {
$user = Auth::user();
}
$query->whereHas('todolist', function (Builder $toDoListQuery) use ($user) {
$toDoListQuery->where('user_id', $user->id);
});
}
}
在评论中回答your questions。
问题1:我必须将
Auth::user()->can('view', $todolist);
放在if-else子句中才能起作用。猜猜这是它的工作方式吗?第二季度:
$this->authorize('edit', $todo)
和Auth::user()->can('edit', $todo)
有什么区别?
对不起,这是我的错误。如果授权失败,则Auth::user()->can()
返回一个布尔值,而$this->authorize()
(这是通常包含在BaseController中的AuthorizesRequests特性的一种方法)throws an exception。
答案 1 :(得分:1)
如果要让每个用户只能使用自己的Todo,则需要添加Global Scope。此实现将使您的应用程序感到Todos(除了已登录用户之外的其他用户)不存在。
Global Scope可用于许多模型,这意味着它将减少样板代码。