GithubHelp home page GithubHelp logo

laravel-fillable-relations's Introduction

Laravel Fillable Relations

This library provides a trait for mixing in to an Eloquent Model. Doing so will enable support for fillable relations.

To use, first require in your composer file:

composer require troelskn/laravel-fillable-relations

Then, in your code:

<?php
namespace MyApp\Models;

use Illuminate\Database\Eloquent\Model;
use LaravelFillableRelations\Eloquent\Concerns\HasFillableRelations;

class Foo extends Model
{
    use HasFillableRelations;
    protected $fillable_relations = ['bar'];

    function bar()
    {
        return $this->hasOne(Bar::class);
    }
}

class Bar extends Model
{
    use HasFillableRelations;
    protected $fillable_relations = ['foos'];

    function foos()
    {
        return $this->hasMany(Foo::class);
    }
}

And you can now fill relations, like so:

$foo = new Foo(
    [
        'cuux' => 42,
        'bar' => [
            'id' => 42
        ]
    ]
);

Or perhaps:

$foo = new Foo(
    [
        'cuux' => 42,
        'bar' => [
            'name' => "Ye Olde Pubbe"
        ]
    ]
);

And also:

$bar = new Bar(
    [
        'name' => "Ye Olde Pubbe",
        'foos' => [
            [
                'cuux' => 42
            ],
            [
                'cuux' => 1337
            ]
        ]
    ]
);

In order to automatically detach empty relations, pass an empty array:

$bar->fill([
    'foos' => [] // Detach all foos
]);

$bar->save();

You can use Laravel validator array rule to preserve empty arrays passed to the request:

class UpdateRequest extends FormRequest
{
    public function rules()
    {
        return [
            'foos' => [
                'array',
            ],
        ];
    }
}

And then update attributes and relations in one line in the controller:

public function update(UpdateRequest $request, Bar $bar)
{
    $bar->fill($request->validated())->save();
}

laravel-fillable-relations's People

Contributors

calinalexandru avatar hoyte avatar jopicornell avatar lexuzieel avatar mattwells avatar montyclt avatar tostercx avatar troelskn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

laravel-fillable-relations's Issues

Model record does not updated, on self or on relation

Hi,

My controller has,

$Data = Data::findOrFail($id);
$DataData = $request->all();
$Data = $Data->fill($DataData);

Result of dump($DataData) is below:

array:21 [▼
  "_method" => "PATCH"
  "_token" => "dfsfsfsdfsdfxfghcgxtydh"
  "name_prefix" => "Mr"
  "first_name" => "Abhi"
  "last_name" => "Test"
  "primary_email" => "[email protected]"
  "primary_phone" => "9999999999"
  "company_name" => null
  "secondary_email" => "[email protected]"
  "secondary_phone" => "8888888888"
  "country" => "IN"
  "source" => "MOW"
  "destination" => "AMS"
  "departure_date" => "2019-08-29 05:09:50"
  "return_date" => null
  "booking_status" => "FOLLOW"
  "bookings" => array:1 [▼
    0 => array:8 [▼
      "passenger_first_name" => null
      "passenger_last_name" => null
      "passenger_age" => null
      "passenger_pnr" => "322433"
      "ticket_number" => null
      "airline_name" => null
      "topline" => "0"
      "cost" => "0"
    ]
  ]
  "attachments" => array:1 [▼
    0 => null
  ]
  "comment" => null
  "comment_created_by" => "1"
  "updated_by" => "1"
]

Result of dump($Data) is below:

