基本的な構造は前回をベースとします。
Laravel5.4でシンプルなCMSを作るチュートリアル
Laravel 5.4
laravelcollective/html 5.4
を使用します。
One To Many(1対多)
Postは一つのCategoryに属している。
Categoryは複数のPostを持っている。
というのを例にやってみます。
テーブルの設定をします。
リレーションは「リレーション先のモデル名_id」でカラムを作成します。
posts
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->integer('category_id')->nullable();
$table->timestamps();
});
categories
Schema::create('categories', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
});
モデルの設定
一つのCategoryに属しているPostモデルからリレーションの定義をしてみます。
categoryメソッドにbelongsToを指定します。
app/Post.php
class Post extends Model
{
public function category() {
return $this->belongsTo(Category::class);
}
}
Categoryは複数のPostを持っているのでhasManyを指定します。
app/Category.php
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クエリが発行されパフォーマンスが低下に繋がります。
これはコントローラーを下記のようにすることで解消できます。
app/Http/Controllers/Admin/PostsController.php
$posts = Post::with('category')->orderBy('id', 'desc')->paginate(20);
フォームの作成
関連付けするフォームも作成してみます。
Post側から一つのCategoryを選択したいので、セレクトボックスかラジオボタンがよさそうです。
ビューを表示する前にコントローラーでカテゴリー情報を渡します。
pluckでidとtitleだけにしてtoArrayで配列にします。
app/Http/Controllers/Admin/PostsController.php
$categories = Category::pluck('title', 'id')->toArray();
return view('admin.posts.create', compact('categories'));
これでcategoriesを展開してフォームを作成しましょう。
resources/views/admin/posts/fields.blade.php
{{-- ラジオボタン --}}
@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テーブルを作成します。
tags
Schema::create('tags', function (Blueprint $table) {
$table->increments('id');
$table->string('slug');
$table->string('title');
});
post_tag
Schema::create('post_tag', function (Blueprint $table) {
$table->increments('id');
$table->integer('post_id');
$table->integer('tag_id');
});
モデルの設定
多対多はbelongsToManyを指定します。
中間テーブルのモデルは必要ありません。
app/Post.php
class Post extends Model
{
public function tags()
{
return $this->belongsToMany(Tag::class);
}
}
app/Tag.php
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>
フォームの作成
複数関連付けの場合フォームはチェックボックスがよさそうです。
コントローラーでタグを配列で渡します。
app/Http/Controllers/Admin/PostsController.php
$tags = Tag::pluck('title', 'id')->toArray();
return view('admin.posts.create', compact('tags'));
resources/views/admin/posts/fields.blade.php
@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);
色々間違ってるかもしれませんが以上です。
リレーションは他にもありますが、とりあえずここさえ押さえておけばなんとかなるのかなとか。

