0

I'm a Laravel noob rewriting some old code to Laravel.

I have a system for managing purchases and games and I'm writing the store method of the PurchaseController. The form for creating new purchases contains data about the purchase and an array with data about the games.

There is a many-to-many relationship between games and purchases: a purchase can contain many games and a game may be linked to multiple purchases.

The thing is that the game may already exist in the database. I want to do the following:

  1. Insert the new purchase into the database (this part I got sorted out already ;))
  2. Check if the POSTed name of the game already exists in the database.
  3. If it exists, attach it to the newly inserted purchase. If it doesn't exist, insert it and attach it to the newly inserted purchase.

I don't want to update the game if it already exists in the database, just to attach it to the purchase.

I've looked into firstOrCreate but that doesn't do what I want. It checks on all the arguments you feed it, you can't just make it check only the name (this issue basically).

The undocumented method updateOrCreate does accept two arrays (one for attributes to check on, another for values to insert) but it updates the record if it exists, which is not what I want.

So, is there a nice, proper way to do this with Eloquent or do I simply need to manually write some code that checks if the game exists in the database and inserts the game unless that's the case?

EDIT:

It seems that this is possible with firstOrCreate after all in Laravel 5.3: https://github.com/laravel/framework/pull/13236

Community
  • 1
  • 1
Compizfox
  • 748
  • 2
  • 7
  • 17
  • 1
    How in the world coult someone purchase a Game that is NOT in the database? – Borjante Jul 25 '16 at 14:48
  • Sorry if I was unclear. This is not a webshop, but a system for managing a game library. If a game isn't in the database, it's a new game that should be inserted. – Compizfox Jul 25 '16 at 15:05
  • You don't have to use Eloquent for everything. `DB::raw("INSERT IGNORE INTO...` is still available for you, and you're letting the database do its job. – N.B. Jul 25 '16 at 18:42

2 Answers2

0

firstOrCreate is what you need, but you can feed it just the game name, then attach it to your purchase.

$game = Game::firstOrCreate(['name' => $gameName]);
$purchase = new Purchase(['otherArgs' => ...]);
$purchase->games()->attach($game);
Styphon
  • 10,304
  • 9
  • 52
  • 86
  • Thanks for your answer. That doesn't work though because the *game* has more properties that just the *name*. So I'd would need to set those other properties and `save()` it, but that would update the *game* if it exists which isn't what I want. Even worse, I can't even insert the game with just the name because of a foreign key constraint. – Compizfox Jul 25 '16 at 15:03
0

I was probably overthinking this too much. The following code does what I want:

// Insert games (unless they exist) and attach to new purchase
foreach($request->games as $game) {
    $gameModel = Game::firstOrNew(['name' => $game['name']]);
    if(!$gameModel->exists) {
        $gameModel->status_id = $game['status'];
        $gameModel->note = $game['note'];
        $gameModel->save();
    }
    $gameModel->purchases()->attach($purchase->id);
}

I just thought maybe there was a nicer/shorter way to do this.

Compizfox
  • 748
  • 2
  • 7
  • 17