Data {#271 ▼
  #fillable_relations: array:3 [▼
    0 => "bookings"
    1 => "comments"
    2 => "attachment"
  ]
  #fillable: array:21 [▼
    0 => "name_prefix"
    1 => "first_name"
    2 => "last_name"
    3 => "primary_email"
    4 => "primary_phone"
    5 => "company_name"
    6 => "secondary_email"
    7 => "secondary_phone"
    8 => "country"
    9 => "travel_type"
    10 => "source"
    11 => "destination"
    12 => "departure_date"
    13 => "customer_type"
    14 => "customer_type"
    15 => "return_date"
    16 => "booking_source"
    17 => "booking_status"
    18 => "booking_gds"
    19 => "supplier_id"
    20 => "created_by"
  ]
  #dates: array:4 [▼
    0 => "departure_date"
    1 => "return_date"
    2 => "created_at"
    3 => "updated_at"
  ]
  #connection: "mysql"
  #table: "Datas"
  #primaryKey: "id"
  #keyType: "int"
  +incrementing: true
  #with: []
  #withCount: []
  #perPage: 15
  +exists: true
  +wasRecentlyCreated: false
  #attributes: array:23 [▶]
  #original: array:23 [▶]
  #changes: []
  #casts: []
  #dateFormat: null
  #appends: []
  #dispatchesEvents: []
  #observables: []
  #relations: []
  #touches: []
  +timestamps: true
  #hidden: []
  #visible: []
  #guarded: array:1 [▼
    0 => "*"
  ]
}

Data was updated earlier using Data::fill() but not updating any information anymore. I believe I have not changed anything in the controller, Model or View related to Data or related Models.

Question: Does this work with MorphMany?

I noticed a method suggesting that MorphTo might work but didn't see any examples in the tests that suggest polymorphic relations are actually working.

Was hoping to be able to do something like:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use LaravelFillableRelations\Eloquent\Concerns\HasFillableRelations;

class Forum extends Model
{
    use HasFillableRelations;

    protected $fillable_relations = ['title'];

    public function title()
    {
        return $this->morphMany(TitleTranslation::class, 'translatable');
    }
}

And the inverse:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class TitleTranslation extends Model
{
    protected $fillable = [
        'lang_id',
        'text',
        'translatable_id',
    ];

    public function translatable()
    {
        return $this->morphTo();
    }
}

Which I understand is a little tricky to support. Just curious if there's any headway on supporting polymorphic fillables like this or if you need any help.

Thanks for making this by the way!

BEWARE!!! DON'T USE WITHOUT READING THIS FIRST!!!

The package is deceitful!

If you will make a new model and fill relationships, the package will automatically persist all of that in your database!!!

//HasFillableRelations.php:139

if (!$this->exists) {
    $this->save();
    $relation = $this->{camel_case($relationName)}();
 }
.....
$relation->save($related);

Clear attached belongsTo relation

In #19 and #18, support was added for detaching relations by passing an empty array. This works fine for most relation types. However, in the case of belongsTo relations, this doesn't work because fillBelongsToRelation reacts to an array by searching a related model in the database. An empty array isn't naturally used to represent the absence of a belongsTo relation.

$foo = new Foo([
    'creator' => [ 'name' => 'Tom' ],
]);
$foo->fill([
    'creator' => [], // doesn't work for belongsTo, it results in a search for any creator in the database (empty criteria array)
]);

I'd like to propose to also interpret null as "detach all attached related records":

$foo->fill([
    'creator' => null,
]);

The developer could then still to leave a relation untouched by leaving or filtering it out of the attributes array:

$foo->fill([
    'something' => 'else', // creator will be untouched
]);

The advantage is that this way, Laravel FormRequests can be used directly (as is the purpose of this library I think):

<select name="creator">
  <option value="">None</option> <!-- when the user selects this option and submits the form -->
  <option value="3" selected="selected">Tom</option>
</select>
$foo->fill($request->validated()); // then this will work

I have created a PR #27 for this using Arr::has instead of a null check. I am aware this is a small breaking change. If requested, I could also do an alternate implementation that only accepts this type of detaching for belongsTo relations. But that might involve some refactoring in the fill method in order to reduce code duplication.

Update illuminate/database to ^6.0

As of the new Laravel framework version 6.0, it could be great to update the dependency to 6.0, as this is blocking the update to that version for projects that use this library. I think it should be pretty straightforward because in Upgrade guides seem that there's nothing that should cause any problems.

Can this package use versioning?

Can this package use majors versions compatible with different versions of Laravel?

I mean, use branch with major versions of package thats doesn't broke public API and doesn't stop being compatible with different Laravel versions.

An example can be:

  • Package version 3, compatible with Laravel 5.3
  • Package version 4, compatible with Laravel 5.4
  • Package version 5, compatible with Laravel 5.5

I have a project with Laravel 5.4 (currently will not be upgraded) and with last commit I lose compatibility.

