Laravel入門トップページ


目次

  1. Composerのダウンロードとインストール
  2. コメント掲示板を作成してみよう
  3. リレーションシップを使いこなそう
    1. 概要
    2. Laravelプロジェクトの作成と初期設定
    3. 一対多のリレーションシップ
    4. 多対多のリレーションシップ
    5. ビューを使った表示
    6. 学部の情報を表示・変更する
    7. キャンパスの情報を表示・変更・削除する
    8. 学生の情報を表示・変更・削除する
  4. ユーザ認証の機能を実現しよう
  5. マルチ認証の機能を実現しよう
  6. MongoDB に接続しよう
  7. キューを利用しよう
  8. コマンド(コンソール)を利用しよう
  9. 本番環境にデプロイしよう

リレーションシップを使いこなそう

キャンパスの情報を表示・変更・削除する

目次に戻る

ここでの概要

ここでは,キャンパスの個別情報を表示するページを作成する.また,キャンパス情報を更新(変更)するためのフォームも作成する.特に,キャンパスの情報を削除したときに,所属する学部の情報を連鎖的に削除する方法についても解説する.なお,バックエンドのデータベースにMySQLを使っている場合は問題なく連鎖削除ができるが,sqlite を使っている場合は別途設定が必要になるので注意しよう.

キャンパス情報の表示

ここでは,キャンパス個別の情報を表示するページを作成しよう.まず,ルートを定義する.たとえば,/campuses/1 では id = 1 のKACのページを表示する.

routes/web.php (抜粋)
Route::get('/campuses', 'CampusesController@index');
Route::get('/campuses/{id}', 'CampusesController@show');

次は,コントローラに show 関数を追加する.まずは,引数だけを表示するようにしておこう.

app/Http/Controllers/CampusesController.php  (抜粋)
class CampusesController extends Controller
{
  public function index()
  {
    $campuses = Campus::get();
    return view('campuses.index')
            ->with('campuses', $campuses);
  }

  public function show($id)
  {
    dd($id);
  }
}

/faculties と /campuses のページに /campuses/{id} へのリンクを設置する.ビューを次のように変更すれば良い.

resources/views/faculties/index.blade.php  (抜粋)
<body>
    <h1>学部一覧</h1>
    <ul>
        @foreach ($faculties as $faculty)
            <li><a href="{{ action('FacultiesController@show', $faculty->id) }}">{{ $faculty->faculty }}</a>は{{ $faculty->established }}年に設置され,キャンパスは <a href="{{ action('CampusesController@show', $faculty->campus->id )}}">{{ $faculty->campus->campus }}</a> です</li>
        @endforeach
    </ul>
</body>
resources/views/campuses/index.blade.php  (抜粋)
<body>
    <h1>キャンパス一覧</h1>
    <ul>
        @foreach ($campuses as $campus)
            <li><a href="{{ action('CampusesController@show', $campus->id )}}">{{ $campus->campus }}</a></li>
            <ul>
                @foreach ($campus->faculties as $faculty)
                    <li><a href="{{ action('FacultiesController@show', $faculty->id) }}">{{ $faculty->faculty }}</a></li>
                @endforeach
            </ul>
        @endforeach
    </ul>
</body>

これによって,下図のように /faculties や /campuses から /campuses/1 などへリンクされるようになった.

edit-campus-1

次は CampusesController の show を編集して,学部情報を実際に取得しビューに渡す処理を記述する.

app/Http/Controllers/CampusesController.php  (抜粋)
  public function show($id)
  {
    $campus = Campus::where('id', $id)
                      ->first();
    if (!$campus) { // キャンパスを取得できない(IDが不正の)場合はリダイレクト
      return redirect('/campuses');
    }
    return view('campuses.show')
              ->with('campus', $campus);
  }

resouces/views/campuses/show.blade.php を作成し,次のような内容を記述する(index.blade.php とほとんど同じなので,コピーして一部を修正するだけでも良い).

resources/views/campuses/show.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ $campus->campus }}</title>
</head>
<body>
    <h1>{{ $campus->campus }}</h1>
    <ul>
        @foreach ($campus->faculties as $faculty)
            <li><a href="{{ action('FacultiesController@show', $faculty->id) }}">{{ $faculty->faculty }}</a></li>
        @endforeach
    </ul>
</body>
</html>

これでキャンパス詳細情報の表示ページができました.

