---
title: "Subjekty v multi-tenant aplikácii — Lifecycle for Laravel | Roundly"
description: "Sweep, plány vo fronte, upozornenia, opakovania a lifecycle:adopt spracujú každý subjekt v jeho tenante — s teams-for-laravel aj s vlastným riešením."
url: https://roundly-consulting.com/sk/open-source/navody/lifecycle-for-laravel/multi-tenant-subjekty
language: sk
---

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

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

# Subjekty v multi-tenant aplikácii

Sweep, job naplánovaného prechodu vo fronte, upozornenia na expiráciu, schedules()->retry() a adoptAll() / lifecycle:adopt pracujú so subjektom mimo kontextu vášho kódu: konzolový príkaz ani worker fronty nemá žiadneho tenanta, prípadne má iného. Od verzie 2.1 každý z nich spracuje subjekt v kontexte jeho tenanta, a to cez jeden kontrakt:

```php
namespace RoundlyConsulting\Lifecycle\Contracts;

use Closure;
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Lifecycle\Exceptions\SubjectContextUnavailableException;

interface RestoresSubjectContext
{
    /** @throws SubjectContextUnavailableException when the subject's tenant is gone */
    public function run(Model $subject, Closure $callback): mixed;
}
```

run() vstúpi do kontextu subjektu, zavolá callback a potom obnoví predchádzajúci kontext — aj vtedy, keď callback vyhodí výnimku. V tomto kontexte bežia všetky udalosti, hooky, handlery aj joby prechodu subjektu, takže sa záznam uloží ako vlastný tenant a e-maily, notifikácie a nadväzujúce joby, ktoré spustí, patria tiež tomuto tenantovi.

| Cesta | Čo beží v kontexte |
| --- | --- |
| `lifecycle:sweep, sweep(), schedules()->runDue()` | Transakcia každého splatného plánu — priamo alebo v RunScheduledTransitionJob vo fronte. |
| `sweep(), schedules()->warn()` | Odoslanie LifecycleExpiring pri každom upozornení na expiráciu. |
| `schedules()->retry($id), retryScheduled()` | Opakovanie plánu. |
| `adoptAll(), model()->adopt(), lifecycle:adopt` | Adopcia každého riadku. |

Všetko, čo voláte s modelom v ruke — $ticket->transition(), Lifecycles::for($ticket), adopt($ticket), zmrazenie, rollback — beží ako doteraz vo vašom vlastnom kontexte. Kontrola kvóty len číta a počíta celú tabuľku bez globálnych scopes, preto pre limit na tím nastavte scope kvóty na stĺpec tímu:

```php
$lifecycle->state(TicketStatus::Open)
    ->quota(5, scope: 'team_id');   // a per-team limit: the quota count itself ignores global scopes
```

## Ktorý kontext

lifecycle.subjects.context určuje triedu RestoresSubjectContext. Kľúč je vedľa zoznamu modelov; obyčajný zoznam modelov funguje ďalej:

```php
// config/lifecycle.php
'subjects' => [
    App\Models\Ticket::class,

    'context' => null,   // null = the teams-for-laravel bridge when teams is installed, else none
],
```

- null (predvolené) — most k teams-for-laravel, ak je teams nainštalovaný, inak Support\\NullSubjectContext, ktorý callback spustí bez zmeny.
- Vlastná trieda — ľubovoľný spôsob multi-tenancy. Hodnota, ktorá nie je inštanciovateľnou implementáciou (samotný kontrakt, abstraktná trieda, neexistujúca či nesúvisiaca trieda, hodnota, ktorá nie je reťazec), vyhodí InvalidLifecycleConfigurationException.
- php artisan about v sekcii Lifecycle zobrazí Subject context: teams, none alebo krátky názov vašej triedy.

## teams-for-laravel

