LoginSignup
0
2

実行環境

framework Laravel(10.20.0)

本記事で得られる結果

  • ClosureTable(閉包テーブル)をModelとした実装例

経緯

コメント機能実装時に編集や削除を考慮し、閉包テーブルとしようと思ったところ、当該ライブラリを見つけた。

使ってみるとあっさりとした実装ができて楽だったのでご紹介します。

(似たような記事がたくさんありますが、自己学習のためですのでお許しを)

参考資料

事前準備

  • Laravelの環境構築済みであること
    • Laravel Sail を利用する

手順

  1. 公式の通りにインストール

    Composerを使ってインストール

    sail composer require franzose/closure-table
    

    config/app.phpに追加 (基本は自動で追記されているはず)

    config/app.php
    return [
        'providers' => [
    +        Franzose\ClosureTable\ClosureTableServiceProvider::class
        ]
    ];
    
  2. Modelの作成

    sail artisan closuretable:make Node
    
    • Node はモデル名なので変更可
    • 出力先のパスはオプションで指定可能です
    • 以下が自動で作成されます
      • Node (Model)
      • NodeClosure (Model)
      • 2023_00_00_000000_create_nodes_table_migration (Migration)

    なぜか、マイグレーションファイルにSchemaの定義が無いので追加しました

    2023_00_00_000000_create_nodes_table_migration.php
        use Illuminate\Database\Schema\Blueprint;
        use Illuminate\Database\Migrations\Migration;
    +   use Illuminate\Support\Facades\Schema;
    

    さらに、必要なカラムを追加してあげてください

    2023_00_00_000000_create_nodes_table_migration.php
    Schema::create('nodes', function (Blueprint $table) {
            $table->increments('id');
    +       $table->string('content');
    +       $table->integer('parent_model_id');
            $table->integer('parent_id')->unsigned()->nullable();
            $table->integer('position', false, true);
            $table->softDeletes();
    
    +       $table->foreign('parent_model_id')
    +           ->references('id')
    +           ->on('parents');
            ...
        });
    

    もし、他のテーブルを参照している場合にはモデルに関係を追加します

    Node.php
    /* Entityはラッパークラスであることに注目したい */
    use Franzose\ClosureTable\Models\Entity;
    
    class Node extends Entity
    {
        ...
    
    +   public function ParentModel() : BelongsTo
    +   {
    +       return $this->belongsTo(ParentModel::class, 'parent_model_id');
    +   }
    }
    
  3. 使い方

    あとは、それぞれ色々と操作できます

    $nodes = [
        new Node(['id' => 1]),
        new Node(['id' => 2]),
        new Node(['id' => 3]),
        new Node(['id' => 4, 'parent_id' => 1])
    ];
    
    /** 取得 */
    Node::find(1)->isRoot(); // 自身は親であり、自身の親はいない
    Node::find(1)->isParent(); // 自身は親である
    
    Node::find(4)->hasAncestors(); // 自身の先祖がいる
    Node::find(4)->getAncestors(); // 自身の先祖
    Node::find(4)->ancestorsWithSelf() // 自身と先祖
    
    Node::find(1)->hasDescendants(); // 自身の子孫がいる
    Node::find(1)->getDescendants() // 自身の子孫
    Node::find(1)->descendantsWithSelf() // 自身と子孫
    
    Node::find(1)->hasChildren(); // 子がいるか
    Node::find(1)->getChildren(); // 子のみ
    
    // 他にもConutやNext, Prev, 兄弟の操作も可能
    
    /** 追加、削除 */
    Node::find(1)->addChild(new Node(['id' => 5])); // 子を追加
    Node::find(1)->removeChild(4); // 子を削除
    
    // 一括で追加(addChildren)、削除(removeChildren)も可能
    

おまけ

これを仕組みから手作業で実装すると、冗長だし、面倒なので、非常に便利でした

その前に設計段階で投入する決断ができることのほうが大事かも

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2