<?php
namespace App\Models;
use App\Enums\MiniatureStateEnum;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Kiwilan\Steward\Services\Query\FilterModule;
use Kiwilan\Steward\Services\Query\SortModule;
use Kiwilan\Steward\Traits\HasSearchableName;
use Kiwilan\Steward\Traits\HasSeo;
use Kiwilan\Steward\Traits\HasUsername;
use Kiwilan\Steward\Traits\LiveQueryable;
use Kiwilan\Steward\Traits\Mediable;
use Kiwilan\Steward\Traits\Publishable;
use Laravel\Scout\Searchable;
/**
* @property \App\Enums\MiniatureStateEnum|null $state
*/
class Miniature extends Model
{
use HasFactory;
use Mediable;
use HasSeo;
use LiveQueryable;
use HasUsername;
use HasSearchableName, Searchable {
HasSearchableName::searchableAs insteadof Searchable;
}
use Publishable;
protected $username_with = 'name';
protected $username_column = 'slug';
protected $meta_description_from = 'about';
protected array $mediables = [
'picture',
'gallery',
];
protected $fillable = [
'name',
'about',
'description',
'price',
'height',
'width',
'depth',
'state',
'release_year',
'is_out',
'is_new',
'is_hidden',
'picture',
'gallery',
'base_is_square',
'base_width',
'base_height',
];
protected $casts = [
'state' => MiniatureStateEnum::class,
'gallery' => 'array',
'height' => 'float',
'width' => 'float',
'depth' => 'float',
'release_year' => 'integer',
'is_out' => 'boolean',
'is_new' => 'boolean',
'is_hidden' => 'boolean',
'base_is_square' => 'boolean',
'base_width' => 'float',
'base_height' => 'float',
];
public static function sortable()
{
return [
SortModule::make('name', 'Name'),
SortModule::make('price', 'Price'),
SortModule::make('height', 'Size'),
SortModule::make('state', 'State'),
SortModule::make('created_at', 'Created At'),
SortModule::make('updated_at', 'Updated At'),
SortModule::make('published_at', 'Published At'),
SortModule::scope('name', 'Name', 'orderByName'),
];
}
public static function filterable()
{
return [
FilterModule::search('q', ['name', 'creator.name']),
FilterModule::partial('name'),
FilterModule::scope('state', 'whereState'),
FilterModule::scope('states', 'whereStates'),
FilterModule::scope('armies', 'whereArmies'),
FilterModule::scope('universes', 'whereUniverses'),
FilterModule::scope('matters', 'whereMatters'),
FilterModule::scope('techniques', 'whereTechniques'),
];
}
public function scopeOrderByName(Builder $query, string $direction = 'asc'): Builder
{
return $query->join('collectors', 'miniatures.creator_id', '=', 'collectors.id')
->orderBy('collectors.name', $direction)
->select('miniatures.*')
;
}
public function scopeWhereState(Builder $query, mixed $state = null): Builder
{
if (is_array($state)) {
$state = $state[0] ?? null;
}
if ($state) {
return $query->where('state', '=', $state);
}
return $query;
}
public function scopeWhereStates(Builder $query, array $states): Builder
{
return $states
? $query->whereIn('state', $states)
: $query;
}
public function scopeWhereArmies(Builder $query, array $armies): Builder
{
return $armies
? $query->whereHas('armies', fn (Builder $query) => $query->whereIn('slug', $armies))
: $query;
}
public function scopeWhereUniverses(Builder $query, array $universes): Builder
{
return $universes
? $query->whereHas('universe', fn (Builder $query) => $query->whereIn('slug', $universes))
: $query;
}
public function scopeWhereMatters(Builder $query, array $matters): Builder
{
return $matters
? $query->whereHas('matter', fn (Builder $query) => $query->whereIn('slug', $matters))
: $query;
}
public function scopeWhereTechniques(Builder $query, array $techniques): Builder
{
return $techniques
? $query->whereHas('techniques', fn (Builder $query) => $query->whereIn('slug', $techniques))
: $query;
}
public function scopeWhereIsNotHidden(Builder $query): Builder
{
return $query->where('is_hidden', '=', false);
}
public function getArmiesListAttribute(): string
{
$armies = $this->armies->implode('name', ', ');
return empty($armies) ? 'N/A' : $armies;
}
public function getGameplaysListAttribute(): string
{
$gameplays = $this->gameplays->implode('name', ', ');
return empty($gameplays) ? 'N/A' : $gameplays;
}
public function getTechniquesListAttribute(): string
{
$techniques = $this->techniques->implode('name', ', ');
return empty($techniques) ? 'N/A' : $techniques;
}
public function getMattersListAttribute(): string
{
$matters = $this->matters->implode('name', ', ');
return empty($matters) ? 'N/A' : $matters;
}
public function getDimensionsListAttribute(): string
{
$height = $this->height ? "{$this->height} cm" : 'N/A';
$width = $this->width ? "{$this->width} cm" : 'N/A';
$depth = $this->depth ? "{$this->depth} cm" : 'N/A';
if (! $this->height && ! $this->width && ! $this->depth) {
return 'Unknown';
}
return implode(' x ', array_filter([$height, $width, $depth]));
}
public function matter(): BelongsTo
{
return $this->belongsTo(Matter::class, 'matter_primary_id');
}
public function matters(): BelongsToMany
{
return $this->belongsToMany(Matter::class);
}
public function armies(): BelongsToMany
{
return $this->belongsToMany(Army::class);
}
public function universe(): BelongsTo
{
return $this->belongsTo(Universe::class);
}
public function gameplays(): BelongsToMany
{
return $this->belongsToMany(Gameplay::class);
}
public function techniques(): BelongsToMany
{
return $this->belongsToMany(Technique::class);
}
// public function supplier(): BelongsTo
// {
// return $this->belongsTo(Supplier::class);
// }
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'creator_id');
}
public function owner(): BelongsTo
{
return $this->belongsTo(User::class, 'owner_id');
}
public function bundle(): BelongsTo
{
return $this->belongsTo(Bundle::class);
}
public function paints(): BelongsToMany
{
return $this->belongsToMany(Paint::class);
}
public function reviews(): MorphMany
{
return $this->morphMany(Review::class, 'reviewable')
->orderBy('created_at', 'desc')
;
}
public function favorites(): MorphMany
{
return $this->morphMany(Favorite::class, 'favoritable')
->orderBy('created_at', 'desc')
;
}
public function toSearchableArray()
{
$this->load('armies', 'universe', 'gameplays', 'creator', 'owner');
return [
'id' => $this->id,
'name' => $this->name,
'slug' => $this->slug,
'about' => $this->about,
'price' => $this->price,
'height' => $this->height,
'state_locale' => $this->state?->locale(),
'state_value' => $this->state?->value,
'creator_name' => $this->creator?->name,
'creator_display_name' => $this->creator?->display_name,
'owner_name' => $this->owner?->name,
'owner_display_name' => $this->owner?->display_name,
'armies' => $this->armies_list,
'universe' => $this->universe?->name,
'gameplays' => $this->gameplays_list,
'picture' => $this->mediable('picture'),
];
}
protected function makeAllSearchableUsing(Builder $query)
{
Model::handleLazyLoadingViolationUsing(fn () => $query->with([
'creator',
'owner',
'armies',
'universe',
'gameplays',
]));
}
}
<?php
namespace App\Http\Livewire\Listing;
use App\Enums\MiniatureStateEnum;
use App\Models\Army;
use App\Models\Matter;
use App\Models\Miniature;
use App\Models\Technique;
use App\Models\Universe;
use Kiwilan\Steward\Traits\LiveListing;
use Livewire\Component;
use Livewire\WithPagination;
class Miniatures extends Component
{
use WithPagination;
use LiveListing;
public $queryString = [];
public array $filter = [
'statuses' => [],
'armies' => [],
'universes' => [],
'matters' => [],
'techniques' => [],
];
public function model(): string
{
return Miniature::class;
}
public function relations(): array
{
return ['creator', 'owner'];
}
public function defaultSort(): string
{
return '-created_at';
}
public function sortable(): array
{
return Miniature::getSortable();
}
public function render()
{
$models = Miniature::query()
->liveFilter([
...$this->filter,
'q' => $this->q,
])
->liveSort($this->sort)
->with($this->relations())
->paginate(perPage: $this->size, page: $this->page)
;
return view('components.livewire.listing.miniatures', [
'models' => $models,
'states' => MiniatureStateEnum::toArray(),
'armies' => $this->setFilter(Army::class, 'name', 'slug'),
'matters' => $this->setFilter(Matter::class, 'name', 'slug'),
'techniques' => $this->setFilter(Technique::class, 'name', 'slug'),
'universes' => $this->setFilter(Universe::class, 'name', 'slug'),
]);
}
}