Laravel5のEloquentORMで1対多のリレーション

さて、今日からLaravel5のEloquentORMでリレーションを構築する方法を調べてみましょう。コレクション管理アプリの初日にスキーマビルダーでcategoriesテーブルを作成したのですが、全く使用していませんでした。そのcategoriesテーブルとgathersテーブルとのリレーションを構築していきます。

categoryモデルの生成

コマンドプロンプトのartisanコマンドでcategoryモデルを生成します。

php artisan make:model Category

生成されたモデルを下記のように修正します。

app/Category.php

<?php namespace App;

use Illuminate\Database\Eloquent\Model;

class Category extends Model {
 
 //複数代入のホワイトリスト
 protected $fillable=['name','description'];

 //自動タイムスタンプの無効化
 public $timestamps=false;
 
 /* 
 * 1対多のリレーション
 * Categoryは多くのGatherを持っています
 */
 public function Gather(){
 return $this->hasMany('Gather');
 }
}

11行目:カテゴリテーブルには、created_atとupdated_atカラムは作成していませんので、自動タイムスタンプを無効にします。

17-19行目:1対多のリレーションを設定します。第2引数は、外部キーを指定します。外部キーがcategory_id(テーブル名の単数_id)の場合は、省略できます。第3引数には、内部キーを指定します。内部キーがidの場合は省略できます。

gatherモデルの修正

gatherモデルも修正します。

<?php namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Gather extends Model {
 
 //複数代入のブラックリスト
 protected $guarded=['id','created_at','updated_at','deleted_at'];
 
 //ソフトデリート
 use SoftDeletes;
 protected $dates=['deleted_at'];
 
 /* 
 * 1対多のリレーション
 * GatherはCagegoryに属しています
 */
 public function category(){
 return $this->belongsTo('App\Category');
 }
}

17-19行目:categoryモデルに従属関係のリレーションを設定します。

createアクションの修正

カテゴリの作成は、カテゴリ作成専用のページではなく、コレクション(gather)の新規作成のページから作成したいと思いますので、createアクションに、カテゴリのリストを作成するために下記のように修正します。

public function getCreate()
{
 $categories=Category::lists('name','id');
 return view('gathers.create',compact('categories'));
}
public function postCreate(GathersPostRequest $request)
{
 $gather=Gather::create($request->all());
 $gather->category_id=$request->category_id;
 $gather->save();
 return redirect()->to("gathers");
}

createビューの修正

createビューを下記のように修正します。

app/resources/views/category/create.blade.php

