koss-shtukert / laravel-nova-select2-auto-complete

Laravel Nova Select2 Auto-Complete
https://novapackages.com/packages/koss-shtukert/laravel-nova-select2-auto-complete
MIT License
38 stars 11 forks source link

SQL error when creating using multiple select for many-to-many #11

Closed unarmedwombat closed 5 years ago

unarmedwombat commented 5 years ago

I have a product model which can belong to one or more categories (many to many). I'm using select2 with the multiple option, but when I try to create a new product it responds with an integrity violation on the category_product model, saying the product_id can't be null. Looking at the trace, it appears to be trying to sync the category link before it stores the new product record? The field works fine on update, its just creating that has the problem. It's possible I've missed something, but I think I've tried the example code exactly as in the read.me

koss-shtukert commented 5 years ago

Hi @unarmedwombat , could you provide your code?

unarmedwombat commented 5 years ago

Here is the Nova controller fields method:

public function fields(Request $request)
{
    $categories = Category::all()->pluck('full_name', 'id');
    return [
        ID::make()->hideFromIndex(),
        Text::make('Code')
            ->sortable()
            ->creationRules(['required', 'unique:products,code', 'max:255'])
            ->updateRules(['required', 'max:255']),
        Text::make('Name')
            ->sortable()
            ->rules(['required', 'max:255']),
        Trix::make('description'),
        Select2::make('Categories', 'categoryList')
            ->hideWhenCreating()
            ->options($categories)
            ->configuration([
                'allowClear' => true,
                'minimumResultsForSearch' => 1,
                'multiple' => true,
                'placeholder' => 'Choose one or more categories',
            ]),
        new Panel('Images', $this->imagesPanel()),
        new Panel('Visibility', $this->displayPanel()),
        new Panel('Other data', $this->otherPanel()),
    ];
}

And the Categories model:

class Category extends Model implements HasMedia, Sortable { use HasMediaTrait, SortableTrait;

protected $appends = ['full_name'];

public $sortable = [
    'order_column_name' => 'order',
    'sort_when_creating' => true,
];

public function registerMediaCollections()
{
    $this->addMediaCollection('main_image')
        ->singleFile();
}

public function registerMediaConversions(Media $media = null)
{
    $this->addMediaConversion('thumb')
        ->fit(Manipulations::FIT_CONTAIN, 250, 250);
}

public function section()
{
    return $this->belongsTo(Section::class);
}

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

public function getFullNameAttribute()
{
    $section = Section::find($this->attributes['section_id'])->name;
    return $section . " - " . $this->attributes['name'];
}

} Hope this is what you need, if not, let me know. Thanks.

unarmedwombat commented 5 years ago

Sorry , meant to include the product model:

class Product extends Model implements HasMedia {

use HasMediaTrait;
public function registerMediaCollections()
{
    $this->addMediaCollection('main_image')
        ->singleFile();
    $this->addMediaCollection('other_images');
    $this->addMediaCollection('documents');
}

public function registerMediaConversions(Media $media = null)
{
    $this->addMediaConversion('thumb')
        ->fit(Manipulations::FIT_CONTAIN, 150, 150);
    $this->addMediaConversion('display')
        ->fit(Manipulations::FIT_CONTAIN, 500, 500);
}

public function material() {
    return $this->belongsTo(Material::class); }

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

public function related_to() {
    return $this->belongsToMany(Product::class, 'product_related', 'product_id', 'related_id')
        ->withTimestamps();
}

public function details() {
    return $this->belongsToMany(DetailTitle::class, 'details')
        ->withPivot('content')
        ->withTimestamps()
        ->as('detail');
}

public function getCategoryListAttribute()
{
    return $this->categories->pluck('id')->all();
}

public function setCategoryListAttribute($value)
{
    $categories = explode(",", $value);
    $this->categories()->sync($categories);
}

}

koss-shtukert commented 5 years ago

Hi @unarmedwombat , i think problem on your relations, i tryed reproduce it in my side and i have all fine. You can try replace Select2 to native Select component or remove Select2 component from fields method and try create new product.

koss-shtukert commented 5 years ago

Hi @unarmedwombat , i'm double investigated in your issue and solve it! :)

  1. Remove setter public function setCategoryListAttribute($value)
  2. Remove hideWhenCreating() method on your Select2 field
  3. Add
public static function boot()
{
        parent::boot();

        static::created(function ($model) {
            $items = explode(',', request('categoryList'));

            $model->categories()->sync($items);
        });

        static::updated(function ($model) {
            $items = explode(',', request('categoryList'));

            $model->categories()->sync($items);
        });
}

on your Product model

  1. Done!
martyn62 commented 5 years ago

I have a very similar problem to above but making the changes do not seem to work for me so I would really appreciate assistance.

I can update existing 'workorders' without issue apart from when I remove all engineers I get the following error. error1

If I try to create a new 'workorder', I get the following error if I have engineers selected. error2

If I try to create a new 'workorder', I get the following error if I have no engineers selected. error3

App\WorkOrder.php `<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

use Mail; use Carbon\Carbon; use App\Mail\WorkorderProfile;

class WorkOrder extends Model {

protected static function boot()
{
    parent::boot();

    /*static::saving(function ($workorder) {
    });*/
}

protected $fillable = [
    'system_id',
    'body',
    'requirements',
    'date',
];

protected $casts = [
    'date' => 'datetime'
];

public function system()
{
    return $this->belongsTo('App\System');
}

public function engineers()
{
    return $this->belongsToMany(__NAMESPACE__.'\\User', 'workorder_engineer', 'workorder_id', 'engineer_id');
}

public function getEngineerListAttribute()
{
    return $this->engineers->pluck('id')->all();
}

public function setEngineerListAttribute($value)
{
    $engineers = explode(",", $value);
    $this->engineers()->sync($engineers);
}

} App\Nova\WorkOrder <?php

namespace App\Nova;

use Laravel\Nova\Fields\ID; use Illuminate\Http\Request; use Laravel\Nova\Http\Requests\NovaRequest; use Laravel\Nova\Fields\BelongsTo; use Laravel\Nova\Fields\BelongsToMany; use Laravel\Nova\Fields\Textarea; use Laravel\Nova\Fields\DateTime; use App\User; use Koss\LaravelNovaSelect2\Select2;

class WorkOrder extends Resource { /**