Saltar al contenido

Llamar a Eloquent desde Blade: 6 consejos para el rendimiento – Laravel Daily

Uno de los problemas de rendimiento más comunes que he visto en Laravel es el uso de métodos y relaciones Eloquent de Blade, creando bucles y consultas adicionales innecesarios. En este artículo, mostraré diferentes escenarios y cómo manejarlos de manera efectiva.
Escenario 1. La carga pertenece a la relación (): no olvide la carga ansiosa
Probablemente, el caso más típico: está haciendo un bucle con @foreach a través de los registros, y en alguna columna necesita mostrar su registro principal con algún campo. @foreach ($ sesiones como $ sesión)

{{$ sesión-> created_at}} {{$ sesión-> usuario-> nombre}}

@endforeach
Y, por supuesto, la sesión pertenece al usuario, en app / Session.php: función pública user () {return $ this-> attendsTo (User :: class); }
Ahora, el código puede parecer inofensivo y correcto, pero dependiendo del código del controlador, es posible que tengamos un gran problema de rendimiento aquí.
Manera incorrecta en el controlador: índice de función pública () {$ sesiones = Sesión :: all (); return view (‘sesiones.index’, compact (‘sesiones’);}
Forma correcta: función pública index () {$ sesiones = Sesión :: con (‘usuario’) -> get (); return view (‘sesiones.index’, compact (‘sesiones’);}
¿Notaste la diferencia? Estamos cargando la relación con la consulta principal de Eloquent, se llama Eager Loading.
Si no hacemos eso, en nuestro Blade, cada ciclo de foreach llamará a una consulta SQL para cada sesión, preguntando por su usuario directamente desde la base de datos cada vez. Entonces, si tiene una tabla con 100 sesiones, entonces tendría 101 consultas: 1 para la lista de sesiones y otras 100 para usuarios relacionados.
Por lo tanto, no se olvide de Eager Loading.
Escenario 2. Cargando relación hasMany ()
Otro escenario típico es que necesita enumerar todas las entradas secundarias en el ciclo de registros principales. @foreach ($ publicaciones como $ publicación)

{{$ publicación-> título}} @foreach ($ post-> etiquetas como $ etiqueta) {{$ etiqueta-> nombre}} @endforeach

@endforeach
Adivina qué, lo mismo se aplica aquí. Si no usa Eager Loading, por cada publicación habrá una solicitud a la base de datos.
Entonces, en su controlador, debe hacer esto: public function index () {$ posts = Post :: with (‘tags’) -> get (); // ¡no solo Post :: all ()! return view (‘posts.index’, compact (‘posts’)); }
Escenario 3. NO usar corchetes en la relación hasMany ()
Imaginemos que tiene una encuesta con votos y desea mostrar todas las encuestas con la cantidad de votos que tuvieron.
Por supuesto, está realizando una carga ansiosa en el controlador: función pública index () {$ polls = Poll :: with (‘votes’) -> get (); return view (‘encuestas’, compact (‘encuestas’)); }
Y luego, en el archivo Blade, lo muestra algo como esto: @foreach ($ polls as $ poll) {{$ poll-> question}} ({{$ encuesta-> votos () -> recuento ()}})
@endforeach
Parece que está bien, ¿verdad? Pero observe -> votes (), entre corchetes. Si lo deja así, TODAVÍA habrá una consulta para cada encuesta. Debido a que no obtiene los datos de relación cargados, en su lugar, vuelve a llamar a su método desde Eloquent.
Así que haz esto: {{$ poll-> votes-> count ()}}. Sin corchetes.
Y, por cierto, lo mismo se aplica a la relación pertenece a. No use corchetes mientras carga relaciones en Blade.
Offtopic: mientras navegaba por StackOverflow, he visto ejemplos aún peores de esto. Como: {{$ poll-> votes () -> get () -> count ()}} o @foreach ($ poll-> votes () -> get () as $ vote)…. Pruébelo con Laravel Debugbar y vea la cantidad de consultas SQL.
Escenario 4. ¿Qué pasa si la relación puede ser vacía?
Uno de los errores más comunes en Laravel es “intentar obtener la propiedad de un no objeto”, ¿lo has visto antes en tus proyectos? (vamos, no mientas)
Por lo general, proviene de algo como esto:

{{$ pago-> usuario-> nombre}}

No hay garantía de que el usuario de ese pago todavía exista. ¿Quizás fue eliminado temporalmente? ¿Quizás falta una clave externa en la base de datos, lo que permitió a alguien eliminar al usuario de forma permanente?
Ahora, la solución depende de la versión de Laravel / PHP. Antes de Laravel 5.7, la sintaxis típica para mostrar el valor predeterminado era esta: {{$ pago-> usuario-> nombre o ‘Anónimo’}}
Desde Laravel 5.7, cambió la sintaxis para seguir el operador PHP común, que se introdujo en PHP 7: {{$ pago-> usuario-> nombre ?? ‘Anónimo’}}
¿Pero sabías que también puedes asignar un valor predeterminado en el nivel Eloquent? función pública usuario () {devolver $ esto-> pertenece a (Usuario :: clase) -> conDefecto (); }
Este método withDefault () devolverá un modelo vacío de la clase User, si la relación no existe.
No solo eso, ¡también puedes llenar ese modelo predeterminado con valores! función pública usuario () {devolver $ esto-> pertenece a (Usuario :: clase) -> conDefecto ([‘name’ => ‘Anonymous’]); }
Escenario 5. Evitar declaraciones Where en Blade con relaciones adicionales
¿Has visto un código como este en Blade? @foreach ($ publicaciones como $ publicación) @foreach ($ publicación-> comentarios-> donde (‘aprobado’, 1) como $ comentario) {{$ comentario-> comentario_texto}} @endforeach @endforeach
Entonces, está filtrando comentarios (ansiosos cargados, por supuesto, ¿verdad? ¿Verdad?) Con otra condición where (‘aprobado’, 1).
Funciona y no causa ningún problema de rendimiento, pero mi preferencia personal (y también el principio MVC) dice que la lógica debería estar fuera de la Vista, en algún lugar, bueno, la capa “lógica”. Que puede ser el modelo Eloquent en sí mismo, donde puede especificar una relación separada para los comentarios aprobados en app / Post.php. comentarios de función pública () {return $ this-> hasMany (Comment :: class); } función pública aprobados_comments () {return $ this-> hasMany (Comment :: class) -> where (‘aprobado’, 1); }
Y luego carga esa relación específica en Controller / Blade: $ posts = Post :: with (‘Approved_comments’) -> get ();
Escenario 6. Evitar condiciones muy complejas con los descriptores de acceso
Recientemente, en un proyecto tuve una tarea: enumerar trabajos, con el icono de sobre para los mensajes y con el precio del trabajo que debería tomarse del ÚLTIMO mensaje que contenía ese precio. Suena complicado, y lo es. Pero bueno, ¡la vida real también es bastante compleja!
En el código primero escribí algo como esto: @foreach ($ trabajos como $ trabajo)… @if ($ trabajo-> mensajes-> donde (‘el precio no es nulo’) -> recuento ()) {{$ trabajo- > mensajes-> donde (‘el precio no es nulo’) -> sortByDesc (‘id’) -> primero () -> precio}} @endif @endforeach
Oh, el horror. Por supuesto, debe verificar si el precio existe, luego tomar el último mensaje con ese precio, pero … Al diablo, no debería estar en el Blade.
Así que terminé usando el método Accessor en Eloquent y definí esto en app / Job.php: public function getPriceAttribute () {$ price = $ this-> messages -> where (‘price is not null’) -> sortByDesc (‘id ‘) -> primer (); if (! $ precio) return 0; return $ precio-> precio; }
Por supuesto, con situaciones tan complejas también es fácil saltar al problema de consultas N + 1 o simplemente lanzar consultas varias veces por accidente. Entonces, use Laravel Debugbar para encontrar los defectos.
Además, puedo recomendar un paquete llamado Laravel N + 1 Query Detector.
Bono. Quiero dejarles probablemente el peor ejemplo del código que he visto en Laracasts, mientras investigaba este tema. Alguien quería un consejo para este código a continuación. Desafortunadamente, un código como este se ve en proyectos en vivo con demasiada frecuencia. Porque, bueno, funciona … (no intentes esto en casa) @foreach ($ usuario-> pagos () -> obtener () como $ pago)

{{$ pago-> tipo}} {{$ pago-> monto}} $ {{$ pago-> created_at}} @if ($ pago-> método () -> primer () -> tipo == ‘PayPal’)

Paypal: {{$ pago-> método () -> primer () -> paypal_email}}

@demás

Tarjeta: {{$ pago-> método_pago () -> primer () -> marca_tarjeta}} **** **** **** {{$ pago-> método_pago () -> primer () -> tarjeta_último_cuatro}}

@terminara si

@para cada
¿Te gustan nuestros artículos? ¡Consulta nuestros cursos en línea de Laravel!

Este contenido se publicó originalmente aquí.