Validácia, filtre a resources
TagsRule
Validačné pravidlo, ktoré kontroluje vstup so štítkami rovnako ako uloženie — kurátorované zoznamy, limity, rozsahy a povolené typy — bez vedľajších účinkov: nikdy nevytvorí štítok:
use RoundlyConsulting\Tags\Rules\TagsRule;
public function rules(): array
{
return [
'category' => ['nullable', TagsRule::for('genre')], // single type: one value
'labels' => ['array', TagsRule::for('label')->taggable($this->route('concert'))],
'skills' => ['array', TagsRule::for('skill')->within($this->user()->team)->max(3)],
'topic_ids' => ['array', TagsRule::for('topic')->byId()->taggable($this->route('article'))],
];
}| Volanie | Význam |
|---|---|
TagsRule::for($type) | Validuje vstup pre typ bez vedľajších účinkov — nikdy nevytvorí štítok. Zoznam alebo jedna hodnota pri type s jedným štítkom. |
->taggable($model) | Validuje tak, ako bude označovať tento model: jeho kandidátske rozsahy, globalOnly typu a povolené typy. Funguje aj neuložený model — číta sa len jeho trieda, tagScope() a tagTypes(). Overené kľúče premeníte na štítky cez Tags::for($model)->findMany($keys, $type). |
->within(?Model $scope) | Vyhodnocuje v rozsahu a globálnych štítkoch (null: len globálne); prepíše to, čo by dal taggable(). |
->byId() | Položky sú primárne kľúče (prijímajú sa aj číselné reťazce), nie názvy. |
->max(?int $max) | Najviac toľko rôznych štítkov — nikdy viac, než povoľuje typ. |
Každá položka musí byť platný názov alebo slug (pri byId() kľúč). Pri kurátorovanom type musí každá položka označovať živý štítok v kandidátskych rozsahoch; pri voľnom type neznáme názvy prejdú. Zlyhanie uvedie prvú neúspešnú položku; kľúč iného rozsahu a chýbajúci kľúč zlyhajú rovnako, takže hlásenie o iných rozsahoch nič neprezradí:
| Zlyhanie | Hlásenie (anglicky) |
|---|---|
| Nie je zoznam pri type s viacerými štítkami | The :attribute must be a list of tags. |
| Typ, ktorý model nesmie niesť (s taggable()) | The :attribute cannot be used on this record. |
| Prázdna, pridlhá či na slug nevhodná položka alebo chybný kľúč | The :attribute contains an invalid tag [:tag]. |
| Neznámy názov kurátorovaného typu; kľúč chýbajúci, iného typu či rozsahu — rovnako | The :attribute contains the unknown tag [:tag]. |
| Viac rôznych štítkov, než dovoľuje limit (neznáme voľné názvy sa rátajú podľa slugu) | The :attribute may hold at most :max tags. |
Hlásenia pochádzajú z tags::validation v angličtine a slovenčine.
Od validovaného vstupu k zápisu
// Validated keys arrive as strings, and a string is a name to the tagging verbs:
// turn them into tags first, in the scopes the rule validated them in
$topics = Tags::for($article)->findMany($request->validated('topic_ids'), 'topic'); // a rule with taggable($article)
$article->syncTags($topics, 'topic');
$skills = Tags::type('skill')->within($team)->findMany($skillIds); // a rule with within($team)->byId()
// Names and slugs go straight through
$concert->syncTags($request->validated('labels'), 'label');TagsFilter
Filter pre query-builder-for-laravel, ktorý aplikuje scopes štítkov modelu, takže indexový endpoint filtruje presne ako withAnyTags() a spol.:
use RoundlyConsulting\QueryBuilder\AllowedFilter;
use RoundlyConsulting\QueryBuilder\QueryBuilder;
use RoundlyConsulting\Tags\Http\Filters\TagsFilter;
// GET /concerts?filter[category]=music&filter[labels]=live,outdoor
$concerts = QueryBuilder::for(Concert::class, $request)
->allowedFilters(
AllowedFilter::custom('category', TagsFilter::any('genre')->withDescendants()),
AllowedFilter::custom('labels', TagsFilter::all('label')),
AllowedFilter::custom('without', TagsFilter::none('label')),
AllowedFilter::custom('team_labels', TagsFilter::any('label')->within(fn () => $request->user()?->team)),
AllowedFilter::custom('topic', TagsFilter::any('topic')->byId()),
)
->paginate();| Volanie | Význam |
|---|---|
TagsFilter::any($type) / all($type) / none($type) | Scope withAnyTags / withAllTags / withoutTags ako filter pre query-builder — PHP aj HTTP filtrujú rovnako. |
->withDescendants() | Štítok vyhovie aj každým štítkom pod sebou. |
->within(Model|Closure|null $scope) | Obmedzí reťazce na rozsah a globálne štítky; closure sa vyhodnotí až pri aplikovaní filtra — pre filtre deklarované skôr, než je používateľ známy. |
->byId() | Hodnoty sú primárne kľúče, nie slugy ani názvy. |
Hodnoty sú slugy alebo názvy (pri byId() kľúče), oddelené čiarkou alebo ako pole. Prázdna hodnota — ?filter[category]= alebo vymazaný výber — v žiadnom režime nič neobmedzí. Pre modely bez štítku daného typu použite scope withoutTags([], $type) v AllowedFilter::callback.
TagSummaryResource
Tvar na vnorenie štítkov do vlastných JSON resources — kategórie a označení podujatia:
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
use RoundlyConsulting\Tags\Http\Resources\TagSummaryResource;
final class ConcertResource extends JsonResource
{
public function toArray(Request $request): array
{
$genre = $this->resource->tagOf('genre');
return [
'id' => $this->resource->getKey(),
'title' => $this->resource->title,
'genre' => $genre === null ? null : new TagSummaryResource($genre),
'labels' => TagSummaryResource::collection($this->resource->tagsOf('label')),
];
}
}
// The controller: tags in one query, category ancestors in one more
$concerts = Concert::with('tags')->paginate(20);
Tags::loadAncestors($concerts->getCollection()->map(fn (Concert $concert) => $concert->tagOf('genre')));
return ConcertResource::collection($concerts);{
"id": 7,
"title": "Night at the club",
"genre": {
"id": 2,
"type": "genre",
"name": "Jazz",
"slug": "jazz",
"color": null,
"icon": "sax",
"depth": 2,
"ancestors": [
{ "id": 1, "type": "genre", "name": "Music", "slug": "music", "color": "#f24e29", "icon": null, "depth": 1 }
]
},
"labels": [
{ "id": 9, "type": "label", "name": "Live", "slug": "live", "color": null, "icon": null, "depth": 1 }
]
}id, type (názov typu), name a slug v aktuálnom jazyku, farba, ikona a hĺbka — plus ancestors (od koreňa, každý v rovnakom tvare), ak ich načítal loadAncestors(). Bez počtu použití, rozsahu, rodiča a časových pečiatok, takže vykreslenie nespustí žiadnu Gate kontrolu ani dotaz a ku klientovi sa nedostane žiadny názov PHP triedy ani rozsah.
Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.