Skip to content

Instantly share code, notes, and snippets.

@leek
Created March 21, 2025 14:51
Show Gist options
  • Save leek/6b6bdb16c76469ad2268b02158d18361 to your computer and use it in GitHub Desktop.
Save leek/6b6bdb16c76469ad2268b02158d18361 to your computer and use it in GitHub Desktop.
<?php
namespace App\Concerns;
use Illuminate\Contracts\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
trait HasPrimary
{
/**
* Automatically apply default values and casts.
*/
public function initializeHasPrimary(): void
{
$this->attributes['is_primary'] ??= false;
$this->casts['is_primary'] = 'boolean';
}
/**
* The "booted" method of the model.
*/
protected static function bootHasPrimary(): void
{
static::saved(function (Model $model) {
if ($model->is_primary) {
// Ensure only one primary exists
static::getHasPrimaryQueryBuilder($model)
->whereKeyNot($model->getKey())
->update([
static::getHasPrimaryField() => false,
])
;
} else {
// If no primary exists, set this as primary
$hasPrimary = static::getHasPrimaryQueryBuilder($model)
->where(static::getHasPrimaryField(), true)
->exists()
;
if (! $hasPrimary) {
$model->forceFill([static::getHasPrimaryField() => true])->saveQuietly();
}
}
});
static::deleted(function (Model $model) {
if ($model->is_primary) {
static::getHasPrimaryQueryBuilder($model)
->whereKeyNot($model->getKey())
->first()?->update([
static::getHasPrimaryField() => true,
])
;
}
});
}
/**
* Scope a query to only include default records.
*/
public function scopePrimary(Builder $query): void
{
$query->where(static::getHasPrimaryField(), true);
}
public static function getHasPrimaryField(): string
{
return 'is_primary';
}
/**
* Check if the model is primary.
*/
public function isPrimary(): bool
{
return (bool) $this->{static::getHasPrimaryField()};
}
/**
* Base query builder that ensures soft deletes are handled.
*/
public static function getHasPrimaryQueryBuilder(Model $model): Builder
{
$query = $model->newQuery();
// Apply model-specific constraints
static::applyHasPrimaryQueryFilters($query, $model);
// Ensure soft-deleted records are ignored
if (in_array(SoftDeletes::class, class_uses_recursive($model))) {
$query->withoutTrashed();
}
return $query;
}
/**
* Allow models to define their own constraints on the query.
*/
protected static function applyHasPrimaryQueryFilters(Builder $query, Model $model): void
{
// Models can override this method to apply custom filtering logic.
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment