Udemy - Nuxt JS with Laravel API - Building SSR Vue JS Apps 笔记15 Laravel Nuxt–Update and Delete
Topic Update
首先更新api.php添加一个route
TopicController.php增加这个update方法。
update方法需要一个Request参数,直接用custom request 来处理,这样字段验证也方便。
执行:
php artisan make:request TopicUpdateRequest
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class TopicUpdateRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'title' => 'required|string|max:255', ]; } }
Postman测试:
或者raw方式:
接下来实现:只有创建topic的用户才能更新topic,所以需要Policy
Topic Update Policy
执行:
php artisan make:policy TopicPolicy
<?php namespace App\Policies; use App\Topic; use App\User; use Illuminate\Auth\Access\HandlesAuthorization; class TopicPolicy { use HandlesAuthorization; /** * Create a new policy instance. * * @return void */ public function __construct() { // } public function update(User $user, Topic $topic) { return $user->ownsTopic($topic); } }
然后user模型 中新建这个ownsTopic方法。
然后更新AuthServiceProvider.php,注册这个policy:
然后更新TopicController.php:
PostMan测试,如果没有权限:
Topic Delete Policy
添加api.php中一个route处理delete
TopicController中新建destroy方法:
同样需要一个Policy方法验证当前用户是否有权限:
Postman测试:
Topic Update – Nuxt
首先需要在pages/topics/index.vue中添加一个 逻辑 只有属于登录用户的topic才能被该用户更新及删除。
由于判断是根据用户id来判断,之前后端返回的值没有id,所以添加之:
pages/topics/index.vue
效果:不属于本用户的Topic 看不到Edit
新建pages/topics/edit/index.vue文件:
更新pages/topics/index.vue
这个nuxt-link to参数中name是自动generate的,可以看:
稍微更新一下style
在nuxt.config.js中:
topics/index.vue:
效果如下:
Update Topic Title
pages/topics/edit/index.vue更新:
<template> <div class="container"> <h2>Update Topic Title</h2> <form @submit.prevent="update"> <div class="form-group mt-5"> <input type="text" class="form-control" v-model="topic.title"> <small class="form-text text-danger" v-if="errors.title">{{errors.title[0]}}</small> </div> <button class="btn btn-outline--success">Update</button> </form> <p class="mt-5 btn btn-outline-warning"> <nuxt-link to="/topics">Back to Topics</nuxt-link> </p> </div> </template> <script> export default { name: "index", data() { return { topic: { title: '', } } }, async asyncData({$axios, params}) { let {data} = await $axios.$get(`/topics/${params.id}`); return { topic: data, } }, methods: { async update() { await this.$axios.patch(`/topics/${this.$route.params.id}`, {title: this.topic.title}); //redirect this.$router.push('/topics'); }, } } </script> <style scoped> </style>
更新优化一下:
把topics/edit文件夹及内部的index.vue迁移到topics/_id文件夹内:
根据.nuxt/router.js中这个name
更新topics/index.vue:
效果:
这样url就带有id了;
Delete Topic
topics/index.vue更新:
<template> <div class="container"> <h2>Latest Topics</h2> <div v-for="(topic,index) in topics" :key="index" class="bg-light mt-5 mb-5" style="padding: 20px"> <nuxt-link :to="{name:'topics-id',params:{id:topic.id}}">{{topic.title}}</nuxt-link> <div v-if="authenticated"> <div v-if="user.id===topic.user.id"> <nuxt-link :to="{name:'topics-id-edit',params:{id:topic.id}}"> <button class="btn btn-outline-success fa fa-edit fa-2x pull-right">Edit</button> </nuxt-link> <button class="btn btn-outline-danger fa fa-trash fa-2x pull-right" @click.prevent="deleteTopic(topic.id)"> Delete </button> </div> </div> <p class="text-muted">{{topic.created_at}} by {{topic.user.name}}</p> <div v-for="(content,index) in topic.posts" :key="index" class="ml-5 content"> {{content.body}} <p class="text-muted">{{content.created_at}} by {{content.user.name}}</p> </div> </div> <nav> <ul class="pagination justify-content-center"> <li v-for="(key,value) in links" class="page-item"> <a @click.prevent="loadMore(key)" class="page-link">{{value}}</a> </li> </ul> </nav> </div> </template> <script> export default { name: "index.vue", data() { return { topics: [], links: [], } }, async asyncData({$axios}) { let {data, links,} = await $axios.$get('/topics'); return { topics: data, links, } }, methods: { async loadMore(key) { if (key === null) { return; } let {data, links} = await this.$axios.$get(key); return this.topics = {...this.topics, ...data}; }, async deleteTopic(id) { await this.$axios.$delete(`/topics/${id}`); this.$router.push('/'); } } } </script> <style scoped> .content { border-left: 10px solid white; padding: 0 10px 0 10px; } .btn-outline-danger, .btn-outline-success { border: none; } </style>
测试:
Add Post to Topic
backend中实现添加post的逻辑
api.php:
创建PostController执行
php artisan make:controller PostController
创建一个PostCreateRequest来处理创建请求的验证,执行
php artisan make:request PostCreateRequest
PostCreateRequest.php:
PostController.php:
用Postman测试:
Update and Delete Post
api.php:
<?php use \Illuminate\Support\Facades\Route; Route::post('register', 'AuthController@register')->middleware('guest'); Route::post('login', 'AuthController@login')->middleware('guest'); Route::get('user', 'AuthController@user')->middleware('auth'); Route::post('logout', 'AuthController@logout')->middleware('auth'); Route::group(['prefix' => 'topics'], function () { Route::post('/', 'TopicController@store')->middleware('auth'); Route::get('/', 'TopicController@index'); Route::get('/{topic}', 'TopicController@show'); Route::patch('/{topic}', 'TopicController@update')->middleware('auth'); Route::delete('/{topic}', 'TopicController@destroy')->middleware('auth'); //post group Route::group(['prefix' => '/{topic}/posts'], function () { Route::post('/', 'PostController@store')->middleware('auth'); Route::patch('/{post}', 'PostController@update')->middleware('auth'); Route::delete('/{post}', 'PostController@destroy')->middleware('auth'); }); });
更新request使用custom request ,执行:
php artisan make:request PostUpdateRequest
PostUpdateRequest.php:
<?php namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; class PostUpdateRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { return true; } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'body' => 'required|string|max:2000', ]; } }
更新删除都添加Policy:
执行:
php artisan make:policy PostPolicy
<?php namespace App\Policies; use App\Post; use App\User; use Illuminate\Auth\Access\HandlesAuthorization; class PostPolicy { use HandlesAuthorization; /** * Create a new policy instance. * * @return void */ public function __construct() { // } public function update(User $user, Post $post) { return $user->ownsPost($post); } public function destroy(User $user, Post $post) { return $user->ownsPost($post); } }
AuthServiceProvider中注册PostPolicy:
在User.php用户模型中添加一个ownsPost方法:
PostController.php:
<?php namespace App\Http\Controllers; use App\Http\Requests\PostCreateRequest; use App\Http\Requests\PostUpdateRequest; use App\Http\Resources\PostResource; use App\Post; use App\Topic; use Illuminate\Http\Request; class PostController extends Controller { public function store(PostCreateRequest $request, Topic $topic) { $post = new Post; $post->body = $request->get('body'); $post->user()->associate(auth()->user()); $topic->posts()->save($post); return PostResource::make($post); } public function update(PostUpdateRequest $request, Topic $topic, Post $post) { $this->authorize('update', $post); $post->body = $request->get('body'); $post->save(); return PostResource::make($post); } public function destroy(Request $request, Topic $topic, Post $post) { $this->authorize('destroy', $post); $post->delete(); return response(null, 204); } }
PostMan测试:
如果未授权:
Create Post
更新pages/topics/_id/index.vue:
<template> <div class="container"> <div class="bg-light mt-5 mb-5" style="padding: 20px;"> <h2>{{topic.title}}</h2> <p class="text-muted">{{topic.created_at}} by {{topic.user.name}}</p> <div v-for="(content,index) in topic.posts" :key="index" class="ml-5 content"> {{content.body}} <p class="text-muted">{{content.created_at}} by {{content.user.name}}</p> </div> </div> <div class="mt-5 ml-5 mb-5" v-if="authenticated"> <form @submit.prevent="create"> <div class="form-group"> <h4>Add a new Post</h4> <textarea class="form-control" placeholder="Write Something" v-model="body" autofocus rows="5"></textarea> <small class="form-text text-danger" v-if="errors.body">{{errors.body[0]}}</small> </div> <button class="btn btn-outline-success">Add Post</button> </form> </div> </div> </template> <script> export default { name: "index.vue", data() { return { topic: '', body: '', } }, async asyncData({$axios, params}) { const {data} = await $axios.$get(`/topics/${params.id}`); return { topic: data, } }, methods: { async create() { await this.$axios.post(`/topics/${this.$route.params.id}/posts`, { body: this.body, }); //redirect this.$router.push(`/topics`); }, } } </script> <style scoped> </style>
效果:
Update Post
新建pages/topics/_id/posts/_postid/edit/index.vue文件
<template> <div class="container"> <h2>Topic Posts Edit Page</h2> </div> </template> <script> export default { name: "index" } </script> <style scoped> </style>
更新pages/topics/_id/index.vue文件:
<template> <div class="container"> <div class="bg-light mt-5 mb-5" style="padding: 20px;"> <h2>{{topic.title}}</h2> <p class="text-muted">{{topic.created_at}} by {{topic.user.name}}</p> <div v-for="(content,index) in topic.posts" :key="index" class="ml-5 content"> <p>{{content.body}}</p> <div v-if="authenticated"> <div v-if="user.id===content.user.id"> <nuxt-link :to="{name:'topics-id-posts-postid-edit',params:{id:topic.id,postid:content.id}}"> <button class="btn btn-outline-success fa fa-edit pull-right">Edit</button> </nuxt-link> <button class="btn btn-outline-danger fa fa-trash pull-right" @click.prevent="deletePost(content.id)"> Delete </button> </div> </div> <p class="text-muted">{{content.created_at}} by {{content.user.name}}</p> </div> </div> <div class="mt-5 ml-5 mb-5" v-if="authenticated"> <form @submit.prevent="create"> <div class="form-group"> <h4>Add a new Post</h4> <textarea class="form-control" placeholder="Write Something" v-model="body" autofocus rows="5"></textarea> <small class="form-text text-danger" v-if="errors.body">{{errors.body[0]}}</small> </div> <button class="btn btn-outline-success">Add Post</button> </form> </div> </div> </template> <script> export default { name: "index.vue", data() { return { topic: '', body: '', } }, async asyncData({$axios, params}) { const {data} = await $axios.$get(`/topics/${params.id}`); return { topic: data, } }, methods: { async create() { await this.$axios.post(`/topics/${this.$route.params.id}/posts`, { body: this.body, }); //redirect this.$router.push(`/topics`); }, async deletePost(postId) { await this.$axios.delete(`/topics/${this.$route.params.id}/posts/${postId}`); //redirect this.$router.push('/topics'); }, } } </script> <style scoped> </style>
Return Single Post
api.php添加:
PostController.php:
Postman测试:
Update Single Post
更新pages/topics/_id/posts/_postid/edit/index.vue:
<template> <div class="container"> <h2>Update Post</h2> <form @submit.prevent="update"> <div class="form-group mt-5"> <textarea class="form-control" v-model="post.body" rows="5"></textarea> <small class="form-text text-danger" v-if="errors.body">{{errors.body[0]}}</small> </div> <button class="btn btn-outline-success">Update</button> </form> <p class="mt-5 btn btn-outline-warning"> <nuxt-link to="/topics">Back to Topics</nuxt-link> </p> </div> </template> <script> export default { name: "index", data() { return { post: { body: '', } } }, async asyncData({$axios, params}) { let {data} = await $axios.$get(`/topics/${params.id}/posts/${params.postid}`); return { post: data, } }, methods: { async update() { await this.$axios.patch(`/topics/${this.$route.params.id}/posts/${this.$route.params.postid}`, {body: this.post.body}); //redirect this.$router.push('/topics'); }, } } </script> <style scoped> </style>
效果:
源代码:
前端
https://github.com/dzkjz/laravel-backend-nuxt-frontend-frontpart
选择:
后端:
https://github.com/dzkjz/laravel-backend-nuxt-frontend
选择: