Skip to content

Commit

Permalink
feat: results cache (#97)
Browse files Browse the repository at this point in the history
  • Loading branch information
andreiio authored Nov 24, 2024
1 parent f5f3c3b commit 9bc97f0
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 63 deletions.
97 changes: 36 additions & 61 deletions app/Livewire/Pages/ElectionResults.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,16 @@

use App\Models\Candidate;
use App\Models\Party;
use App\Models\Record;
use App\Models\Vote;
use App\Repositories\RecordsRepository;
use App\Repositories\VotesRepository;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Number;
use Illuminate\View\View;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use stdClass;
use Tpetry\QueryExpressions\Function\Aggregate\Max;
use Tpetry\QueryExpressions\Function\Aggregate\Min;
use Tpetry\QueryExpressions\Function\Aggregate\Sum;
use Tpetry\QueryExpressions\Language\Alias;

class ElectionResults extends ElectionPage
{
Expand Down Expand Up @@ -60,16 +56,14 @@ public function candidates(): Collection
#[Computed]
public function aggregate(): Collection
{
$result = Vote::query()
->whereBelongsTo($this->election)
->forLevel(
level: $this->level,
country: $this->country,
county: $this->county,
locality: $this->locality,
aggregate: true,
)
->get();
$result = VotesRepository::getForLevel(
election: $this->election,
level: $this->level,
country: $this->country,
county: $this->county,
locality: $this->locality,
aggregate: true,
);

$total = $result->sum('votes');

Expand All @@ -89,17 +83,15 @@ public function aggregate(): Collection
#[Computed]
public function recordStats(): Collection
{
$record = Record::query()
->whereBelongsTo($this->election)
->forLevel(
level: $this->level,
country: $this->country,
county: $this->county,
locality: $this->locality,
aggregate: true,
)
->toBase()
->first();
$record = RecordsRepository::getForLevel(
election: $this->election,
level: $this->level,
country: $this->country,
county: $this->county,
locality: $this->locality,
aggregate: true,
toBase: true,
);

return collect($record)
->forget('place')
Expand All @@ -112,40 +104,23 @@ public function recordStats(): Collection
#[Computed]
public function data(): Collection
{
return DB::query()
->select([
new Alias(DB::raw('ANY_VALUE(votable_id)'), 'votable_id'),
new Alias(DB::raw('ANY_VALUE(votable_type)'), 'votable_type'),
new Alias(new Max('votes'), 'votes'),
new Alias(new Sum('votes'), 'total_votes'),
new Alias(new Min('part'), 'part'),
'place',
])
->groupBy('place')
->fromSub(
Vote::query()
->whereBelongsTo($this->election)
->forLevel(
level: $this->level,
country: null,
county: $this->county,
locality: null,
)
->toBase(),
'votes'
)
->get()
->mapWithKeys(function (stdClass $vote) {
$votable = $this->getVotable($vote->votable_type, $vote->votable_id);

return [
$vote->place => [
'value' => percent($vote->votes, $vote->total_votes, formatted: true),
'color' => $votable->color,
'label' => $votable->getDisplayName(),
],
];
});
return VotesRepository::getMapDataForLevel(
election: $this->election,
level: $this->level,
country: null,
county: $this->county,
locality: null,
)->mapWithKeys(function (stdClass $vote) {
$votable = $this->getVotable($vote->votable_type, $vote->votable_id);

return [
$vote->place => [
'value' => percent($vote->votes, $vote->total_votes, formatted: true),
'color' => $votable->color,
'label' => $votable->getDisplayName(),
],
];
});
}

protected function getVotable(string $type, int $id): Party|Candidate
Expand Down
9 changes: 8 additions & 1 deletion app/Models/Record.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
use App\Concerns\BelongsToElection;
use App\Concerns\CanGroupByDataLevel;
use App\Concerns\HasTemporaryTable;
use App\Contracts\ClearsCache;
use App\Contracts\TemporaryTable;
use App\Enums\DataLevel;
use App\Enums\Part;
use App\Repositories\RecordsRepository;
use Database\Factories\RecordFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
Expand All @@ -18,7 +20,7 @@
use Tpetry\QueryExpressions\Function\Aggregate\Sum;
use Tpetry\QueryExpressions\Language\Alias;

class Record extends Model implements TemporaryTable
class Record extends Model implements TemporaryTable, ClearsCache
{
use BelongsToElection;
use CanGroupByDataLevel;
Expand Down Expand Up @@ -112,4 +114,9 @@ public function getTemporaryTableUniqueColumns(): array
{
return ['election_id', 'county_id', 'country_id', 'section'];
}

public function clearCache(int $electionId): bool
{
return RecordsRepository::clearCache($electionId);
}
}
9 changes: 8 additions & 1 deletion app/Models/Vote.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@
use App\Concerns\BelongsToElection;
use App\Concerns\CanGroupByDataLevel;
use App\Concerns\HasTemporaryTable;
use App\Contracts\ClearsCache;
use App\Contracts\TemporaryTable;
use App\Enums\DataLevel;
use App\Enums\Part;
use App\Repositories\VotesRepository;
use Database\Factories\VoteFactory;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
Expand All @@ -19,7 +21,7 @@
use Tpetry\QueryExpressions\Function\Aggregate\Sum;
use Tpetry\QueryExpressions\Language\Alias;

class Vote extends Model implements TemporaryTable
class Vote extends Model implements TemporaryTable, ClearsCache
{
use BelongsToElection;
use CanGroupByDataLevel;
Expand Down Expand Up @@ -94,4 +96,9 @@ public function getTemporaryTableUniqueColumns(): array
{
return ['election_id', 'county_id', 'country_id', 'section', 'votable_type', 'votable_id'];
}

public function clearCache(int $electionId): bool
{
return VotesRepository::clearCache($electionId);
}
}
47 changes: 47 additions & 0 deletions app/Repositories/RecordsRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

declare(strict_types=1);

namespace App\Repositories;

use App\Enums\DataLevel;
use App\Models\Election;
use App\Models\Record;
use App\Services\CacheService;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;

class RecordsRepository
{
public static function clearCache(int $election): bool
{
return CacheService::make('records', $election)->clear();
}

public static function getForLevel(
Election $election,
DataLevel $level,
?string $country = null,
?int $county = null,
?int $locality = null,
bool $aggregate = false,
bool $toBase = false,
array $addSelect = [],
) {
return CacheService::make('records', $election, $level, $country, $county, $locality, $aggregate, $toBase, $addSelect)
->remember(function () use ($election, $level, $country, $county, $locality, $aggregate, $toBase, $addSelect) {
$query = Record::query()
->whereBelongsTo($election)
->forLevel(
level: $level,
country: $country,
county: $county,
locality: $locality,
aggregate: $aggregate,
)
->when($addSelect, fn (EloquentBuilder $query) => $query->addSelect($addSelect))
->when($toBase, fn (EloquentBuilder $query) => $query->toBase());

return $aggregate ? $query->first() : $query->get();
});
}
}
94 changes: 94 additions & 0 deletions app/Repositories/VotesRepository.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php

declare(strict_types=1);

namespace App\Repositories;

use App\Enums\DataLevel;
use App\Models\Election;
use App\Models\Vote;
use App\Services\CacheService;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Support\Facades\DB;
use Tpetry\QueryExpressions\Function\Aggregate\Max;
use Tpetry\QueryExpressions\Function\Aggregate\Min;
use Tpetry\QueryExpressions\Function\Aggregate\Sum;
use Tpetry\QueryExpressions\Language\Alias;

class VotesRepository
{
public static function clearCache(int $election): bool
{
return CacheService::make('votes', $election)->clear();
}

public static function getForLevel(
Election $election,
DataLevel $level,
?string $country = null,
?int $county = null,
?int $locality = null,
bool $aggregate = false,
bool $toBase = false,
array $addSelect = [],
) {
return CacheService::make('votes', $election, $level, $country, $county, $locality, $aggregate, $toBase, $addSelect)
->remember(function () use ($election, $level, $country, $county, $locality, $aggregate, $toBase, $addSelect) {
$query = Vote::query()
->whereBelongsTo($election)
->forLevel(
level: $level,
country: $country,
county: $county,
locality: $locality,
aggregate: $aggregate,
)
->when($addSelect, fn (EloquentBuilder $query) => $query->addSelect($addSelect))
->when($toBase, fn (EloquentBuilder $query) => $query->toBase());

return $query->get();
});
}

public static function getMapDataForLevel(
Election $election,
DataLevel $level,
?string $country = null,
?int $county = null,
?int $locality = null,
bool $aggregate = false,
bool $toBase = false,
array $addSelect = [],
) {
return CacheService::make(['votes', 'map-data'], $election, $level, $country, $county, $locality, $aggregate, $toBase, $addSelect)
->remember(function () use ($election, $level, $country, $county, $locality, $aggregate, $toBase, $addSelect) {
$query = DB::query()
->select([
new Alias(DB::raw('ANY_VALUE(votable_id)'), 'votable_id'),
new Alias(DB::raw('ANY_VALUE(votable_type)'), 'votable_type'),
new Alias(new Max('votes'), 'votes'),
new Alias(new Sum('votes'), 'total_votes'),
new Alias(new Min('part'), 'part'),
'place',
])
->groupBy('place')
->fromSub(
Vote::query()
->whereBelongsTo($election)
->forLevel(
level: $level,
country: $country,
county: $county,
locality: $locality,
aggregate: $aggregate,
)
->toBase(),
'votes'
)
->when($addSelect, fn (EloquentBuilder $query) => $query->addSelect($addSelect))
->when($toBase, fn (EloquentBuilder $query) => $query->toBase());

return $query->get();
});
}
}

0 comments on commit 9bc97f0

Please sign in to comment.