---
title: "Validácia, filtre a resources — Tags for Laravel | Roundly"
description: "TagsRule pre form requesty, TagsFilter pre indexové endpointy s query-builder a TagSummaryResource na vnorenie štítkov do vlastných API resources."
url: https://roundly-consulting.com/sk/open-source/navody/tags-for-laravel/validacia-a-filtre
language: sk
---

[Všetky balíky](https://roundly-consulting.com/sk/open-source.md)

[Tags for Laravel](https://roundly-consulting.com/sk/open-source/navody/tags-for-laravel.md)

# 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:

```php
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

```php
// 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.:

```php
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:

```php
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);
```

```json
{
  "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 kryptomien](https://roundly-consulting.com/sk/podporte-nas.md)

Odoslaním daru súhlasíte s našimi [podmienkami prijímania darov](https://roundly-consulting.com/sk/podmienky-darov.md).

[Podporte našu open-source prácu (otvorí sa v novej karte)](https://donate.stripe.com/dRmeVe8FX5PF1Qd9pXcEw00) [Pridajte sa k nám na Patreone (otvorí sa v novej karte)](https://www.patreon.com/cw/roundly)

## 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.

[Ponuka do 48 hodín](https://roundly-consulting.com/sk/kontakt.md) [Prezrieť všetky balíky](https://roundly-consulting.com/sk/open-source/navody/tags-for-laravel.md)
