laravelでAPI作って、フロントはvue.js+axios(非同期なajax)で、SPAなCRUDアプリを作ってみた(後編)
API作成とVUEの結合
1, DBは、MySQLではなくSQLite
# SQLiteの下準備は、空ファイルを作成するだけ
touch database/database.sqlite
.envのDB接続設定をmysqlからsqliteにする
1 2 3 4 5 6 |
DB_CONNECTION=sqlite # DB_HOST=127.0.0.1 # DB_PORT=3306 # DB_DATABASE=homestead # DB_USERNAME=homestead # DB_PASSWORD=secret |
これだけでDB(sqlite)が使えるようになる!
php artisan migrate
# たまにmysqlのキャッシュが残っている事があるのでクリアしておく
php artisan config:cache
2, モデル・テーブル・コントローラー・シーダーを一気に作成
1 2 3 4 5 6 7 |
php artisan make:model Task --all Model created successfully. Factory created successfully. Created Migration: 2020_08_21_033554_create_tasks_table Seeder created successfully. Controller created successfully. |
3, テーブル定義
database/migration/2020_08_21_033554_create_tasks_table.php
1 2 3 4 5 6 7 |
Schema::create('tasks', function (Blueprint $table) { $table->id(); $table->string('title', 100); $table->string('content', 100); $table->string('person_in_charge', 100); $table->timestamps(); }); |
4, Taskモデルは、idカラム以外は自由に修正出来るようにする。
1 2 3 4 |
class Task extends Model { protected $guarded = ['id']; } |
5, シーダーで適当なレコード生成
database/seeds/TaskSeeder.php
1 2 3 4 5 6 7 8 9 10 |
public function run() { for ($i = 1; $i <= 10; $i++) { Task::create([ 'title' => 'title' . $i, 'content' => 'content' . $i, 'person_in_charge' => 'person_in_charge' . $i, ]); } } |
database/seeds/DatabaseSeeder.phpで読み込む
1 2 3 4 5 |
public function run() { // $this->call(UserSeeder::class); $this->call(TaskSeeder::class); } |
6, APIのルーティング
routes/api.php
1 2 3 4 |
// TaskのAPI Route::group(['middleware' => ['api']], function(){ Route::resource('task', 'TaskController'); }); |
これで、自動的にRESTful APIのルーティングが完了する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
php artisan route:list +--------+-----------+----------------------+--------------+---------------------------------------------+------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+-----------+----------------------+--------------+---------------------------------------------+------------+ | | GET|HEAD | api/task | task.index | App\Http\Controllers\TaskController@index | api | | | POST | api/task | task.store | App\Http\Controllers\TaskController@store | api | | | GET|HEAD | api/task/create | task.create | App\Http\Controllers\TaskController@create | api | | | GET|HEAD | api/task/{task} | task.show | App\Http\Controllers\TaskController@show | api | | | PUT|PATCH | api/task/{task} | task.update | App\Http\Controllers\TaskController@update | api | | | DELETE | api/task/{task} | task.destroy | App\Http\Controllers\TaskController@destroy | api | | | GET|HEAD | api/task/{task}/edit | task.edit | App\Http\Controllers\TaskController@edit | api | | | GET|HEAD | api/user | | Closure | api | | | | | | | auth:api | | | GET|HEAD | {any} | | Closure | web | +--------+-----------+----------------------+--------------+---------------------------------------------+------------+ |
7, TaskController.phpでAPI処理内容を記述。スゴいシンプル!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
<?php namespace App\Http\Controllers; use App\Task; use Illuminate\Http\Request; class TaskController extends Controller { public function index() { return Task::all(); } public function show(Task $task) { return $task; } public function store(Request $request) { return Task::create($request->all()); } public function update(Request $request, Task $task) { $task->update($request->all()); return $task; //更新したデータを返す。 } public function destroy(Task $task) { $task->delete(); return $task; //削除したデータを返す。 } } |
8, 試しにコマンドラインから、CRUDのAPIを叩いてみる
1 2 3 4 5 6 7 8 9 10 11 12 |
# 全部GET curl http://localhost:8000/api/task # 指定IDのレコードだけGET curl http://localhost:8000/api/task/3 # レコード作成&更新(Win10だとうまく行かなかったので、Advanced Rest Clientで確認したら動作した) curl -X POST -H "Content-Type: application/json" http://localhost:8000/api/task -d "{\"title\": \"ppp\",\"content\": \"テスト\",\"person_in_charge\": \"person_in_chargeテスト\"}" curl -X PUT -H 'Content-Type: application/json' http://localhost:8000/api/task/11 -d '{"title": "ttt","content": "Test Message"}' # 削除はWin10でもOK curl -X DELETE http://localhost:8000/api/task/11 |
9, フロントエンドのvue.js・バックエンドのlaravel apiを、axios(ajax)で結びつける
タスク一覧をAPIで取得
resources/js/components/TaskListComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
<tr v-for="(task, index) in tasks" :key="index"> <th scope="row">{{ task.id }}</th> <td>{{ task.title }}</td> <td>{{ task.content }}</td> <td>{{ task.person_in_charge }}</td> <td> <router-link v-bind:to="{name: 'task.show', params: {taskId: task.id }}"> <button class="btn btn-primary">Show</button> </router-link> </td> <td> <router-link v-bind:to="{name: 'task.edit', params: {taskId: task.id }}"> <button class="btn btn-success">Edit</button> </router-link> </td> <td> <button class="btn btn-danger">Delete</button> </td> </tr> <script> export default { // tasks配列は、最初は空 data: function () { return { tasks: [] } }, methods: { // API経由で全タスクを取得 getTasks() { axios.get('/api/task') .then((res) => { this.tasks = res.data; }); } }, //マウントし終わったらAPIでタスク取得する。 mounted() { this.getTasks(); } } </script> |
タスク詳細ページ
resources/js/components/TaskShowComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
v-model="task.title"> のように動的データバインディングを行う。 <script> export default { props: { taskId: String }, data: function () { return { task: {} } }, methods: { getTask() { axios.get('/api/task/' + this.taskId) .then((res) => { this.task = res.data; }); } }, mounted() { this.getTask(); } } </script> |
ハリボテだったタスク登録画面
resources/js/components/TaskCreateComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form v-on:submit.prevent="submit"> <div class="form-group row"> <label for="title" class="col-sm-3 col-form-label">Title</label> <input type="text" class="col-sm-9 form-control" id="title" v-model="task.title"> </div> <div class="form-group row"> <label for="content" class="col-sm-3 col-form-label">Content</label> <input type="text" class="col-sm-9 form-control" id="content" v-model="task.content"> </div> <div class="form-group row"> <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label> <input type="text" class="col-sm-9 form-control" id="person-in-charge" v-model="task.person_in_charge"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> </template> <script> export default { data: function () { return { task: {} } }, methods: { submit() { axios.post('/api/task', this.task) .then((res) => { this.$router.push({name: 'task.list'}); }); } } } </script> |
同じくハリボテだったタスク更新画面
resources/js/components/TaskEditComponent.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
<template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <form v-on:submit.prevent="submit"> <div class="form-group row"> <label for="id" class="col-sm-3 col-form-label">ID</label> <input type="text" class="col-sm-9 form-control-plaintext" readonly id="id" v-model="task.id"> </div> <div class="form-group row"> <label for="title" class="col-sm-3 col-form-label">Title</label> <input type="text" class="col-sm-9 form-control" id="title" v-model="task.title"> </div> <div class="form-group row"> <label for="content" class="col-sm-3 col-form-label">Content</label> <input type="text" class="col-sm-9 form-control" id="content" v-model="task.content"> </div> <div class="form-group row"> <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label> <input type="text" class="col-sm-9 form-control" id="person-in-charge" v-model="task.person_in_charge"> </div> <button type="submit" class="btn btn-primary">Submit</button> </form> </div> </div> </div> </template> <script> export default { props: { taskId: String }, data: function () { return { task: {} } }, methods: { getTask() { axios.get('/api/task/' + this.taskId) .then((res) => { this.task = res.data; }); }, submit() { axios.put('/api/task/' + this.taskId, this.task) .then((res) => { this.$router.push({name: 'task.list'}) }); } }, mounted() { this.getTask(); } } </script> |
最後に削除ボタン。画面遷移を伴わないのでスムーズな動作(画面のDOMを削除して、裏でAPI経由でレコード削除)
scriptタグに追加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
methods: { // API経由で全タスクを取得 getTasks() { axios.get('/api/task') .then((res) => { this.tasks = res.data; }); }, // API経由で指定IDのタスクを削除 deleteTask(id) { axios.delete('/api/task/' + id) .then((res) => { this.getTasks(); }); } }, |