「学生」と「講義」の関係は「多対多」である.すなわち,「学生」は複数の講義を履修し,「講義」は複数の学生が履修している.
まず,students と lectures というテーブルを作成するためのマイグレーションファイルを生成する.
[GakuinHana@rin06 laravelRelationship]$ pwd ⏎ /home/GakuinHana/Documents/laravel/laravelRelationship [GakuinHana@rin06 laravelRelationship]$ php artisan make:migration create_students_table --create=students ⏎ Created Migration: 2018_06_20_093240_create_students_table [GakuinHana@rin06 laravelRelationship]$ php artisan make:migration create_lectures_table --create=lectures ⏎ Created Migration: 2018_06_20_093300_create_lectures_table [GakuinHana@rin06 laravelRelationship]$
マイグレーションファイルの雛形が生成されたので,students テーブル作成のためのマイグレーションファイル(database/migrations/yyyy-mm-dd_hhmmss_create_students_table.php)を編集する.
database/migrations/yyyy-mm-dd_hhmmss_create_students_table.php (抜粋)
public function up()
{
Schema::create('students', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 100);
$table->timestamps();
});
}
lectures テーブル作成のためのマイグレーションファイル(database/migrations/yyyy-mm-dd_hhmmss_create_lectures_table.php)を編集する.
database/migrations/yyyy-mm-dd_hhmmss_create_lectures_table.php (抜粋)
public function up()
{
Schema::create('lectures', function (Blueprint $table) {
$table->increments('id');
$table->string('name', 100);
$table->timestamps();
});
}
マイグレーションファイルの編集が終われば,マイグレーションを実行して,テーブルを作成しよう.なお,最初に migrate:status
でマイグレーションの実行状況を確認してから,実際にマイグレーションを実行するとよい.今回の場合は campuses と faculties のテーブルはすでに作成済みであるので, students と lectures テーブルのマイグレーションだけが実行された.
[GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------+ | Ran? | Migration | +------+------------------------------------------+ | Y | 2018_06_14_051009_create_campuses_table | | Y | 2018_06_14_051058_create_faculties_table | | N | 2018_06_20_093240_create_students_table | | N | 2018_06_20_093300_create_lectures_table | +------+------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$ php artisan migrate ⏎ Migrating: 2018_06_20_093240_create_students_table Migrated: 2018_06_20_093240_create_students_table Migrating: 2018_06_20_093300_create_lectures_table Migrated: 2018_06_20_093300_create_lectures_table [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------+ | Ran? | Migration | +------+------------------------------------------+ | Y | 2018_06_14_051009_create_campuses_table | | Y | 2018_06_14_051058_create_faculties_table | | Y | 2018_06_20_093240_create_students_table | | Y | 2018_06_20_093300_create_lectures_table | +------+------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$
Git にコミットしておこう.
多対多のリレーションシップを実現するには,中間テーブルが必要である.Laravel では多対多を実現するには,テーブル名を単数にしつつ,アルファベット順に並べ,アンダースコアで区切った中間テーブルを準備しなければならないというルールがある.つまり「students」と「lectures」では「lecture_student」という中間テーブル名になる(複数形の「lectures_students」やアルファベット順でない「student_lecture」ではダメ!).
上記のルールに従って,「lecture_student」テーブルを生成するためのマイグレーションファイルを作成する.
[GakuinHana@rin06 laravelRelationship]$ pwd ⏎ /home/GakuinHana/Documents/laravel/laravelRelationship [GakuinHana@rin06 laravelRelationship]$ php artisan make:migration create_lecture_student_table --create=lecture_student ⏎ Created Migration: 2018_06_20_095541_create_lecture_student_table [GakuinHana@rin06 laravelRelationship]$
マイグレーションファイルを編集する.なお,8-11行目と13-16行目は外部キーの設定で,例えば,ある学生が退学して学生のレコードを消したときに,その操作に伴って中間テーブルから該当学生の履修情報も削除することを意味している.また,cascade 以外の指定も可能である.
database/migrations/yyyy-mm-dd_hhmmss_create_lecture_student_table.php (抜粋)
public function up()
{
Schema::create('lecture_student', function (Blueprint $table) {
$table->increments('id');
$table->integer('lecture_id')->unsigned();
$table->integer('student_id')->unsigned();
$table->timestamps();
$table->foreign('lecture_id')
->references('id')
->on('lectures')
->onDelete('cascade');
$table->foreign('student_id')
->references('id')
->on('students')
->onDelete('cascade');
});
}
マイグレーションファイルの編集が終われば,実際にマイグレーションを実行してテーブルを作成する.
[GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2018_06_14_051009_create_campuses_table | | Y | 2018_06_14_051058_create_faculties_table | | Y | 2018_06_20_093240_create_students_table | | Y | 2018_06_20_093300_create_lectures_table | | N | 2018_06_20_095541_create_lecture_student_table | +------+------------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$ php artisan migrate ⏎ Migrating: 2018_06_20_095541_create_lecture_student_table Migrated: 2018_06_20_095541_create_lecture_student_table [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2018_06_14_051009_create_campuses_table | | Y | 2018_06_14_051058_create_faculties_table | | Y | 2018_06_20_093240_create_students_table | | Y | 2018_06_20_093300_create_lectures_table | | Y | 2018_06_20_095541_create_lecture_student_table | +------+------------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$
Git にコミットしておこう.
まずはシーダーを生成する.
[GakuinHana@rin06 laravelRelationship]$ php artisan make:seeder StudentsTableSeeder ⏎ Seeder created successfully. [GakuinHana@rin06 laravelRelationship]$ php artisan make:seeder LecturesTableSeeder ⏎ Seeder created successfully. [GakuinHana@rin06 laravelRelationship]$ php artisan make:seeder LectureStudentTableSeeder ⏎ Seeder created successfully. [GakuinHana@rin06 laravelRelationship]$
次に,学生テーブル,講義テーブルと中間テーブルにテスト用のデータを記述する.また,database/seeds/DatabaseSeeder.php には3つのシーダーを実行するためのコードを追加する.
database/seeds/StudentsTableSeeder.php (抜粋)
public function run()
{
// 一旦中身を削除する
DB::table('students')->delete();
DB::table('students')->insert([
'name' => '井上'
]);
DB::table('students')->insert([
'name' => '藤井'
]);
DB::table('students')->insert([
'name' => '飯田'
]);
DB::table('students')->insert([
'name' => '足立'
]);
DB::table('students')->insert([
'name' => '武田'
]);
}
database/seeds/LecturesTableSeeder.php (抜粋)
public function run()
{
// 一旦中身を削除する
DB::table('lectures')->delete();
DB::table('lectures')->insert([
'name' => '経営戦略論'
]);
DB::table('lectures')->insert([
'name' => '管理会計'
]);
DB::table('lectures')->insert([
'name' => '情報ネットワーク論'
]);
}
database/seeds/LectureStudentTableSeeder.php (抜粋)
public function run()
{
// 一旦中身を削除する
DB::table('lecture_student')->delete();
DB::table('lecture_student')->insert([
'lecture_id' => 1,
'student_id' => 1
]);
DB::table('lecture_student')->insert([
'lecture_id' => 2,
'student_id' => 1
]);
DB::table('lecture_student')->insert([
'lecture_id' => 1,
'student_id' => 2
]);
DB::table('lecture_student')->insert([
'lecture_id' => 3,
'student_id' => 2
]);
DB::table('lecture_student')->insert([
'lecture_id' => 1,
'student_id' => 3
]);
DB::table('lecture_student')->insert([
'lecture_id' => 2,
'student_id' => 3
]);
DB::table('lecture_student')->insert([
'lecture_id' => 3,
'student_id' => 3
]);
DB::table('lecture_student')->insert([
'lecture_id' => 2,
'student_id' => 4
]);
DB::table('lecture_student')->insert([
'lecture_id' => 3,
'student_id' => 4
]);
}
database/seeds/DatabaseSeeder.php (抜粋)
public function run()
{
$this->call(CampusesTableSeeder::class);
$this->call(FacultiesTableSeeder::class);
$this->call(StudentsTableSeeder::class);
$this->call(LecturesTableSeeder::class);
$this->call(LectureStudentTableSeeder::class);
}
シーダーの準備ができたら,シーダーを実行しよう.なお,初めてシーダーを実行するときにはエラーが表示されることがある.このような場合はphp ../composer.phar dump-autoload
を実行してから,再度シーダを実行する.また,php artisan db:seed
を実行すると,database/seeds/DatabaseSeeder.php に記述されたシーダすべてが順番に実行されてしまう.つまり,CampusTableSeeder や FacultiesTableSeeder が2回実行されて,IDがずれてしまうなどの問題が発生する可能性がある.この場合,まだ実行されていないクラスを指定してシーダを実行するか,データベースを一旦すべてロールバックして,マイグレーションからやり直す方法を取れば良い.ただし,本番環境でロールバックを実施してしまうと,本番データが消えてしまう!!!
[GakuinHana@rin06 laravelRelationship]$ php artisan db:seed --class=StudentsTableSeeder ⏎ In Container.php line 729: Class StudentsTableSeeder does not exist [GakuinHana@rin06 laravelRelationship]$ [GakuinHana@rin06 laravelRelationship]$ php ../composer.phar dump-autoload ⏎ You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug Generating optimized autoload files [GakuinHana@rin06 laravelRelationship]$ php artisan db:seed --class=StudentsTableSeeder ⏎ [GakuinHana@rin06 laravelRelationship]$ php artisan db:seed --class=LecturesTableSeeder ⏎ [GakuinHana@rin06 laravelRelationship]$ php artisan db:seed --class=LectureStudentTableSeeder ⏎
データベースをロールバックする場合は,次のようにする.なお,ロールバックすると,最新のマイグレーションだけがロールバックされる.すべてのテーブルをリセットしたければロールバックを何度か繰り返せば良い.一旦すべてをリセットすると,次にロールバックするとすべてのテーブルがリセットされる.
[GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2018_06_14_051009_create_campuses_table | | Y | 2018_06_14_051058_create_faculties_table | | Y | 2018_06_20_093240_create_students_table | | Y | 2018_06_20_093300_create_lectures_table | | Y | 2018_06_20_095541_create_lecture_student_table | +------+------------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback ⏎ # 一つだけロールバック Rolling back: 2018_06_20_095541_create_lecture_student_table Rolled back: 2018_06_20_095541_create_lecture_student_table [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2018_06_14_051009_create_campuses_table | | Y | 2018_06_14_051058_create_faculties_table | | Y | 2018_06_20_093240_create_students_table | | Y | 2018_06_20_093300_create_lectures_table | | N | 2018_06_20_095541_create_lecture_student_table | +------+------------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback ⏎ # 2つがロールバック Rolling back: 2018_06_20_093300_create_lectures_table Rolled back: 2018_06_20_093300_create_lectures_table Rolling back: 2018_06_20_093240_create_students_table Rolled back: 2018_06_20_093240_create_students_table [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | Y | 2018_06_14_051009_create_campuses_table | | Y | 2018_06_14_051058_create_faculties_table | | N | 2018_06_20_093240_create_students_table | | N | 2018_06_20_093300_create_lectures_table | | N | 2018_06_20_095541_create_lecture_student_table | +------+------------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback ⏎ # 最初の2つがロールバックされて,すべてがリセットされた Rolling back: 2018_06_14_051058_create_faculties_table Rolled back: 2018_06_14_051058_create_faculties_table Rolling back: 2018_06_14_051009_create_campuses_table Rolled back: 2018_06_14_051009_create_campuses_table [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:status ⏎ +------+------------------------------------------------+ | Ran? | Migration | +------+------------------------------------------------+ | N | 2018_06_14_051009_create_campuses_table | | N | 2018_06_14_051058_create_faculties_table | | N | 2018_06_20_093240_create_students_table | | N | 2018_06_20_093300_create_lectures_table | | N | 2018_06_20_095541_create_lecture_student_table | +------+------------------------------------------------+ [GakuinHana@rin06 laravelRelationship]$ php artisan migrate ⏎ # 全てまとめてマイグレーション Migrating: 2018_06_14_051009_create_campuses_table Migrated: 2018_06_14_051009_create_campuses_table Migrating: 2018_06_14_051058_create_faculties_table Migrated: 2018_06_14_051058_create_faculties_table Migrating: 2018_06_20_093240_create_students_table Migrated: 2018_06_20_093240_create_students_table Migrating: 2018_06_20_093300_create_lectures_table Migrated: 2018_06_20_093300_create_lectures_table Migrating: 2018_06_20_095541_create_lecture_student_table Migrated: 2018_06_20_095541_create_lecture_student_table [GakuinHana@rin06 laravelRelationship]$ php artisan db:seed ⏎ Seeding: CampusesTableSeeder Seeding: FacultiesTableSeeder Seeding: StudentsTableSeeder Seeding: LecturesTableSeeder Seeding: LectureStudentTableSeeder [GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback ⏎ # すべてロールバック Rolling back: 2018_06_20_095541_create_lecture_student_table Rolled back: 2018_06_20_095541_create_lecture_student_table Rolling back: 2018_06_20_093300_create_lectures_table Rolled back: 2018_06_20_093300_create_lectures_table Rolling back: 2018_06_20_093240_create_students_table Rolled back: 2018_06_20_093240_create_students_table Rolling back: 2018_06_14_051058_create_faculties_table Rolled back: 2018_06_14_051058_create_faculties_table Rolling back: 2018_06_14_051009_create_campuses_table Rolled back: 2018_06_14_051009_create_campuses_table [GakuinHana@rin06 laravelRelationship]$ php artisan migrate ⏎ Migrating: 2018_06_14_051009_create_campuses_table Migrated: 2018_06_14_051009_create_campuses_table Migrating: 2018_06_14_051058_create_faculties_table Migrated: 2018_06_14_051058_create_faculties_table Migrating: 2018_06_20_093240_create_students_table Migrated: 2018_06_20_093240_create_students_table Migrating: 2018_06_20_093300_create_lectures_table Migrated: 2018_06_20_093300_create_lectures_table Migrating: 2018_06_20_095541_create_lecture_student_table Migrated: 2018_06_20_095541_create_lecture_student_table [GakuinHana@rin06 laravelRelationship]$ php artisan db:seed ⏎ Seeding: CampusesTableSeeder Seeding: FacultiesTableSeeder Seeding: StudentsTableSeeder Seeding: LecturesTableSeeder Seeding: LectureStudentTableSeeder [GakuinHana@rin06 laravelRelationship]$
シーダーでデータの投入ができたので,sqlite3 で確認してみよう.
[GakuinHana@rin06 laravelRelationship]$ pwd ⏎ /home/GakuinHana/Documents/laravel/laravelRelationship [GakuinHana@rin06 laravelRelationship]$ ls ⏎ app bootstrap composer.lock database phpunit.xml readme.md routes storage vendor artisan composer.json config package.json public resources server.php tests webpack.mix.js [GakuinHana@rin06 laravelRelationship]$ cd database/ ⏎ [GakuinHana@rin06 database]$ ls ⏎ database.sqlite factories migrations seeds [GakuinHana@rin06 database]$ sqlite3 database.sqlite ⏎ SQLite version 3.7.17 2013-05-20 00:56:22 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> select * from students; ⏎ 1|井上|| 2|藤井|| 3|飯田|| 4|足立|| 5|武田|| sqlite> select * from lectures; ⏎ 1|経営戦略論|| 2|管理会計|| 3|情報ネットワーク論|| sqlite> select * from lecture_student; ⏎ 1|1|1|| 2|2|1|| 3|1|2|| 4|3|2|| 5|1|3|| 6|2|3|| 7|3|3|| 8|2|4|| 9|3|4|| sqlite> select students.id, students.name, lectures.name ⏎ ...> from students ⏎ ...> inner join lecture_student on ⏎ ...> students.id = lecture_student.student_id ⏎ ...> inner join lectures on ⏎ ...> lectures.id = lecture_student.lecture_id ⏎ ...> order by students.id; ⏎ 1|井上|経営戦略論 1|井上|管理会計 2|藤井|経営戦略論 2|藤井|情報ネットワーク論 3|飯田|経営戦略論 3|飯田|管理会計 3|飯田|情報ネットワーク論 4|足立|管理会計 4|足立|情報ネットワーク論 sqlite> .exit ⏎ [GakuinHana@rin06 database]$
問題なければ,Gitでコミットしておこう.
Student モデルと Lecture モデルを作成しよう.なお,再びモデルは先頭大文字単数形にしておくことに注意する.
[GakuinHana@rin06 laravelRelationship]$ pwd ⏎ /home/GakuinHana/Documents/laravel/laravelRelationship [GakuinHana@rin06 laravelRelationship]$ ls app/ ⏎ Campus.php Console Exceptions Faculty.php Http Providers User.php [GakuinHana@rin06 laravelRelationship]$ php artisan make:model Student ⏎ Model created successfully. [GakuinHana@rin06 laravelRelationship]$ php artisan make:model Lecture ⏎ Model created successfully. [GakuinHana@rin06 laravelRelationship]$ ls app/ ⏎ Campus.php Console Exceptions Faculty.php Http Lecture.php Providers Student.php User.php [GakuinHana@rin06 laravelRelationship]$
Git でコミットしておこう.
StudentsController と LecturesController を作成してコミットしておこう.
[GakuinHana@rin06 laravelRelationship]$ ls app/Http/Controllers/ ⏎ Auth CampusesController.php Controller.php FacultiesController.php [GakuinHana@rin06 laravelRelationship]$ php artisan make:controller StudentsController ⏎ Controller created successfully. [GakuinHana@rin06 laravelRelationship]$ php artisan make:controller LecturesController ⏎ Controller created successfully. [GakuinHana@rin06 laravelRelationship]$ ls app/Http/Controllers/ ⏎ Auth CampusesController.php Controller.php FacultiesController.php LecturesController.php StudentsController.php [GakuinHana@rin06 laravelRelationship]$
app/Http/Controllers/ 以下の StudentsController.php と LecturesController.php に一覧表示の雛形を作成する.
app/Http/Controllers/StudentsController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class StudentsController extends Controller
{
public function index()
{
dd("List of Students");
}
}
app/Http/Controllers/LecturesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class LecturesController extends Controller
{
public function index()
{
dd("List of Lectures");
}
}
次に,ルートの定義を確認する.
[GakuinHana@rin06 laravelRelationship]$ php artisan route:list ⏎ +--------+----------+-----------+------+------------------------------------------------+--------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+-----------+------+------------------------------------------------+--------------+ | | GET|HEAD | / | | Closure | web | | | GET|HEAD | api/user | | Closure | api,auth:api | | | GET|HEAD | campuses | | App\Http\Controllers\CampusesController@index | web | | | GET|HEAD | faculties | | App\Http\Controllers\FacultiesController@index | web | +--------+----------+-----------+------+------------------------------------------------+--------------+ [GakuinHana@rin06 laravelRelationship]$
/students と /lectures の URI が指定されたときに,それぞれのコントローラの index 関数が呼び出されるように routes/web.php を編集する.
routes/web.php (抜粋)
Route::get('/campuses', 'CampusesController@index');
Route::get('/faculties', 'FacultiesController@index');
Route::get('/students', 'StudentsController@index');
Route::get('/lectures', 'LecturesController@index');
再度,ルートの定義を確認する.
[GakuinHana@rin06 laravelRelationship]$ php artisan route:list ⏎ +--------+----------+-----------+------+------------------------------------------------+--------------+ | Domain | Method | URI | Name | Action | Middleware | +--------+----------+-----------+------+------------------------------------------------+--------------+ | | GET|HEAD | / | | Closure | web | | | GET|HEAD | api/user | | Closure | api,auth:api | | | GET|HEAD | campuses | | App\Http\Controllers\CampusesController@index | web | | | GET|HEAD | faculties | | App\Http\Controllers\FacultiesController@index | web | | | GET|HEAD | lectures | | App\Http\Controllers\LecturesController@index | web | | | GET|HEAD | students | | App\Http\Controllers\StudentsController@index | web | +--------+----------+-----------+------+------------------------------------------------+--------------+ [GakuinHana@rin06 laravelRelationship]$
Webサーバを起動して,実際に /students と /lectures にアクセスしてみよう.
[GakuinHana@rin06 laravelRelationship]$ php artisan serve --host=rin06.ba.kobegakuin.ac.jp --port 8385 ⏎ Laravel development server started: <http://rin06.ba.kobegakuin.ac.jp:8385>
うまく表示できたら Git でコミットしておこう.
ようやくリレーションシップを設定する準備ができた.学生が履修できる講義は複数,講義には複数の学生が履修するので,「学生と講義は多対多」である.一対多の場合と似ているが,「学生 (Student)」クラスでは,その学生が履修している講義のリストを取得するための lectures メソッドを記述する.同様に,「講義 (Lecture)」クラスでも,その講義を履修している学生のリストを取得するための students メソッドを記述する.app/Student.php と app/Lecture.php にそれぞれメソッドを記述するが,多対多の場合にはどちらも belongsToMany()
を使えば良い.
app/Student.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
// 多対多のリレーションシップ
public function lectures()
{
return $this->belongsToMany('App\Lecture');
}
}
app/Lecture.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Lecture extends Model
{
// 多対多のリレーションシップ
public function students()
{
return $this->belongsToMany('App\Student');
}
}
モデルにメソッドを記述できたら,コントローラから呼び出してみよう.
app/Http/Controllers/StudentsController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Student;
class StudentsController extends Controller
{
public function index()
{
$students = Student::get();
foreach ($students as $student) {
$lectures = $student->lectures;
var_dump("++++++++++++");
var_dump($student->name);
foreach ($lectures as $lecture) {
var_dump($lecture->name);
}
}
}
}
app/Http/Controllers/LecturesController.php
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Lecture;
class LecturesController extends Controller
{
public function index()
{
$lectures = Lecture::get();
foreach ($lectures as $lecture) {
$students = $lecture->students;
var_dump("++++++++++++");
var_dump($lecture->name);
foreach ($students as $student) {
var_dump($student->name);
}
}
}
}
Webサーバを起動して,実際に /students と /lectures にアクセスしてみよう.
[GakuinHana@rin06 laravelRelationship]$ php artisan serve --host=rin06.ba.kobegakuin.ac.jp --port 8385 ⏎ Laravel development server started: <http://rin06.ba.kobegakuin.ac.jp:8385>
うまく表示できたら Git でコミットしておこう.