Laravel Vuejs 实战:开发知乎 (20)使用 Vuejs 组件化

1.vue 目录位置:resources/js/components

批注 2020-03-01 105353

PhpStorm语法设置:

批注 2020-03-01 105541

1.1 在reources/js/components文件夹内新建一个QuestionFollowButton.vue文件【问题关注按钮vue组件

批注 2020-03-01 105857


将原show.blade.php中的 关注按钮部分 移入新vue组件内部:

批注 2020-03-01 111423

初步样式:

批注 2020-03-01 113240

批注 2020-03-01 113221

执行

  1 npm install & npm run watch-poll

刷新页面看到

批注 2020-03-01 113341

现在只是大致样式,还不能工作,因为要用ajax请求 所以后面要去掉blade中的if can逻辑 放到vue组件中。

2.ajax

注意 vue在laravel中使用的时候,参考 laravel 中使用ajax和vue总结 

但一般使用axios ,参考:

入坑vue laravel,配置vue-router和axios手记 

Vue axios的使用(解决laravel API跨域访问)

通过 Axios 库构建 API 请求

基于 Laravel + Vue 构建一个类似 Twitter 的 Web 应用

vue laravel axios 教程 


CRUD App with Laravel and Vue

laravel 使用 prop 传递blade中的数据到vue组件中;

laravel axios post 401

401 during axios request

API requests with axios always unauthorized with Laravel API

axios请求要注意 :

then里面赋值的时候 报错property undefine 是因为在then里面 this作用域超出了,需要如下示例的代码才能起作用:

批注 2020-03-01 153409

this.followable = response.data.followable; 在then里面 this作用域超出了

这样会超出作用域,要修改如下:

  1 mounted() {
  2     let currentObj = this;
  3     axios.post('/api/questions/follow', {'question': this.question, 'user': this.user})
  4         .then(function (response) {
  5             currentObj.followable = response.data.followable;
  6         });
  7 },
  8 methods: {
  9     follow() {
 10         let currentObj = this;
 11         axios.post('/api/questions/follow', {'question': this.question, 'user': this.user})
 12             .then(function (response) {
 13                     currentObj.followable = response.data.followable;
 14                 }
 15             );
 16     },
 17 }
 18 


QuestionFollowButton.vue文件:

  1 <template>
  2     <button :href="href"
  3             :class="classObject"
  4             @click="follow"
  5             v-text="text">
  6     </button>
  7 </template>
  8 
  9 <script>
 10     export default {
 11         props: ['question', 'user'],
 12         name: "QuestionFollowButton",
 13         data() {
 14             return {
 15                 href: '',
 16                 followable: false,
 17             }
 18         },
 19         computed: {
 20             text() {
 21                 return this.followable ? "关注用户" : "取消关注";
 22             },
 23             classObject() {
 24                 return this.followable ? "btn btn-block btn-primary" : "btn btn-block btn-danger";
 25             },
 26         },
 27         mounted() {
 28             let currentObj = this;
 29             axios.post('/api/questions/follow', {'question': this.question, 'user': this.user})
 30                 .then(function (response) {
 31                     currentObj.followable = response.data.followable;
 32                 });
 33         },
 34         methods: {
 35             follow() {
 36                 let currentObj = this;
 37                 axios.post('/api/questions/follow', {'question': this.question, 'user': this.user})
 38                     .then(function (response) {
 39                             currentObj.followable = response.data.followable;
 40                         }
 41                     );
 42             },
 43         }
 44     }
 45 </script>
 46 
 47 <style scoped>
 48 
 49 </style>
QuestionFollowButton.vue

api.php文件:

  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 
 25 Route::middleware('api')->post('/questions/follow', 'QuestionController@followThroughApi');
 26 
api.php

