Worked extension examples
These developer-facing recipes are kept beside the package contract. Replace the example values with the site-specific records and data objects used by the calling workflow.
<?phpdeclare(strict_types=1);final class ExampleImportSessionExecutorImplementation implements \Capell\MigrationAssistant\Contracts\ImportSessionExecutor{ public function supports(\Capell\MigrationAssistant\Models\ImportSession $session): bool { throw new LogicException('Implement this package contract for the calling site.'); } public function canRetry(\Capell\MigrationAssistant\Models\ImportSession $session): bool { throw new LogicException('Implement this package contract for the calling site.'); } public function execute(\Capell\MigrationAssistant\Models\ImportSession $session): void { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\ImportSessionExecutor::class, ExampleImportSessionExecutorImplementation::class);<?phpdeclare(strict_types=1);final class ExampleImportSessionSubNavigationExtenderImplementation implements \Capell\MigrationAssistant\Contracts\ImportSessionSubNavigationExtender{ /** @return array<int, NavigationItem> */ public function getItems(): array { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\ImportSessionSubNavigationExtender::class, ExampleImportSessionSubNavigationExtenderImplementation::class);<?phpdeclare(strict_types=1);final class ExampleImportSourceReaderImplementation implements \Capell\MigrationAssistant\Contracts\ImportSourceReader{ public function supports(string $extension): bool { throw new LogicException('Implement this package contract for the calling site.'); } public function read(string $path): \Capell\MigrationAssistant\Data\ExternalImportReadResult { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\ImportSourceReader::class, ExampleImportSourceReaderImplementation::class);<?phpdeclare(strict_types=1);final class ExampleMigrationAssistantContextResolverImplementation implements \Capell\MigrationAssistant\Contracts\MigrationAssistantContextResolver{ /** * Execute $callback inside any ambient scope the resolver manages. * The resolver decides what state to set up before the callback and * tears it down after. * * @template TReturn * * @param Closure(): TReturn $callback * @return TReturn */ public function wrap(\Closure $callback, ?int $sourceWorkspaceId = null): mixed { throw new LogicException('Implement this package contract for the calling site.'); } /** * @param array<int, int|string> $pageIds * @return array<int, int|string> */ public function resolvePageIds(array $pageIds, ?int $sourceWorkspaceId = null): array { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\MigrationAssistantContextResolver::class, ExampleMigrationAssistantContextResolverImplementation::class);<?phpdeclare(strict_types=1);final class ExampleMigrationAssistantRowContributorImplementation implements \Capell\MigrationAssistant\Contracts\MigrationAssistantRowContributor{ /** * Extra attributes to include in an exported row. Return [] when no * extra columns exist on the model's table. * * @return array<string, mixed> */ public function extraAttributes(\Illuminate\Database\Eloquent\Model $model): array { throw new LogicException('Implement this package contract for the calling site.'); } /** * Strip attributes from an incoming imported row that core cannot persist. * * @param array<string, mixed> $attributes * @return array<string, mixed> */ public function normalizeIncomingRow(array $attributes): array { throw new LogicException('Implement this package contract for the calling site.'); } /** * Apply any filtering the contributor requires to restrict the query * to rows that should be exportable. Core calls this and does not * describe what the contributor should filter by. * * @template TModel of Model * * @param Builder<TModel> $query * @return Builder<TModel> */ public function scopeExportable(\Illuminate\Database\Eloquent\Builder $query): \Illuminate\Database\Eloquent\Builder { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\MigrationAssistantRowContributor::class, ExampleMigrationAssistantRowContributorImplementation::class);<?phpdeclare(strict_types=1);final class ExamplePageCollisionDetectorImplementation implements \Capell\MigrationAssistant\Contracts\PageCollisionDetector{ /** * @param list<array{site_id: int|null, language_id: int|null, url: string}> $urls * @return array{0: string, 1: list<string>, 2: string} [collisionState, conflictMessages, suggestedAction] */ public function detect(array $urls, ?int $resolvedSiteId): array { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\PageCollisionDetector::class, ExamplePageCollisionDetectorImplementation::class);<?phpdeclare(strict_types=1);final class ExamplePageImportTargetResolverImplementation implements \Capell\MigrationAssistant\Contracts\PageImportTargetResolver{ public function create(string $name): \Capell\MigrationAssistant\Data\PageImportTargetData { throw new LogicException('Implement this package contract for the calling site.'); } public function resolve(\Capell\MigrationAssistant\Models\ImportSession $session): \Capell\MigrationAssistant\Data\PageImportTargetData { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\PageImportTargetResolver::class, ExamplePageImportTargetResolverImplementation::class);<?phpdeclare(strict_types=1);final class ExamplePathAwareImportSourceReaderImplementation implements \Capell\MigrationAssistant\Contracts\PathAwareImportSourceReader{ public function supportsPath(string $path): bool { throw new LogicException('Implement this package contract for the calling site.'); }}
app()->bind(\Capell\MigrationAssistant\Contracts\PathAwareImportSourceReader::class, ExamplePathAwareImportSourceReaderImplementation::class);