Thanks you 😘 !

Is there any way to clear attached relations?

Problem

I use request validation and my relation schema is a simple array of ids:

'permissions' => [
  [ 'id' => 1 ],
  [ 'id' => 2 ],
]

Problem arises when relation array is empty:

'permissions' => []

In this case relations aren't cleared and stay the same.
The only way I found to clear them is to add additional check after filling:

$data = $request->validated();

$role->fill($data)->save();

if (empty(array_get($data, 'permissions', []))) {
    $role->permissions()->sync([]);
}

However I find this cumbersome because then I would need to write such check for every relation.
After inspecting mixin code I found that this is caused by the check inside extractFillableRelations method:

if($val) {

This statement returns false on empty array and therefore doesn't allow to populate $relationsAttributes

Possible solution

I cloned your repository and altered this code to use new parameter $allowEmpty which is false by default which allows it to act same as before.

Using this parameter can clean up the code:

$role->fill($request->validated(), true)->save();

I will create a pull request in order for you to check out the code.

Idea: Raise custom event after save method

Currently there is no way to register a listener after both the model and its fiillable relations have been saved. I would like to propose something like:
$model->fireHasFillableRelationsEvent('created_with_relations');

after the save method in the HasFillableRelations trait.

protected function fireHasFillableRelationsEvent($event)
    {
        if (! isset($this->dispatchesEvents[$event])) {
            return;
        }
        $result = static::$dispatcher->dispatch(new $this->dispatchesEvents[$event]($this));
        if (! is_null($result)) {
            return $result;
        }
    }

getForeignKey does not exist

$foreign_key = str_after($relation->getForeignKey(), '.');

Seems Laravel renamed this method to getQualifiedForeignKeyName in 5.4?

https://github.com/laravel/framework/blob/5.3/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php#L378

https://github.com/laravel/framework/blob/5.4/src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php#L413

There's also a getForeignKeyName now, not sure what's the difference between the two.

edit

I guess getForeignKeyName takes the part after the last dot, so there's no need to do str_after now.

edit

Just noticed there's already a similar fix for HasMany relations, this bug is just for HasOne.

if (method_exists($relation, 'getHasCompareKey')) { // Laravel 5.3
$foreign_key = explode('.', $relation->getHasCompareKey());
$related[$foreign_key[1]] = $relation->getParent()->getKey();
} else { // Laravel 5.5+
$related[$relation->getForeignKeyName()] = $relation->getParentKey();
}

Nested belongsTo relationship

This is a similar issue to #5.

The trait is failing on the following example:

$contract = new Contract([
    'price' => '12',
    'customer' => [ // belongsTo relation
            'name' => 'First Last',
            'reference' => [ // hasMany relation
                [
                    'name' => 'First Last'
                ]
            ]
        ]
    ]
]);

The fillBelongsToRelation save for customer fails with the following, because it's trying to select reference, which is the relation and not a column, from the customers table.

Column not found: 1054 Unknown column 'reference' in 'where clause' 

The following seems to fix the issue for belongsTo relations, it's extracting the relations out and only using the attributes in that lookup.

    /**
     * @param BelongsTo $relation
     * @param array|Model $attributes
     */
    public function fillBelongsToRelation(BelongsTo $relation, $attributes, $relationName)
    {
        $entity = $attributes;
        if (!$attributes instanceof Model) {
            if (method_exists($relation->getRelated(), 'extractFillableRelations')) {
                list($relations, $attributes) = $relation->getRelated()->extractFillableRelations($attributes);
            }

            $entity = $relation->getRelated()
                ->where($attributes)->firstOrFail();
        }
        $relation->associate($entity);
    }

Would this be an appropriate fix? Also I haven't tested the belongs to many, but I'd imagine it would need a similar fix.

No query results for model

Hello everybody,

I am trying to implement this package. Unfortunately I am getting this error:

No query results for model [App\PositionProductCategory]

once I am running

   new PositionProduct($position['product']);