Ak máte nainštalovaný [teams-for-laravel](https://roundly-consulting.com/sk/open-source/navody/teams-for-laravel/oddelenie-dat-timov.md), subjekt s ScopedToTeam beží v Teams::runAs() svojho vlastného tímu — bez akéhokoľvek nastavovania. Kľúč tímu sa prečíta z getTeamColumn() a tím sa načíta cez nakonfigurovaný model tímu. Bez toho by ochrana zápisov v teams zápis odmietla, pretože chýba aktuálny tím, takže naplánovaný prechod ani expirácia cez TTL (expiresVia) subjektu viazaného na tím by sa z konzoly nikdy nespustili — vyriešený tiket by sa nikdy automaticky neuzavrel. Joby odoslané v tomto kontexte nesú tím ďalej, pretože teams prenáša svoj kontext do jobov vo fronte. Model bez ScopedToTeam, aj taký, ktorý používa len BelongsToTeam, beží bez zmeny.

## Tenant, ktorý už neexistuje

Ak bol tím subjektu zmazaný (počítajú sa aj soft-deleted tímy), uložený tím neexistuje alebo je stĺpec tímu NULL, kontext vyhodí SubjectContextUnavailableException a so subjektom sa nič neurobí:

| Cesta | Výsledok |
| --- | --- |
| Splatný plán | Nespustí sa, nejde o chybu a nezapočíta sa pokus: due\_at sa posunie na teraz + schedules.retry\_after a last\_denial bude context\_missing. Započíta sa ako contextMissing — stĺpec context missing v lifecycle:sweep a k tomu riadok s varovaním. Spustí sa, keď sa tím vráti, napríklad po obnovení. |
| Upozornenie na expiráciu | Neodošle sa ani nenahlási: next\_warn\_at sa posunie na teraz + schedules.retry\_after. |
| `schedules()->retry()` | Vráti false; riadok zostane failed. |
| `adoptAll() / lifecycle:adopt` | Riadok sa preskočí a odovzdá callbacku skipped; príkaz vypíše pre každý riadok Skipped: … a nakoniec súhrn Skipped N subject(s). |

```php
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Log;
use RoundlyConsulting\Lifecycle\Exceptions\SubjectContextUnavailableException;
use RoundlyConsulting\Lifecycle\Facades\Lifecycles;

$result = Lifecycles::sweep();
$result->contextMissing;                       // schedules put off: their subject's team is gone

Lifecycles::schedules()->retry($scheduleId);   // false while the subject's team is gone — the row stays failed

Lifecycles::model(Ticket::class)->adopt(
    skipped: fn (Model $ticket, SubjectContextUnavailableException $e) => Log::warning($e->getMessage()),
);
```

Plán tímu, ktorý sa už nevráti, sa odkladá o každé retry\_after. Keď tím odstraňujete natrvalo, ukončite plány jeho subjektov v Teams::withoutTeamScope() — cancelScheduled('…'), neverExpire() — alebo subjekty natrvalo zmažte, čím sa vyčistia aj ich plány.

## Vlastná multi-tenancy

Implementujte kontrakt, pri tenantovi, ktorý už neexistuje, vyhoďte SubjectContextUnavailableException — balík ju na všetkých vyššie uvedených cestách zachytí — a triedu uveďte v konfigurácii:

```php
use Closure;
use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Lifecycle\Contracts\RestoresSubjectContext;
use RoundlyConsulting\Lifecycle\Exceptions\SubjectContextUnavailableException;

final class TenantSubjectContext implements RestoresSubjectContext
{
    public function run(Model $subject, Closure $callback): mixed
    {
        $tenant = Tenant::find($subject->tenant_id)
            ?? throw new SubjectContextUnavailableException("Tenant {$subject->tenant_id} is gone.");

        return $tenant->execute($callback); // restores the previous tenant, also on an exception
    }
}

// config/lifecycle.php
'subjects' => [
    App\Models\Ticket::class,
    'context' => App\Lifecycle\TenantSubjectContext::class,
],
```

## 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/lifecycle-for-laravel.md)
