ウェブログラム

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

Webrogram

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

ウェブログラムサイト移行のお知らせ

ウェブログラムはさらなるプログラミング記事作成の効率化や品質向上に目的に以下URLへ移行させていただきます。

ウェブログラム ~学習するな実践せよ~

引き続き、実践できるコンテンツを作成していきますので、よろしくお願いいたします。

Laravel5.6 1対多のリレーションを学ぼう

今回は1対多のリレーションを学びましょう。

以前は1対1のリレーションを学びました。リレーションとはについても記述しているので、言葉の意味が分からない方は一度読んでみて下さい。

webrogram.hatenadiary.jp

1対多のリレーションの具体例

1対多は開発をしていると、様々な場面で登場します。

例えば、ブログシステムでは

  • 記事
  • コメント

といったデータを取り扱いますが、この時1つの「記事」に対して、複数の「コメント」が結びついています。

また、ブログサービスを利用するためにユーザを登録する必要がありますが、

1人の「ユーザ」に対して複数の「記事」といったこともありえます。

  • 記事 : コメント = 1 : 多
  • ユーザ : 記事 = 1 : 多

このように1の方も多になりえたりします。最初はイメージが付きづらいとこかもしれませんが、 リレーションを利用しているうちに確実に理解できるようになるので、安心してください。

今回はユーザと記事との関係を1対多のリレーションで実装してみましょう。

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

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

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

app/User.php

<?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 articles()
    {
        return $this->hasMany('App\Article');
    }
}

ここで重要なのはarticles()という関数内にhasManyでArticleモデルを指定しているところです。

ユーザは複数の記事(articles)を持っているという定義になります。

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

app/Article.php

<?php

namespace App;

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

Articleモデルにはuser()という関数で、Userモデルを指定しています。 belongsToでUserモデルに紐づけていることになります。

つまり、記事は1人のユーザに属しているという定義になります。

この辺の定義をしなければ、Laravelの便利なEloquentが利用できなくなってしまいます。

テーブルを作成

まず、作成するものとしてUserとArticleが必要です。

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

php artisan migrate

Articleのテーブルは実際にSQLをDB上で流して作成しましょう。

かなり簡易的なテーブルですが、以下の通り作成してください。

CREATE TABLE `articles` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `text` varchar(255) DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`)
);

user_idというカラムがユーザと紐付けるため必要なってくるので忘れず作成するようにしましょう。

これでテーブルの準備はOKです。

データを作成

テーブルは出来たので、テストデータを作成しましょう。

まずはusersテーブルから

以下のSQLを流して下さい。

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人分データを作成しました。

次にarticlesデータを作成しましょう。記事は複数作成しておきます。

INSERT INTO `webrogram_db`.`articles` (`user_id`, `text`) VALUES ('1', 'article 1');
INSERT INTO `webrogram_db`.`articles` (`user_id`, `text`) VALUES ('1', 'article 2');
INSERT INTO `webrogram_db`.`articles` (`user_id`, `text`) VALUES ('2', 'article 3');
INSERT INTO `webrogram_db`.`articles` (`user_id`, `text`) VALUES ('2', 'article 4');

データとしてはユーザ1人につき、それぞれ2つ記事をもっているという感じになっています。

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

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

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

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

php artisan tinker

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

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

ユーザから記事を取得してみる

まずはユーザからそれぞれの記事を取得してみます。

User::find(1)->articles()->get()

実行すると以下の結果が得られるはずです。

       App\Article {#2328
         id: 1,
         user_id: 1,
         text: "article 1",
         created_at: null,
         updated_at: null,
       },
       App\Article {#2316
         id: 2,
         user_id: 1,
         text: "article 2",
         created_at: null,
         updated_at: null,
       },

ユーザID = 1に紐付いている記事すべてが取得できました。

試しに、find(1)の箇所をfind(2)に変えて実行してみてください。ユーザID = 2の記事すべてが取得できると思います。

記事からユーザを取得する

先程はユーザに紐付いている全ての記事を取得しましたが、今度は得意の記事から、その記事はどのユーザのものなのかということを調べたいと思います。

記事からユーザを取得しましょう。

tinker上で一度Articleモデルを読み込む必要があります

use app\Article

次に「記事ID = 3」からユーザを取得していましょう

Article::find(3)->user();

上記を実行すると以下の「ユーザID = 3」のものが取得できたかと思います。

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

このように1対多のリレーションをLaravelで使うと、どちらの関係からでももう片方のデータを取得することができます。

しかも直感的でとても操作がしやすかったかと思います。

まとめ

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

リレーションの中では一番使用頻度が高いかと思うので、この機会に習得しましょう。何度も操作しているうちに直感的に操作出来るようになるかと思います。

リレーションにはあと多対多のものがあります。

これについて例を挙げるとすると、生徒と教師の関係があります。

一人の生徒は複数の教師に教えてもらい、一人の教師は複数の生徒を教えます。

つまりこれは多対多の関係(リレーション)ということですね。

多対多のリレーションについてはまた別の記事で紹介します。

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対多のリレーションです。

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