ziffmedia / nova-select-plus

A Laravel Nova Select Field
MIT License
95 stars 26 forks source link

Not working with UUID's #9

Closed newtongamajr closed 4 years ago

newtongamajr commented 4 years ago

Hi, In my relation table I have both origin model and related model identified by UUIDs.

create table `607585c0a06311e9bf656ba9c86393fd`.microregionscities
(
    idmicroregioncities bigint unsigned auto_increment
        primary key,
    idmicroregions char(36) not null,
    idcities char(36) not null,
    created_at timestamp null,
    updated_at timestamp null,
    constraint MicRegsMunics_MicRegs_fk
        foreign key (idmicroregions) references `607585c0a06311e9bf656ba9c86393fd`.microregions(idmicroregions),
    constraint MicRegs_Munics_fk
        foreign key (idcities) references cities (idcities)
);

I'm populating my data from microregions resource and when I do so, idcities always remain empty.

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`607585c0a06311e9bf656ba9c86393fd`.`microregionscities`, CONSTRAINT `MicRegs_Munics_fk` FOREIGN KEY (`idcities`) REFERENCES `redditumdb`.`cities` (`idcities`)) (SQL: insert into `607585c0a06311e9bf656ba9c86393fd`.`microregionscities` (`updated_at`, `created_at`, `idmicroregions`, `idcities`) values (2020-04-20 01:01:25, 2020-04-20 01:01:25, bc06fba0-82a7-11ea-967e-a1507a7c70b6, ))

Thanks!

ralphschindler commented 4 years ago

How is your SelectPlus::make()... configured?

newtongamajr commented 4 years ago

My belongToMany:

  public function cities()
  {
    $db = DB::connection(config('galileosoft.galileo-tenant'))->getDatabaseName();
    return $this->belongsToMany(City::class,"$db.microregionscities",'idmicroregions','idcities')
                ->withTimestamps('created_at','updated_at');
  }

My select-plus component call:

      SelectPlus::make('Municípios Pertencentes','cities',City::class)
                ->label('munic_estado')
                ->usingDetailLabel(function ($models) {
                  $result= [];
                  foreach ($models as $model)
                  {
                    $city= $model->find($model->idcities);
                    $uf = $city->state()->first();
                    $resultado[] = "$city->cityname/$uf->code";
                  }
                  return $result;
                })
                ->ajaxSearchable(function ($search) {
                    return \Galileo\Models\Gll\City::where('cityname','like', "%{$search}%")->limit(5);
                }),

I'm using hyn/multitenant package to create a system-tentant environment that's why I'm capturing the dbname from my connection. But this is totally transparent since from tinker I'm not just associated cities to microregions, as it was finely visible from select-plus component after a manual data load! select-plus-01 select-plus-02

ralphschindler commented 4 years ago

It looks like you have a tendency to use non-standard names for your primary key names, (typically it is id). I would check to see what the value of this is: $city = new City; $city->getKeyName() and make sure it matches what the actual key name is of the model.

This is what fillAttribute() is doing:

https://github.com/ziffmedia/nova-select-plus/blob/master/src/SelectPlus.php#L175-L187

newtongamajr commented 4 years ago

Yes, all my PK are id because doing so, everyplace I use this id I have always the same key name instead of, for exemple, for a User Class, id and user_id. But I didn't see any problem in adopting this name strategy because Laravel has full support for that. My models are using all Laravel features for this naming convention and use of UUID as keys.

This is my MicroRegions model: `<?php

namespace Galileo\Models\Gll;

use DB; use Galileo\Support\Gll\GalileoModel; use Galileo\Traits\Gll\GerarUuid; use Hyn\Tenancy\Traits\UsesTenantConnection;

class MicroRegion extends GalileoModel { use GerarUuid, UsesTenantConnection;

protected $table = 'microregions'; protected $primaryKey = 'idmicroregions'; protected $keyType = 'string'; protected $fillable = [ 'name', 'code' ];

public function cities() { $db = DB::connection(config('galileosoft.galileo-tenant'))->getDatabaseName(); return $this->belongsToMany(City::class,"$db.microregionscities",'idmicroregions','idcities') ->withTimestamps('created_at','updated_at'); } }`

And this is my Cities model: `<?php

namespace Galileo\Models\Gll;

use DB; use Galileo\Support\Gll\GalileoModel; use Galileo\Traits\Gll\GerarUuid; use Hyn\Tenancy\Traits\UsesSystemConnection;

class Cityextends GalileoModel { use GerarUuid, UsesSystemConnection;

protected $table = 'cities'; protected $primaryKey = 'idcities'; protected $keyType = 'string'; protected $fillable = [ 'idpaisesufs', 'cityname', 'cepprincipal', 'codibge', ];

protected $appends = ['munic_estado'];

public function unidfederativa() { return $this->belongsTo(PaisesUF::class, 'idpaisesufs', 'idpaisesufs'); }

public function bairros() { return $this->hasMany(Bairro::class,'idmunicipios','idmunicipios'); }

public function enderecos() { return $this->hasMany(Endereco::class,'idmunicipios', 'idmunicipios'); }

public function microregions() { $db = DB::connection(config('galileosoft.galileo-tenant'))->getDatabaseName(); return $this->belongsToMany(MicroRegion::class,"$db.microregionscities",'idcities','idmicroregions') ->withTimestamps('created_at','updated_at'); } /**

ralphschindler commented 4 years ago

I don't think this is specific to UUID's necessarily.

In the exception message you provided, I see this:

insert into `607585c0a06311e9bf656ba9c86393fd`.`microregionscities` (`updated_at`, `created_at`, `idmicroregions`, `idcities`) values (2020-04-20 01:01:25, 2020-04-20 01:01:25, bc06fba0-82a7-11ea-967e-a1507a7c70b6, ))

That seems to indicate that the idcities's value is missing from the values that are passed back.

If you can, try and see what that POST body is for the update (using the developer tools with the browser) that is sent to the server when you click save on your form that has the Select field in it. What do the keys and values look like for the selected things?

newtongamajr commented 4 years ago

@ralphschindler, just correct me if I misunderstood something, but I was analyzing the code you sent me: ´ protected function fillAttribute(NovaRequest $request, $requestAttribute, $model, $attribute) { // returning a function allows this to run after the model has been saved (which is crucial if this is a new model) return function () use ($request, $requestAttribute, $model, $attribute) { $values = collect(json_decode($request[$requestAttribute], true));

        $keyName = $model->getKeyName();

        if ($this->reorderable) {
            $syncValues = $values->mapWithKeys(function ($value, $index) use ($keyName) {
                return [$value[$keyName] => [$this->reorderable => $index + 1]];
            });
        } else {
            $syncValues = $values->pluck($keyName);
        }

        $model->{$attribute}()->sync($syncValues);
    };
}`

From my understanding, $model in this case is MicroRegion::class. When you do ' $keyName = $model->getKeyName(); you're getting the key from Microregion::class, thats 'idmicroregions', when you pluck the values to populate $syncvalues, it will result in a collection of null values. I replicated this situation on Tinker, and obtained the ame error, I've got using 'select-plus'. $keyName should come from related model instead of the origin model.

What do you think?

ralphschindler commented 4 years ago

This is a bug, I've pushed a fix and released 1.0.6. let me know if it works for you...

https://github.com/ziffmedia/nova-select-plus/commit/804ab09926ed3c9d728f6bfa6b94984d95971aaa

newtongamajr commented 4 years ago

Working as a Swiss clock! Thanks for your attention, @ralphschindler and for your great package!

ralphschindler commented 4 years ago

Fixed in v1.0.6