「学部」は一つの「キャンパス」に所属し,「キャンパス」には複数の「学部」が所属することから,「キャンパス」と「学部」の関係は「一対多」です.
その後のセクションでは,「学部」の情報を編集したときに新たな「キャンパス」を追加したり,「キャンパス」の情報を削除したときに該当する学部の情報を連鎖的に削除したりする方法について説明します.
キャンパスと学部の情報を格納するテーブル campuses と faculties を作成します.なお,テーブル名は先頭小文字の複数形を用いることに注意してください.また,一対多の一側(キャンパス)のテーブルから先に作成するようにしてください.
vagrant@ubuntu2204 laravelRelationship $ pwd ⏎ /home/vagrant/Documents/laravel/laravelRelationship vagrant@ubuntu2204 laravelRelationship $ php artisan make:migration create_campuses_table --create=campuses ⏎ INFO Migration [database/migrations/2023_10_17_131656_create_campuses_table.php] created successfully. vagrant@ubuntu2204 laravelRelationship $ php artisan make:migration create_faculties_table --create=faculties ⏎ INFO Migration [database/migrations/2023_10_17_131720_create_faculties_table.php] created successfully. vagrant@ubuntu2204 laravelRelationship $
まず,一対多の一側である campuses テーブルを設計します.上のコマンドで databases/migrations ディレクトリに yyyy_mm_dd_hhmmss_create_campuses_table.php というファイルが作成されたので,このファイルを編集します.一側のテーブルには外部キーは必要ありません.
database/migrations/2023_10_17_131656_create_campuses_table.php(抜粋)
public function up(): void
{
Schema::create('campuses', function (Blueprint $table) {
$table->id();
$table->string('campus', 100);
$table->timestamps();
});
}
次に,一対多の多側である faculties テーブルを設計します.yyyy_mm_dd_hhmmss_create_faculties_table.php 次の通り編集します.多側のテーブルには外部キーを設置する必要があり,campus_id
がそれにあたります.また10行目から13行目でリレーションシップを設定します.ここで,onDelete('cascade')
はカスケード削除,つまり連鎖削除を行う設定です.この設定を行うと,例えば,campuses テーブルで KPC のデータが削除されると,法学部や薬学部など KPC に所属する学部の情報が faculties テーブルから連鎖的に削除されます.
database/migrations/2023_10_17_131720_create_faculties_table.php(抜粋)
public function up(): void
{
Schema::create('faculties', function (Blueprint $table) {
$table->id();
$table->string('faculty', 100);
$table->integer('established')->unsigned();
$table->unsignedBigInteger('campus_id');
$table->timestamps();
$table->foreign('campus_id')
->references('id')
->on('campuses')
->onDelete('cascade');
});
}
マイグレーションファイルの編集ができれば,マイグレーションを実行してテーブルを作成します.
vagrant@ubuntu2204 laravelRelationship $ pwd ⏎ /home/vagrant/Documents/laravel/laravelRelationship vagrant@ubuntu2204 laravelRelationship $ php artisan migrate ⏎ INFO Preparing database. Creating migration table ................................... 8ms DONE INFO Running migrations. 2014_10_12_000000_create_users_table ...................... 14ms DONE 2014_10_12_100000_create_password_reset_tokens_table ....... 5ms DONE 2019_08_19_000000_create_failed_jobs_table ................ 12ms DONE 2019_12_14_000001_create_personal_access_tokens_table ..... 17ms DONE 2023_10_17_131656_create_campuses_table .................... 6ms DONE 2023_10_17_131720_create_faculties_table ................... 7ms DONE vagrant@ubuntu2204 laravelRelationship $
マイグレーションでエラーなくテーブルの作成ができたら git でコミットしておくと良いでしょう.
vagrant@ubuntu2204 laravelRelationship $ git status ⏎ On branch master Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) modified: database/migrations/2023_10_17_131656_create_campuses_table.php modified: database/migrations/2023_10_17_131720_create_faculties_table.php no changes added to commit (use "git add" and/or "git commit -a") vagrant@ubuntu2204 laravelRelationship $ git add . ⏎ vagrant@ubuntu2204 laravelRelationship $ git status ⏎ On branch master Changes to be committed: (use "git restore --staged <file>..." to unstage) modified: database/migrations/2023_10_17_131656_create_campuses_table.php modified: database/migrations/2023_10_17_131720_create_faculties_table.php vagrant@ubuntu2204 laravelRelationship $ git commit -m'migration' ⏎ [master 1b42c83] migration 2 files changed, 9 insertions(+) vagrant@ubuntu2204 laravelRelationship $
検証用のデータを投入するために,まずはシーダを生成します.
vagrant@ubuntu2204 laravelRelationship $ php artisan make:seeder CampusesTableSeeder ⏎ INFO Seeder [database/seeders/CampusesTableSeeder.php] created successfully. vagrant@ubuntu2204 laravelRelationship $ php artisan make:seeder FacultiesTableSeeder ⏎ INFO Seeder [database/seeders/FacultiesTableSeeder.php] created successfully. vagrant@ubuntu2204 laravelRelationship $
次に,キャンパステーブルと学部テーブルに検証用のデータを設定します.
database/seeders/CampusesTableSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class CampusesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// 一旦中身を削除する
DB::table('campuses')->delete();
DB::table('campuses')->insert([
'campus' => 'KAC'
]);
DB::table('campuses')->insert([
'campus' => 'KPC'
]);
}
}
database/seeders/FacultiesTableSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;
class FacultiesTableSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
// 一旦中身を削除する
DB::table('faculties')->delete();
DB::table('faculties')->insert([
'faculty' => '栄養学部',
'established' => 1966,
'campus_id' => 1
]);
DB::table('faculties')->insert([
'faculty' => '法学部',
'established' => 1967,
'campus_id' => 2
]);
DB::table('faculties')->insert([
'faculty' => '経済学部',
'established' => 1967,
'campus_id' => 1
]);
DB::table('faculties')->insert([
'faculty' => '薬学部',
'established' => 1972,
'campus_id' => 2
]);
DB::table('faculties')->insert([
'faculty' => '人文学部',
'established' => 2004,
'campus_id' => 1
]);
DB::table('faculties')->insert([
'faculty' => '経営学部',
'established' => 2004,
'campus_id' => 2
]);
DB::table('faculties')->insert([
'faculty' => '総合リハビリテーション学部',
'established' => 2005,
'campus_id' => 1
]);
DB::table('faculties')->insert([
'faculty' => '現代社会学部',
'established' => 2014,
'campus_id' => 2
]);
DB::table('faculties')->insert([
'faculty' => 'グローバル・コミュニケーション学部',
'established' => 2015,
'campus_id' => 2
]);
DB::table('faculties')->insert([
'faculty' => '心理学部',
'established' => 2018,
'campus_id' => 1
]);
}
}
また,DatabaseSeeder.php には2つのシーダを実行するためのコードを記載します.一対多の一側であるキャンパスのデータを先に投入し,その後に多側である学部のデータを投入するという順序が重要です.学部のデータを先に投入しようとしても,外部キーであるキャンパスIDが存在しないことになるので,学部のデータを先には投入できません.
database/seeders/DatabaseSeeder.php
class DatabaseSeeder extends Seeder
{
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call(CampusesTableSeeder::class);
$this->call(FacultiesTableSeeder::class);
}
}
シーダの準備ができたら実行してデータを実際に投入します.
vagrant@ubuntu2204 laravelRelationship $ php artisan db:seed ⏎
INFO Seeding database.
Database\Seeders\CampusesTableSeeder ........... RUNNING
Database\Seeders\CampusesTableSeeder ..... 26.16 ms DONE
Database\Seeders\FacultiesTableSeeder .......... RUNNING
Database\Seeders\FacultiesTableSeeder .... 43.54 ms DONE
vagrant@ubuntu2204 laravelRelationship $
シーダでデータの投入ができたので,sqlite3 で確認してみます.なお,テーブルの結合(内部結合)は INNER JOIN
を使います.
vagrant@ubuntu2204 laravelRelationship $ cd database/ ⏎ vagrant@ubuntu2204 database $ ls ⏎ database.sqlite factories migrations seeders vagrant@ubuntu2204 database $ sqlite3 database.sqlite ⏎ SQLite version 3.38.2 2022-03-26 13:51:10 Enter ".help" for usage hints. sqlite> .tables ⏎ campuses migrations users faculties password_reset_tokens failed_jobs personal_access_tokens sqlite> .headers on ⏎ sqlite> SELECT * FROM campuses; ⏎ id|campus|created_at|updated_at 1|KAC|| 2|KPC|| sqlite> SELECT * FROM faculties ORDER BY established DESC; ⏎ id|faculty|established|campus_id|created_at|updated_at 10|心理学部|2018|1|| 9|グローバル・コミュニケーション学部|2015|2|| 8|現代社会学部|2014|2|| 7|総合リハビリテーション学部|2005|1|| 5|人文学部|2004|1|| 6|経営学部|2004|2|| 4|薬学部|1972|2|| 2|法学部|1967|2|| 3|経済学部|1967|1|| 1|栄養学部|1966|1|| sqlite> SELECT faculties.id, faculties.faculty, campuses.campus ...> FROM faculties INNER JOIN campuses ...> ON faculties.campus_id = campuses.id; ⏎ id|faculty|campus 1|栄養学部|KAC 2|法学部|KPC 3|経済学部|KAC 4|薬学部|KPC 5|人文学部|KAC 6|経営学部|KPC 7|総合リハビリテーション学部|KAC 8|現代社会学部|KPC 9|グローバル・コミュニケーション学部|KPC 10|心理学部|KAC sqlite> .exit ⏎ vagrant@ubuntu2204 database $ cd .. ⏎ vagrant@ubuntu2204 laravelRelationship $
上手くできたら,git でコミットしておくと良いでしょう.
Campus モデルと Faculty モデルを作成します.なお,テーブル名は小文字の複数形でしたが,モデルは先頭大文字の単数形にしておくことに注意してください.
vagrant@ubuntu2204 laravelRelationship $ pwd ⏎ /home/vagrant/Documents/laravel/laravelRelationship vagrant@ubuntu2204 laravelRelationship $ ls app/Models/ ⏎ User.php vagrant@ubuntu2204 laravelRelationship $ php artisan make:model Campus ⏎ INFO Model [app/Models/Campus.php] created successfully. vagrant@ubuntu2204 laravelRelationship $ php artisan make:model Faculty ⏎ INFO Model [app/Models/Faculty.php] created successfully. vagrant@ubuntu2204 laravelRelationship $ ls app/Models/ ⏎ Campus.php Faculty.php User.php vagrant@ubuntu2204 laravelRelationship $
モデルの次にコントローラを作成します.コントローラは先頭大文字で複数形(または単数形)です.
vagrant@ubuntu2204 laravelRelationship $ ls app/Http/Controllers/ ⏎ Controller.php vagrant@ubuntu2204 laravelRelationship $ php artisan make:controller CampusesController ⏎ INFO Controller [app/Http/Controllers/CampusesController.php] created successfully. vagrant@ubuntu2204 laravelRelationship $ php artisan make:controller FacultiesController ⏎ INFO Controller [app/Http/Controllers/FacultiesController.php] created successfully. vagrant@ubuntu2204 laravelRelationship $ ls app/Http/Controllers/ ⏎ CampusesController.php Controller.php FacultiesController.php vagrant@ubuntu2204 laravelRelationship $
まず,現時点でのルートの定義を確認します.
vagrant@ubuntu2204 laravelRelationship $ php artisan route:list ⏎
GET|HEAD / .......................................................................
POST _ignition/execute-solution ignition.executeSolution › Spatie\LaravelIgni…
GET|HEAD _ignition/health-check ignition.healthCheck › Spatie\LaravelIgnition › H…
POST _ignition/update-config ignition.updateConfig › Spatie\LaravelIgnition …
GET|HEAD api/user ................................................................
GET|HEAD sanctum/csrf-cookie sanctum.csrf-cookie › Laravel\Sanctum › CsrfCookieCo…
Showing [6] routes
vagrant@ubuntu2204 laravelRelationship $
/campuses と /faculties の URI が指定されたときに,それぞれのコントローラの index 関数が呼び出されるように routes/web.php を編集します.
routes/web.php(抜粋)
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\CampusesController;
use App\Http\Controllers\FacultiesController;
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider and all of them will
| be assigned to the "web" middleware group. Make something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::get('/campuses', [CampusesController::class, 'index']) -> name('campuses.index');
Route::get('/faculties', [FacultiesController::class, 'index']) -> name('faculties.index');
CampusesController.php と FacultiesController.php に一覧表示の雛形を作成します.
app/HTTP/Controllers/CampusesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class CampusesController extends Controller
{
public function index()
{
dd('キャンパス一覧');
}
}
app/HTTP/Controllers/FacultiesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class FacultiesController extends Controller
{
public function index()
{
dd('学部一覧');
}
}
再びルートの定義を確認します.
vagrant@ubuntu2204 laravelRelationship $ php artisan route:list ⏎
GET|HEAD / .......................................................................
POST _ignition/execute-solution ignition.executeSolution › Spatie\LaravelIgni…
GET|HEAD _ignition/health-check ignition.healthCheck › Spatie\LaravelIgnition › H…
POST _ignition/update-config ignition.updateConfig › Spatie\LaravelIgnition …
GET|HEAD api/user ................................................................
GET|HEAD campuses ...................... campuses.index › CampusesController@index
GET|HEAD faculties ................... faculties.index › FacultiesController@index
GET|HEAD sanctum/csrf-cookie sanctum.csrf-cookie › Laravel\Sanctum › CsrfCookieCo…
Showing [8] routes
vagrant@ubuntu2204 laravelRelationship $
Web サーバを起動して,実際に /campuses と /faculties にアクセスします.
vagrant@ubuntu2204 laravelRelationship $ php artisan serve --host=192.168.56.101 --port=8000
INFO Server running on [http://192.168.56.101:8000].
Press Ctrl+C to stop the server
まだリレーションシップができていないが,とりあえずデータベースからキャンパスと学部の一覧を取得してみます.
app/HTTP/Controllers/CampusesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Campus; // 追加
class CampusesController extends Controller
{
public function index()
{
$campuses = Campus::get();
dd($campuses);
}
}
app/HTTP/Controllers/FacultiesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Faculty; // 追加
class FacultiesController extends Controller
{
public function index()
{
$faculties = Faculty::get();
dd($faculties);
}
}
Web サーバを起動して,実際に /campuses と /faculties にアクセスします.全てのデータを取得できていることを確認してください.しかし,法学部の campus_id
は 2 であるが,それが KPC であるという関連(リレーションシップ)はまだはっきりしない(取得できていない)ことにも注意してください.
vagrant@ubuntu2204 laravelRelationship $ php artisan serve --host=192.168.56.101 --port=8000
INFO Server running on [http://192.168.56.101:8000].
Press Ctrl+C to stop the server
ようやくリレーションシップを設定する準備ができました.ある学部が所属できるキャンパスは一つ,キャンパスには複数の学部が所属しているので,「キャンパスと学部は一対多」の関係です.Laravel ではモデルの中でリレーションシップを定義します.具体的には,一側 (Campus) では,多側を取得する faculties メソッドを記載します.一方で,多側 (Faculty) では,一側を取得する campus メソッド を記載します.それぞれ複数形,単数形の違いに注意してください.次の通りそれぞれのモデルにリレーションシップのメソッドを追加します.
app/Models/Campus.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Campus extends Model
{
use HasFactory;
// One to Many の One 側
public function faculties()
{
return $this->hasMany('App\Models\Faculty');
}
}
一側の Campus モデルでは所属する学部が複数あるため faculties()
メソッドを準備し,その中では hasMany()
によって学部を取得するように設定します.一方で,多側の Faculty モデルでは所属するキャンパスが一つであることから,次のとおり campus()
メソッドを準備し,belongsTo()
によってキャンパスを取得するように設定します.
app/Models/Faculty.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Faculty extends Model
{
use HasFactory;
// One to Many の Many 側
public function campus()
{
return $this->belongsTo('App\Models\Campus');
}
}
モデルにメソッドを記載できたので,コントローラから呼び出してみます.16行目にあるとおり $campus->faculties
によって指定したキャンパスに所属する学部をすべて取得できていることがわかります.
app/HTTP/Controllers/CampusesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Campus; // 追加
class CampusesController extends Controller
{
public function index()
{
$campuses = Campus::get();
foreach ($campuses as $campus) {
dump("------");
dump($campus->campus);
foreach ($campus->faculties as $faculty) {
$str = $faculty->faculty . " : " . $faculty->established;
dump($str);
}
}
}
}
学部のコントローラからキャンパスを取得します.具体的には $faculty->campus->campus
でキャンパス名を取得できていることがわかります.
app/HTTP/Controllers/FacultiesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Faculty; // 追加
class FacultiesController extends Controller
{
public function index()
{
$faculties = Faculty::get();
foreach ($faculties as $faculty) {
$str = $faculty->faculty . " : " . $faculty->campus->campus;
dump($str);
}
}
}