Laravel Vuejs 实战:开发知乎 (23)用户关注之组件化开发
开发vue的时候最好在开始的时候执行:
1 npm install && npm run watch-poll
这样每次编辑了vue都会后台自动生成一下,方便查看效果。
1.生成一个控制器:
php artisan make:controller FollowerController
代码:
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use App\User; 6 use Illuminate\Http\Request; 7 8 class FollowerController extends Controller 9 { 10 // 11 public function __construct() 12 { 13 $this->middleware('auth') 14 ->except( 15 [ 16 'getFollowStats', 17 'followThroughApi', 18 ] 19 ); 20 } 21 22 23 public function getFollowStats(Request $request) 24 { 25 //解析出用户 26 $user = User::find($request->get('user')); 27 //解析出当前登录用户 28 $currentUser = auth()->user(); 29 //返回能否关注的状态 [follow是FollowPolicy中的follow方法,FollowPolicy已经在AuthServiceProvider中注册] 30 return response()->json(['followable' => $currentUser->can('follow', $user), 31 'self' => $user->id === $currentUser->id,]); 32 } 33 34 35 public function followThroughApi(Request $request) 36 { 37 //解析出用户 38 $user = User::find($request->get('user')); 39 40 //解析出当前登录用户 41 $currentUser = auth()->user(); 42 43 //执行关注/取关操作 [不能关注自己] 44 if ($user->id !== $currentUser->id) { 45 $currentUser->followings()->toggle($user->id); 46 } 47 48 //返回新能否关注的状态 [follow是FollowPolicy中的follow方法,FollowPolicy已经在AuthServiceProvider中注册] 49 return response()->json( 50 [ 51 'followable' => $currentUser->can('follow', $user), 52 'self' => $user->id === $currentUser->id, 53 ] 54 ); 55 } 56 } 57 58
1.2 添加一个授权策略
php artisan make:policy FollowPolicy
1 <?php 2 3 namespace App\Policies; 4 5 use App\User; 6 use Illuminate\Auth\Access\HandlesAuthorization; 7 8 class FollowPolicy 9 { 10 use HandlesAuthorization; 11 12 /** 13 * Create a new policy instance. 14 * 15 * @return void 16 */ 17 public function __construct() 18 { 19 // 20 } 21 22 public function follow(User $currentUser, User $user) 23 { 24 //用户不能关注自己,也不能关注已经关注的用户 25 return $currentUser->id !== $user->id && !($currentUser->followings->contains('id', $user->id)); 26 } 27 } 28 29
记得添加到AuthServiceProvider的 $policies中
代码:
1 <?php 2 3 namespace App\Providers; 4 5 use App\Models\Question; 6 use App\Policies\FollowPolicy; 7 use App\Policies\QuestionPolicy; 8 use App\User; 9 use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider; 10 use Illuminate\Support\Facades\Gate; 11 12 class AuthServiceProvider extends ServiceProvider 13 { 14 /** 15 * The policy mappings for the application. 16 * 17 * @var array 18 */ 19 protected $policies = [ 20 // 'App\Model' => 'App\Policies\ModelPolicy', 21 //添加授权映射关系 22 Question::class => QuestionPolicy::class, 23 User::class => FollowPolicy::class, 24 ]; 25 26 /** 27 * Register any authentication / authorization services. 28 * 29 * @return void 30 */ 31 public function boot() 32 { 33 $this->registerPolicies(); 34 35 // 36 } 37 } 38 39
2.添加api route:
1 2 #region 用户关注 3 //加载页面时取关注状态 4 Route::middleware('auth:api')->post('/users/follow/stats', 'FollowerController@getFollowStats'); 5 //执行关注/取关操作 6 Route::middleware('auth:api')->post('/users/follow', 'FollowerController@followThroughApi'); 7 8 #endregion 9 10
1 <?php 2 3 use Illuminate\Http\Request; 4 5 /* 6 |-------------------------------------------------------------------------- 7 | API Routes 8 |-------------------------------------------------------------------------- 9 | 10 | Here is where you can register API routes for your application. These 11 | routes are loaded by the RouteServiceProvider within a group which 12 | is assigned the "api" middleware group. Enjoy building your API! 13 | 14 */ 15 16 Route::middleware('auth:api')->get('/user', function (Request $request) { 17 return $request->user(); 18 }); 19 20 Route::middleware('api')->get('/topics', function (Request $request) { 21 $query = $request->query('q'); 22 return \App\Topic::query()->where('name', 'like', '%' . $query . '%')->get(); 23 }); 24 #region 问题关注 25 //加载页面时取关注状态 26 Route::middleware('auth:api')->post('/questions/follow/stats', 'QuestionController@getFollowStats'); 27 //执行关注/取关操作 28 Route::middleware('auth:api')->post('/questions/follow', 'QuestionController@followThroughApi'); 29 #endregion 30 31 #region 用户关注 32 //加载页面时取关注状态 33 Route::middleware('auth:api')->post('/users/follow/stats', 'FollowerController@getFollowStats'); 34 //执行关注/取关操作 35 Route::middleware('auth:api')->post('/users/follow', 'FollowerController@followThroughApi'); 36 37 #endregion 38 39
3.添加用户关注按钮vue组件:
1 <template> 2 <button :class="classObject" 3 @click="follow" 4 v-text="text"> 5 </button> 6 </template> 7 8 <script> 9 export default { 10 props: ['user'], 11 name: "UserFollowButton", 12 data() { 13 return { 14 followable: true, 15 self: true, 16 } 17 }, 18 computed: { 19 text() { 20 return this.followable ? "关注" : "取关"; 21 }, 22 classObject() { 23 if (this.self) { 24 return "d-none" 25 } 26 return this.followable ? "btn btn-sm btn-secondary" : "btn btn-sm btn-danger"; 27 }, 28 }, 29 mounted: function () { 30 let currentObj = this; 31 axios.post('/api/users/follow/stats', {'user': this.user}) 32 .then(function (response) { 33 currentObj.followable = response.data.followable; 34 currentObj.self = response.data.self; 35 }) 36 .catch(function (e) { 37 console.log(e); 38 }); 39 }, 40 methods: { 41 follow() { 42 let currentObj = this; 43 axios.post('/api/users/follow', {'user': this.user}) 44 .then(function (response) { 45 currentObj.followable = response.data.followable; 46 currentObj.self = response.data.self; 47 } 48 ) 49 .catch(function (e) { 50 console.log(e); 51 }); 52 }, 53 } 54 } 55 </script> 56 57 <style scoped> 58 59 </style> 60 61
1 <div class="card-footer float"> 2 <div class="text-center float-left mr-4"> 3 <div class="text">提问数</div> 4 <div class="number">{{ $question->user->questions_count }}</div> 5 </div> 6 <div class="text-center float-left mr-4"> 7 <div class="text">回答数</div> 8 <div class="number">{{ $question->user->answers->count() }}</div> 9 </div> 10 <div class="text-center float-left "> 11 <div class="text">粉丝数</div> 12 <div class="number">{{ $question->user->followers->count() }}</div> 13 </div> 14 </div> 15 16
补充:之前User模型中 外键设置理解错误了,参考:
修改后:
1 <?php 2 3 namespace App; 4 5 use App\Models\Question; 6 use Illuminate\Contracts\Auth\MustVerifyEmail; 7 use Illuminate\Database\Eloquent\SoftDeletes; 8 use Illuminate\Foundation\Auth\User as Authenticatable; 9 use Illuminate\Notifications\Notifiable; 10 11 class User extends Authenticatable implements MustVerifyEmail 12 { 13 use Notifiable; 14 #region 支持软删除 15 use SoftDeletes; 16 protected $dates = ['deleted_at']; 17 #endregion 18 /** 19 * The attributes that are mass assignable. 20 * 21 * @var array 22 */ 23 protected $fillable = [ 24 'name', 'email', 'password', 'avatar', 'activation_token', 'api_token' 25 ]; 26 27 /** 28 * The attributes that should be hidden for arrays. 29 * 30 * @var array 31 */ 32 protected $hidden = [ 33 'password', 'remember_token', 34 ]; 35 36 /** 37 * The attributes that should be cast to native types. 38 * 39 * @var array 40 */ 41 protected $casts = [ 42 'email_verified_at' => 'datetime', 43 ]; 44 45 46 /**添加用户模型和问题模型的模型关联 47 * @return \Illuminate\Database\Eloquent\Relations\HasMany 48 */ 49 public function questions() 50 { 51 return $this->hasMany(Question::class); 52 } 53 54 55 /** 添加用户模型和回答模型的模型关联 一个用户可以有多个回答 56 * @return \Illuminate\Database\Eloquent\Relations\HasMany 57 */ 58 public function answers() 59 { 60 return $this->hasMany(Answer::class); 61 } 62 63 64 public function followQuestions() 65 { 66 //默认表名 可以不设置后面三个参数,自定义表名需要设置 67 return $this->belongsToMany(Question::class, 'users_questions', 'question_id', 'user_id')->withTimestamps(); 68 } 69 70 71 /** 用户的粉丝 72 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 73 */ 74 public function followers() 75 { 76 77 return $this->belongsToMany 78 ( 79 self::class, 80 'followers', 81 'user_id', //foreignPivotKey:当前模型在中间表的字段(当前模型类的外键) //【当前模型是leader】的外键id 82 'follower_id'//relatedPivotKey:另一模型在中间表的字段(另一模型类的外键) 83 )->withTimestamps(); 84 } 85 86 87 /** 用户关注的作者 88 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany 89 */ 90 public function followings() 91 { 92 return $this->belongsToMany 93 ( 94 self::class, 95 'followers', 96 'follower_id',//foreignPivotKey:当前模型在中间表的字段(当前模型类的外键) //【当前模型是粉丝】的外键id 97 'user_id'//relatedPivotKey:另一模型在中间表的字段(另一模型类的外键) 98 ) 99 ->withTimestamps(); 100 } 101 } 102 103