show.blade.php文件:

  1 @extends('layouts.app')
  2 @section('content')
  3     <div class="container">
  4         <div class="row">
  5             <div class="col-md-8 col-md offset-1">
  6                 {{--问题--}}
  7                 <div class="card">
  8                     <div class="card-header">
  9                         {{ $question->title }}
 10 
 11                         @foreach(['success','warning','danger'] as $info)
 12                             @if(session()->has($info))
 13                                 <div class="alert alert-{{$info}}">{{ session()->get($info) }}</div>
 14                             @endif
 15                         @endforeach
 16 
 17                         @can('update',$question)
 18                             <a href="{{ route('questions.edit',$question) }}" class="btn btn-warning">编辑</a>
 19                         @endcan
 20 
 21                         @can('destroy',$question)
 22                             <form action="{{ route('questions.destroy',$question) }}" method="post">
 23                                 @csrf
 24                                 @method('DELETE')
 25                                 <button type="submit" class="btn btn-danger">删除</button>
 26                             </form>
 27                         @endcan
 28 
 29                         @forelse($question->topics as $topic)
 30                             <button class="btn btn-secondary float-md-right m-1">{{ $topic->name }}</button>
 31                         @empty
 32                             <p class="text text-warning float-md-right"> "No Topics"</p>
 33                         @endforelse
 34 
 35                         <p class="text text-info float-md-right"> 已有{{ count($question->answers) }}个回答</p>
 36 
 37                     </div>
 38                     <div class="card-body">
 39                         {!! $question->content !!}
 40                     </div>
 41                 </div>
 42 
 43 
 44                 {{--回答提交form--}}
 45                 {{--只有登录用户可以提交回答--}}
 46                 @if(auth()->check())
 47                     <div class="card mt-2">
 48                         <div class="card-header">
 49                             提交回答
 50                         </div>
 51                         <div class="card-body">
 52                             <form action="{{ route('answers.store',$question) }}" method="post">
 53                             @csrf
 54                             <!-- 回答编辑器容器 -->
 55                                 <script id="container" name="content" type="text/plain"
 56                                         style="width: 100%;height: 200px">{!! old('content') !!}</script>
 57                                 <p class="text text-danger"> @error('content') {{ $message }} @enderror </p>
 58                                 <!--提交按钮-->
 59                                 <button type="submit" class="btn btn-primary float-md-right mt-2">提交回答</button>
 60                             </form>
 61                         </div>
 62                     </div>
 63                 @else
 64                     {{--显示请登录--}}
 65                     <a href="{{ route('login') }}" class="btn btn-success btn-block mt-4">登录提交答案</a>
 66                 @endif
 67                 {{--展示答案--}}
 68                 @forelse($question->answers as $answer)
 69                     <div class="card mt-4">
 70                         <div class="card-header">
 71                             <div class="float-left">
 72                                 <img src="{{ $answer->user->avatar }}" class="img-thumbnail imgWrap"
 73                                      style="height: 50px" alt="{{ $answer->user->name }}">
 74                                 <span class="text text-info">{{ $answer->user->name }}</span>
 75                             </div>
 76                             <span class="float-right text text-info m-auto">{{ $answer->updated_at }}</span>
 77                         </div>
 78 
 79                         <div class="card-body">
 80                             {!!  $answer->content  !!}
 81                         </div>
 82                     </div>
 83 
 84                 @empty
 85 
 86                 @endforelse
 87             </div>
 88 
 89             <div class="col-md-3">
 90                 <div class="card">
 91                     <div class="card-header">
 92                         <h2> {{ $question->followers_count }}</h2>
 93                         <span>关注者</span>
 94                     </div>
 95 
 96                     <div class="card-body">
 97                         <div id="app">
 98                             <question-follow-button question="{{$question->id}}"
 99                                                     user="{{ auth()->user()->id }}"user()->id }}">
100                             </question-follow-button>
101                         </div>
102                     </div>
103                 </div>
104             </div>
105         </div>
106     </div>
107 @endsection
108 @section('footer-js')
109     @include('questions._footer_js')
110 @endsection
111 
112 
api.php

