you are loading all tasks and then paginating the in-memory collection?
Dec 19, 2021
11
Level 7
Help with refactoring
The query for a report I'm building is maxing out the memory on the live server. I think I'm overthinking this whole query, tbh, but could use some honest feedback and help simplifying and speeding up my junk code. :/
Three associated tables:
quotes,
quote_task, and
tasks.
The report has five options/filters – the limit of results returned, the type of quote, the timestamp field to filter by, and the start and end dates.
Beat me up!
<?php
namespace App\Http\Livewire\Admin\Reporting\Tasks;
use App\Models\Task;
use App\Models\Quote;
use App\Models\QuoteTask;
use Livewire\Component;
use Livewire\WithPagination;
use Illuminate\Support\Collection;
use Illuminate\Pagination\Paginator;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
class TopUsedTasksReport extends Component
{
/*
* TASKS
*
* Foreach Task
* - Task Description
* - Task Category
* - Number of Quotes that include that Task
* --- Associated Quotes (for linking)
* --- From QuoteTasks pivot
* ------ Average && Sum of Task's Labor Price used on Quote
* ------ Average && Sum of Task's Material Price used on Quote
* ------ Number of Quotes that were Approved that include this Task
*
* Sort results by Total Number of Quotes, desc
*
* Report Options
* - Initial Quotes | Active Quotes | Approved Quotes | Completed Quotes
* - created_at | updated_at | approved_at | scheduled_at | completed_at
* - Timeframe of Quotes
*
*/
use WithPagination;
public $tasks;
public $quoteType;
public $calendar_start;
public $range_start;
public $range_end;
public $time_type;
public $topLimit = 10;
public $requests = [];
public $unique_ids;
public $panelOpen = false;
const RELATION = [
'Active' => [ // Active Quote, may not be initial quote
'request_method' => 'activeQuote',
'task_method' => 'activeQuotes'
],
'Approved' => [ // Approved Quote, may not be initial quote
'request_method' => 'approvedQuote',
'task_method' => 'approvedQuotes'
],
'Completed' => [ // Completed Quote, may not be initial quote
'request_method' => 'completedQuote',
'task_method' => 'completedQuotes'
],
'Initial' => [ // First created quote
'request_method' => 'initialQuote',
'task_method' => 'initialQuotes'
],
];
public function updatedTimeType()
{
$this->buildResults();
}
public function updatedQuoteType()
{
$this->buildResults();
}
public function updatedTopLimit()
{
$this->buildResults();
}
public function updatedRangeStart()
{
$this->range_start = \Carbon\Carbon::parse($this->range_start)->startOfDay();
}
public function updatedRangeEnd()
{
$this->range_end = \Carbon\Carbon::parse($this->range_end)->endOfDay();
}
public function boot()
{
// date_default_timezone_set( "America/Los_Angeles" );
$this->quoteType = $this->quoteType ?? 'Initial';
$this->calendar_start = now()->startOfYear()->addYear(-2);
$this->range_start = $this->range_start ?? now()->addYear(-1)->startOfDay();
$this->range_end = $this->range_end ?? now();
$this->time_type = $this->time_type ?? 'created_at';
$this->tasks = collect([]);
}
public function reloadResults()
{
return $this->buildResults();
}
protected function buildResults()
{
// Get associated tasks
$this->tasks = Task::query()
->with(
self::RELATION[$this->quoteType]['task_method'],
function($quote) {
$quote->whereNotNull('was_submitted_on');
$quote->whereBetween(
'quotes.'.$this->time_type,
[
\Carbon\Carbon::parse($this->range_start),
\Carbon\Carbon::parse($this->range_end)
]
);
}
)
->whereHas(
self::RELATION[$this->quoteType]['task_method'],
function($quote) {
$quote->whereNotNull('was_submitted_on');
$quote->whereBetween(
'quotes.'.$this->time_type,
[
\Carbon\Carbon::parse($this->range_start),
\Carbon\Carbon::parse($this->range_end)
]
);
}
)->withCount([
'approvedQuotes',
'approvedQuotes as approved_quotes_count' => function (Builder $query) {
return $query->when(
$this->quoteType === 'Initial',
function($quote) {
$quote->where('version',1);
}
)->whereBetween(
'quotes.'.$this->time_type,
[
\Carbon\Carbon::parse($this->range_start),
\Carbon\Carbon::parse($this->range_end)
]
);
},
])->get();
// Add requested data to collections
$this->tasks->map( function($task) {
$task->quotes_count = $task->{self::RELATION[$this->quoteType]['task_method']}->count();
$task->average_labor = number_format(
$task->{self::RELATION[$this->quoteType]['task_method']}->avg('info.labor_price') / 100,
2);
$task->average_materials = number_format(
$task->{self::RELATION[$this->quoteType]['task_method']}->avg('info.material_price') / 100,
2);
});
// Sort data by usage
$this->tasks = $this->tasks->sortBy([
['quotes_count', 'desc'],
['id', 'asc'],
]);
}
public function render()
{
return view('livewire.admin.reporting.tasks.top-used-tasks-report', [
'results' => $this->tasks->take($this->topLimit),
]);
}
}
Please or to participate in this conversation.