Με προσοχή το hydrate σε queries με joins

Το laravel είναι ωραιότατο! Βάζει μια τάξη στην άναρχη PHP, της δίνει έναν αέρα από ASP.NET με MVC, χωρίς όμως το γνωστό βαρύ περίβλημα. Παραμένουμε δηλαδή στην PHP, γιατί είναι γρήγορη και δυναμική, δομώντας την όμως καθαρά και σωστά άλα laravel. Αυτά όμως τα ξέρετε και δεν είναι επί του παρόντος. Θα ξέρετε ακόμα και ότι το laravel είναι αυτό που είναι χάρη στο eloquent, το συμπαθητικό ORM, της ίδιας ακριβώς ιδιοσυγκρασίας με το laravel. Έχει όμως και τα τερτίπια του. Και ειδικά όταν η ΒΔ μεγαλώνει, τα πράγματα περιπλέκουν, τα queries απαιτούν πολλά join και το eloquent στενάζει. Ή πιο σωστά ο προγραμματιστής στενάζει προσπαθώντας να απεικονίσει με eloquent αυτό που τόσα χρόνια έκανε με τρεις γραμμές sql. Και γιατί όχι λέει ο Τaylor! Γράψτο όπως ξέρεις και ενυδάτωσέ το!

Λογοπαίγνιο για το hydrate ήταν αυτό, ένα παράδειγμα πάντα βοηθάει:

$result = \DB::table('users')->take(2)->get();
$users = \App\User::hydrate($result); 

Και για τα πιο hardcore τυπάκια, χωρίς table, με ξερό select:

$result = \DB::select('select * from users limit 2 ')->get();
$users = \App\User::hydrate($result); 

Τι κάναμε; Στην πρώτη γραμμή, με ένα ξερό select, η PHP μας γυρνάει ένα γενικού τύπου collection. Στη δεύτερη, χάρη στο hydrate, το αγνώστου τύπου collection παίρνει σάρκα και οστά και γίνεται collection του model User!

Και φυσικά, για ένα τόσο απλό παράδειγμα, δεν έχει νόημα!

Θα μπορούσαμε να πετύχουμε ακριβώς τα ίδια με μια γραμμή eloquent, χωρίς ίχνος sql!

Tα ωραία κόλπα μας θα αρχίσουν με τα join στους ρόλους, στους people και όπου αλλού λαχταράει η ψυχή σας για join. Ειδικά μάλιστα αν είναι αποφύγουμε τα ιδιάζοντα whereHas, whereIn, wherePivot, whereSomethingNew… και να χρησιμοποιήσουμε κάτι πιο οικείο, αυτό που ξέρουμε από τη δεκαετία του ενενήντα: S.Q.L. χωρίς να σπάσουμε το κεφάλι μας για το πως γίνεται το καθετί.

Μην ξεχάσετε όμως στο τέλος να ενυδατώσετε το ξερό query.

$result = \DB::select('select * from users inner join roles on users.role_id = roles.id where roles.type="admin" ')->get();
$users = \App\User::hydrate($result); 

Πως σας φαίνεται το παραπάνω; Ωραιότατο! Δεν θα ήθελα ούτε καν να σκεφτώ πως πρέπει να το συντάξω με eloquent. Αυτό το παραπάνω querάκι μου βγήκε έτσι, αυθόρμητα, χωρίς πολλά-πολλά, σαν να γράφω τη λίστα με τα ψώνια.

Προσοχή όμως γιατί κρύβεται κάτι σατανικό – κάτι που μόνο ένα δίωρο debugging θα το αποκαλύψει, ή εγώ ευθύς αμέσως:

Τι γίνεται αν users και roles έχουν πεδία με ίδια ονόματα – πχ. κλειδάκι με όνομα id; Τι θα κάνει το hydrate σε αυτή την περίπτωση; Θα τα σκατώσει! Και καλά θα κάνει, δεν θα φταίει αυτό! Εμείς φταίμε που δεν ορίσαμε με σαφήνεια τι θα ενυδατωθεί! Ζητήσαμε να ενυδατωθεί το αστεράκι (*) συνεπώς δεν αποκλείεται το hydrate να βάλει στον χρήστη το id του ρόλου!

Ποια είναι η λύση;

Η αποσαφήνιση του αστερίσκου. Πολλά λόγια δεν χρειάζονται. Δείτε παρακάτω και θα καταλάβετε:

$result = \DB::select('select users.* from users inner join roles on users.role_id = roles.id where roles.type="admin" ')->get();
$users = \App\User::hydrate($result); 

Έτσι αποφεύγουμε τις κακοτοπιές, δηλώνουμε ρητά ότι είναι τα πεδία του πίνακα users που θα γίνουν map με το model User!

Αφήστε μια απάντηση

Η ηλ. διεύθυνση σας δεν δημοσιεύεται. Τα υποχρεωτικά πεδία σημειώνονται με *

5 + 7 =