Manos Nathanail
Manos Nathanail

Reputation: 57

Save model outside of for-each loop

Assuming the Model Order

class Order extends Model {
    use HasFactory;

    protected $table = 'order';

    protected $primaryKey   = 'id';
    public $incrementing = false;
    protected $keyType = 'string';
    protected $guarded = [];

    public function extra(){
        return $this->hasOne(Extra::class);
    }

    public function products(){
        return $this->hasMany(Product::class);
    }
}

and the Model Extra

class Extra extends Model {

    use HasFactory;
    protected $table = 'extra';
    protected $guarded = [];

    public function order(){
        $this->belongsTo(Order::class);
    }
}

and the Model product

class Product extends Model {
    use HasFactory;
    protected $table = 'product';
    protected $guarded = [];
    public function order(){
        return $this->belongsTo(Order::class);
    }
}

Now, from an API I receive data. With these data, I want to feed the models and then store the info to DB.

The approach there is atm is:

foreach ($list as $item) {
    $order = new Order();
    $order->id = $item['id'];
    $order->title = $item['title'];
    $order->save();

    $extra = new Extra();
    $extra->foo= $item['path']['to']['foo'];
    $extra->bar= $item['path']['to']['bar'];
    $order->extra()->save($extra)
    
    $order->products()->createMany($item['path']['to']['products']);
}

The problem is that this code saves three times for each loop, one for order, one for extra, one for the product. I would like to know if there is another way that I can use in order to gather the data inside the for-each and outside of it, to make something like

Order::insert($array_of_data);

Upvotes: 2

Views: 684

Answers (1)

Thomas Grabarczyk
Thomas Grabarczyk

Reputation: 36

I imagine it would look something like this, try it and if doesn't work please let me know i'll delete answer

$orders = [];
$extras = [];
$products = [];

foreach ($list as $item) {
   
    $orders[] = [
        'id' => $item['id'],
        'title' => $item['title'],
    ];

    $extras[] = [
        'foo' => $item['path']['to']['foo'],
        'bar' => $item['path']['to']['bar'],
    ];

    $products[] = [
       'order_id' => $item['id'],
       'foo' => $item['path']['to']['products']['foo'] // or data it has
    ];
}

Order::insert($orders);
Extra::insert($extras);
Product::insert($products); // make sure each product has order id and data which is not visible here

I also suggest looking into converting $list into collection and then iterating over it, if the data is quite big you might make a use of LazyCollection which is the same as collection but better for processing larger data sets

Here's an example how you'd do it using lazy collection

LazyCollection::make($list)
    ->each(function (array $item) {
        $order = Order::create(
            [
                'id' => $item['id'], 
                'title' => $item['title']
            ],
        );
                
        Extra::create(
            [
                'order_id' => $item['id'], 
                'foo' => $item['path']['to']['foo'],
                'bar' => $item['path']['to']['bar'],
            ],
        );
                
        $order->products()->createMany($item['path']['to']['products']);
    });

While it doesn't necessarily create many at once, it it memory saviour and will process quite quickly

Upvotes: 1

Related Questions