基本的な構造は前回をベースとします。
Laravel5.4でシンプルなCMSを作るチュートリアル
Laravel 5.4
laravelcollective/html 5.4
を使用します。
One To Many(1対多)
Postは一つのCategoryに属している。
Categoryは複数のPostを持っている。
というのを例にやってみます。
テーブルの設定をします。
リレーションは「リレーション先のモデル名_id」でカラムを作成します。
Schema::create('posts', function (Blueprint $table) { $table->increments('id'); $table->string('title'); $table->text('body'); $table->integer('category_id')->nullable(); $table->timestamps();});Schema::create('categories', function (Blueprint $table) { $table->increments('id'); $table->string('title');});モデルの設定
一つのCategoryに属しているPostモデルからリレーションの定義をしてみます。
categoryメソッドにbelongsToを指定します。
class Post extends Model{ public function category() { return $this->belongsTo(Category::class); }}Categoryは複数のPostを持っているのでhasManyを指定します。
class Category extends Model{ public function posts() { return $this->hasMany(Post::class); }}ビューの作成
post側のビューでは$post->categoryのようにすると所属しているカテゴリーが表示できます。
{{ $post->category->title }}詳細画面のように一つ表示なら問題ないのですが一覧ページはforeachを使用して表示しますよね。
@foreach($posts as $post) {{ $post->category->title }}@endforeachこのような場合はN+1問題といって複数SQLクエリが発行されパフォーマンスが低下に繋がります。
これはコントローラーを下記のようにすることで解消できます。
$posts = Post::with('category')->orderBy('id', 'desc')->paginate(20);フォームの作成
関連付けするフォームも作成してみます。
Post側から一つのCategoryを選択したいので、セレクトボックスかラジオボタンがよさそうです。
ビューを表示する前にコントローラーでカテゴリー情報を渡します。
pluckでidとtitleだけにしてtoArrayで配列にします。
$categories = Category::pluck('title', 'id')->toArray();return view('admin.posts.create', compact('categories'));これでcategoriesを展開してフォームを作成しましょう。
{{-- ラジオボタン --}}@foreach ($categories as $key => $category) <label class="radio-inline"> {!! Form::radio('category_id', $key) !!} {{ $category }} </label>@endforeach
{{-- セレクトボックス --}}{{ Form::select('category_id', $categories, isset($post->category->id) ? $post->category->id : null )}}Many To Many(多対多)
Postは複数のTagに持っている。
Tagは複数のPostを持っている。
というのを例にやってみます。
最初にテーブルの設定します。
One To Many で作成したpostsテーブルの他にtagsと2つを繋ぐ中間テーブルのpost_tagテーブルを作成します。
Schema::create('tags', function (Blueprint $table) { $table->increments('id'); $table->string('slug'); $table->string('title');});Schema::create('post_tag', function (Blueprint $table) { $table->increments('id'); $table->integer('post_id'); $table->integer('tag_id');});モデルの設定
多対多はbelongsToManyを指定します。
中間テーブルのモデルは必要ありません。
class Post extends Model{ public function tags() { return $this->belongsToMany(Tag::class); }}class Tag extends Model{ public function posts() { return $this->belongsToMany(Post::class); }}ビューの作成
post側のビューでは$post->tagsのようにするとタグを表示できますが、カテゴリーと違い複数持ってるのでforeachなどを使用して表示します。
<p>タグ:</p><ul>@foreach($post->tags as $tag) <li>{{ $tag->title }}</li>@endforeach</ul>フォームの作成
複数関連付けの場合フォームはチェックボックスがよさそうです。
コントローラーでタグを配列で渡します。
$tags = Tag::pluck('title', 'id')->toArray();return view('admin.posts.create', compact('tags'));@foreach ($tags as $key => $tag) @php $isCheck = false; if (isset($post->tags) && $post->tags->contains($key)) { $isCheck = true; } @endphp <label class="checkbox-inline"> {!! Form::checkbox( 'tag_id[]', $key, $isCheck) !!} {{ $tag }} </label>@endforeachこんな感じになりましたが、多分もっと簡潔にできる方法があるはず。
コントローラーの作成
1対多は基本的に一つのテーブルで完結していたので、コントローラーは基本そのまま使えたのですが、多対多の場合は中間テーブルがあるので、連動して更新する必要がありコントローラーも修正します。
まずは新規作成のstoreメソッド。
最後にattachすることでタグが作成されます。
$tags = $request->input('tag_id', []);unset($request['tag_id']);$post = Post::create($request->all());$post->tags()->attach($tags);}updateメソッドです。
syncでタグも更新されます。
$post = Post::findOrFail($id);$post->tags()->sync($request->input('tag_id', []));unset($request['tag_id']);$post->update($request->all());destroyメソッドです。
削除はdetachです。
$post = Post::findOrFail($id);$post->tags()->detach();$post->delete($id);色々間違ってるかもしれませんが以上です。
リレーションは他にもありますが、とりあえずここさえ押さえておけばなんとかなるのかなとか。


