area17 / twill

Twill is an open source CMS toolkit for Laravel that helps developers rapidly create a custom admin console that is intuitive, powerful and flexible. Chat with us on Discord at https://discord.gg/cnWk7EFv8R.
https://twillcms.com
Apache License 2.0
3.63k stars 560 forks source link

Multi select with dynamic values doesn't load selected values on page edit #2622

Closed shabaz-ejaz closed 2 weeks ago

shabaz-ejaz commented 3 weeks ago

Description

When using Multi select with dynamic values, I have got the data saving fine, however when reloading the form, the selected values are not being checked in the form by default.

Steps to reproduce

I have a pivot table which joins a pianos table with itself to create 'comparable_pianos'.

Pivot model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class PianoComparable extends Model
{
    protected $table = 'piano_comparables';

    protected $fillable = [
        'piano_id',
        'comparable_piano_id',
    ];

    // Define the relationship with the Piano model
    public function piano()
    {
        return $this->belongsTo(Piano::class, 'piano_id');
    }

    // Define the relationship with the Comparable Piano model (same Piano model)
    public function comparablePiano()
    {
        return $this->belongsTo(Piano::class, 'comparable_piano_id');
    }
}

PianoController:

<?php

namespace App\Http\Controllers\Admin;

use A17\Twill\Http\Controllers\Admin\ModuleController;
use App\Models\Piano;
use App\Models\PianoComparable;
use App\Repositories\PianoRepository;
class PianoController extends ModuleController
{
    protected $moduleName = 'pianos';

    protected $indexOptions = [
        'create' => true,
        'edit' => true,
        'publish' => true,
        'duplicate' => true,
        'bulkPublish' => true,
        'feature' => false,
        'bulkFeature' => false,
        'restore' => true,
        'bulkRestore' => true,
        'delete' => true,
        'bulkDelete' => true,
        'reorder' => true,
        'permalink' => true,
        'bulkEdit' => true,
        'editInModal' => false,
        'forceDelete' => true,
        'bulkForceDelete' => true,
    ];

    protected function formData($request)
    {
        return [
            'comparable_pianos' => app()->make(PianoRepository::class)->listAll()
        ];
    }

}

PianoRepository:

<?php

namespace App\Repositories;

use A17\Twill\Repositories\Behaviors\HandleBlocks;
use A17\Twill\Repositories\Behaviors\HandleSlugs;
use A17\Twill\Repositories\Behaviors\HandleMedias;
use A17\Twill\Repositories\Behaviors\HandleFiles;
use A17\Twill\Repositories\Behaviors\HandleRevisions;
use A17\Twill\Repositories\ModuleRepository;
use App\Models\Piano;

class PianoRepository extends ModuleRepository
{
    use HandleBlocks, HandleSlugs, HandleMedias, HandleFiles, HandleRevisions;

    public function __construct(Piano $model)
    {
        $this->model = $model;
    }

    public function afterSave($object, $fields)
    {
        $object->comparablePianos()->sync($fields['comparable_pianos'] ?? []);

        parent::afterSave($object, $fields);
    }

}

Blade file:


// omitted irrelevant form data

@formField('multi_select', [
    'name' => 'comparable_pianos',
    'label' => 'Comparable Pianos',
    'options' => $comparable_pianos
    ])

Expected result

I expect the multiselect to have pre-selected values based on the ones in the database on form edit. However I only ever see the options with all of them unchecked.

Actual result

The values do save in the database, however are not shown as selected when reading from the table int he form.

Screenshot 2024-06-25 at 12 04 18

Versions

Twill version: 2.0.1 Laravel version: 7.30.6 PHP version: 7.4.33 Database engine: MySQL InnoDB

zeezo887 commented 2 weeks ago

@shabaz-ejaz I see the relation's name in your Piano model is called comparablePianos but in the multi-select field it is in snake-case comparable_pianos. Twill uses eager loading when accessing model relationships so, $item>comparable_pianos would return null because it is not a property of the Piano model. Updating your multi-select field to

@formField('multi_select', [
  'name' => 'comparablePianos',
  'label' => 'Comparable Pianos',
  'options' => $comparable_pianos
])

will work fine

shabaz-ejaz commented 2 weeks ago

@zeezo887 Thanks, I've tried that and now it doesn't actually save the the values in the pivot table.

Weirdly it did save when I have it like this:

@formField('multi_select', [
    'name' => 'comparable_pianos',
    'label' => 'Comparable Pianos',
    'options' => $comparable_pianos2,
    ])
zeezo887 commented 2 weeks ago

@shabaz-ejaz you should update your PianoRepository too

public function afterSave($object, $fields)
    {
        $object->comparablePianos()->sync($fields['comparablePianos'] ?? []);

        parent::afterSave($object, $fields);
    }
shabaz-ejaz commented 2 weeks ago

@zeezo887 That's what it was! That works great now thanks.