jarektkaczyk / eloquence

Extensions for the Eloquent ORM
http://softonsofa.com
MIT License
1.09k stars 142 forks source link

Update and/or save with aliased columns does not work #236

Closed carestad closed 5 years ago

carestad commented 6 years ago

Hi

I have a pretty simple class that remaps some column names:

class Contact extends Model {
  use Eloquence, Mappable;

  protected $primaryKey = 'contactId';
  protected $maps = [
    'mobile' => 'phone',
    'email' => 'mail',
  ];
  protected $hidden = ['phone'];
  protected $appends = ['mobile'];
}

Then, doing this in my controller will fail:

$contact = Contact::find(1);
$contact->mobile = 123456789;
$contact->email = 'foo@bar.com';
$contact->save();

And so does this:

$contact = Contact::find(1);
$contact->update(['mobile' => 123456789, 'email' => 'foo@bar.com']);

Both with the error of not finding the column mobile. Does this not map properly with ->save() and ->update()? Works very well when printing (->toArray()) but not for updating and saving it seems.

Been trying to look through old issues here, the wiki etc. but haven't really found much. But from my understanding and the wiki, at least ->save() should work?

Using: PHP 7.0.31 on Debian 9 Laravel 5.5.42

DanielBoettner commented 5 years ago

@carestad you seem to have the same problem as I did. Or at least is seems to be related.

As soon as I add $maps to the Model $this->getAttributes() returns nothing.

I was able to get the model data via

  public function toArray()
    {
       /** obviously the fields we want have to be fillable **/ 
        $values = $this->getFillable();

       /** only do it when the model has maps **/
        if (isset($this->maps)) {
            foreach ($values as $key => $value) {
                $return[$value] = $this->$value;
            }
        }

       /** get all the unmapped attributes **/
        $return = array_merge($return, $this->getAttributes());
        $return = array_merge($return, $this->relationsToArray());

        return $return;
    }

But this only helps when checking on data you filled. I doubt that this helps when saving data to the database.

chrisgo commented 5 years ago

This also does not work

$contact = Contact::firstOrCreate([
    'mobile' => 123456789,     // find using this
], [
    'email' => 'foo@bar.com',  // update email to this
]);
JimmyBastos commented 5 years ago

@chrisgo I was facing the same problem with mass assignment, and i end up with the solution below:

MappableModel.php

<?php namespace App\Models;

use Sofa\Eloquence\Mappable;
use Sofa\Eloquence\Eloquence;
use Illuminate\Database\Eloquent\Model;

/**
 *  API Key
 *
 * @category Helper
 * @package  App\Models
 * @author   [Jimmy Bastos] <jimmybastos@outlook.com>
 * @license  [<url>] [MIT]
 * @link     (target, link)
 */
class MappableModel extends Model
{
    use Eloquence, Mappable;

    /**
     * Convert the model instance to an array.
     *
     * @return array
     */
    public function toArray()
    {
        if (isset($this->maps)) {
            $this->appends = @array_merge($this->appends, array_keys($this->maps));
            $this->hidden = @array_merge($this->hidden, array_keys(array_flip($this->maps)));
        }

        return array_merge($this->attributesToArray(), $this->relationsToArray());
    }
}

It's important that the mapped attributes you want to assing on create/update being in the $fillable array

JobOffer.php

<?php namespace App\Models;

class JobOffer extends MappableModel
{
    protected $table = 'job_offer';
    protected $primaryKey = 'job_offer_id';
    const     prefix = 'jbo_';
    const     CREATED_AT = 'jbo_created_at';
    const     UPDATED_AT = 'jbo_updated_at';

    protected $fillable = [,
        'title',
        'description',
        'location'
    ];

    protected $maps = [
        'id' => 'job_offer_id',
        'title' => 'jbo_title',
        'description' => 'jbo_description',
        'location' => 'jbo_location',
        'created_at' => 'jbo_created_at',
        'updated_at' => 'jbo_updated_at'
    ];
}
Corben78 commented 4 years ago

Shouldn't this work with $guarded as well? Apparently I have to use $fillable instead of $guarded so mass-assignment via update() works.