ウェブログラム

実践しながらじっくり学ぶ、Webプログラム習得サイトです。自作サービスの公開までWeb開発を実践していきましょう!

Webrogram

自分のオリジナルサービスを作って運営しよう!

Laravel5.6 1対1のリレーション

今回はDB(モデル)に関数話で、リレーションについて見ていきましょう。

Laravelのリレーションはいくつかありますが、その中でも「1対1のリレーション」について見ていきます。

リレーションについても説明しますが、Laravelの1対1のリレーションの書き方、仕組みをいきなり知りたい方は飛ばして下さい。

リレーションについて、言葉の意味とか仕組みとかが分からない場合は一度読んでみてくださいね。

リレーションとは

リレーションというのはデータとデータの関係のことです。

例えば、ブログサービスを考えてみましょう。

ブログには記事とコメントという2つのデータがあったとします。

記事にはいくつかのコメントが紐付いています。 一方、コメントには1つの記事が紐付いています。

記事にコメントがいっぱいぶら下がっていると考えるとイメージが付きやすいかもしれません。

これがリレーションです。

データの関係をデータベースでどうやって扱うのか?というのが、リレーションになります。

概念は少しむずかしいですが、関係性をデータとして表しているんだなっと思っておいてもらえればOKです。

1対1のリレーションとは

1対1のリレーションについて説明しますが、

さっきブログの例を紹介しました。

この例は1対多のリレーションになります。

1つの記事には複数のコメントが紐付いていて、1つのコメントは1つの記事にしか紐付かない。

1対1ってなる場合、対等な関係性である必要があります。

例えば、一人暮らしの場合に限りますが、家と人が行けるかなと思います。

基本的に1人は1つの家にくらし、1つの家には1人住んでいる。これなら1対1の関係になります。

※家を複数持っている場合や、家族で住んでいる場合は省かせて下さい...

他にも、人間と心臓とかは1対1ですね。結婚とかも1対1です(日本なら)

UserモデルとHouseモデルを作成

実際に2つのモデルを作成してみましょう。

Userモデルはデフォルトで作成されているので、そのまま利用しましょう。

app/User.php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];
}

Houseモデルも以下のように作成してください。

app/House.php

<?php

namespace App;

class House extends Model
{
}

テーブルの作成

DBにテーブルをUserとHouse両方作りましょう。 Userのテーブルはマイグレーションファイルで定義されているので、以下のコマンドをアプリケーションのディレクトリ直下で実行すれば勝手に作成されます。

php artisan migrate

Houseテーブルの方はSQLを直接流しましょう。

dbの名前はwebrogram_dbになっていますが、お使いの環境に合わせて変えて下さい。

CREATE TABLE `webrogram_db`.`houses` (
  `id` INT NOT NULL AUTO_INCREMENT,
  `user_id` INT NOT NULL,
  `name` VARCHAR(45) NULL,
  `created_at` TIMESTAMP NULL DEFAULT NULL,
  `updated_at` TIMESTAMP NULL DEFAULT NULL,
  PRIMARY KEY (`id`));

ここでポイントがあります。

housesテーブルにはuser_idという外部キーを作成しています。これで、だれのハウスなのか特定します。

しかし、Userの方のテーブル定義を見てみると、

f:id:iku8:20180925004443p:plain

house_idがありません。普通に考えれば、UserとHouseは対等の関係なので、housesテーブルにuser_idがあるなら、

usersテーブルにもhouse_idがあるべきです。

実はこれLaravelの仕組みで決められているからです。関係する片方のテーブルに外部キーが存在すれば、Laravelのプログラムから1対1の関係でデータを扱う事ができるんです。

なんか不思議な感じですね。

ともかく、これでテーブルの作成は終わりました。

モデルに1対1の関係性を定義

データもモデルもできましたが、現在UserとHouseの関係を定義しているのはデータベースだけです。

Laravelのプログラム上で、これらのデータは1対1の関係にあるんだよっていうのを定義する必要があります。

これを定義することによって、便利な操作ができるようになります。

まず、Userモデルから編集していきましょう。 以下のように追記してください。

app/User.php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    // 追記
    public function house()
    {
        return $this->hasOne('App\House');
    }
}

単純にクラスメソッドとして、house()を作成しました。 そして、hasOne()というメソッドにHouseモデルを引数として与えます。

これによって、house()を呼び出すと、とあるユーザに紐づく家を取得できるようになります。 あとで動きを見てみましょう。

