Via een formulier krijg ik grote hoeveelheden data binnen die ik wil opslaan. Met de kennis die ik heb van Laravel heb ik verschillende methoden gevonden, maar deze methoden vuren allemaal veel queries af op de database. Ik ben bang dat ik door de beperkte kennis van Laravel iets simpels over het hoofd zie.
Case
Met Laravel maak ik een website waar ik wedstrijdstatistieken kan opslaan van een team. Een team bestaat uit maximaal 12 spelers en elke speler heeft 12 verschillende statistieken die worden opgeslagen. De opslag van de data vindt als volgt plaats:
De invoer van de data aan de gebruikerskant vindt plaats via een formulier. Deze is zo opgebouwd dat er per speler een rij aan data is, bestaande uit 12 verschillende invoervelden. Het verzamelen van deze data is geen probleem, maar ik loop tegen bepaalde dingen aan bij het verwerken ervan.
Verwerken data
Er zijn twee verschillende bewerkingen mogelijk: a) aanmaken van een nieuwe game en de daaraangekoppelde spelersstatistieken en b) het bewerken van een eerder opgeslagen game en de daaraan gekoppelde statistieken. Het verwerken van bewerking a is simpel, dit zijn allemaal inserts. Het verwerken van bewerking b ligt wat ingewikkelder. Dit kan namelijk een combinatie zijn van update of een insert (in het geval van niet bestaande data in de database)
Oplossingen
Op dit moment heb ik twee oplossingen:
Oplossing 1:
Aan de hand van een foreach loop verwerk ik per speler de 12 statistieken. Hierbij maak ik gebruik van Modelnaam::firstOrNew om te kijken of er al een record is of dat er een nieuwe aangemaakt moet worden. In beide gevallen krijg ik een 'collection' terug waarvan ik de waarden kan vervangen door de ingegeven waarden en via ->save() kan opslaan in de database.
Voordeel Ongeacht of het een nieuw of een bestaand record is werkt deze methode altijd dankzij ::firstOrNew. In het geval van bestaande records is het een update, in het geval van een nieuwe een insert.
Nadeel: Deze methode vuurt elke keer twee queries af: een om de data op te halen en een om de data te bewaren. Voor 12 spelers met 12 statistieken zijn dit al 2*(12*12) = 288 queries.
Oplossing 2:
Oplossing 2 gaat nog steeds uit van de foreach loop, maar inplaats van eerst de data op te halen en dan te verwerken doen we dit in een keer met ::updateOrCreate:
Voor zover ik begrepen heb probeert deze functie de data te updaten en als dat niet lukt wordt het een insert. Waarschijnlijk is dit voor MySQL 1 query waardoor het aantal queries ten opzichte van oplossing 1 met een factor 2 verminderd wordt. Voor zover ik begrepen heb kan ik niet een array met meerdere rijen tegelijk aanbieden, iets wat met ::insert wel kan.
Variant op oplossingen 1 en 2
Deze oplossing brengt me op een variant voor zowel oplossing 1 als oplossing 2: indien er een nieuwe game aangemaakt wordt kan ik de statistieken altijd via een ::insert doen. Hierbij kan ik een array meegeven met meerdere records. Analoog hieraan kan ik bij het bewerken het aantal update queries terugbrengen aan de hand van de volgende procedure. In de foreach loop wordt een collection opgevraagd of aangemaakt. In het laatste geval heeft de collection nog geen unieke id omdat dit record nog niet bestaat. Bij het bewerken van een bestaande record heeft de collection wel een unieke id. In dat geval kan ik checken of de waarde uit het formulier anders is dan de waarde in de database. Als dat niet het geval is, is het opslaan van de collection via ->save() dus niet nodig. Hiermee beperk ik het aantal update queries tot het bewerken van alleen de gewijzigde waarden. Nadeel is dan nog steeds wel dat je voor elke parameter eerst de data moet opvragen en je zo dus nog steeds 144 queries + eventuele updates uitvoert. In dat geval kan ik nog beter ::updateOrCreate doen, hiermee is het aantal queries altijd 144.
Bovenstaande variant vereist natuurlijk dat ik wel wat logic in mijn controller ga programmeren en voordat ik dit ga doen wil ik wel graag weten of er geen andere mogelijkheden zijn.
Case
Met Laravel maak ik een website waar ik wedstrijdstatistieken kan opslaan van een team. Een team bestaat uit maximaal 12 spelers en elke speler heeft 12 verschillende statistieken die worden opgeslagen. De opslag van de data vindt als volgt plaats:
game_id | player_id | stat_id | value |
---|---|---|---|
1 | 1 | 1 | 100 |
1 | 1 | 2 | 340 |
.. | .. | .. | .. |
De invoer van de data aan de gebruikerskant vindt plaats via een formulier. Deze is zo opgebouwd dat er per speler een rij aan data is, bestaande uit 12 verschillende invoervelden. Het verzamelen van deze data is geen probleem, maar ik loop tegen bepaalde dingen aan bij het verwerken ervan.
Verwerken data
Er zijn twee verschillende bewerkingen mogelijk: a) aanmaken van een nieuwe game en de daaraangekoppelde spelersstatistieken en b) het bewerken van een eerder opgeslagen game en de daaraan gekoppelde statistieken. Het verwerken van bewerking a is simpel, dit zijn allemaal inserts. Het verwerken van bewerking b ligt wat ingewikkelder. Dit kan namelijk een combinatie zijn van update of een insert (in het geval van niet bestaande data in de database)
Oplossingen
Op dit moment heb ik twee oplossingen:
Oplossing 1:
Aan de hand van een foreach loop verwerk ik per speler de 12 statistieken. Hierbij maak ik gebruik van Modelnaam::firstOrNew om te kijken of er al een record is of dat er een nieuwe aangemaakt moet worden. In beide gevallen krijg ik een 'collection' terug waarvan ik de waarden kan vervangen door de ingegeven waarden en via ->save() kan opslaan in de database.
Voordeel Ongeacht of het een nieuw of een bestaand record is werkt deze methode altijd dankzij ::firstOrNew. In het geval van bestaande records is het een update, in het geval van een nieuwe een insert.
Nadeel: Deze methode vuurt elke keer twee queries af: een om de data op te halen en een om de data te bewaren. Voor 12 spelers met 12 statistieken zijn dit al 2*(12*12) = 288 queries.
Oplossing 2:
Oplossing 2 gaat nog steeds uit van de foreach loop, maar inplaats van eerst de data op te halen en dan te verwerken doen we dit in een keer met ::updateOrCreate:
PHP:
1
2
3
4
| Model::updateOrCreate( ['game_id' => 1, 'player_id' => 1, 'stat_id' => 1], ['value' => 'value'] ); |
Voor zover ik begrepen heb probeert deze functie de data te updaten en als dat niet lukt wordt het een insert. Waarschijnlijk is dit voor MySQL 1 query waardoor het aantal queries ten opzichte van oplossing 1 met een factor 2 verminderd wordt. Voor zover ik begrepen heb kan ik niet een array met meerdere rijen tegelijk aanbieden, iets wat met ::insert wel kan.
Variant op oplossingen 1 en 2
Deze oplossing brengt me op een variant voor zowel oplossing 1 als oplossing 2: indien er een nieuwe game aangemaakt wordt kan ik de statistieken altijd via een ::insert doen. Hierbij kan ik een array meegeven met meerdere records. Analoog hieraan kan ik bij het bewerken het aantal update queries terugbrengen aan de hand van de volgende procedure. In de foreach loop wordt een collection opgevraagd of aangemaakt. In het laatste geval heeft de collection nog geen unieke id omdat dit record nog niet bestaat. Bij het bewerken van een bestaande record heeft de collection wel een unieke id. In dat geval kan ik checken of de waarde uit het formulier anders is dan de waarde in de database. Als dat niet het geval is, is het opslaan van de collection via ->save() dus niet nodig. Hiermee beperk ik het aantal update queries tot het bewerken van alleen de gewijzigde waarden. Nadeel is dan nog steeds wel dat je voor elke parameter eerst de data moet opvragen en je zo dus nog steeds 144 queries + eventuele updates uitvoert. In dat geval kan ik nog beter ::updateOrCreate doen, hiermee is het aantal queries altijd 144.
Bovenstaande variant vereist natuurlijk dat ik wel wat logic in mijn controller ga programmeren en voordat ik dit ga doen wil ik wel graag weten of er geen andere mogelijkheden zijn.
"The thing under my bed waiting to grab my ankle isn't real. I know that, and I also know that if I'm careful to keep my foot under the covers, it will never be able to grab my ankle." - Stephen King
Quinta: 3 januari 2005