Riot.js 3.3 と Lumen 5.4 でつくる 初めてのSPA バックエンド編
はじめに
最近 Riot が気になっていたので、
Laravel製軽量フレームワーク Lumen 5.4 と Riot 3.3 で簡単なブログを作ってみることにした。
今回はバックエンド編。
ここでは DB や API をさくっと作っていく。
ダミーテキストはWebtoolsのダミーテキストジェネレータで作成している。
これを使えば日本語・英語・数字混合のダミーテキストが作れる。
他のエントリーはこちら。
スポンサーリンク
作るもの
いろいろ頑張って最終的にこんな感じのブログを作る。
- 記事一覧の表示(カテゴリーの絞り込みあり)
- 記事詳細の表示
- 記事の新規作成・更新
記事一覧

記事詳細

記事編集

記事保存

前提
- Lumen 5.4.5
- Riot.js 3.3.1
手順
- ファサードと Eloquent ORM を使えるようにする
- テーブルの作成
- モデルの作成
- ルーティングの作成
- コントローラの作成
- データの挿入
1. ファサードと Eloquent ORM を使えるようにする
ファサードと Eloquent ORM を使えるようにするため、 bootstarp/app.php の
// $app->withFacades(); // $app->withEloquent();
の部分のコメントをはずしておく。
2. テーブルの作成
3つのテーブルを作成する。
- posts
- categories
- posts_categories
まずはマイグレートファイルの作成。
ドキュメントルートに移動してコマンドを実行。
$ php artisan make:migration create_posts_table --create posts $ php artisan make:migration create_categories_table --create categories $ php artisan make:migration create_posts_categories_table --create posts_categories
database/migrations ディレクトリ以下に現在日時のマイグレートファイルが作成されるので、
それぞれテーブル定義を書いていく。
posts テーブル
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('text');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}
categories テーブル
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCategoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 100);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categories');
}
}
posts_categories テーブル
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreatePostsCategoriesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts_categories', function (Blueprint $table) {
$table->increments('id');
$table->integer('post_id');
$table->integer('category_id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts_categories');
}
}
テーブル定義ができたらマイグレートを実行してテーブルを作成する。
$ php artisan migrate
3つのテーブルができた!
mysql> desc posts; +------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | title | varchar(255) | NO | | NULL | | | text | text | NO | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +------------+------------------+------+-----+---------+----------------+ mysql> desc categories; +------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(100) | NO | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +------------+------------------+------+-----+---------+----------------+ mysql> desc posts_categories; +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | id | int(10) unsigned | NO | PRI | NULL | auto_increment | | post_id | int(11) | NO | | NULL | | | category_id | int(11) | NO | | NULL | | | created_at | timestamp | YES | | NULL | | | updated_at | timestamp | YES | | NULL | | +-------------+------------------+------+-----+---------+----------------+
3. モデルの作成
app の直下にモデルを作成する。
app/Post.phpapp/Category.phpapp/PostsCategory.php
の3つのモデルを作成。
Posts.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = [
'title', 'text'
];
}
Category.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $fillable = [
'name'
];
}
PostsCategory.php
<?php namespace App;
use Illuminate\Database\Eloquent\Model;
class PostsCategory extends Model
{
protected $fillable = [
'post_id', 'category_id'
];
}
4. ルーティングの作成
routes/web.php に API のルーティングを追記する。
$app->group(['prefix' => 'api/'. env('API_VERSION', 'v0')], function ($app) {
$app->get('posts', 'PostController@index');
$app->get('posts/{id: \d+}', 'PostController@show'); // {param: 正規表現} とすることで正規表現に一致するパラメータのみ許可する。
$app->post('posts', 'PostController@create');
$app->put('posts/{id: \d+}', 'PostController@update');
$app->get('categories', 'CategoryController@index');
});
また、env('API_VERSION', 'v0') のところで API のバージョンを指定しているため、
.env に API_VERSION の定数を追記しておく。
API_VERSION=v1
5. コントローラの作成
app/Http/Controllers にコントローラを作成する。
app/Http/Controllers/PostController.phpapp/Http/Controllers/CategoryController.php
の2つのコントローラを作成。
PostController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use DB;
use App\Post;
use App\Category;
use App\PostsCategory;
class PostController extends Controller
{
/**
* 記事一覧を取得する。
*
* @param Request $request
* @return string
*/
public function index(Request $request)
{
$post = Post::orderBy('id', 'desc');
if (!empty($request['category'])) {
$category = Category::where('name', $request['category'])->first();
if (empty($category)) abort(404);
$postIds = PostsCategory::where('category_id', $category->id)
->get(['post_id'])
->toArray();
$ids = [];
foreach ($postIds as $key => $value) {
array_push($ids, $value['post_id']);
}
$post->whereIn('id', $ids);
}
$post = $post->get();
if (empty($post)) abort(404);
foreach ($post as $key => $value) {
$post[$key]->categories = $this->getCategories($value->id);
}
return response()->json($post);
}
/**
* 特定の記事を取得する。
*
* @param int $id
* @return string
*/
public function show($id)
{
$post = Post::find($id);
if (empty($post)) abort(404);
$post->categories = $this->getCategories($post->id);
return response()->json($post);
}
/**
* 記事を作成する。
*
* @param Request $request
* @return string
*/
public function create(Request $request)
{
$post = new Post;
$post->title = $request->title;
$post->text = $request->text;
if ($post->save()) {
if (isset($request->categories) && is_array($request->categories)) {
foreach ($request->categories as $key => $value) {
$postsCategory = new PostsCategory;
$postsCategory->post_id = $post->id;
$postsCategory->category_id = $value;
$postsCategory->save();
}
}
return response()->json(true);
} else {
abort(501);
}
}
/**
* 記事を更新する。
*
* @param Request $request
* @param int $id
* @return string
*/
public function update(Request $request, $id)
{
if (empty($id)) abort(404);
$post = Post::find($id);
if (empty($post)) abort(404);
$post->title = $request->title;
$post->text = $request->text;
if ($post->save()) {
PostsCategory::where('post_id', $post->id)->delete();
if (isset($request->categories) && is_array($request->categories)) {
foreach ($request->categories as $key => $value) {
$postsCategory = new PostsCategory;
$postsCategory->post_id = $post->id;
$postsCategory->category_id = $value;
$postsCategory->save();
}
}
return response()->json(true);
} else {
abort(501);
}
}
/**
* 特定の記事が属しているカテゴリーを取得する。
*
* @param int $post_id
* @return array $categories
*/
private function getCategories($post_id)
{
$query = 'select pc.post_id, pc.category_id, (select name from categories as c where c.id=pc.category_id) as category_name from posts_categories as pc where pc.post_id=?';
$rawCategries = DB::select($query, [$post_id]);
$categories = [];
if (!empty($rawCategries)) {
foreach ($rawCategries as $key => $value) {
array_push($categories, $value->category_name);
}
}
return $categories;
}
}
CategoryController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Category;
class CategoryController extends Controller
{
/**
* カテゴリー一覧を取得する。
*
* @return string
*/
public function index()
{
$data = Category::orderBy('id', 'asc')->get();
if (empty($data)) abort(404);
return response()->json($data);
}
}
6. データの挿入
categories テーブルにマスターデータを挿入しておく。
insert into categories (name) values ('Tech'), ('Book'), ('Hobby'), ('Others');
また、posts テーブルと posts_categories テーブルにはダミーデータを入れておくとつなぎこみ時に便利。
insert into posts (title, text) values ('Lorem ipsum dolor si', 'これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consectetur.これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consec'), ('Lorem ipsum dolor si', 'これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consectetur.これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consec'),('Lorem ipsum dolor si', 'これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consectetur.これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consec'),('Lorem ipsum dolor si', 'これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consectetur.これはダミーテキストです。フォントやレイアウトを確認するために入れています。Lorem ipsum dolor sit amet, consec');
insert into posts_categories (post_id, category_id) values (1, 1), (1, 2), (2, 1), (2, 3), (2, 4), (3, 1), (4, 1), (4, 2), (4, 3), (4, 4);
まとめ
Lumen で ブログシステムのバックエンドを作成した。
Lumen は API だけを作るときとかは簡単に作れるので便利。
関連記事
スポンサーリンク