sleeping-owl / admin

Administrative interface builder for Laravel
http://sleeping-owl.github.io/
MIT License
504 stars 258 forks source link

Multiselect not showing selected options in edit form in Laravel 5.1 #206

Open Easiey opened 9 years ago

Easiey commented 9 years ago

Hello, in my application I use form with multiselect. I followed instruction in this issue: https://github.com/sleeping-owl/admin/issues/97. I only changed things that were required by Laravel 5.1 (e.g \App\File::class instead of 'File'). Saving works perfectly fine however edit doesn't show already selected items. Below is my code:

I need to do something like: each 'ArchitectEntry' can have multiple files attached and file can be attached to multiple 'ArchitectEntries'

Related Model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use SleepingOwl\Models\Interfaces\ModelWithFileFieldsInterface;
use SleepingOwl\Models\Traits\ModelWithImageOrFileFieldsTrait;
use SleepingOwl\Models\SleepingOwlModel;
use Symfony\Component\HttpFoundation\File\UploadedFile;

class File extends SleepingOwlModel implements ModelWithFileFieldsInterface
{
    use ModelWithImageOrFileFieldsTrait;

    protected $fillable = [
        'file_src',
        'title',
        'architect_entries'
    ];

    public function getFileFields()
    {
        return [
            'file_src' => 'documents/',
        ];
    }

    public static function getList()
    {
        return static::lists('title', 'id')->all();
    }

    public function scopeDefaultSort($query)
    {
        return $query->orderBy('title', 'asc');
    }

    public function forms()
    {
        return $this->hasMany(Form::class);
    }

    public function architectEntries()
    {
        return $this->belongsToMany('architect_entry', "architect_entry_file");
    }

}

Base model

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use SleepingOwl\Models\SleepingOwlModel;
use SleepingOwl\Models\Interfaces\ModelWithOrderFieldInterface;
use SleepingOwl\Models\Traits\ModelWithOrderFieldTrait;
use SleepingOwl\Models\Traits\ModelWithImageOrFileFieldsTrait;
use SleepingOwl\Models\Interfaces\ModelWithFileFieldsInterface;

class ArchitectEntry extends SleepingOwlModel
{

    //protected $table = 

    protected $fillable = [
        'title',
        'file_src',
        'file_id',
        'sort_field',
    'description',
    'files'
    ];

    public function getSortField()
    {
        return 'sort_field';
    }

    public function files()
    {
        return $this->belongsToMany('App\File', "architect_entry_file");
    }

    public static function getList()
    {
        return static::lists('title', 'id')->all();
    }

    public function setFilesAttribute($files)
    {
        $this->files()->detach();
        if ( ! $files) return;
        if ( ! $this->exists) $this->save();
        $this->files()->attach($files);
    }

}

Admin model

<?php 

 Admin::model(App\ArchitectEntry::class)->title('whatever')->as('whatever')->with('files')->filters(function ()
{

})->columns(function ()
 {
    Column::string('title', 'Tytuł');
 })->form(function ()
 {
    FormItem::text('title', 'Title')->attributes(['placeholder' => 'Title']);
    FormItem::multiSelect('files', 'Select related files')->list(\App\File::class)->value('files.id');

 });

Does anyone know solution to this problem? Thanks.

pedrofaria commented 9 years ago

This is a veeeeery old problem with javascript chosen library...

take a look at https://github.com/harvesthq/chosen/issues/92 and you will found many solutions to prevent this behavior.

tylerteh commented 9 years ago

I encountered the same problem. I am using SleepingOwl v.2.0.43, which I believe it is not using Chosen library. Does anyone know solution to this problem? Thanks.

pedrofaria commented 9 years ago

For me, this is a BUG on chosen... buuuut... there is a solution. Just set the width of .chosen-container on you css. maybe with !important clausule.

Regards...

tylerteh commented 9 years ago

I found the reason, this is caused by the changes of lists method in Laravel 5.1, as stated at http://laravel.com/docs/5.1/upgrade#upgrade-5.1.0

The lists method now returns a Collection instance instead of a plain array for Eloquent queries. If you would like to convert the Collection into a plain array, use the all method:

