Morphto can't save in morph table and depend on the main table , "Unknown Column 'itemable_model' in Polymorphic Relationship with Laravel Nova"
Closed this issue · 0 comments
salmazz commented
Laravel Version:
10.10
Nova Version:
4.0
PHP Version:
8.1
Database Driver & Version:
MySQL 8.0
Operating System and Version:
macOS
Browser Type and Version:
Chrome 101
Description:
I'm encountering an issue with polymorphic relationships in Laravel Nova where the itemable_model
column is not found in the cardables
table, leading to a SQL error. The exact error message is:
PDOException(code: 42S22): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'itemable_model' in 'field list'
Detailed Steps to Reproduce the Issue on a Fresh Nova Installation:
-
Create Migrations:
- Migration for
cards
table:
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('cards', function (Blueprint $table) { $table->id(); $table->json('title'); $table->json('body'); $table->string('avatar_icon')->nullable(); $table->string('background_color', 7)->nullable(); $table->string('artwork', 255)->nullable(); $table->string('card_design_type')->comment('basic, elevated, or narrow'); $table->string('cta_type'); $table->string('cta_content'); $table->string('cta_design')->nullable()->comment('box, underline'); $table->string('cta_action'); $table->unsignedBigInteger('campaign_id'); $table->foreign('campaign_id')->references('id')->on('campaigns')->onDelete('cascade'); $table->boolean('is_active')->default(0); $table->timestamps(); }); } public function down() { Schema::dropIfExists('cards'); } };
- Migration for
cardables
table:
use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up() { Schema::create('cardables', function (Blueprint $table) { $table->id(); $table->unsignedBigInteger('itemable_id'); $table->string('itemable_model'); // Ensure this column exists $table->unsignedBigInteger('card_id'); $table->unsignedBigInteger('screen_id'); $table->foreign('screen_id')->references('id')->on('screens')->onDelete('cascade'); $table->foreign('card_id')->references('id')->on('cards')->onDelete('cascade'); $table->timestamps(); }); } public function down() { Schema::dropIfExists('cardables'); } };
- Migration for
-
Define Models:
- Card Model:
namespace Modules\Card\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphTo; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Modules\Category\Models\Category; use Spatie\Translatable\HasTranslations; class Card extends Model { use HasTranslations; protected $fillable = [ 'title', 'body', 'avatar_icon', 'background_color', 'artwork', 'card_design_type', 'cta_type', 'cta_content', 'cta_design', 'cta_action', 'campaign_id', 'is_active', ]; protected $translatable = [ 'title', 'body', 'cta_content' ]; protected $casts = [ 'title' => 'json', 'body' => 'json', 'cta_content' => 'json', 'is_active' => 'bool' ]; public function campaign() { return $this->belongsTo(Campaign::class); } public function itemable(): MorphTo { return $this->morphTo('itemable', 'itemable_model', 'itemable_id'); } public function categories(): MorphToMany { return $this->morphedByMany(Category::class, 'itemable', 'cardables'); } public function screen() { return $this->belongsTo(Screen::class); } }
- Category Model:
namespace Modules\Category\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\MorphToMany; use Modules\Card\Models\Card; use Modules\HomeSection\Models\Item; class Category extends Model { use HasFactory; protected $primaryKey = 'cat_id'; public const CREATED_AT = 'date_created'; public const UPDATED_AT = 'date_modified'; protected $fillable = []; protected static function newFactory() { return \Modules\Category\Database\factories\CategoryFactory::new(); } public function items() { return $this->morphMany(Item::class, 'itemable'); } public function cards(): MorphToMany { return $this->morphToMany(Card::class, 'itemable', 'cardables'); } }
-
Define Nova Resource:
- Card Nova Resource:
namespace Modules\Card\Resources; use App\Nova\Resource; use Illuminate\Http\Request; use Laravel\Nova\Fields\BelongsTo; use Laravel\Nova\Fields\Boolean; use Laravel\Nova\Fields\Color; use Laravel\Nova\Fields\ID; use Laravel\Nova\Fields\MorphTo; use Laravel\Nova\Fields\Select; use Laravel\Nova\Fields\Text; use Laravel\Nova\Fields\Textarea; use Laravel\Nova\Fields\URL; use Modules\Card\Enums\CardDesignTypeEnum; use Modules\Card\Enums\CtaDesignEnum; use Modules\Card\Enums\CtaTypeEnum; use Modules\Category\Resources\Category; class Card extends Resource { public static $model = \Modules\Card\Models\Card::class; public static $title = 'title'; public static $search = [ 'title', 'card_design_type', 'cta_type', 'cta_content', 'cta_action', 'is_active' ]; public static $group = 'CARD'; public function fields(Request $request) { return [ ID::make()->sortable(), Text::make('Title', 'title') ->rules('required') ->translatable(), Textarea::make('Body', 'body') ->rules('required') ->hideFromIndex() ->translatable(), Select::make('Card Design Type', 'card_design_type') ->rules('required') ->searchable() ->options(getEnumOptionsAsArray(CardDesignTypeEnum::cases())) ->displayUsingLabels(), Color::make('Background Color', 'background_color') ->rules('required') ->hideFromIndex(), URL::make('Artwork', 'artwork') ->rules('required') ->hideFromIndex(), URL::make('Avatar Icon') ->hideFromIndex(), Select::make('CTA Type', 'cta_type') ->rules('required') ->searchable() ->options(getEnumOptionsAsArray(CtaTypeEnum::cases())) ->displayUsingLabels(), Textarea::make('CTA Content', 'cta_content') ->rules('required') ->hideFromIndex() ->translatable(), Text::make('CTA Action', 'cta_action') ->rules('required') ->hideFromIndex(), Select::make('CTA Design', 'cta_design') ->rules('required') ->options(getEnumOptionsAsArray(CtaDesignEnum::cases())) ->hideFromIndex(), BelongsTo::make('Campaign', 'campaign', Campaign::class), MorphTo::make('Itemable', 'itemable') ->types([ Category::class, // Add other polymorphic types here if needed ])->hideFromIndex(), BelongsTo::make('Screen', 'screen', Screen::class) ->display('name') ->rules('required') ->hideFromIndex(), Boolean::make('Active', 'is_active'), ]; } public function cards(Request $request) { return []; } public function filters(Request $request) { return []; } public function lenses(Request $request) { return []; } public function actions(Request $request) { return []; } }
-
Run Migrations:
php artisan migrate
Expected Outcome:
The polymorphic relationships should be set up correctly, and data should be inserted into the cardables
table without any SQL errors.
Actual Outcome:
I encountered the following error:
PDOException(code: 42S22): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'itemable_model' in 'field list'