【laravel】1対多のbelongsTo(hasMany)まとめ。select, insertなどの複数保存や検索も解説

laravelでbelongsToを設定する方法を説明します!

いわゆる1対多をどうやって実装するのかという話しですね。

さらに、select, insertなどの検索と保存についても説明しておきます。

 

 

laravelで1対多(belongsTo)を実装する

まずはモデルファイルの設定から。

今回はUserモデルと、Postモデルで説明します。

ブログ記事のような、あるユーザ(usersテーブル)が記事データ(postsテーブル)を持っている場合で考えます。

LaravelのモデルのリレーションでいえばUserモデルがhasManyでPostとひも付きを持っていることになります。

だって、記事はあくまで書き手であるユーザに所属するものだから。

ER図にするとこうなる関係です。(contentは記事の中身の文章データ)

usersテーブルとpostsがhasmanyでつながっている

User hasMany Post な1対多の関係です。

先にさっさと答えを知りたい人向けにモデルファイルだけ書きます。

User.php

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Models\Post; // Laravel9だとこうです。あなたのバージョンにnamespaceをあわせてください

class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;

    protected $fillable = [
        'name',
    ];


    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

Post.php

<?php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\User;
class Post extends Model
{
   protected $fillable = [ 'content', 'user_id' ];
 
  // リレーション
  public function user()
  {
    return $this->belongsTo(User::class);
  }
}

ここからは、これだけでは分からない人向けに丁寧に解説をしていきます。

 

いま日本一、Laravelの解説が分かりやすい本

プロフェッショナルWebプログラミング Laravel

 

User.php つまり親のモデル

<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;
use App\Models\Post; // Laravel9だとこうです。あなたのバージョンにnamespaceをあわせてください

class User extends Authenticatable implements MustVerifyEmail
{
    use Notifiable;

    protected $fillable = [
        'name',
    ];


    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}

ここのポイントは2点です。

まずはuse App\Models\Post;の部分。

hsaManyで紐付けるモデルのPostモデルをuse で読み込みましょう。

これがなくてもreturn $this->hasMany(App\Models\Post::class);にすればいいわけです。

でも長々と記述するのが面倒くさいからuseするわけです。

もうひとつのポイントがより大切なところ。

    public function posts()
    {
        return $this->hasMany(Post::class);
    }

public function posts()のメソッド名がpostの単数ではなくpostsの複数にしましょう。

じつは別にメソッド名はpostの単数だったとしてでもプログラムの動作には問題はありません。

でも名前で複数にしておけばUserが複数のpostを持つ、というのがエンジニア自身がコードを読んだときに分かりやすいのです。

ちなみにメソッド名を own_postsとかにしても動きます。ようは名前は何でもいいんです。

それでもLaravelのお作法としてモデル名の複数形にします。

外部キーにuser_idが必要

これはpostsテーブルにuser_idという名前のカラム(外部キー)が存在する前提です。

もしこれが別の名前だったら、第二引数にreturn $this->hasMany(Post::class, 'another_user_id'); というようにカラム名を指定します。

細かく知りたい人はhasManyの公式リファレンスを読みましょう。

https://laravel.com/docs/9.x/eloquent-relationships#one-to-many

 

次はPost.php つまり子のモデル

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use App\Models\User;

class Post extends Model
{
    protected $fillable = [
        'content',
        'user_id'
    ];

    // リレーション
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

説明していませんでしたがUser::class は文字列としてApp\Models\User を返します。

つまり以下と同じ意味になります。でも記述量が少ないからUser::classと書きます。

$this->belongsTo('App\Models\User');

なぜuserはusersの複数形ではなくuserの単数形なんでしょう?

ある1つのPost、つまり1つの記事の視点になったときには、書き手であるUserは1つですよね。

1つの記事の所有者がA、Bの2名いることは無いと思います。

つまりPostにとって自分自身を所有するのが1人だけなのでuserと単数形にするんです。

 

そしてuserに所属している側のデータなのでbelongsToで繋ぎます。

user_idという親になるデータを保持しているモデル側はbelognsToにします。

public function user() 
{
  return $this->belongsTo(User::class);
}

いちおう公式リファレンスのリンクです。

ちなみにこちらもuser_id以外の場合には第2引数にanother_user_idとカラム名を記載します。

またこれは親のUserモデルのusers.idカラムが存在する前提です。

もしusers.parent_id などの違う名前の場合には第三引数に

`$this->belongsTo(User::class, ‘user_id’, ‘parent_id’);`のように指定します。

このへんも細かく知りたい人は↑の公式リファレンスをチェックしてくださいね。

 

belongsTo, hasManyなデータにアクセスする方法|select

controllerでbelongsTo, hasManyなデータを方法です。

モデルファイルにpostsメソッドを書いてあると、postsプロパティにアクセスできます。

use App\User;
これをお忘れなく。

$user = User::find(1);
$user->posts; //これでuserがもつpostsが全部取得できる

foreach($user->posts as $post){
 $post->content;
}

SQLでいうとこんな検索をします。

SELECT posts.* FROM posts WHERE posts.user_id = 1

postsテーブルのcontentカラムにアクセスするときは上記のようにforeachしてカラム名のメンバ変数にアクセスします。

簡単ですね!

続いてPostモデルからその所有者であるuserを取得する場合

user App\User; を忘れずに。

$post = Post::find(1);
$post->user->name;

Postモデルにuserメソッドを実装しましたよね?そうするとuserというメンバ変数でアクセスできます。

簡単ですね!

続いてinser系です。

belongsTo, hasManyなデータをDBに保存する方法|insert

こんな感じです。

Auth::user()->posts()->create($request->all());

posts のようにメンバ変数っぽくアクセスするのではなくposts() のようにメソッドとして呼び出すことがポイント

 

Auth::user()はApp\Models\Userのインスタンスを返します。

つまりUserモデルのインスタンスですね。

 

それでAuth::user()->posts()はこのインスタンスを返します。

Illuminate\Database\Eloquent\Relations\HasMany

公式リファレンスはこちら

 

ここ間違いやすいので注意点です。

postsに()がない下記だとCollectionインスタンスを返します。

Auth::user()->posts

これはuserがhasManyなpostをすべて取得するわけです。

今回はcreateしたいので、そうではなくて Auth::user()->posts()にするのです。

公式ドキュメントを見るとわかるのですが、createメソッドがありますね。

引数は配列を取りますので、$request->all()で配列を渡してあげます。

hasManyな子モデルに一括で複数のデータをbuld insertする場合

ちなみに、postを1つではなく、複数一気に保存したい場合にはcreateManyメソッドを使いましょう。

引数を、保存したい値の配列を、さらに配列で囲みます。

Auth::user()->posts()->createMany([$request->all()]);

createManyの公式リファレンスはこちら

こんな感じ。簡単ですね!