はじめに
関連する hasOne / belongsTo も紹介しているので、そちらも興味があればご覧ください!
環境
- php version
php -v PHP 7.4.33
- Laravel version
php artisan -V Laravel Framework 8.83.27
準備!!
記事のコマンドはすべて正常に動くか検証済みです。
かつ、最小限の構成をとっていますので、ぜひとも手を動かしながらやってみてください!
- migration 作成( DDL は1つのファイルにまとめます。)
php artisan make:migration create_tables
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTables extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// ブログテーブル
Schema::create('blogs', function (Blueprint $table) {
$table->id('blog_id');
$table->unsignedBigInteger('category_id');
$table->unsignedBigInteger('user_id');
$table->string('title');
$table->timestamps();
});
// コメントテーブル
Schema::create('comments', function (Blueprint $table) {
$table->id('comment_id');
$table->unsignedBigInteger('blog_id');
$table->string('comment');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('blogs');
Schema::dropIfExists('comments');
}
}- migration 実行
php artisan migrate:fresh
※確実に実行できるように fresh を付けています。
- Model 作成
php artisan make:model Blog && php artisan make:model Comment
- それぞれの Blogモデル Categoryモデル に対して、
primary key となるカラム名を $guarded に定義する。
▼ Blogモデル
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Blog extends Model
{
use HasFactory;
// ↓ここの部分(createメソッドで挿入できるようにする。)
protected $guarded = ['blog_id'];
// ↓プライマリキーを明示的に指定する。(後で説明)
protected $primaryKey = 'blog_id';
/**
* プライマリキーを返す(後で説明)
* @return string
*/
public function get_primary_key(): string
{
return $this->primaryKey;
}
}▼Commentモデル
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
// ↓ここの部分(createメソッドで挿入できるようにする。)
protected $guarded = ['comment_id'];
// ↓プライマリキーを明示的に指定する。(後で説明)
protected $primaryKey = 'comment_id';
/**
* プライマリキーを返す(後で説明)
* @return string
*/
public function get_primary_key(): string
{
return $this->primaryKey;
}
}▼Userモデル
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
// ↓プライマリキーはデフォルトのIdを使用する。(標準搭載のテーブルはいじらない方がいい。)
protected $primaryKey = 'id';
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* プライマリキーを返す(後で説明)
* @return string
*/
public function get_primary_key(): string
{
return $this->primaryKey;
}
}Laravel ではテーブルのプライマリキーは暗黙的に [id] となっている。
しかし、実務において [id] では、テーブル構造が複雑になってくると、カラム名の衝突 や なんのId? といった具合にわかりにくさが増してしまうので、明示的に指定してあげる。
また、後に説明する hasMany(引数1, 引数2, 引数3)の 引数2,3 が混乱を招きやすいので、
分かりやすいように get_primary_key() を記述しておきます。
- テストデータを tinker を用いて作成する
php artisan tinker
\App\Models\User::create([
'name' => 'ユーザー1',
'email' => 'hogehoge@example.com',
'password' => bcrypt('kenbaske4'),
]);
\App\Models\Blog::create([
'category_id' => 1,
'user_id' => 1,
'title' => 'タイトル1',
]);
\App\Models\Comment::upsert([
[
'blog_id' => 1,
'comment' => 'コメント1',
],
[
'blog_id' => 2,
'comment' => 'コメント2',
],
], ['comment_id']);もちろん、Seeder でもいいんですが、そこの説明をすると長ったらしくなるので、tinker で代替。

tinker はこういうとき便利だにゃ~。
- Route 定義
Route::get('', [\App\Http\Controllers\IndexController::class, 'index']);- Controller 作成
php artisan make:controller IndexController
本題: hasMany
hasMany の考え方は、1: 多 です。
そのModelのデータは、他のModelのデータを複数持ちうるのか?という視点から考えるとすんなり理解できるかと思います。

共通して、リレーションは該当のModelデータを主観にして考えると混乱せずに済むかと思います。正直、ここは慣れの部分が大きいです。
上記のことを具体例を用いていうと、
1つの Blog には、複数の Comment がつく可能性があるので、hasMany。
1つの「面白い!」という Comment は、1つの Blog につくので、hasOne。
おまけに、1つの「面白い!」という Comment は、User によって生成されるので、belogsTo。
コードベースでみていく!
- \App\Modes\Blog に以下を記載
public function comments(): hasMany
{
$comment_model = new \App\Models\Comment();
return $this->hasMany($comment_model, 'blog_id', $this->primaryKey);
}
必ず、namespace 下に
use Illuminate\Database\Eloquent\Relations\HasMany;
↑を記述してください。
- IndexController に以下を記載
public function index()
{
// blog_id = 1 を条件にまずデータを取得している。ここが肝。
$blog_data = \App\Models\Blog::find(1);
$comment_data = $blog_data->comments;
dd($comment_data);
}->comments の部分が、Modelで定義した関数部分となる。
- ↓実際に流れているクエリを確認
$blog_data->comments()->dd();
select * from `comments` where `comments`.`blog_id` = 1 and `comments`.`blog_id` is not null

categoriesテーブルから、category_id を条件に付与して取得してるんだけなんだな~。
さいごに
hasMany のリレーションを解説しました!
クエリの確認で dd() していたように、追加で↓のようにどんどん繋げられるので応用も効きます!
$blog_data->comments()->where('comment', 'コメント1')->get()作成したファイルを削除 (不要ファイルが残るのが気持ち悪い人向け)※Linux コマンドで削除しています。
cd /path/to/project
※ /path/to/project には、artisanファイル が格納されているディレクトリパスを指定する。
- Controller 削除
rm app/Http/Controllers/IndexController.php
- Model 削除
find $(pwd)/app/Models/ -type f -not -name "User.php" | xargs rm
- migrationファイル 削除
find $(pwd)/database/migrations/ -name "*create_table*" | xargs rm
- Route定義は行削除(これは Linuxコマンド ではなく普通に削除)
Route::get('', [\App\Http\Controllers\IndexController::class, 'index']);
またね~~~~。


コメント