Jorge
Jorge

Reputation: 353

foreach too slow with Blade in Laravel 5.2

I am working with Laravel 5.2 and I have a section which is responding too slow, I have detected that the big delay occurs in a foreach loop, it takes one second per row and I am listing around 100 registers which makes the site very slow. It's rare because I am displaying only 7 columns and the table has in total 213 rows which is very little having the table 16 columns.

What can I do? This is the foreach block. It takes that long only running in the production environment

@foreach($tickets as $ticket)
          <tr>
            <td>
              {{ $ticket->id }}
            </td>
            <td>
              {{ $ticket->creator->worksAt->nombre }}
            </td>
            <td>
              {{ $ticket->patient->toArray()['rut'] }}
            </td>
            <td>
              {{ $ticket->getProcessName() }}
            </td>
            <td>
              {{ $ticket->created_at->format('d M Y') }}
            </td>
            @if ($ticket->getProcessSla() == $ticket->getLatency() )
              <td  class="alert-warning">
            @elseif ($ticket->getProcessSla() < $ticket->getLatency() )
              <td  class="alert-danger">
            @else
              <td  class="alert-success">
            @endif
              {{ $ticket->getLatency() }} días
              (Límite: {{ $ticket->getProcessSla() }})
            </td>
            <td>
              @if ( null !== Auth::user()->roles->find($ticket->getProfileId()) )
                {{ Form::open(array('action'=>'StageController@redirect')) }}
                  {{ Form::hidden('ticket_id', $ticket->cryptId()) }}
                  {{ Form::hidden('process_id', $ticket->cryptProcessId()) }}
                  {{ Form::submit('Iniciar', ['class'=>'btn btn-xs btn-block btn-primary']) }}
                {{ Form::close() }}
              @endif
            </td>
          </tr>
    @endforeach

The ticket's model is this one

 <?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use App\Http\Controllers\Controller;
use Crypt;
use DB;

class Ticket extends Model
{


protected $fillable = [
  'patient_id',
  'id_empresa',
  'id_usuario',
  'requiere_clinico',
  'requiere_ept',
  'activo',
  'responsable',

  'ept_id',
  'clinico_id',
  'is_massive',
  'inasistente',

  'creacion_original',
  'ficha_id'

];




/*
 * Relations
 */
public function creator()
{
  return $this->belongsTo('App\User', 'id_usuario');
  //'App\Partner', 'clinical_provider', 'id_client', 'id_provider'
}


public function assignedTo()
{
  return $this->belongsTo('App\User', 'responsable', 'id');
}

public function appointment()
{
  return $this->hasMany('App\Appointment', 'id_ticket');
}

public function annulments()
{
  return $this->hasMany('App\Anullment');
}

public function eptCriteria()
{
  return $this->hasOne('App\EptCriteria');
}


public function patient()
{
  return $this->belongsTo('App\Patient');
}

public function enterprise()
{
  return $this->belongsTo('App\Enterprise', 'id_empresa');
}

public function process(){
  return $this->belongsToMany('App\Process', 'flows', 'id_ticket', 'id_proceso');
}

public function witness(){
  return $this->belongsToMany('App\Witness')->withTimestamps();
}

public function documents()
{
  return $this->hasMany('App\Document', 'id_ticket');
}

public function flows()
{
  return $this->hasMany('App\Flow', 'id_ticket');
}

public function comments()
{
  return $this->hasMany('App\Models\Tickets\Comment', 'ticket_id');
}

/**
 * Esto permite dejar la evidencia de cuando se envió el contacto a las empresas
 * @return [type] [description]
 */
public function enterpriseContacts(){
  return $this->hasMany('App\Enterprisecontact');
}

public function getProcessName(){
  return \App\Process::find($this->process()->max('id_proceso'))->nombre;
}

public function getProcessDescription(){
  return \App\Process::find($this->process()->max('id_proceso'))->descripcion;
}

public function getProfileId(){
return \App\Process::find($this->process()->max('id_proceso'))->id_perfil;
}

public function getProcessSla(){
  return \App\Process::find($this->process()->max('id_proceso'))->sla;
}

public function getLatency(){
  $created = new \Carbon\Carbon($this->created_at);
  return $created->startOfDay()->diffInDays();
}
public function getProcessId(){

  return \App\Process::find($this->process()->max('id_proceso'))->id;
}

public function getClinicalAppointment(){
  return $this->appointment()->where('tipo_citacion', '=', 1)->first()['fecha'];
}


public function cryptProcessId(){
  return  Crypt::encrypt($this->getProcessId());
}

public function cryptId(){
  return  Crypt::encrypt($this->id);
}

public function scopeMassive($query){
  $query->where('activo', 1)
        ->where('is_massive', 1);
}

public function eptTest()
{
  return $this->hasMany('App\EptTest');
}

public function clinicalMovements()
{
  return $this->hasMany('App\Models\MR\Movimiento');
}


 }

Upvotes: 0

Views: 2457

Answers (1)

Paras
Paras

Reputation: 9465

Let's count the number of queries and other ops here:

  1. $ticket->creator: query
  2. creator->worksAt probably another query (guessing as source code not provided)
  3. $ticket->patient: query
  4. $ticket->getProcessName(): query
  5. $ticket->getProcessSla(): query
  6. Auth::user()->roles: query
  7. $ticket->cryptId(): Encryption
  8. $ticket->cryptProcessId(): Encryption + query (for finding process)

In total: 7 queries + 2 encryptions per ticket times 213 rows.

Equates to 1,491 queries and 426 encryptions. No wonder it's slow? It's not foreach.

You need to optimize by eager loading, pagination, persistent storage (for encryptions) and memoization (instead of repeatedly finding the same process model multiple times in the database).

Upvotes: 4

Related Questions