edit-campus-2

目次に戻る

キャンパス情報の編集

キャンパス詳細情報のページから情報編集のページへリンクを作成しよう.まずはルートの定義を行う.ここでは,編集画面を表示するための定義と,編集画面で「更新」ボタンを押した後の処理を行うための定義を追加しておく.

routes/web.php (抜粋)
Route::get('/campuses', 'CampusesController@index');
Route::get('/campuses/{id}', 'CampusesController@show');
Route::get('/campuses/{id}/edit', 'CampusesController@edit');
Route::patch('/campuses', 'CampusesController@update');

キャンパス詳細ページには編集画面へのリンクを設置しておく.

resources/views/campuses/show.blade.php (抜粋)
<body>
    <h1>{{ $campus->campus }}</h1>
    <ul>
        @foreach ($campus->faculties as $faculty)
            <li><a href="{{ action('FacultiesController@show', $faculty->id) }}">{{ $faculty->faculty }}</a></li>
        @endforeach
    </ul>
    <hr>
    <p><a href="{{ action('CampusesController@edit', $campus->id) }}">[編集]</a></p>
</body>

コントローラに edit と update 関数を追加する.なお,update 関数は更新ボタンが押されたときに,どのような情報が渡されてくるかを確認するためだけのコードを記述しておき,まだ実際にデータベースを更新しないようにしておく.

app/Http/Controllers/CampusesController.php  (抜粋)
  public function edit($id)
  {
    $campus = Campus::where('id', $id)
                      ->first();
    if (!$campus) {  // キャンパスを取得できない(IDが不正の)場合はリダイレクト
      return redirect('/campuses');
    }
    return view('campuses.edit')
            ->with('campus', $campus);
  }

  public function update(Request $request)
  {
    dd($request);
  }

resouces/views/campuses/edit.blade.php を作成し,次のような内容を記述する.

resources/views/campuses/edit.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ $campus->campus }} の編集</title>
</head>
<body>
    <h1>{{ $campus->campus }} の編集</h1>

    <div>
      <form method="post"
      action="{{ action('CampusesController@update') }}"
      enctype='multipart/form-data'>
        {{ csrf_field() }}
        {{ method_field('patch') }}
        <input type="hidden" name="id" value="{{ $campus->id }}">
        <p>
          <label for="campus">キャンパス名:</label>
          <input type="text" name="campus"
          id="campus"
          value="{{ $campus->campus }}{{ old('campus') }}">
        </p>
        <p>
          <input type="submit" value="更新">
        </p>
      </form>
    </div>

</body>
</html>

これまでの作業の結果,キャンパス詳細ページの[編集]リンクをクリックすれば,キャンパス名を編集できるフォームが表示されるようになる.

edit-campus-3

また,「KAC」を「有瀬キャンパス」に書き換えて「更新」ボタンをクリックすれば,その情報が update 関数に渡されて,「request」の「ParameterBag」の中に格納されていることが確認できた(まだ,実際にデータベースを更新をしているわけではないことにも注意しよう).

edit-campus-4

では実際にデータベースの更新を行う処理を記述しよう.コントローラの update 関数にその処理を記述する.

app/Http/Controllers/CampusesController.php  (抜粋)
  public function update(Request $request)
  {
    // 掲示板の例を参考にここで Validation を記述しよう(ここでは省略します.)

    $faculty = Faculty::where('id', $request->id)
                        ->first();
    $faculty->faculty = $request->faculty;
    // 入力されたキャンパスが正しいかどうかをチェックする
    $campus = Campus::where('campus', $request->campus)->first();
    if (!$campus) {
      // 未知のキャンパスなので,一切更新せずに戻る
      return redirect()->action('FacultiesController@show', $request->id);
    }

    // $faculty に campus_id をセットする
    $faculty->campus_id = $campus->id;
    // データベースを更新する
    $faculty->save();
    return redirect()->action('FacultiesController@show', $request->id);
  }

例えば,「KAC」を「有瀬キャンパス」に書き換えて「更新」ボタンをクリックすれば,正しく更新できていることが確認できる.

edit-campus-5

また学部一覧ページでも正しく更新できていることが確認できる.

edit-campus-6

目次に戻る

キャンパス情報の削除

次は,KPCキャンパスの情報を削除してみよう.まず,ルートを定義する.