QuestionController.php文件:

  1 <?php
  2 
  3 namespace App\Http\Controllers;
  4 
  5 use App\Http\Requests\QuestionStoreRequest;
  6 use App\Models\Question;
  7 use App\Repositories\QuestionRepository;
  8 use App\User;
  9 use Illuminate\Http\Request;
 10 
 11 class QuestionController extends Controller
 12 {
 13 
 14     /**
 15      * @var QuestionRepository
 16      */
 17     private $questionRepository;
 18 
 19     public function __construct(QuestionRepository $questionRepository)
 20     {
 21         $this->middleware(
 22             'auth',
 23             [
 24                 'except' =>
 25                     [
 26                         'index',
 27                         'show',
 28                         'followThroughApi'
 29                     ]//非注册用户只能查看不能编辑添加更改删除
 30             ]
 31         );
 32 
 33         $this->questionRepository = $questionRepository;
 34     }
 35 
 36 
 37     /** Display a listing of the resource.
 38      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 39      */
 40     public function index()
 41     {
 42         //
 43         $questions = $this->questionRepository->getQuestionPublished();
 44         return view('questions.index', compact('questions'));
 45     }
 46 
 47 
 48     /**
 49      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 50      */
 51     public function create()
 52     {
 53         //
 54         return view('questions.create');
 55     }
 56 
 57 
 58     /**
 59      * @param QuestionStoreRequest $request
 60      * @return \Illuminate\Http\RedirectResponse
 61      */
 62     public function store(QuestionStoreRequest $request)//依赖注入QuestionStoreRequest实例
 63     {
 64         //
 65 //        $data = $request->validate([
 66 //            'title' => 'required|min:8',
 67 //            'content' => 'required|min:28',
 68 //        ]);
 69         //存储topics
 70         $topics = $this->questionRepository->normalizeTopics($request->get('topics'));
 71         //初始化question要用到的数据
 72         $data = $request->all();
 73         $data['user_id'] = auth()->user()->id;
 74 
 75 //        $question=Question::create($data); 被下方代码取代
 76         $question = $this->questionRepository->create($data);
 77 
 78         //使用我们再question model里面添加的topics方法获得 topics关联,再使用attach方法
 79         $question->topics()->attach($topics);
 80 
 81         return redirect()->route('questions.show', $question);
 82     }
 83 
 84 
 85     /**
 86      * @param Question $question
 87      * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
 88      */
 89     public function show(Question $question)
 90     {
 91         //使用关系关联加载,with方法会将分类之下的主题一起查询出来,而且不会出现N+1影响性能的问题
 92         $question->with('topics')->get();
 93         //使用关系关联加载,with方法会将分类之下的回答一起查询出来,而且不会出现N+1影响性能的问题
 94         $question->with('answers')->get();
 95 
 96         return view('questions.show', compact('question'));
 97     }
 98 
 99 
100     /**判断权限 返回视图
101      * @param Question $question
102      * @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\View\View
103      */
104     public function edit(Question $question)
105     {
106         if (auth()->user()->can('update', $question)) //判断当前用户是否有权编辑更新该question实例
107         {
108             //返回编辑视图
109             return view('questions.edit', compact('question'));
110         } else {
111             //返回警告 没有权限
112             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
113         }
114     }
115 
116 
117     /** Update the specified resource in storage.
118      * @param QuestionStoreRequest $questionStoreRequest
119      * @param Question $question
120      * @return \Illuminate\Http\RedirectResponse
121      */
122     public function update(QuestionStoreRequest $questionStoreRequest, Question $question)
123     {
124         //更新前 判断下权限
125         if (!(auth()->user()->can('update', $question))) {
126             //返回警告 没有权限
127             return redirect()->back()->with('warning', '你不能编辑不属于你的问题!');
128         }
129         //取得更新的字段 使用Eloquent提供的update方法执行问题更新
130         $question->update([
131             'title' => $questionStoreRequest->get('title'),
132             'content' => $questionStoreRequest->get('content'),
133         ]);
134 
135 
136         //topics的操作这时候看起来有点臃肿 可以使用TopicController来管理,暂时省略
137         //存储topics
138         $topics = $this->questionRepository->normalizeTopics($questionStoreRequest->get('topics'));
139         //使用我们再question model里面添加的topics方法获得 topics关联,
140         //再使用sync方法同步tag 【删除的会被删除掉,没删除的就保留,新的就增加】
141         $question->topics()->sync($topics);
142 
143         //更新完成,跳转回去
144         return redirect()->back();
145     }
146 
147 
148     /**Remove the specified resource from storage.
149      * @param Question $question
150      * @return \Illuminate\Http\RedirectResponse
151      * @throws \Exception
152      */
153     public function destroy(Question $question)
154     {
155         //
156         if (auth()->user()->can('destroy', $question)) {
157             $question->delete();
158             return redirect()->route('questions.index')->with('success', "删除成功!");
159         }
160         return redirect()->back()->with('danger', "你不能删除不属于你的问题!");
161     }
162 
163 
164     public function follow(Question $question)
165     {
166         if (auth()->user()->can('follow', $question)) //通过QuestionPolicy的follow方法判断用户是否可以关注问题
167         {
168             $message = "关注";
169         } else {
170             $message = "取关";
171         }
172         //同步记录
173         auth()->user()->followQuestions()->toggle($question);
174         $question->followers_count = $question->followUsers()->count();
175         $question->save();
176         return redirect()->back()->with('success', $message . '成功!');
177     }
178 
179     public function followThroughApi(Request $request)
180     {
181         $user = User::find($request->get('user'));
182         $question = Question::find($request->get('question'));
183 
184         if ($user->can('follow', $question)) {
185             $followable = false;
186         } else {
187             $followable = true;
188         }
189         //同步记录
190         $user->followQuestions()->toggle($question->id);
191         $question->followers_count = $question->followUsers()->count();
192         $question->update();
193         return response()->json([
194             'followable' => $followable,
195         ]);
196     }
197 }
198 
QuestionController.php

