11

I am trying to save an order with order_items but I am not really finding anything in the docs to support this use case. A hasMany relationship.

Basically there is an orders table with something like id | user_id and an order_items table with id | order_id | product_id.

How can I save() the order and use an array of items at the same time without having to loop over the items and save them individually?

Is this possible?

Pseudo code assuming $items is an array:

$items = Session::get("cart.items");

$order = new Order;
$order->user_id = Auth::user()->id;
$order->order_items = $items;
$order->save();
CJ Dennis
  • 4,226
  • 2
  • 40
  • 69
Jazzy
  • 6,029
  • 11
  • 50
  • 74
  • Have you tried using `attach()`? Although in my case I had to loop through the array. – lozadaOmr Jul 17 '14 at 06:10
  • Could you provide an example? I am happy to loop if it solves this :) I am not able to make it work. Please post as an answer. – Jazzy Jul 17 '14 at 06:32
  • @Jason Re: "Update Here is the working code I ended up using:", is that your final code that solved your problem? If yes, it is an answer and should be moved to the answer section. Thank you. – Pang Jul 18 '14 at 02:51
  • @Pang The correct answer was by deczo and I only included my code based on their answer to help others in the future. Thank you for looking out for this stuff :) – Jazzy Jul 18 '14 at 02:53
  • @Jason Stackoverflow is a Q & A site. The question section is for questions only and the answer section is for answers. You should move your final code to the answer section because it is NOT part of the question, it is a solution to your question. Thanks. – Pang Jul 18 '14 at 02:57
  • @Pang This is getting long. I removed my update but I do not feel comfortable creating a new answer as there is already a correct answer. Should I edit that answer or create a new answer? I see a lot of users putting their working code in their question such as I have done. – Jazzy Jul 18 '14 at 03:01
  • You should never edit some other's answer. That's vandalism. You can answer your own question and it is actually encouraged (as long as it actually answers the question). See [Can I answer my own question?](http://stackoverflow.com/help/self-answer) in the Help Center. Putting answers in the question section is wrong and should be removed, although the poor moderators can't catch them all. – Pang Jul 18 '14 at 03:05

3 Answers3

11

What you need for a hasMany relation is either saveMany or createMany, depending on what's in your $items array:

// array of attributes:
$items = [
  ['name'=>'item1','price'=>'price1'],
  ...
];

// then createMany:
$order->orderItems()->createMany($items);

This will create new rows in Items table.


// array of models:
$items = [
  Item::find($someId),
  Item::find($anotherId),
  // and/or newly instantiated:
  new Item(['name'=>'item1','price'=>'price1']),
  ...
];

// then createMany:
$order->orderItems()->saveMany($items);

This will associate (save) existing models, and create non-existing ones.


Also notice that I use camelCase relation name orderItems instead of your order_items. This is an important detail, since Eloquent (Laravel v4) looks for camelCased methods on the model when working with relations (dynamic properties).

//Order model
public function orderItems()
{
  return $this->hasMany(...);
}

$order->orderItems; // collection
$order->order_items; // collection as well

// --------------------
// BUT
public function order_items()
{
  return $this->hasMany(...);
}

$order->orderItems; // null
$order->order_items; // null

// the only way you can work with relation then, is explicitly use method like:
$order->order_items()->get(); 
Jarek Tkaczyk
  • 78,987
  • 25
  • 159
  • 157
  • This worked perfectly! Where did you learn about this? I am unable to find docs on this at all. I will update my question with working code. Thank you. – Jazzy Jul 18 '14 at 02:32
  • Brilliant. Was looking for a way to create an order model and a load of orderlines, but not save any of them until the very end. The orderlines are put into a collection, then the final save is `$order->save(); $order->orderlines()->saveMany($orderlines);`. Either an array or a collection can be used. – Jason Jul 15 '16 at 14:14
2

Probably not the best solution you are looking for, but this should work.

Let's say that the array is named $items, I'm under the impression that you will be saving it into a pivot table. In my example below I also have a 3rd field on item_order pivot table named item_quantity.

foreach ($items as $item)
{
    $order->items()
    ->attach($item['item_id'], ['item_quantity' => $item['item_quantity']]);
}

Basically you will be looping through the $items array. This will assume that you have defined the relationship on your Order model called items().

Then use the attach() method

->attach([insert the item_id], array('3rd field name' => 'value to be inserted')

Finally, if you don't have a 3rd field on your pivot table you could just do

->attach($item_id)

You can check the example given at the Laravel docs

Note

attach() is the method used when the you are only creating a record on the Database, otherwise you need a different method when you want to update.

lozadaOmr
  • 2,565
  • 5
  • 44
  • 58
  • Thanks, I will try this tomorrow. There are just two tables related on orders.id = order_items.order_id – Jazzy Jul 17 '14 at 06:54
  • 1
    `attach` is a `BelongsToMany` method and it doesn't save related model, only a row in the pivot table. – Jarek Tkaczyk Jul 17 '14 at 11:32
  • The below answer was correct. Thank you for your time though, I still learned from your answer. – Jazzy Jul 18 '14 at 02:33
0

@jareks answer helped in a similar scenario except for a mass assignment exception . so on digging up docs i found that you need to set a guarded or fillable property for mass assignment in latest versions of laravel (4.2) .

please refer this along with his answer .

Fillable or guarded properties

When creating a new model, you pass an array of attributes to the model constructor. These attributes are then assigned to the model via mass-assignment. This is convenient; however, can be a serious security concern when blindly passing user input into a model. If user input is blindly passed into a model, the user is free to modify any and all of the model's attributes. For this reason, all Eloquent models protect against mass-assignment by default.

So set the fillable or guarded properties on your model. Docs and Source

class User extends Eloquent {

    protected $fillable = array('first_name', 'last_name', 'email');

}
Sojan Jose
  • 3,168
  • 6
  • 33
  • 52