```

This is the trace:

```
array:43 [
  0 => array:5 [
    "file" => "/home/vagrant/code/vendor/troelskn/laravel-fillable-relations/src/Eloquent/Concerns/HasFillableRelations.php"
    "line" => 113
    "function" => "firstOrFail"
    "class" => "Illuminate\Database\Eloquent\Builder"
    "type" => "->"
  ]
  1 => array:5 [
    "file" => "/home/vagrant/code/vendor/troelskn/laravel-fillable-relations/src/Eloquent/Concerns/HasFillableRelations.php"
    "line" => 78
    "function" => "fillBelongsToRelation"
    "class" => "App\PositionProduct"
    "type" => "->"
  ]
  2 => array:5 [
    "file" => "/home/vagrant/code/vendor/troelskn/laravel-fillable-relations/src/Eloquent/Concerns/HasFillableRelations.php"
    "line" => 88
    "function" => "fillRelations"
    "class" => "App\PositionProduct"
    "type" => "->"
  ]
  3 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php"
    "line" => 173
    "function" => "fill"
    "class" => "App\PositionProduct"
    "type" => "->"
  ]
  4 => array:5 [
    "file" => "/home/vagrant/code/app/Http/Controllers/OfferApiController.php"
    "line" => 311
    "function" => "__construct"
    "class" => "Illuminate\Database\Eloquent\Model"
    "type" => "->"
  ]
  5 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Support/Traits/EnumeratesValues.php"
    "line" => 202
    "function" => "App\Http\Controllers\{closure}"
    "class" => "App\Http\Controllers\OfferApiController"
    "type" => "->"
  ]
  6 => array:5 [
    "file" => "/home/vagrant/code/app/Http/Controllers/OfferApiController.php"
    "line" => 359
    "function" => "each"
    "class" => "Illuminate\Support\Collection"
    "type" => "->"
  ]
  7 => array:3 [
    "function" => "update"
    "class" => "App\Http\Controllers\OfferApiController"
    "type" => "->"
  ]
  8 => array:3 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Controller.php"
    "line" => 54
    "function" => "call_user_func_array"
  ]
  9 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php"
    "line" => 45
    "function" => "callAction"
    "class" => "Illuminate\Routing\Controller"
    "type" => "->"
  ]
  10 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Route.php"
    "line" => 239
    "function" => "dispatch"
    "class" => "Illuminate\Routing\ControllerDispatcher"
    "type" => "->"
  ]
  11 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Route.php"
    "line" => 196
    "function" => "runController"
    "class" => "Illuminate\Routing\Route"
    "type" => "->"
  ]
  12 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
    "line" => 685
    "function" => "run"
    "class" => "Illuminate\Routing\Route"
    "type" => "->"
  ]
  13 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 128
    "function" => "Illuminate\Routing\{closure}"
    "class" => "Illuminate\Routing\Router"
    "type" => "->"
  ]
  14 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Middleware/SubstituteBindings.php"
    "line" => 41
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  15 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Illuminate\Routing\Middleware\SubstituteBindings"
    "type" => "->"
  ]
  16 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Auth/Middleware/Authenticate.php"
    "line" => 44
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  17 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Illuminate\Auth\Middleware\Authenticate"
    "type" => "->"
  ]
  18 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Middleware/ThrottleRequests.php"
    "line" => 59
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  19 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Illuminate\Routing\Middleware\ThrottleRequests"
    "type" => "->"
  ]
  20 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 103
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  21 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
    "line" => 687
    "function" => "then"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  22 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
    "line" => 662
    "function" => "runRouteWithinStack"
    "class" => "Illuminate\Routing\Router"
    "type" => "->"
  ]
  23 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
    "line" => 628
    "function" => "runRoute"
    "class" => "Illuminate\Routing\Router"
    "type" => "->"
  ]
  24 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Routing/Router.php"
    "line" => 617
    "function" => "dispatchToRoute"
    "class" => "Illuminate\Routing\Router"
    "type" => "->"
  ]
  25 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
    "line" => 165
    "function" => "dispatch"
    "class" => "Illuminate\Routing\Router"
    "type" => "->"
  ]
  26 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 128
    "function" => "Illuminate\Foundation\Http\{closure}"
    "class" => "Illuminate\Foundation\Http\Kernel"
    "type" => "->"
  ]
  27 => array:5 [
    "file" => "/home/vagrant/code/vendor/fideloper/proxy/src/TrustProxies.php"
    "line" => 57
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  28 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Fideloper\Proxy\TrustProxies"
    "type" => "->"
  ]
  29 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php"
    "line" => 21
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  30 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Illuminate\Foundation\Http\Middleware\TransformsRequest"
    "type" => "->"
  ]
  31 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/TransformsRequest.php"
    "line" => 21
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  32 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Illuminate\Foundation\Http\Middleware\TransformsRequest"
    "type" => "->"
  ]
  33 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/ValidatePostSize.php"
    "line" => 27
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  34 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Illuminate\Foundation\Http\Middleware\ValidatePostSize"
    "type" => "->"
  ]
  35 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/CheckForMaintenanceMode.php"
    "line" => 63
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  36 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode"
    "type" => "->"
  ]
  37 => array:5 [
    "file" => "/home/vagrant/code/app/Http/Middleware/SetLocale.php"
    "line" => 20
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  38 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 167
    "function" => "handle"
    "class" => "App\Http\Middleware\SetLocale"
    "type" => "->"
  ]
  39 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php"
    "line" => 103
    "function" => "Illuminate\Pipeline\{closure}"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  40 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
    "line" => 140
    "function" => "then"
    "class" => "Illuminate\Pipeline\Pipeline"
    "type" => "->"
  ]
  41 => array:5 [
    "file" => "/home/vagrant/code/vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php"
    "line" => 109
    "function" => "sendRequestThroughRouter"
    "class" => "Illuminate\Foundation\Http\Kernel"
    "type" => "->"
  ]
  42 => array:5 [
    "file" => "/home/vagrant/code/public/index.php"
    "line" => 55
    "function" => "handle"
    "class" => "Illuminate\Foundation\Http\Kernel"
    "type" => "->"
  ]
]
```


This is the relation in the model:

```
 public function category()
    {
        return $this->belongsTo(PositionProductCategory::class);
    }
