Search code examples
phplaravelmany-to-manylaravel-novaself-reference

laravel nova - how to make a self referential model with restriction


I have a laravel model based on the following table:

public function up()
{
    Schema::create('things', function (Blueprint $table) {
        $table->id();
        $table->timestamps();
        $table->string('label');
        $table->foreignId('user_id')->nullable()->constrained('users');
    });

There is also a pivot table that makes this a many-to-many self-referential model.

  public function up()
  {
    Schema::create('thing_thing', function (Blueprint $table) {
      $table->id();
      $table->timestamps();
      $table->string('message')->nullable();
      $table->unsignedBigInteger('parent_id')->nullable();
      $table->unsignedBigInteger('child_id')->nullable();
      $table->unique(['parent_id', 'child_id']);
      $table->foreign('parent_id')->references('id')->on('things')->onDelete('cascade');
      $table->foreign('child_id')->references('id')->on('things')->onDelete('cascade');
    });
  }

When I create a Nova resource linked to this model, I would like to restrict the attaching of a thing to itself. So a thing with id = 1, for example, would not show up in the selector for attachments for the thing with id = 1. Here's my Nova resource:

  public function fields(Request $request)
  {
    return [
      ID::make(__('ID'), 'id')->sortable(),
      Text::make('label'),
      ID::make('user_id')->hideWhenUpdating()->hideWhenCreating(),
      BelongsToMany::make('Trees', 'trees'),
      BelongsToMany::make('Things', 'childOf'),
      BelongsToMany::make('Things', 'parentOf')
    ];
  }

Solution

  • You can solve this through the App\Nova\Ressource's relatableQuery method. Simply override the method in your nova resource:

    class Thing extends Resource {
    
        // ...
    
        public static function relatableQuery(NovaRequest $request, $query)
        {
            // Make sure you only apply the filter to the things-things relatable query
            if( $request->route('resource') === 'things' ) {
                $currentId = $request->route('resourceId');
                $query->where('id', '!=', $currentId);
            }
            return $query
        }
    }
    
    

    You can find the docs here

    In addition, you might want to make the column-combination of parent_id and child_id unique in your migration to further ensure uniqueness.