今回は投稿へのタグ付機能を実装したので、自分用の振り返りとして流れを書いていきます。
目次
大まかな流れ
必要なモデルとマイグレーションファイル作成。外部キー設定。
モデルにアソシエーション等の設定。
コントローラー設定。
ビューの設定。
こんな感じの流れを今から説明します。
itemはcrud機能終わってる前提で進めます。
モデルとマイグレーションファイル生成
php artisan make:model Tag --m
上記のような感じでモデルとマイグレーションファイルを作成していく。
こんな感じでitemとitem_tagsテーブルも作成していく。
タグテーブル
public function up()
{
Schema::create('tags', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('name');
$table->timestamps();
});
}
public function up()
{
Schema::create('post_tags', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('post_id');
$table->unsignedBigInteger('tag_id');
$table->timestamps();
$table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade');
$table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade');
});
}
外部キーの設定はいつも通り。
上まで書けたら
php artisan migrate
エラーが出たら下記
php artisan migrate:fresh
モデルにアソシエーションを設定
ここではモデルにアソシエーションを設定したりします。
基本的に多対多の関係をbelongsToManyで書いていきます。
Tagテーブル
class Tag extends Model
{
protected $fillable = ['name'];
public function posts()
{
return $this->belongsToMany('App\Item', 'post_tags'); ここで第2引数に中間テーブル名を入れる。大事。
}
}
Itemテーブル
class Item extends Model
{
public function tags()
{
return $this->belongsToMany('App\Tag', 'post_tags'); ここも
}
}
$fillableで保存を許可するカラムとして追加しておきましょう。itemは色々あると思うのであえて書いてません。
中間テーブル
class post_tags extends Model
{
protected $fillable = ['post_id', 'tag_id'];
}
以上がアソシエーションの設定です。
コントローラーの設定
コントローラーはcreateとupdateで若干違います。
まずはcreateから
use App\Post;
use App\Me;
use App\Tag;
use App\Http\Requests\CreatePost;
上記は必要
public function create(CreatePost $request)
{
$new_post = new Post();
// preg_match_allを使用して#タグのついた文字列を取得している
preg_match_all('/#([a-zA-z0-90-9ぁ-んァ-ヶ亜-熙]+)/u', $request->tags, $match);
$tags = [];
// $matchの中でも#が付いていない方を使用する(配列番号で言うと1)
foreach($match[1] as $tag) {
// firstOrCreateで重複を防ぎながらタグを作成している。
$record = Tag::firstOrCreate(['name' => $tag]);
array_push($tags, $record);
}
$tags_id = [];
foreach($tags as $tag) {
array_push($tags_id, $tag->id);
}
$new_post->content = $request->content;
$new_post->save();
タグはpostがsaveされた後にattachするように。
$new_post->tags()->attach($tags_id);
return redirect()->route('post_index');
}
preg_match_allこれで#のついた文字のみを取得。
$match[1]で$matchの中でも#が付いていない方を使用する(配列番号で言うと1)
tag テーブルにはnameを、post_tagsテーブルにはtag_idを保存するようにしています。(アソシエーション組んでるのでpost_idも入ります)
eval(\Psy\sh());やdd();を使用してでバックしながら行けると良いかと思います。
次はupdate
基本的にほとんど同じ流れですが、updateはattachでは無く、syncを使用します。
public function update(CreatePost $request, $post)
{
$edit_post = Post::find($post);
// preg_match_allを使用して#タグのついた文字列を取得している
preg_match_all('/#([a-zA-z0-90-9ぁ-んァ-ヶ亜-熙]+)/u', $request->tags, $match);
$before = [];
foreach($edit_post->tags as $tag){
array_push($before, $tag->name);
}
$after = [];
foreach($match[1] as $tag){
// 普通に新しいのが来たら新規作成する動き
$record = Tag::firstOrCreate(['name' => $tag]);
array_push($after, $record);
}
$tags_id = [];
foreach($after as $tag) {
array_push($tags_id, $tag->id);
}
$edit_post->tags()->sync($tags_id); ここが重要です。
$edit_post->content = $request->content;
$edit_post->update();
return redirect()->route('post_index');
}
このようにsyncを使用することによって、attachとdetachの昨日プラス存在するのかどうかまで確認してくれます。
重複や、idのずれ、中間テーブルのずれなどが解消されます。
ビューの設定
特に変わったことはしていません。大事なのはname属性でtagsを指定してあげること。これを記述することでコントローラーで受け取れるようになります。
<form action="{{route('post_create')}}" method="POST">
<div class="form-group">
<label for="exampleFormControlInput1">タグ</label>
<input type="text" name="tags" value="{{old('tags')}}">
</div>
<button type="submit" class="btn btn-primary">作成する</button>
</form>
まとめ
attachとsyncが新しく出てきました。モデルでのリレーション関係などが設定できるようになれば、フォロー機能とかも色々できそうですね。
参考にしたサイト
次は画像の複数枚投稿かスケジューラーと触ってみます。
今回はこんな感じで!
以上!