Laravel 10 入門トップページ


目次

  1. 概要
  2. プロジェクトの作成と初期設定
  3. 一対多のリレーションシップ
  4. 多対多のリレーションシップ
  5. ビューを使った表示
  6. 学部の情報を表示・変更する
  7. キャンパスの情報を表示・変更・削除する
  8. 学生の情報を表示・変更・削除する

Laravel でリレーションシップを使いこなす

一対多のリレーションシップ

概要

「学部」は一つの「キャンパス」に所属し,「キャンパス」には複数の「学部」が所属することから,「キャンパス」と「学部」の関係は「一対多」です.

campus_faculty

その後のセクションでは,「学部」の情報を編集したときに新たな「キャンパス」を追加したり,「キャンパス」の情報を削除したときに該当する学部の情報を連鎖的に削除したりする方法について説明します.

目次に戻る

テーブルの作成

キャンパスと学部の情報を格納するテーブル 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.phpclass 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 $

目次に戻る

SQLite で確認する

シーダでデータの投入ができたので,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

laravel10-2023-relationship-02.png

laravel10-2023-relationship-03.png

目次に戻る

データベースから一覧を取得

まだリレーションシップができていないが,とりあえずデータベースからキャンパスと学部の一覧を取得してみます.

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

laravel10-2023-relationship-04.png

目次に戻る

リレーションシップを設定する

ようやくリレーションシップを設定する準備ができました.ある学部が所属できるキャンパスは一つ,キャンパスには複数の学部が所属しているので,「キャンパスと学部は一対多」の関係です.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);
            }
        }
    }
}

laravel10-2023-relationship-05.png

学部のコントローラからキャンパスを取得します.具体的には $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);
        }
    }
}

laravel10-2023-relationship-06.png

目次に戻る