hkのweblog

ひよっこエンジニアがにわとりになるまでの軌跡

Laravel 5.5 + Vue 2.1でSPA的なものを作っている話

 月2回更新を目標としながら丸1ヶ月ブログの更新をサボってしまいました…
気を取り直して更新を再開していきます…

 最近、個人的にLaravelとVue.jsを組み合わせてSPA的なサイトを作っています。
LaravelもVue.jsも初めて扱うのでそこそこ苦労していますが、新しい技術を学ぶのはやっぱり楽しいものです。
以前、「Vagrant上にLaravelの開発環境を作る」という記事を書きましたが、これからしばらくはLaravelとVue.jsでサイトを作りながら気付いたことや学んだことを書いていこうと思います(たぶん他のことも書くけど)。

 今日は手始めにLaravelのmodelのリレーションあたりについて書いてみようと思います。

modelの生成

まずはmodelを作るところからスタートです。
DBにあらかじめ以下のような2つのtableが用意されているとします。
銭湯の基本情報と銭湯の位置情報の2つのテーブルです。1件のレコードに対して1件のレコードが対応する最もシンプルなhasOneのパターンです。

sento_info←銭湯の基本情報

カラム名 長さ データ例 備考
sento_code int 7 1 primaryKey
name varchar 50 テルマー湯
address varchar 100 東京都新宿区歌舞伎町1-1-2
tel varchar 15 03-5285-1726

sento_map←銭湯の位置情報

カラム名 長さ データ例 備考
sento_code int 7 1 sento_info.sento_codeのforeignKey
lat double 10,6 35.694535
lng double 10,6 139.705160

これもLaravelのmigrateで作ればOKですが、長くなるので今回は割愛します。 モデルは職人さんが作ってくれます。

php artisan make:model SentoInfo

同様にSentoMapも作ります。
なお、デフォルトではappのすぐ下にmodelが生成されるのですが、以下のようにパスを追加してやるとちゃんとフォルダの下に入れてくれます。

php artisan make:model Models/SentoMap

modelの修正

次に生成したモデルをDBと紐付ける必要があります。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SentoInfo extends Model
{
    protected $table = 'sento_info';
    protected $primaryKey = 'sento_code';

}

鍵となるのは最後の2行です。
Laravelはデフォルトではテーブル名としてmodel名の複数形(この場合はsento_infos)、primaryKeyとしてidを指定してくれます。
今回のように別名を付けている場合は必ずこのように明示してやる必要があります。

SentoMap.phpの方も同じようにDBと紐付けておきます。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SentoMap extends Model
{
    protected $table = 'sento_map';
    protected $primaryKey = 'sento_code';
    
    }
}

リレーションを設定する

ようやく本題のリレーションの設定です。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class SentoInfo extends Model
{
    protected $table = 'sento_info';
    protected $primaryKey = 'sento_code';
    public function sentoMap() {
        return $this->hasOne('App\Models\SentoMap', 'sento_code');
    }
}

こんな感じ。hasOneの第1引数に紐付けるモデルのパス、第2引数にforeignKeyを設定します。
特にforeignKeyの方を忘れるとControllerから引っ張る時に正しいクエリを生成してくれなくてエラーになります。

Controllerから呼び出してみる

まずはapi.phpの方にルーティング情報を書いてみます。

<?php

use Illuminate\Http\Request;

/* 中略 */

Route::group(['middleware' =>'api'], function() {
    Route::get('sento/{sento_code?}', 'SentoController@detail');
}) ;

今回はルーティングが本題ではないので詳しく書きませんが、/api/sento/2 とかを叩くとsento_code=2でAPIが返ってきます。
このAPIをVue側で取得してテンプレートに埋め込んで表示するわけですが、そのあたりはまた次回にでも書こうと思います。

次にSentoController.phpです。コントローラーも職人さんが生成してくれます。

php artisan make:controller SentoController

で、生成されたSentoController.phpにdetailメソッドを書き加えます。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\SentoInfo;

class SentoController extends Controller
{
  public function detail($sento_code) {
    $sento = SentoInfo::with('sentoMap')->find($sento_code);
    return $sento;
  }
}

with('リレーション名')はeager loadingと呼ばれるもので、SentoInfoをロードする段階でSentoMapも先に取得してしまう処理を指しています。
このやり方をしないとsento_mapの列の数だけクエリが発行されることになるため無駄な負荷がかかってしまうようです。
今回は1対1のhasOneなのでこだわる必要性は薄いかもしれませんが、スピード面やクエリの発行回数で課金されるような状況を考えると、このやり方をしておいた方が良さそうです。

find(primaryKey)でそのprimaryKeyを持ったレコード1件を取得できます。
さて、 /api/sento/1を叩いてみると…

{
    sento_code: 1,
    sento_name: "テルマー湯",
    address: "東京都新宿区歌舞伎町1-1-2",
    tel: "03-5285-1726",
    sento_map: {
        sento_code: 2,
        lat: 35.694535,
        lng: 139.705160
    }
}

こんな感じでjsonが返ってくれば成功です。
今日はとりあえずこんなところで。次回は返ってきたjsonをVue.jsの方で扱う記事でも書きたいと思います。 →次の記事はこちら