次にHouseモデルを編集しましょう。

app/House.php

<?php

namespace App;

class House extends Model
{
    // 追記
    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

Userモデルとは違って、belongsTo()というメソッドを使っています。 これのいみとしては、UserはHouseに属しているという意味になり、user()を呼ぶと、ある家に属しているユーザが一人取得できます。

どうでしょうか?なんとなくイメージは掴めましたか?

イメージがあまり沸かなくても、

実際にデータを作成して動きをみることで確実に理解できると思うので、次に進みましょう。

データを作成する

テーブル定義、モデルの作成が終わったので、

次は実際に操作するデータをいくつか作っておきましょう。

ユーザデータを2つくらい作成します。

INSERT INTO `webrogram_db`.`users` (`name`, `email`, `password`) VALUES ('takeshi', 'takeshi@ex.com', '123456');
INSERT INTO `webrogram_db`.`users` (`name`, `email`, `password`) VALUES ('minamino', 'minamino@ex.com', '123456');

家データも2つ作ります。

INSERT INTO `webrogram_db`.`houses` (`user_id`, `name`) VALUES ('1', 'TAKESHI_HOUSE');
INSERT INTO `webrogram_db`.`houses` (`user_id`, `name`) VALUES ('2', 'MINAMINO_HOUSE');

今回作ったデータとしては、 ユーザが「takeshi」と「minamino」で、家が「TAKESHI_HOUSE」と「MINAMINO_HOSUE」です。

つまり、1人に1つの家が紐づけられている状態です。

この紐付いている状態をデータとして持っているのは、housesテーブルのuser_idになります。

例えば「TAKESHI_HOUSE」のuser_idは1となっており、usersテーブルでidが1のものは「takeshi」に該当するということですね。

プログラムからデータを扱う

データも出来たので次はプログラムから扱ってみましょう。

実際にコードを書くのは手間なので、tinkerという対話形式の仕組みを使って試してみましょう。

プロジェクト直下で以下コマンドを実行して下さい。

php artisan tinker

すると、>>>という文字が出てきて入力できるようになっていると思います。

ここで、eloquentモデルとかを扱うことができるので実際にしてみましょう。

ユーザから家を取得

まずユーザを取得します。今回は「takeshi」を取得して、takeshiの家を取得します。

User::find(1)->house()->get();

これで以下のように家が取得できればOKです。

     all: [
       App\House {#2323
         id: 1,
         user_id: 1,
         name: "TAKESHI_HOUSE",
         created_at: null,
         updated_at: null,
       },
     ],

仕組みとしては、User::find(1)で、usersテーブルのidが1のデータを取得。つまりここではtakeshiですね。 そして、takeshiというユーザのデータが関係している家データをhouse()で取得しています。

このように、特定のユーザから特定の家が取得できるのは、Laravelの便利な機能になります。

家からユーザを取得

今回は「MINAMINO_HOUSE」から「minamono」を取得してみましょう。さっきの逆ですね。

tinkerを開いたばかりだと、Houseモデルが読み込まれていないので、まず読み込みます。

use App\House;

次に、家を取得し、そこからユーザを取得してみます。

House::find(2)->user()->get();

以下のようなデータが取得出来れば成功です。

     all: [
       App\User {#2337
         id: 2,
         name: "minamino",
         email: "minamino@ex.com",
         created_at: null,
         updated_at: null,
       },
     ],

今回は、

House::find(2)->user()->get();

のように家->ユーザという形で一気に取得しましたが、一つずつ取得していくというのも可能です。

$house = House::find(2);

$user = $house->user()->get();

このようにすることで、家($house)も、ユーザ($user)も変数として取得できるので、プログラム上で書く際に便利な場合があります。

覚えておくと良いと思います。

まとめ

1対1のリレーションを学びました。

片方のテーブルにもう片方の外部キー(今回だとhousesテーブルにuser_id)を持つことで、1対1のリレーションを実現することが可能になりました。

eloquentを通して、リレーションを扱うことで、直感的にプログラムを書くことができます。

今回は、1対1を学びましたが、あまりこのリレーションは使う機会はないかなと思います。

実は、一番多く出てくるのは「1対多」のリレーションです。

例えば、学校と生徒という感じですね。1つの学校に対して、複数の生徒。これが1対多のリレーションです。

また別の記事で詳しく紹介していきます。