Files
landing-api/app/Models/Concerns/HasContentViews.php
Josep Piguillem 3897113313 Initial commit: Pireon Admin — headless admin panel and public API
Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:55:27 +02:00

51 lines
1.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Models\Concerns;
use App\Models\ContentView;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\MorphMany;
/**
* A record the public site can be reading.
*
* The counts are a sum rather than a column on the record, so nothing has to
* be kept in step and no write path can forget to increment anything. What
* costs attention is reading them in a table, which is what the scope below is
* for: one grouped query for the whole page of rows instead of one per row.
*/
trait HasContentViews
{
/**
* @return MorphMany<ContentView, $this>
*/
public function views(): MorphMany
{
return $this->morphMany(ContentView::class, 'viewable');
}
/**
* How often this was read lately, in every language at once.
*/
public function viewsSince(int $days = 30): int
{
return (int) $this->views()->since($days)->sum('count');
}
/**
* Attach that number to a query, for listings.
*
* @param Builder<static> $query
* @return Builder<static>
*/
public function scopeWithRecentViews(Builder $query, int $days = 30): Builder
{
return $query->withSum(
['views as recent_views' => fn ($views) => $views->since($days)],
'count',
);
}
}