@extends('app')
@section('content')
<div class="container">
 <h2>新規作成</h2>
 @if($errors->all())
 @foreach($errors->all() as $error)
 <p class="alert alert-danger">
 {{ $error }}
 </p>
 @endforeach
 @endif
 {!! Form::open(["class"=>"form-horizontal"]) !!}
 <div class="form-group">
 {!! Form::label("category_id","カテゴリ",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::select("category_id",$categories) !!}
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("category","カテゴリの作成",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 <a href="#" data-toggle="modal" data-target="#category" class="btn btn-warning">カテゴリの新規作成</a>
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("name","名前",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::text("name","",["class"=>"form-control"]) !!}
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("code","Code",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::text("code","",["class"=>"form-control"]) !!}
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("description","説明",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::textarea("description","",["class"=>"form-control"]) !!}
 </div>
 </div>
 <div class="form-group">
 <div class="col-sm-10">
 {!! Form::submit("新規作成",["class"=>"btn btn-primary col-sm-offset-2"]) !!}
 </div>
 </div>
{!! Form::close() !!}
 <div class="modal fade" id="category">
 <div class="modal-dialog">
 <div class="modal-content">
 <div class="modal-header">
 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 <h4 class="modal-title alert alert-success">新規カテゴリの作成</h4>
 </div>
 {!! Form::open(['url'=>'gathers/category-create']) !!}
 <div class="modal-body">
 {!! Form::label("name","カテゴリ名*",["class"=>"control-label"]) !!}
 {!! Form::text('name','',["class"=>"form-control","required"=>"required"]) !!}
 {!! Form::label("description","説明",["class"=>"control-label"]) !!}
 {!! Form::text('description','',["class"=>"form-control"]) !!}
 </div>
 <div class="modal-footer">
 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
 <input type="submit" class="btn btn-primary" value="作成">
 {!! Form::close() !!}
 </div>
 </div><!-- /.modal-content -->
 </div><!-- /.modal-dialog -->
 </div><!-- /.modal -->
</div>
@endsection

13-18行目:カテゴリリストの表示のための行です。

19-24行目:49行目のモーダルページへのリンクです。

49-62行目:モーダルページです。

59行目:入力はカテゴリ名だけですので、Laravelのバリデーションは使用せず、HTML5のrequired属性を使用します。

postCategorycreateの作成

次に、Gathersコントローラにカテゴリ作成用のアクションメソッドを作成します。

app/Html/Controllers/GatherController.php

use App\Category ;

public function postCategoryCreate(Request $request){
 $category=Category::firstOrCreate($request->only('name'));
 $category->description=$request->description ?: null;
 $category->save();
 return redirect()->back();
}

2行目:$request->only(‘name’,’description’)や$request->except(‘_token’)等は、記述しないでください。こう記述した場合、同じカテゴリ名でもdescription(説明)が違っていれば、複数登録されてしまいます。ですので、いったんolny(‘name’)で、nameだけがユニークのオブジェクトを作成して、3行目でdescriptionを登録しています。

ブラウザでカテゴリの新規作成ボタンをクリックしてみます。

2015-05-24 7-11-04

これでカテゴリを登録することができます。

新規データの作成

それでは、新規にコレクションデータを登録してみましょう。

2015-05-24 8-05-20

下記のようにカテゴリidも登録されました。

2015-05-24 8-32-19

updateビューの修正

既存のデータのcategory_idのデータを変更するために、updateビューも修正します。

app/resources/views/update.blade.php

@extends('app')
@section('content')
<div class="container">
 <h2>修正</h2>
 @if($errors->all())
 @foreach($errors->all() as $error)
 <p class="alert alert-danger">
 {{ $error }}
 </p>
 @endforeach
 @endif
 {!! Form::open(["class"=>"form-horizontal"]) !!}
 <div class="form-group">
 {!! Form::label("category_id","カテゴリ",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::select("category_id",$categories,$gather->category_id) !!}
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("category","カテゴリの作成",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 <a href="#" data-toggle="modal" data-target="#category" class="btn btn-warning">カテゴリの新規作成</a>
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("name","名前",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::text("name",$gather->name,["class"=>"form-control"]) !!}
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("code","Code",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::text("code",$gather->code,["class"=>"form-control"]) !!}
 </div>
 </div>
 <div class="form-group">
 {!! Form::label("description","説明",["class"=>"col-sm-2 control-label"]) !!}
 <div class="col-sm-10">
 {!! Form::text("description",$gather->description,["class"=>"form-control"]) !!}
 </div>
 </div>
 <div class="form-group">
 <div class="col-sm-10">
 {!! Form::hidden("id",$gather->id) !!}
 {!! Form::submit("修正",["class"=>"btn btn-primary col-sm-offset-2"]) !!}
 </div>
 </div> 
{!! Form::close() !!}
 <div class="modal fade" id="category">
 <div class="modal-dialog">
 <div class="modal-content">
 <div class="modal-header">
 <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
 <h4 class="modal-title alert alert-success">新規カテゴリの作成</h4>
 </div>
 {!! Form::open(['url'=>'gathers/category-create']) !!}
 <div class="modal-body">
 {!! Form::label("name","カテゴリ名*",["class"=>"control-label"]) !!}
 {!! Form::text('name','',["class"=>"form-control","required"=>"required"]) !!}
 {!! Form::label("description","説明",["class"=>"control-label"]) !!}
 {!! Form::text('description','',["class"=>"form-control"]) !!}
 </div>
 <div class="modal-footer">
 <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
 <input type="submit" class="btn btn-primary" value="作成">
 {!! Form::close() !!}
 </div>
 </div><!-- /.modal-content -->
 </div><!-- /.modal-dialog -->
 </div><!-- /.modal -->
</div>
@endsection

13-24行目と50-71行目を追加しています。

updateアクションの修正

そして、updateアクションも修正します。

app/Http/Controllers/GatherController.php

public function getUpdate($id)
 {
 $categories=Category::lists('name','id');
 $gather=Gather::find($id);
 return view("gathers.update")
 ->with('gather',$gather)
 ->with('categories',$categories);
 }
 
 public function postUpdate(GathersPostRequest $request){
 $gather=Gather::find($request->id);
 $gather->name=$request->name;
 $gather->category_id=$request->category_id;
 $gather->code=$request->code;
 $gather->description=$request->description;
 $gather->save();
 return redirect()->to("gathers");
 }

3行目:カテゴリのリストを生成していますが、リレーションしていますので、この行と7行目は必要ありません。$gatherオブジェクトのみをビューに送れば、リストはビューで生成することができます。上記updateビューは、下記のようにもすることができます。

{!! Form::select(“category_id”,$categorie,$gather->category_id) !!}

の$categoriesを下記のようにも記述できます。

$gather->category->lists(‘name’,’id’)

これがリレーションの醍醐味です。

これで、既存データの全てにカテゴリを割り当てることができます。

本日のポイント

  1. 自動タイムスタンプを使用しない場合は無効に設定する
    • public $timstamps=false;
  2. 1対多のリレーションはhasManyを使用する。
    • public function Gather(){
      return $this->hasMany(‘Gather’);
      }
  3. hasManyメソッドの第2引数は、外部キー、第3キーは内部キーを指定する。
  4. リレーションの配下のデータには、foreachで回しながらアクセスする
  5. 1対多のリレーションの従属は、belongsToを使用する
    • public function category(){
      return $this->belongsTo(‘App\Category’);
      }
  6. リレーションの従属先には、$gather->category->プロパティ又は、メソッドでアクセスできる
  7. listsメソッドでリストを作成する
  8. listsメソッドの第1引数は、値、第2引数は、キーを指定する

本日は、以上です。次回は、1対多のリレーションの表示方法を調べてみます。

このエントリーを含むはてなブックマーク Buzzurlにブックマーク livedoorクリップ Yahoo!ブックマークに登録

トラックバック&コメント

この投稿のトラックバックURL:

コメントをどうぞ

このページの先頭へ