Vlastníci a oprávnenia
Každý import beží pre vlastníka — tím, firmu alebo používateľa v aplikácii pre jedného nájomcu — a každé vyhľadávanie aj zápis zostáva v jeho rámci: párovanie prirodzeného kľúča, vyhľadávanie odkazov, záznamy, ktoré import vytvorí alebo aktualizuje, aj to, čo načíta vrátenie importu.
$imports = Imports::for($team)->by($user);
$imports->session($id); // another owner's id → ImportNotFoundException (404)
$imports->batch($id);
$imports->sessions('assets', SessionStatus::Previewed)->paginate(); // newest first
$imports->running('assets'); // ?SessionHandle — committing or rolling back
Imports::session($id); // trusted path: no owner check — never from HTTP code
Imports::batch($id);Imports::session() a ::batch() bez vlastníka sú dôveryhodná cesta pre PHP kód; HTTP kód ich nikdy nepoužíva.
Ownership — deklarované, bezpečné odmietnutie
use RoundlyConsulting\Imports\Definition\Ownership;
Ownership::column('team_id'); // where team_id = owner key; new records: $context->ownerAttributes()
Ownership::belongsTo('team'); // whereBelongsTo($owner, 'team')
Ownership::morph('owner'); // owner_type + owner_id
Ownership::using(
query: fn (Builder $q, Model $owner) => $q->whereHas('project', fn ($p) => $p->whereBelongsTo($owner)),
owns: fn (Model $record, Model $owner) => $record->project->team_id === $owner->getKey(),
);
Ownership::global(); // the explicit opt-out- Ownership::inherit() je predvolené pre pole s odkazom — pravidlo importéra.
- Každé pravidlo ponúka attributes(Model $owner) — to, čo vráti ImportContext::ownerAttributes(): ['team_id' => 7] pre stĺpec, dvojicu <name>_type / <name>_id pre morph, inak [] — a k tomu isGlobal() a describe().
- Pravidlo importéra obmedzuje párovanie prirodzeného kľúča aj načítanie pri vrátení a po každom vytvorení či aktualizácii musí OwnerScope::owns() záznam prijať, inak riadok zlyhá ako outside_owner.
OwnerScope
interface OwnerScope
{
public function run(Model $owner, Closure $callback): mixed; // ambient context
public function constrain(Builder $query, Model $owner, Ownership $rule): Builder;
public function owns(Model $record, Model $owner, Ownership $rule): bool;
}imports.owner_scope je predvolene Scoping\ExplicitOwnerScope: bez ambientného kontextu — run() len zavolá callback — a uplatní deklarované pravidlá (stĺpec, whereBelongsTo, morph dvojicu, vaše closure v using() alebo nič pri global()). Closure z using() sa uplatní vo vnorenom where a všetko, čo pridá constrain() vlastného scope, sa zoskupí do jednej podmienky, takže orWhere v ňom nikdy nerozšíri vyhľadávanie. Každý krok spracovania, ktorý číta vaše dáta, a každý vstupný bod na pozadí — úloha náhľadu, úlohy častí a vrátenia, callbacky dávky — beží v run($owner).
Recept: teams-for-laravel
teams-for-laravel je voliteľný. S ním spustíte každý import v rámci tímu, ktorý ho vlastní:
use RoundlyConsulting\Imports\Contracts\OwnerScope;
use RoundlyConsulting\Imports\Definition\Ownership;
use RoundlyConsulting\Imports\Scoping\ExplicitOwnerScope;
use RoundlyConsulting\Teams\Facades\Teams;
use RoundlyConsulting\Teams\Models\Team;
final class TeamsOwnerScope implements OwnerScope
{
public function run(Model $owner, Closure $callback): mixed
{
if (! $owner instanceof Team) {
throw new InvalidArgumentException('Imports owned by teams need a team as their owner.');
}
return Teams::runAs($owner, static fn (): mixed => $callback());
}
public function constrain(Builder $query, Model $owner, Ownership $rule): Builder
{
return (new ExplicitOwnerScope)->constrain($query, $owner, $rule);
}
public function owns(Model $record, Model $owner, Ownership $rule): bool
{
return (new ExplicitOwnerScope)->owns($record, $owner, $rule);
}
}// config/imports.php
'owner_scope' => App\Imports\TeamsOwnerScope::class,
'routes' => ['owner_resolver' => App\Imports\CurrentTeamOwner::class], // ResolvesImportOwner → the current teamV Teams::runAs() izolujú TeamScope a TeamWriteGuard z balíka teams každé čítanie aj zápis Eloquentu a tím vlastníka importu nahradí tím, ktorý mal ten, kto úlohu do frontu odoslal. Presne táto trieda je testovaná so skutočným balíkom teams.
Oprávnenia
authorize(?Authenticatable $user, Model $owner, ImportOperation $operation) je povinné — bezpečné odmietnutie. Každá operácia handle sa naň pýta pre aktéra daného handle: čítanie ako View, zápisy ako vlastná operácia.
public function authorize(?Authenticatable $user, Model $owner, ImportOperation $operation): bool
{
return match ($operation) {
ImportOperation::View => $user?->can('view-imports', $owner) ?? false,
ImportOperation::Upload, ImportOperation::Commit => $user?->can('import-assets', $owner) ?? false,
ImportOperation::Rollback => $user?->can('undo-imports', $owner) ?? false,
};
}
// or a Gate one-liner
return $user?->can('import-assets', $owner) ?? false;- Upload — pri nahratí, skôr než sa súbor prečíta alebo uloží, a pri zmenách mapovania, režimu, zdroja, náhľadu a uložených mapovaní.
- Commit — pri importe a pokračovaní a znova na začiatku každého behu dávky; ten, kto import spustil a medzitým stratil prístup, spôsobí zlyhanie dávky (unauthorized).
- Rollback — pri vrátení importu.
- View — každé čítanie: stav, hárky, hlavičky, počty, priebeh, dáta náhľadu, riadky, návrh mapovania a report chýb pri relácii; stav, priebeh, súhrn, záznamy, canRollback, report vrátenia a predikáty pri dávke; zoznam relácií, running(), uložené mapovania a importers()->available(). sessions() bez importéra vypíše len importéry, ktorých authorize() povolí View.
Aktér null (bez by() alebo cez Imports::session() / ::batch()) je systémový kód: čo smie, rozhodne vaše authorize() — žiadne skryté obídenie neexistuje. Výsledok sa uloží pre inštanciu handle a operáciu, takže slučka, ktorá sa opakovane pýta na jeden handle, sa opýta raz.
Audit
Každá udalosť importu nesie ImportAudit — reláciu, dávku, importér, vlastníka, aktéra danej operácie, odkaz na súbor a počty:
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Imports\Events\ImportCompleted;
Event::listen(fn (ImportCompleted $e) => AuditLog::create([
'action' => 'import.completed', 'team_id' => $e->audit->owner->getKey(), 'user_id' => $e->audit->actor?->getKey(),
'details' => ['importer' => $e->audit->importer, 'file' => $e->audit->file->originalName, 'created' => $e->summary->created],
]));So sentinel-for-laravel (voliteľný) zapečatíte importované záznamy z evidencie v listeneri ImportCompleted ($batch->records(RecordAction::Created)) alebo udalosť zapíšete do vlastného zapečateného logu.
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.