Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to mass assign relations in laravel 5?

I have two Model classes with a oneToMany relation like this

App\Car
class Car extends Model
{

    public $timestamps = true;

    protected $fillable = [
        'name',
        'price'
    ];

    public function parts ()
    {
        return $this->hasMany('App\Part');
    }

}
App\Part
class Part extends Model
{

    public $timestamps = false;

    protected $fillable = [
        'name',
        'price'
    ];

    public function car ()
    {
        return $this->belongsTo('App\Car');
    }

}

The client makes a POST request with a JSON representing a Car with a nested Array of Parts

{
    "name": "Fiat Punto",
    "price": 15000,
    "parts": [
        {
            "name": "wheel",
            "price": 300
        },
        {
            "name": "engine",
            "price": 5000
        }
    ]
}

Is there a way to save the Car model and create the relations in just one hit?

I tried to do this in my controller but it doesn't work:

...
public function store (Request $request) {
    $input = $request->all();
    Car::create($input);
}
...

PS: I already know how to do the task with a foreach or array_reduce, just wondering if laravel could do that for me


-- Edit --

Here is how i implemented the controller right now:

...
public function store (Request $request) {
    $input = $request->all();
    $car = Car::create($input);
    $parts = array_reduce(
        $input['parts'],
        function ($carry, $item) {
            array_push($carry, new Part($item));
            return $carry;
        },
        []
    );
    $car->parts()->saveMany($parts);
}
...

Any improvement is welcome

like image 990
L. Catallo Avatar asked Jan 14 '16 17:01

L. Catallo


1 Answers

I don´t think there is a way of getting around the fact that you need to create each of the App\Part model instances that are defined in the request object. So, at some point you have to iterate over those items in the request object, meaning you have (at least) two options. Those are both described here.

On a side note I think in this case it is better to use a foreach loop for the first option:

foreach ($request->input('parts') as $item) {
    $car->parts()->save(new Part($item));
}

If you'd go for the second option of storing them in an array first, then I think array_map is the more appropriate method to use (because there is no actual "reducing" going on):

$parts = array_map(function($part) {
     return new App\Part($part);
}, $request->input('parts'));

$car->parts()-saveMany($parts);

Edit: As per suggestion from @TomasKim, with only 2 queries - one for the Car-model and another for the Part-models:

$parts = array_map(function($part) {
  return [...$part, 'car_id' => $car->id];
}, $input['parts'])

DB::table('parts')->insert($parts);
like image 75
David Wickström Avatar answered Sep 28 '22 07:09

David Wickström