Laravel polymorphic relation in Nova

Viewed 2750

My current blog like app has a Page that could have many Paragraphs of a different structure (Text, Image, File download, Registration form, ... ). When trying to translate this to Eloquent Models with relations, I think this is an easy way to do so:

Table pages:

  • id
  • title, ...

Table paragraphs:

  • id
  • page_id
  • paragraphable_id
  • paragraphable_type

Table paragraph_texts:

  • id
  • text

Table paragraph_images:

  • id
  • image_path

Table paragraph_downloads:

  • id
  • file_path

And the Models:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Page extends Model
{

    public function paragraphs() {
        return $this->hasMany(Paragraph::class);
    }
}

?>

The Paragraph Model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Paragraph extends Model
{
    public $table = 'paragraphs';
    public $timestamps = [];

    public function page() {
        return $this->belongsTo(Page::class);
    }

    public function paragraphable() {
        return $this->morphTo();
    }
}

The Text Type model as sample:

<?php

namespace App\Models\ParagraphTypes;


use Illuminate\Database\Eloquent\Model;

class Text extends Model
{
    protected $table = 'paragraph_texts';

    public function paragraph() {
        return $this->morphOne(Paragraph::class, 'paragraphable');
    }

}

I now want to view the page in nova and create a new paragraph by a selectable type. I created the Resource classes (for page, for paragraph and for each paragraph type) according to the eloquent relations (HasMany field for the page, MorphTo for the paragraph and just a TextArea for the Text type). When I see the details of a page and want to add a new paragraph, I can see the form to add a paragraph and can select the paragraph type in a dropdown but can only see the already existing entries, not new one. I will never want to add an existing paragraph type like this.

So questions:

  • Is the transformation of the structure into eloquent correct, are there any improvements?
  • How could laravel nova handle the creation of a new paragraph type for a page "through" the paragraph model? I feel like having to add a custom field to do so, that maybe asks for the type to create first...
1 Answers

Regarding your first question: You should use the relationships from the documentation:

class Page extends Model
{
    public function texts() {
        return $this->morphedByMany(Text::class, 'paragraphable', 'paragraphs');
    }
}

class Text extends Model
{
    public function pages() {
        return $this->morphToMany(Page::class, 'paragraphable', 'paragraphs');
    }
}

You can get multiple paragraph types with an accessor:

class Page extends Model
{
    public function getParagraphsAttribute() {
        return $this->texts->toBase()->merge($this->files)->merge([...]);
    }
}

$paragraphs = $page->paragraphs;
Related