mstdmstd's avatar

Why form with Repeater field on a tab raise error on save?

IN laravel 10 app I have 3 related tables :

Schema::create('products', function (Blueprint $table) {
    $table->id();
    $table->string('title', 255);
...

Schema::create('categories', function (Blueprint $table) {
    $table->smallIncrements('id')->unsigned();
    $table->string('name',50)->unique();
...

Schema::create('category_product', function (Blueprint $table) {
    $table->id();
    $table->foreignId('product_id')->references('id')->on('products')->onDelete('CASCADE');

    $table->unsignedSmallInteger('category_id')->unsigned();
    $table->foreign('category_id')->references('id')->on('categories')->onUpdate('RESTRICT')->onDelete('CASCADE');
...

Model app/Models/Product.php has relation :

class Product extends Model
{
    use Sluggable;

    use HasFactory;

    protected $table = 'products';
    protected $primaryKey = 'id';
    public $timestamps = false;

    public function categories(): BelongsToMany
    {
        return $this->belongsToMany(Category::class);
    }
    ...

And model app/Models/Product.php has relation :

public function products(): BelongsToMany
{
    return $this->belongsToMany(Product::class);
}

Reading manuals

I made component with command :

php artisan nova:repeatable ProductCategories

and in file app/Nova/Repeater/ProductCategories.php I added select field :

<?php

namespace App\Nova\Repeater;

use App\Models\Category;
use Laravel\Nova\Fields\Repeater\Repeatable;
use Laravel\Nova\Fields\Select;
use Laravel\Nova\Http\Requests\NovaRequest;
use Laravel\Nova\Fields\ID;

class ProductCategories extends Repeatable
{
    public function fields(NovaRequest $request)
    {
        $categories = Category::get()->pluck('name', 'id');
        return [
            ID::make(),

            Select::make(__('Category'), 'category_id')
                ->options($categories)
                ->rules('required')
                ->displayUsingLabels()
                ->searchable(),

        ];

    }
}

and use it on separate tab of :

namespace App\Nova;

...
use Laravel\Nova\Http\Requests\NovaRequest;
use ShuvroRoy\NovaTabs\Tab;
use ShuvroRoy\NovaTabs\Tabs;
use ShuvroRoy\NovaTabs\Traits\HasActionsInTabs;
use ShuvroRoy\NovaTabs\Traits\HasTabs;
use Laravel\Nova\Fields\Repeater;
use App\Nova\Repeater\ProductCategories;

class Product extends Resource
{
    use HasTabs, HasActionsInTabs;

    public function fields(NovaRequest $request)
    {
        return [
            Tabs::make('Product', [
                Tab::make(__('Categories'), [
                    Repeater::make('Product Categories')
                        ->repeatables([
                            ProductCategories::make(),
                        ])
                ]), // Product Categories Tab
            ...

On the "Categories" tab I have Repeater field with valid options for selection, but :

  1. If I open product editor with rows category_product the Repeater component is empty, but I expected it would be filled with data from category_product table.

  2. when I add row in Repeater and select some option on save I got error :

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'product_categories' in 'field list' (Connection: mysql, SQL: update products set published_at = 2024-09-13 00:00:00, product_categories = [{"type":"product-category","fields":{"category_id":"3"}}], .`user_id` = 4 where `id` = 1) {"userId":1,"exception":"[object] (Illuminate\\Database\\QueryException(code: 42S22): SQLSTATE[42S22]: Column not found: 1054 Unknown column 'product_categories' in 'field list' (Connection: mysql, SQL: update `products` set `published_at` = 2024-09-13 00:00:00, `product_categories` = [{\"type\":\"product-category\",\"fields\":{\"category_id\":\"3\"}}], .user_id = 4 where id = 1) at /mnt/_work_sdb8/wwwroot/lar/NovaProducts/vendor/laravel/framework/src/Illuminate/Database/Connection.php:829)

I expected the Repeater would be saved as separate table, but not as field of product table What is wrong in my code ?

0 likes
0 replies

Please or to participate in this conversation.