次のチュートリアル コラムでは、Laravel Vue コンポーネントに基づいて記事の公開、編集、閲覧機能を実装する方法を紹介します。必要な友人の助けになれば幸いです。
Laravel をベースとしたバックエンド インターフェイス、フロントエンド JavaScript コンポーネント開発フレームワークとして Vue.js、CSS フレームワークとして Bootstrap を提供します。Laravel バックエンド インターフェイス
まず、前のチュートリアルで作成したリソース コントローラーをベースにします。
PostController<?php namespace App\Http\Controllers; use App\Models\Post; use Exception; use Illuminate\Contracts\Foundation\Application; use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\View; use Illuminate\Http\Request; use Illuminate\Http\Response; use Illuminate\Support\Collection; use Illuminate\Support\Facades\Auth; class PostController extends Controller { public function __construct() { $this->middleware('auth')->except('index', 'all', 'show', 'data'); } /** * Display a listing of the resource. * * @return Application|Factory|View|Response|\Illuminate\View\View */ public function index() { return view('posts.index', ['pageTitle' => '文章列表页']); } /** * Show the form for creating a new resource. * * @return Application|Factory|View|Response|\Illuminate\View\View */ public function create() { return view('posts.create', ['pageTitle' => '发布新文章']); } /** * Store a newly created resource in storage. * * @param Request $request * @return array */ public function store(Request $request) { $data = $request->validate([ 'title' => 'required|max:128', 'content' => 'required' ]); $post = new Post($data); $post->status = 1; $post->user_id = Auth::user()->id; if ($post->save()) { return ['success' => true, 'message' => '文章发布成功']; } return ['success' => false, 'message' => '保存文章数据失败']; } /** * Display the specified resource. * * @param Post $post * @return Application|Factory|View|Response|\Illuminate\View\View */ public function show(Post $post) { return view('posts.show', ['id' => $post->id, 'pageTitle' => $post->title]); } /** * Show the form for editing the specified resource. * * @param Post $post * @return Application|Factory|View|Response|\Illuminate\View\View */ public function edit(Post $post) { return view('posts.edit', ['pageTitle' => '编辑文章', 'id' => $post->id]); } /** * Update the specified resource in storage. * * @param Request $request * @param Post $post * @return array */ public function update(Request $request, Post $post) { $data = $request->validate([ 'title' => 'required|max:128', 'content' => 'required' ]); $post->fill($data); $post->status = 1; if ($post->save()) { return ['success' => true, 'message' => '文章更新成功']; } return ['success' => false, 'message' => '更新文章数据失败!']; } /** * Remove the specified resource from storage. * * @param Post $post * @return array * @throws Exception */ public function destroy(Post $post) { if ($post->delete()) { return ['success' => true, 'message' => '文章删除成功']; } return ['success' => false, 'message' => '删除文章失败']; } /** * 获取所有文章数据 * * @return Collection */ public function all() { return Post::orderByDesc('created_at')->get(); } /** * 获取单个文章数据 * * @param Post $post * @return Post */ public function data(Post $post) { $post->author_name = $post->author->name; return $post; } }
Laravel リソース コントローラーに付属のメソッドに加えて、記事の取得に使用される all
と
という 2 つの追加メソッドが提供されています。 Vue コンポーネントの AJAX リクエストを通じて、リスト データと記事の詳細データを取得します。したがって、リソース ルートをルーティング ファイル routes/web.php
: <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">use App\Http\Controllers\PostController;
Route::get('posts/all', [PostController::class, 'all']);
Route::get('posts/{post}/data', [PostController::class, 'data']);
Route::resource('posts', PostController::class);</pre><div class="contentsignin">ログイン後にコピー</div></div>
に登録する前に、これら 2 つのメソッドに対応するルートを追加する必要があります。ここでは、提供されている暗黙的モデルを使用していることに注意してください。 Laravel ルーティングによるバインディング関数は、モデル インスタンスを迅速に取得します。さらに、対応するビュー テンプレート パスも調整されました。これらのビュー テンプレート ファイルについては、間もなく紹介します。 フィラーを使用してテスト データを入力します。
前のチュートリアルで入力したテスト データに基づいて他のデータを追加した場合は、
php Artisan Midnight:refresh## を実行できます。 # command データ テーブルを再構築して、既存のデータをすばやく消去して再充填します。 返されたインスタンス データ形式の詳細を表示したくない場合は、組み込みフィラーdatabase/seeders/DatabaseSeeder.php:
<?php namespace Database\Seeders; use App\Models\Post; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { /** * Seed the application's database. * * @return void */ public function run() { // \App\Models\User::factory(10)->create(); Post::factory(10)->create(); } }
php Artisan Migrate:refresh --seed を実行します。このコマンドは、データ テーブルの再構築、テスト データのクリアと再充填を 1 つのステップで完了できます。
テンプレート継承によるビュー テンプレートの再構築
laravel/ui## によって提供される Bootstrap および Vue フロントエンド スキャフォールディング コードを使用しているため、 # Laravel が提供する拡張パッケージ、拡張パッケージもユーザー認証に関連するスキャフォールディング コード実装を提供し、ビュー テンプレート レイアウト ファイル
resources/views/layouts/app.blade.php異なるページに異なるタイトルを設定する以前、
PostController## のすべての GET ルートによってレンダリングされるビュー ファイル内の別のページとして
pageTitle
title ラベルに対応するラベル テキスト値を変更する必要があります。 ##<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false"><title>{{ $pageTitle ?? config('app.name', 'Laravel') }}</title></pre><div class="contentsignin">ログイン後にコピー</div></div>
記事リスト ビュー次に、元の記事関連のビュー ファイルをすべて
resources/views/posts
ディレクトリに移動し、記事リスト ビュー ファイルのテンプレート コードを書き換えます。次のようにします (元の posts.blade.php
を
@extends('layouts.app') @section('content') <p> <post-list></post-list> </p> @endsection
公開後のビュー名前を変更します元の
form.blade.php
名前を create.blade.php
に変更し、フォーム ページ ビュー ファイルのテンプレート コードを公開する記事を次のように記述します。フォームの公開と編集は Vue フォーム コンポーネントを共有するため、ここではフォーム タイトル (
)、フォームなどの追加の props 属性をコンポーネント テンプレートに渡します。送信 URL (url
) については、フォーム コンポーネントの調整を後ほど紹介します。 記事編集ビュー
新しい edit.blade.php
を resources/views/posts
ディレクトリにファイル編集ページ ビュー ファイルとして作成します<div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:php;toolbar:false">@extends('layouts.app')
@section('content')
<p>
</p><p>
<post-form>
</post-form>
</p>
</pre><div class="contentsignin">ログイン後にコピー</div></div>
記事詳細ページビューについては後ほど別途紹介します。 Vue フォーム コンポーネントのコードを再構築する
記事編集フォームに適応させ、バックエンド インターフェイスから返されるデータ形式を調整するには、Vue フォーム コンポーネントの実装コードを変更する必要があります。 :
@extends('layouts.app') @section('content') <p> </p><p> <post-form> $id]) }}"> </post-form> </p> @endsection
記事の公開ページと編集ページはタイトルで区別する必要があるため、親スコープから title
属性を通じてタイトル値を渡します。
記事編集フォームの場合、まず、親から渡された
idフック関数で新しい
load を呼び出します。 メソッドは、バックエンド インターフェイス /posts/{post}/data
から対応する記事データを読み込み、フォームに入力します。
现在后端接口可以自动获取当前认证用户的 ID,所以 author
字段就没有必要填写了,直接将其移除。
文章创建和编辑对应的请求方式是不一样的,操作成功后处理逻辑也是不一样的(前者重定向到列表页,后者重定向到详情页),所以根据 action
属性值分别进行了处理。
此外,由于后端对表单数据进行验证后,保存数据阶段依然可能失败,所以前端提交表单后返回的响应状态码为 200 并不表示表单提交处理成功,还需要借助响应实体(JSON 格式)中的 success
字段进一步判断,进而通过 ToastMsg
子组件渲染成功或失败的提示文本。
ToastMsg
是从之前的 SuccessMsg
组件升级而来,直接将 SuccessMsg
组件重命名为 ToastMsg
并改写组件代码如下:
<style> .alert { margin-top: 10px; } </style> <template> <p> <slot></slot> </p> </template> <script> export default { props: ['validated', 'success'] } </script>
可以看到,如果表单提交处理成功(依然基于父级作用域传递的 form.success
属性)则显示成功提示样式及文案,否则显示失败提示样式和文案,而是否渲染该组件取决于表单验证是否成功,该字段基于父级作用域传递的 form.validated
属性,之前是没有这个属性的,所以我们需要额外添加,在 resources/js/form.js
中,调整相关代码实现如下:
class Form { constructor(data) { ... this.validated = false; } ... /** * 表单提交处理 * * @param {string} url * @param {string} method */ submit(url, method) { return new Promise((resolve, reject) => { axios[method](url, this.data()) .then(response => { this.onSuccess(response.data); this.validated = true; if (this.success === true) { resolve(response.data); } else { reject(response.data); } }) .catch(error => { this.onFail(error.response.data.errors); reject(error.response.data); }); }); } /** * 处理表单提交成功 * * @param {object} data */ onSuccess(data) { this.success = data.success; this.message = data.message; this.reset(); } ... }
这样一来,文章发布和编辑共用的 Vue 表单组件就重构好了。
我们接着来实现文章详情页。
在 component-practice/resources/js/components
目录下新建一个 PostDetail.vue
文件作为渲染文章详情的 Vue 单文件组件,并编写组件代码如下:
<style> .post-detail { width: 100%; } .post-title { margin-bottom: .25rem; font-size: 2.5rem; } .post-meta { margin-bottom: 1.25rem; color: #999; } .post-content { font-size: 1.1rem; font-weight: 400; line-height: 1.5; color: #212529; } </style> <template> <p> <span>Loading...</span> </p> <p> </p> <h2>{{ title }}</h2> <p> Created at {{ created_at | diff_for_human }} by <a>{{ author_name }}</a>, Status: {{ status | post_status_readable }}, Action: <a>编辑</a> </p> <p> {{ content }} </p> </template> <script> export default { props: ['post_id'], data() { return { id: this.post_id, title: '', content: '', status: '', author_name: '', created_at: '', loaded: false } }, mounted() { if (!this.loaded) { this.load(Number(this.id)); } }, methods: { load(id) { axios.get('/posts/' + this.id + '/data').then(resp => { this.title = resp.data.title; this.content = resp.data.content; this.status = resp.data.status; this.author_name = resp.data.author_name; this.created_at = resp.data.created_at; this.loaded = true; }).catch(err => { alert('加载文章数据失败'); }); } } } </script>
这个组件功能比较简单,在 mounted
钩子函数中通过父级作用域传递的 id
属性值调用 load
函数加载后端接口返回的文章数据,并通过数据绑定将其渲染到模板代码中,在加载过程中,会有一个动态的加载状态提示用户文章数据正在加载。
这里我们还使用了过滤器对数据进行格式化,日期过滤器已经是全局的了,状态过滤器之前是本地的,这里我们将其从文章列表卡片组件 CardItem
中将其迁移到 app.js
中作为全局过滤器:
Vue.filter('post_status_readable', status => { switch(status) { case 0: return '草稿'; case 1: return '已发布'; default: return '未知状态'; } });
然后就可以在任何 Vue 组件中调用它了(CardItem
中过滤器调用代码做一下相应调整)。
在 app.js
中注册这个组件:
Vue.component('post-detail', require('./components/PostDetail.vue').default);
再到 component-practice/resources/views/posts
目录下新建 show.blade.php
视图文件引用 post-detail
组件即可:
@extends('layouts.app') @section('content') <p> <post-detail></post-detail> </p> @endsection
最后,我们到文章列表组件中新增一个发布文章入口。
打开子组件 ListSection
,在视图模式切换按钮右侧新增一个插槽,用于从父级作用域传递更多额外操作按钮:
<style> .card-header h5 { margin-top: 0.5em; display: inline-block; } .card-header .float-right { float: right; } </style> <template> <p> </p> <p> </p> <h5><slot></slot></h5> <p> <button> {{ view.switch_to }} </button> <slot></slot> </p> ...</template>
然后在 PostList
中将发布文章按钮放到这个插槽中(样式代码也做了微调):
<style> .post-list { width: 100%; } </style> <template> <p> <listsection> <template>文章列表</template> <template> <a>新文章</a> </template> <template> <listitem> {{ post.title }} </listitem> </template> ...</listsection></p></template>
顺便也为文章列表所有文章设置详情页链接,ListItem
链接是从 PostList 通过 props 属性传递的,CardItem
需要去子组件中设置:
<a><slot></slot></a>
至此,我们就完成了文章列表、发布、编辑和详情页的所有前后端功能代码编写。
如果你已经在本地运行了 npm run watch
并且通过 php arstisan serve
启动 PHP 内置 Web 服务器的话,就可以在浏览器通过 http://127.0.0.1:3002/posts
(启用了 BrowserSync 代理)访问新的文章列表页了:
点击任意文章链接,即可进入文章详情页,加载数据成功之前,会有如下动态加载效果:
你可以点击「编辑」链接对这篇文章进行编辑:
更新成功后,会跳转到文章详情页,对应字段均已更新,并且状态也从草稿变成了已发布:
もちろん、記事公開・編集機能はログインが必要です(現時点では権限検証は行われません)。ログインしていない場合は、編集ボタンと新規記事ボタンをクリックすると、まずログインページにジャンプします(この機能はPostController
コントローラーコンストラクターで定義されたミドルウェアメソッドの実装によって提供されます)、ログインした状態で、記事一覧ページの右上隅にある「新しい記事」ボタンをクリックして記事公開に入りますページ:
公開が成功すると、記事一覧ページにジャンプし、作成したばかりの記事が一覧に表示されます:
追加、削除、修正、確認してください。まだ 1 つ残っています。「削除」は次のチュートリアルで記事削除機能の実装を説明します。なぜ個別に紹介するのでしょうか。削除機能を組み合わせて、Vue コンポーネントに基づくモーダル ボックス、ダイアログ ボックス、トランジション効果の実装をデモンストレーションしたいと考えています。
以上がLaravel+Vue コンポーネントに基づいて記事の公開、編集、閲覧機能を実装する方法を説明します。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。