laravel6.*での投稿へのタグ機能実装

2021.01.18

見出し画像

今回は投稿へのタグ付機能を実装したので、自分用の振り返りとして流れを書いていきます。

目次

  1. 大まかな流れ
  2. モデルとマイグレーションファイル生成
  3. モデルにアソシエーションを設定
  4. コントローラーの設定
  5. ビューの設定
  6. まとめ

大まかな流れ

必要なモデルとマイグレーションファイル作成。外部キー設定。

モデルにアソシエーション等の設定。

コントローラー設定。

ビューの設定。

こんな感じの流れを今から説明します。

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が新しく出てきました。モデルでのリレーション関係などが設定できるようになれば、フォロー機能とかも色々できそうですね。

参考にしたサイト

次は画像の複数枚投稿かスケジューラーと触ってみます。

今回はこんな感じで!

以上!