ここでは,キャンパスの個別情報を表示するページを作成する.また,キャンパス情報を更新(変更)するためのフォームも作成する.特に,キャンパスの情報を削除したときに,所属する学部の情報を連鎖的に削除する方法についても解説する.なお,バックエンドのデータベースに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 などへリンクされるようになった.
次は 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);
}
resources/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>
これでキャンパス詳細情報の表示ページができました.
キャンパス詳細情報のページから情報編集のページへリンクを作成しよう.まずはルートの定義を行う.ここでは,編集画面を表示するための定義と,編集画面で「更新」ボタンを押した後の処理を行うための定義を追加しておく.
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);
}
resources/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>
これまでの作業の結果,キャンパス詳細ページの[編集]リンクをクリックすれば,キャンパス名を編集できるフォームが表示されるようになる.
また,「KAC」を「有瀬キャンパス」に書き換えて「更新」ボタンをクリックすれば,その情報が update 関数に渡されて,「request」の「ParameterBag」の中に格納されていることが確認できた(まだ,実際にデータベースを更新をしているわけではないことにも注意しよう).
では実際にデータベースの更新を行う処理を記述しよう.コントローラの 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」を「有瀬キャンパス」に書き換えて「更新」ボタンをクリックすれば,正しく更新できていることが確認できる.
またキャンパス一覧ページでも正しく更新できていることが確認できる.
次は,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」のレコードを削除してみよう.一見うまく削除できたようである.
しかしながら,/faculties のページを開くとエラーになってしまった.なお,バックエンドのデータベースに MySQL を使っている場合は問題が起こりません.Sqlite を使っているとエラーになる.
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 ⏎
なお,この例では faculiteis テーブルのマイグレーションで onDelete('cascade')
を指定した.CASCADE 以外にも,RESTRICT, SET NULL, NO ACTION が利用可能である.詳細はWebページなどで確認しよう.