```


And of course  `PositionProductCategory` does exist.

Maybe the problem is, that my model do not extend `Model` but `Product` and `ProductCategory`?

```
class PositionProduct extends Product
{
    use ProductTrait, HasFillableRelations;
    
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable
        = [
              ...
        ];
    
    protected $fillable_relations = ['category'];

fill deleting records on second order relation

i am calling the following code on my product object:

$data = App\products::findOrFail($id);
$inputs=request()->all();
$parts=array_values($inputs["parts"]);
$data->fill(["parts"=>$parts]);

the part being filled looks like this:

Array
(
    [parts] => Array
        (
            [0] => Array
                (
                    [id] => 1
                    [product_id] => 1
                    [display_name] => body
                    [formula_name] => body
                    [part_export_formula] => 
                    [precision_ID] => 12
                    [attributes] => Array
                        (
                            [1] => Array
                                (
                                    [id] => 1
                                    [parts_id] => 1
                                    [attribute_name] => portion
                                    [output_page_width] => 
                                    [attribute_formula] => 0.6666
                                )

                        )

                )

            [1] => Array
                (
                    [id] => 2
                    [product_id] => 1
                    [display_name] => formula
                    [formula_name] => formula
                    [part_export_formula] => 
                    [precision_ID] => 7
                    [attributes] => Array
                        (
                            [2] => Array
                                (
                                    [id] => 2
                                    [parts_id] => 2
                                    [attribute_name] => formulation
                                    [output_page_width] => 
                                    [attribute_formula] => assembly2.variable*cabinet_width*test.density
                                )

                        )

                )

            [2] => Array
                (
                    [id] => 
                    [product_id] => 1
                    [display_name] => foo
                    [formula_name] => foo
                    [part_export_formula] => 
                    [precision_ID] => 1
                    [attributes] => Array
                        (
                            [1566399634] => Array
                                (
                                    [id] => 
                                    [parts_id] => 
                                    [attribute_name] => bar
                                    [output_page_width] => 
                                    [attribute_formula] => 10
                                )

                        )

                )

        )

)

the two relevant tables before the fill look like this:

partsAttributes:
+----+----------------+-------------------+-----------------------------------------------+--------+-----------------------+----------+---------------------+------------+------------+
| id | attribute_name | output_page_width | attribute_formula                             | is_qty | should_hide_on_output | parts_id | created_at          | updated_at | deleted_at |
+----+----------------+-------------------+-----------------------------------------------+--------+-----------------------+----------+---------------------+------------+------------+
|  1 | portion        |              NULL | 0.6666                                        |      0 |                     0 |        1 | 2019-07-17 18:11:19 | NULL       | NULL       |
|  2 | formulation    |              NULL | assembly2.variable*cabinet_width*test.density |      0 |                     0 |        2 | 2019-07-17 18:11:19 | NULL       | NULL       |
+----+----------------+-------------------+-----------------------------------------------+--------+-----------------------+----------+---------------------+------------+------------+
parts:
+----+--------------+--------------+---------------------+--------------+---------------------+---------------------+------------+------------+
| id | display_name | formula_name | part_export_formula | precision_ID | created_at          | updated_at          | deleted_at | product_id |
+----+--------------+--------------+---------------------+--------------+---------------------+---------------------+------------+------------+
|  1 | body         | body         | NULL                |           12 | 2019-08-21 16:24:08 | 2019-08-21 16:24:08 | NULL       |          1 |
|  2 | formula      | formula      | NULL                |            7 | 2019-08-21 16:24:08 | 2019-08-21 16:24:08 | NULL       |          1 |
+----+--------------+--------------+---------------------+--------------+---------------------+---------------------+------------+------------+

and after like this:

partsAttributes:
+----+----------------+-------------------+-------------------+--------+-----------------------+----------+---------------------+---------------------+------------+
| id | attribute_name | output_page_width | attribute_formula | is_qty | should_hide_on_output | parts_id | created_at          | updated_at          | deleted_at |
+----+----------------+-------------------+-------------------+--------+-----------------------+----------+---------------------+---------------------+------------+
|  7 | bar            |              NULL | 10                |      0 |                     0 |        7 | 2019-08-21 16:37:19 | 2019-08-21 16:37:19 | NULL       |
+----+----------------+-------------------+-------------------+--------+-----------------------+----------+---------------------+---------------------+------------+
parts:
+----+--------------+--------------+---------------------+--------------+---------------------+---------------------+------------+------------+
| id | display_name | formula_name | part_export_formula | precision_ID | created_at          | updated_at          | deleted_at | product_id |
+----+--------------+--------------+---------------------+--------------+---------------------+---------------------+------------+------------+
|  1 | body         | body         | NULL                |           12 | 2019-08-21 16:37:19 | 2019-08-21 16:37:19 | NULL       |          1 |
|  2 | formula      | formula      | NULL                |            7 | 2019-08-21 16:37:19 | 2019-08-21 16:37:19 | NULL       |          1 |
|  7 | foo          | foo          | NULL                |            1 | 2019-08-21 16:37:19 | 2019-08-21 16:37:19 | NULL       |          1 |
+----+--------------+--------------+---------------------+--------------+---------------------+---------------------+------------+------------+

it deleted my existing attributes, i thought i might be the on delete cascade from the foreign key (maybe you were deleting the attribute, adding it back in, then deleting the part, adding it back in and the attribute was being deleted due to the cascade, but i removed the foreign key and this still happened. only the new attribute is left in the table)

i'm not sure what is happening here, but i'm pretty sure it is not the correct behaviour.

some more info from the sql log:

144 Prepare   delete from `parts` where `parts`.`product_id` = ? and `parts`.`product_id` is not null
144 Execute   delete from `parts` where `parts`.`product_id` = 1 and `parts`.`product_id` is not null
144 Close stmt
144 Prepare   insert into `parts` (`id`, `product_id`, `display_name`, `formula_name`, `part_export_formula`, `precision_ID`, `updated_at`, `created_at`) values (?, ?, ?, ?, ?, ?, ?, ?)
144 Execute   insert into `parts` (`id`, `product_id`, `display_name`, `formula_name`, `part_export_formula`, `precision_ID`, `updated_at`, `created_at`) values ('1', 1, 'body', 'body', NULL, '12', '2019-08-21 18:07:14', '2019-08-21 18:07:14')
144 Close stmt
144 Prepare   delete from `partsattributes` where `partsattributes`.`parts_id` = ? and `partsattributes`.`parts_id` is not null
144 Execute   delete from `partsattributes` where `partsattributes`.`parts_id` = 1 and `partsattributes`.`parts_id` is not null
144 Close stmt
144 Prepare   update `partsattributes` set `id` = ?, `parts_id` = ?, `attribute_name` = ?, `output_page_width` = ?, `attribute_formula` = ?, `partsattributes`.`updated_at` = ? where `id` = ?
144 Execute   update `partsattributes` set `id` = '1', `parts_id` = 1, `attribute_name` = 'portion', `output_page_width` = NULL, `attribute_formula` = '0.6666', `partsattributes`.`updated_at` = '2019-08-21 18:07:14' where `id` = 1
144 Close stmt

whatever it is you are doing the sql is deleting the attributes and then trying to update the deleted attributes instead of inserting a new one.

you can see the statement it makes for parts is different:

144 Prepare   delete from `parts` where `parts`.`product_id` = ? and `parts`.`product_id` is not null
144 Execute   delete from `parts` where `parts`.`product_id` = 1 and `parts`.`product_id` is not null
144 Close stmt
144 Prepare   insert into `parts` (`id`, `product_id`, `display_name`, `formula_name`, `part_export_formula`, `precision_ID`, `updated_at`, `created_at`) values (?, ?, ?, ?, ?, ?, ?, ?)
144 Execute   insert into `parts` (`id`, `product_id`, `display_name`, `formula_name`, `part_export_formula`, `precision_ID`, `updated_at`, `created_at`) values ('1', 1, 'body', 'body', NULL, '12', '2019-08-21 18:07:14', '2019-08-21 18:07:14')
144 Close stmt

in that case it inserts instead of updating, but for attributes it tries to update the existing ones it just deleted. i'll keep looking over the code to see why it's updating and not inserting (the delete and recreate is fine as long as it does parts then attributes which it seems to be doing, and insert is more correct here since the attribute it's trying to delete doesn't exist as it was deleted in the cascade, but it doesn't know that so it makes a kind of sense) i can tell where it does the delete, it stands out well enough, the update though...not so sure

ok, so after some work i have found if you change the line:

    $related->exists = $related->getKey() != null;

to

$related->exists = $related->wasRecentlyCreated;

the problem goes away and it works fine.

what i think is happening is the line:

$related = $relation->getRelated()->newInstance($related);

is calling fill on the object, which then calls fillHasOneOrManyRelation on the child object, which then saves and creates the parent object in the process, the parent object needs to have exists set to true after that or it will try to insert again and get a pk violation, which i'm thinking is what you were doing here, however you were using the existence of a key as an indicator that the record exists, but the extant attributes also had a key, however you had deleted them in an earlier line so they weren't extant at that point.

so what i did was check if the record had recently been added (since you had previously deleted it the only way it can be in the database is if it had been recently inserted)

i'll clone down your repository, make the changes and put in a pull request

Should it work for multiple levels?

I have a 3-level hierarchy using hasMany. Models are in this gist.

This gives an error:

$tool = new Tool([
    'name' => 'vacuum',
    'fields' => [
        [
            'name' => 'full',
            'choices' => [
                [
                    'name' => 'on'
                ]
            ]
        ]
    ]
]);
SQLSTATE[HY000]: General error: 1364 Field 'tool_id' doesn't have a default value (SQL: insert into `tool_fields` (`name`, `updated_at`, `created_at`) values (full, 2017-12-01 18:00:40, 2017-12-01 18:00:40))

If I leave out the choice, it's fine. The field is inserted with the correct tool_id.

$tool = new Tool([
    'name' => 'vacuum',
    'fields' => [
        [
            'name' => 'full'
        ]
    ]
]);

SQLSTATE[HY000]: General error: 1364 Field 'xxx_id' doesn't have a default value

Basically looks like the foreign key field is not added into the SQL statement for a nested table element.
Is there anything obvious I'm missing ?

$order = new Order($x); // Naturally items in array have no ids

table is linked as follows:

public function details()
{
    return $this->hasMany('App\Detail', 'order_id');
}

there's also belong to on detail:

public function order()
{
    return $this->belongsTo('App\Order','order_id');
}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.