QuestionPlolicy.php文件:

  1 <?php
  2 
  3 namespace App\Policies;
  4 
  5 use App\Models\Question;
  6 use App\User;
  7 use Illuminate\Auth\Access\HandlesAuthorization;
  8 
  9 class QuestionPolicy
 10 {
 11     use HandlesAuthorization;
 12 
 13     /**
 14      * Create a new policy instance.
 15      *
 16      * @return void
 17      */
 18     public function __construct()
 19     {
 20         //
 21 
 22     }
 23 
 24 
 25     /**
 26      * 判断用户是否有权编辑更新问题
 27      * @param User $user
 28      * @param Question $question
 29      * @return bool
 30      */
 31     public function update(User $user, Question $question)
 32     {
 33         return $user->id === $question->user_id;
 34     }
 35 
 36 
 37     /**
 38      * 判断用户是否有权删除问题
 39      * @param User $user
 40      * @param Question $question
 41      * @return bool
 42      */
 43     public function destroy(User $user, Question $question)
 44     {
 45         return $user->id === $question->user_id;
 46     }
 47 
 48 
 49     /** 用户是否可以关注问题,未登录不行,关注了不行
 50      * @param User $user
 51      * @param Question $question
 52      * @return bool
 53      */
 54     public function follow(User $user, Question $question)
 55     {
 56         //axiox api 需要auth:api 先不实现,注释掉
 57 //        if (auth()->check()) {
 58 //            return !$user->followQuestions->contains('id', $question->id);
 59 //        } else {
 60 //            abort(401, "请登录");
 61 //        }
 62         return !$user->followQuestions->contains('id', $question->id);
 63     }
 64 }
 65 
QuestionController.php
posted @ 2020-03-01 15:28  dzkjz  阅读(507)  评论(0编辑  收藏  举报