是否有办法在laravel 5中大规模分配关系?

时间:2016-01-14 17:23:12

标签: php laravel laravel-5 eloquent

我有两个具有oneToMany关系的Model类,比如这个

应用\车
class Car extends Model
{

    public $timestamps = true;

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

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

}
应用程序\部分
class Part extends Model
{

    public $timestamps = false;

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

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

}

客户端发出一个POST请求,其中JSON表示带有嵌套数组部件的Car

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

有没有办法保存Car模型并在一次点击中创建关系?

我试图在我的控制器中执行此操作,但它不起作用:

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

PS:我已经知道如何使用foreacharray_reduce完成任务,只是想知道laravel是否可以为我做这件事

- 编辑 -

以下是我现在如何实施控制器:

...
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);
}
...

欢迎任何改进

2 个答案:

答案 0 :(得分:3)

我认为有一种方法可以解决您需要创建请求对象中定义的每个App \ Part模型实例的问题。因此,在某些时候,您必须迭代请求对象中的那些项,这意味着您(至少)有两个选项。这些都是described here

在旁注中我认为在这种情况下最好使用foreach循环作为第一个选项:

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

如果您要先将第二个选项存储在数组中,那么我认为array_map是更合适的方法(因为没有实际的“减少”):

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

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

编辑:根据@TomasKim的建议,只有2个查询 - 一个用于Car模型,另一个用于Part模型:

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

DB::table('parts')->insert($parts);

答案 1 :(得分:3)

虽然没有一种技术上可以完全自动完成此操作的方法,但只需调整fillable属性并添加方法,就可以使模型的雄辩api以这种方式运行:

class Car extends Model
{

    public $timestamps = true;

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

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

    public function setPartsAttribute($parts)
    {
        foreach ($parts as $part) {
            Part::updateOrCreate(
                ['id' => array_get($parts, 'id')],
                array_merge($part, ['car_id' => $this->id])
            );
        }
        // ... delete parts not in the list if you want
    }
}

注意我们刚刚将parts添加到fillable属性,然后使用Eloquent的属性设置hook set[Field]Attribute()来执行赋值。这样就可以让Car api看起来正确:

Car::find(1)->update($carWithParts);

注意:创建Car模型时,这不起作用,因为没有id。如果您想自动执行此操作,则可以对这些更新进行排队,然后使用模型的事件挂钩来执行此操作。