User::lists('id')->all();

Be aware that the Query Builder lists method still returns an array.

So I think the solution is to use Custom Form Elements.

turistua commented 9 years ago

It seems I have similar problem. Laravel 5.1 and the latest sleeping-owl 2 version.

Currently sleeping-owl 2.x uses bootstrap-multiselect component, but not choosen as mentioned above.

I managed the getList() function as required in laravel 5.1

public static function getList()
{
    return static::lists('name', 'id')->all();
}

When I try to edit form with multiselect field, currently selected options are not active (I see "Nothing selected" text instead of it, also checkboxes from dropdown are not selected).

But form contains list of options, and I can create entity with multiselect field, edit it, etc.

Any prediction why edit form is not filled by previously set values for multiselect field?

Thank you in advance.

tylerteh commented 9 years ago

@turistua Your getList() function is good. The problem is caused by the values() function in /vendor/sleeping-owl/admin/src/SleepingOwl/Admin/Models/Form/FormItem/MultiSelect.php

At line 121, if you change

$result = $result->lists($part);

to

$result = $result->lists($part)->all();

then your multiselect field should be working properly.

This solution is quick but not in a proper way. Because the /vendor/ directory is not in version control. When you deploy the same project in other server, you might need to repeat this trick.

For me, I choose to use Custom Form Elements. This is how I did:

1) Create /app/Forms/MultiSelect2.php

<?php

namespace App\Forms;

use SleepingOwl\Admin\Exceptions\ValueNotSetException;
use Illuminate\Database\Eloquent\Relations\Relation;
use SleepingOwl\Admin\Models\Form\FormItem\MultiSelect;

class MultiSelect2 extends MultiSelect
{
    protected $name;
    protected $label;

    function __construct()
    {
        parent::__construct($this->name, $this->label);
    }

    /**
     * @throws ValueNotSetException
     * @return mixed
     */
    public function values()
    {
        $result = $this->form->instance;

        if (is_null($this->value)) {
            throw new ValueNotSetException;
        }

        $parts = explode('.', $this->value);

        foreach ($parts as $part) {
            if ($result instanceof Relation) {
                $result = $result->lists($part)->all();
            } else {
                $result = $result->$part();
            }
        }

        if (count($result) == 0 && ! $this->form->instance->exists) {
            return $this->getDefault();
        }

        return $result;
    }

    public function setName($name)
    {
        $this->name = $name . '[]';
        return $this;
    }

    public function setLabel($label)
    {
        $this->label = $label;
        return $this;
    }
}

2) Register MultiSelect2 in /app/admin/bootstrap.php

FormItem::register('multiSelect2', \App\Forms\MultiSelect2::class);

3) Usage in model configuration

FormItem::multiSelect2('clusters')
    ->setName('clusters')
    ->setLabel('Business Clusters')
    ->list(\App\Cluster::class)
    ->value('clusters.cluster_id')
;
MurDaD commented 8 years ago

@tylerteh mentioned a good way to solve this problem, but I think it must be updated a little

The main problem (for me) is: Integrity constraint violation: 1052 Column 'id' in field list is ambiguous That's because field 'id' goes without table selector. You can add selector with ->select() method

So, your values() method in file /vendor/sleeping-owl/admin/src/SleepingOwl/Admin/Models/Form/FormItem/MultiSelect.php should look like this

public function values()
{
    $result = $this->form->instance;
    if (is_null($this->value))
    {
        throw new ValueNotSetException;
    }
    $parts = explode('.', $this->value);
    $key = '';
    foreach ($parts as $part)
    {
        if ($result instanceof Relation)
        {
            $result = $result->select($key.'.*')->lists($part)->all();
        } else
        {
            $key = $part;
            $result = $result->$part();
        }
    }
    if (count($result) == 0 && ! $this->form->instance->exists)
    {
        return $this->getDefault();
    }
    return $result;
}
djkaushiksk1 commented 8 years ago

I'm having issues with my multi-select .It works fine but after sometime it submits empty string and it bypasses all the validation