routes/web.php (抜粋)
Route::get('/campuses', 'CampusesController@index');
Route::get('/campuses/{id}', 'CampusesController@show');
Route::get('/campuses/{id}/edit', 'CampusesController@edit');
Route::patch('/campuses', 'CampusesController@update');
Route::delete('/campuses/{id}', 'CampusesController@destroy');

次に,show.blade.php に削除リンクを設置する.

resources/views/campuses/show.blade.php (抜粋)
<body>
    <h1>{{ $campus->campus }}</h1>
    <ul>
        @foreach ($campus->faculties as $faculty)
            <li><a href="{{ action('FacultiesController@show', $faculty->id) }}">{{ $faculty->faculty }}</a></li>
        @endforeach
    </ul>
    <hr>
    <p><a href="{{ action('CampusesController@edit', $campus->id) }}">[編集]</a></p>
    <div>
      <form action="{{ url('/campuses', $campus->id )}}" method="post">
        {{ csrf_field() }}
        {{ method_field('DELETE') }}
        <button>キャンパスの削除</button>
      </form>
  </div>
</body>

コントローラに destroy メソッドを記述する.

app/Http/Controllers/CampusesController.php  (抜粋)
  public function destroy($id)
  {
    $campus = Campus::where('id', $id)
                      ->first();
    if (!$campus) {
      return redirect('/campuses');
    }
    $campus->delete();
    return redirect('/campuses');
  }

これから「KPC」レコードの削除に関する実験を行うので,データベースをロールバック,マイグレーション,シーダーの実行をしておこう.

[GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback; php artisan migrate; php artisan db:seed
 ⏎

「KPC」のレコードを削除してみよう.一見うまく削除できたようである.

edit-campus-7

しかしながら,/faculties のページを開くとエラーになってしまった.なお,バックエンドのデータベースに MySQL を使っている場合は問題が起こりません.Sqlite を使っているとエラーになる.

edit-campus-8

Sqlite で上のようなエラーになる現象を考察しよう.MySQLの場合,campuses テーブルから id=2 のレコードを削除すると,外部キーが設定されているfaculties テーブルから,campus_id =2 のレコードが連鎖的に削除される.つまり,「KPC」キャンパスの情報を削除すると,「法学部」「薬学部」「経営学部」「現代社会学部」「グローバル・コミュニケーション学部」の情報も削除される.これは,/database/migration/xxxx_xx_xx_xxxxxx_create_faculties_table.php に $table-> foreign('campus_id')->references('id')-> on('campuses')-> onDelete('cascade'); を記述することで,外部キー制約が追加され,レコードの削除時に連鎖(カスケード)削除が設定されているからである.campuses テーブルから「KPC」レコードを削除した後に,MySQLでデータベースの内容を確認すると次のようになる.

[GakuinHana@rin06 laravelRelationship]$ mysql> select * from faculties; ⏎
+----+-----------------------------------------+-------------+-----------+------------+------------+
| id | faculty                                 | established | campus_id | created_at | updated_at |
+----+-----------------------------------------+-------------+-----------+------------+------------+
|  1 | 栄養学部                                |        1966 |         1 | NULL       | NULL       |
|  3 | 経済学部                                |        1967 |         1 | NULL       | NULL       |
|  5 | 人文学部                                |        2004 |         1 | NULL       | NULL       |
|  7 | 総合リハビリテーション学部              |        2005 |         1 | NULL       | NULL       |
| 10 | 心理学部                                |        2018 |         1 | NULL       | NULL       |
+----+-----------------------------------------+-------------+-----------+------------+------------+
5 rows in set (0.00 sec)

mysql>

同じ処理をバックエンドのデータベースを sqlite に戻して(.env を編集して)実行すると,連鎖削除がされていないことが確認できる.

[GakuinHana@rin06 laravelRelationship]$ sqlite3 database/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 campuses; ⏎
1|KAC||
sqlite> select * from faculties; ⏎
1|栄養学部|1966|1||
2|法学部|1967|2||
3|経済学部|1967|1||
4|薬学部|1972|2||
5|人文学部|2004|1||
6|経営学部|2004|2||
7|総合リハビリテーション学部|2005|1||
8|現代社会学部|2014|2||
9|グローバル・コミュニケーション学部|2015|2||
10|心理学部|2018|1||
sqlite> .exit ⏎
[GakuinHana@rin06 laravelRelationship]$

これは,sqlite が標準では外部キー制約が有効になっていないからである.データベースをリセット(ロールバック,マイグレーション,シーダーの実行)してから,sqlite を起動しよう.PRAGMA foreign_keys; で外部キー制約が無効 (0) になっていることが分かる.このためレコードの連鎖削除が働かない.

[GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback; php artisan migrate; php artisan db:seed  ⏎
Rolling back: 2018_06_20_095541_create_lecture_student_table
...(中略)...
Seeding: LectureStudentTableSeeder
[GakuinHana@rin06 laravelRelationship]$ sqlite3 database/database.sqlite  ⏎
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> PRAGMA foreign_keys;  ⏎
0
sqlite> select * from campuses;  ⏎
1|KAC||
2|KPC||
sqlite> select * from faculties;  ⏎
1|栄養学部|1966|1||
2|法学部|1967|2||
3|経済学部|1967|1||
4|薬学部|1972|2||
5|人文学部|2004|1||
6|経営学部|2004|2||
7|総合リハビリテーション学部|2005|1||
8|現代社会学部|2014|2||
9|グローバル・コミュニケーション学部|2015|2||
10|心理学部|2018|1||
sqlite> delete from campuses where id = 2;  ⏎
sqlite> select * from faculties;  ⏎
1|栄養学部|1966|1||
2|法学部|1967|2||
3|経済学部|1967|1||
4|薬学部|1972|2||
5|人文学部|2004|1||
6|経営学部|2004|2||
7|総合リハビリテーション学部|2005|1||
8|現代社会学部|2014|2||
9|グローバル・コミュニケーション学部|2015|2||
10|心理学部|2018|1||
sqlite> .exit  ⏎
[GakuinHana@rin06 laravelRelationship]$

次は,sqlite の外部キー制約を有効にして同じ処理を行ってみよう.今度は連鎖削除が働いていることが分かる.

[GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback; php artisan migrate; php artisan db:seed ⏎
Rolling back: 2018_06_20_095541_create_lecture_student_table
... (中略) ...
Seeding: LectureStudentTableSeeder
[GakuinHana@rin06 laravelRelationship]$ sqlite3 database/database.sqlite ⏎
SQLite version 3.7.17 2013-05-20 00:56:22
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> PRAGMA foreign_keys = ON; ⏎
sqlite> PRAGMA foreign_keys; ⏎
1
sqlite> select * from campuses; ⏎
1|KAC||
2|KPC||
sqlite> select * from faculties; ⏎
1|栄養学部|1966|1||
2|法学部|1967|2||
3|経済学部|1967|1||
4|薬学部|1972|2||
5|人文学部|2004|1||
6|経営学部|2004|2||
7|総合リハビリテーション学部|2005|1||
8|現代社会学部|2014|2||
9|グローバル・コミュニケーション学部|2015|2||
10|心理学部|2018|1||
sqlite> delete from campuses where id = 2; ⏎
sqlite> select * from faculties; ⏎
1|栄養学部|1966|1||
3|経済学部|1967|1||
5|人文学部|2004|1||
7|総合リハビリテーション学部|2005|1||
10|心理学部|2018|1||
sqlite> .exit ⏎
[GakuinHana@rin06 laravelRelationship]$

すなわち,sqlite の場合は明示的に外部キー制約を有効にしなければならないことが分かる.Laravel で sqlite の外部キー制約を有効にするには, app/Providers/AppServiceProvider.php に次のようなコードを追加すれば良い.

app/Providers/AppServiceProvider.php  (抜粋)
<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use DB;  //  <<<<---- 追加

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // sqlite のときだけ外部キー制約を有効にする
        if (\DB::getDriverName() == 'sqlite') {
          DB::statement('PRAGMA foreign_keys=ON');
        }
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }
}

これによって,Laravel の sqlite でも連鎖削除が有効になるはずである.データベースをリセットしてから実験してみよう.「KPC」キャンパスの削除を行った後,学部一覧を確認すると,KAC所属の学部だけになっていることが確認できた.

[GakuinHana@rin06 laravelRelationship]$ php artisan migrate:rollback; php artisan migrate; php artisan db:seed ⏎
edit-campus-9

なお,この例では faculiteis テーブルのマイグレーションで onDelete('cascade') を指定した.CASCADE 以外にも,RESTRICT, SET NULL, NO ACTION が利用可能である.詳細はWebページなどで確認しよう.

目次に戻る