Jedinečnosť a súbeh
Slugy sú predvolene jedinečné globálne. Pravidlo zúžite alebo zrušíte pre každú definíciu:
use Illuminate\Database\Eloquent\Builder;
SlugDefinition::for('slug')->from('name')->unique(); // global (default)
SlugDefinition::for('slug')->from('name')->uniqueWithin('clinic_id'); // per clinic — DB-indexable
SlugDefinition::for('slug')->from('name')->uniqueWithin('shop_id', 'category_id');
SlugDefinition::for('slug')->from('name')->uniqueWhere(
fn (Builder $query, Post $post) => $query->where('type', $post->type), // application-level only
);
SlugDefinition::for('code')->from('sku')->notUnique();
SlugDefinition::for('slug')->from('name')->excludeTrashed(); // trashed rows free their slugAko sa overujú kolízie
- Bez globálnych scopes — tenancy, published či SoftDeletes scope nikdy neskryje obsadený slug — potom so stĺpcami rozsahu alebo closure z definície, pravidlom pre zmazané riadky a s vylúčením vlastného kľúča modelu. NULL je pre overenie platná hodnota rozsahu.
- Soft-deleted riadky si predvolene ponechávajú slug rezervovaný (include_trashed), takže obnovenie je bezpečné a zhoduje sa s obyčajným unikátnym indexom; excludeTrashed() ich uvoľní a pri obnovení sa jedinečnosť znovu overí a v prípade potreby doplní nový sufix.
- Rezervované slová (z konfigurácie a z reserved()) sa počítajú ako obsadené.
- Zmena stĺpca rozsahu pri úprave znovu overí jedinečnosť a doplní nový sufix (pri Strict vyhodí výnimku).
- uniqueAcrossLocales() a closures v uniqueWhere() vynucuje len aplikácia.
Ohraničené overovanie
Overovanie je ohraničené a dávkové: najprv základ (v bežnom prípade jeden dopyt), potom sekvenčné kandidáty v dávkach po 10 — najviac šesť dopytov pre 50 kolízií — potom 10 náhodných sufixov a nakoniec SlugGenerationException::exhausted(). Každú hranicu nastavíte cez konfiguračné kľúče limits.*.
Súbeh
Dvaja zapisovatelia si môžu vybrať ten istý voľný slug. Unikátny index odmietne toho druhého; sluggable zachytí UniqueConstraintViolationException, znovu overí kandidátov so zamykajúcim čítaním, doplní nový sufix a zápis zopakuje (concurrency.retries, predvolene 3). Vo vonkajšej transakcii beží každý pokus v SAVEPOINT, takže transakcia zostane v PostgreSQL použiteľná.
- Porušenie, ktoré nespomína žiadny slugový index, sa prepošle ďalej bez zmeny.
- Ak opätovné overenie nenájde nič obsadené, vyhodí sa SlugGenerationException::constraintMismatch() — váš index nesúhlasí s definíciou; spustite sluggable:indexes --dry-run.
- Ručná hodnota pri Strict, ktorá súbeh prehrá, vyhodí SlugAlreadyTakenException; po vyčerpaní opakovaní sa vyhodí retriesExhausted().
- Každé opakovanie odošle SlugCollisionRetried a znovu spustí creating/updating aj pre vaše listenery — pri modeloch s neidempotentnými listenermi použite retries(0).
Nový sufix dostanú len hodnoty, ktoré vytvoril sluggable. Vo vnútri Slugs::withoutGeneration() je vypnuté aj opakovanie: duplicitný slug, ktorý zapíšete sami, skončí databázovou výnimkou UniqueConstraintViolationException. Pri ManualSlugPolicy::Strict hodnota, ktorú pri tomto uložení nevytvorilo žiadne generovanie — ručná hodnota alebo hodnota uložená cez saveQuietly() bez Slugs::apply() — vyhodí SlugAlreadyTakenException namiesto prepísania.
SlugDefinition::for('slug')->from('name')->retries(5); // more race retries
SlugDefinition::for('slug')->from('name')->retries(0); // rethrow the violation untouchedJedinečnosť bez databázového indexu — closure scopes, jedinečnosť naprieč jazykmi alebo tabuľky bez indexu — je pri súbehu len na úrovni najlepšej snahy. Pre spoľahlivú ochranu vytvorte natívne indexy databázy.
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.