Database And Migrations
Packages own their database tables. Core tables should not grow package-specific columns unless the column is part of a stable extension contract.
Package Migrations
Section titled “Package Migrations”Place normal migrations in database/migrations.
Use Spatie Package Tools from the package provider:
$package ->name(self::$name) ->hasMigrations([ 'create_example_items_table', ]);For dynamic discovery, packages can scan migration filenames and pass them to Package Tools.
Extension Lifecycle Ledger
Section titled “Extension Lifecycle Ledger”Composer presence makes a package available. The capell_extensions row controls whether it can affect runtime.
Statuses:
installing: install has started and runtime is inactive.enabled: runtime providers may load.disabled: installed but runtime providers must not load.failed: install failed; runtime remains inactive and error details live inmetadata.install_error.
Package install actions should mark a package installing before running package install commands, enabled only after success, and failed when an install command throws or exits unsuccessfully.
Settings Migrations
Section titled “Settings Migrations”Place settings migrations in database/settings and make them idempotent:
if (! $this->migration-assistant->exists('example.enabled')) { $this->migration-assistant->add('example.enabled', true);}Register settings migrations from the package install command.
Models
Section titled “Models”Register package models with Capell when they should appear in package metadata, protected-table checks, exports, or diagnostics:
CapellCore::registerModels([ExampleModel::class]);Use morph maps for polymorphic package models.
Protected Tables
Section titled “Protected Tables”If a package table should not be removed by cleanup tools, register it:
CapellCore::registerProtectedTable(fn (): string => 'example_items');Cross-Package Data
Section titled “Cross-Package Data”Use extension points and Capell installed-state checks instead of hard dependencies when another package is optional.
Do not use class_exists() alone to decide whether a Capell package’s tables or models are available. Composer can autoload a package class before capell:extension-install has marked the package installed or before its migrations have created the tables.
if (CapellCore::isPackageInstalled('capell-app/blog') && class_exists(Article::class)) { ExampleRegistry::register(Article::class);}If code may run during install, upgrade, diagnostics, or other partial database states, also guard the table before querying:
if ( CapellCore::isPackageInstalled('capell-app/blog') && Schema::hasTable('articles') && class_exists(Article::class)) { Article::query()->latest()->first();}