Creating a component
Step-by-step example based on AuthAdmin from skeleton: admin login, dashboard, user CRUD, migrations, seeder, and CLI command.
Prerequisites: component concept, global middleware in routes/web.php.
1. Directory and component class
The component class implements ComponentInterface directly — metadata, routes,
migrations, views, and assets are declared in methods (not a separate config.php).
Inject PathManager to build paths relative to the application root.
// src/Components/AuthAdmin/AuthAdminComponent.php
namespace Concept\Components\AuthAdmin;
use Concept\Components\AuthAdmin\Commands\UserListCommand;
use Concept\Components\AuthAdmin\Database\Seeders\UserSeeder;
use Concept\Components\AuthAdmin\Extensions\TwigExtension;
use Concept\Core\Foundation\PathManager;
use Concept\Core\Foundation\PathName;
use Concept\Core\Services\Component\Contracts\ComponentInterface;
class AuthAdminComponent implements ComponentInterface
{
private const string NAME = 'AuthAdmin';
private const string VERSION = '1.0.0';
private const string DESCRIPTION = 'Admin authentication, dashboard and user management.';
private const string VIEW_NAMESPACE = 'auth-admin';
protected string $componentDir = __DIR__;
public function __construct(private readonly PathManager $pathManager) {}
public function name(): string { return self::NAME; }
public function version(): string { return self::VERSION; }
public function description(): string { return self::DESCRIPTION; }
public function routes(): ?string
{
return $this->componentDir . '/routes.php';
}
public function providers(): array { return []; }
public function seeders(): array { return [UserSeeder::class]; }
public function migrationPaths(): array
{
return [
$this->pathManager->toRelative($this->componentDir . '/Database/Migrations'),
];
}
public function commands(): array { return [UserListCommand::class]; }
public function viewExtensions(): array { return [TwigExtension::class]; }
public function viewPaths(): array
{
return [
self::VIEW_NAMESPACE => $this->pathManager->toRelative($this->componentDir . '/Views'),
];
}
public function viewContexts(): array
{
return ['/admin' => 'dashboard'];
}
public function assets(): array
{
return [
$this->pathManager->toRelative($this->componentDir . '/Assets/admin-tokens.js') =>
$this->pathManager->getRelative(PathName::PUBLIC, 'components/auth-admin/js/admin-tokens.js'),
];
}
}
viewPaths()— template@auth-admin/login→Views/login.twigviewContexts()— URLs under/admingetroute_namespace = dashboard(see Views)providers()— optional DI service providers (seeAclComponent,DebugBarComponent)
2. Application registration
// config/components.php
use Concept\Components\AuthAdmin\AuthAdminComponent;
return [
'components' => [
AuthAdminComponent::class => AuthAdminComponent::class,
],
];
3. Routes
// src/Components/AuthAdmin/routes.php
/** @var Router $router */
$router->get('/admin/login', [AdminController::class, 'showLogin'])->setName('admin.login');
$router->post('/admin/login', [AdminController::class, 'login'])->setName('admin.login.submit');
$router->group('/admin', function (RouteGroup $route) {
$route->get('/dashboard', [AdminController::class, 'index'])->setName('admin.dashboard');
$route->get('/logout', [AdminController::class, 'logout'])->setName('admin.logout');
})->lazyMiddleware(AuthMiddleware::class);
$router->group('/admin/users', function (RouteGroup $route) {
$route->get('/', [UserController::class, 'index'])->setName('admin.users');
$route->post('/store', [UserController::class, 'store'])->setName('admin.user.store');
// ...
})->lazyMiddleware(AuthMiddleware::class);
Public login — without AuthMiddleware; protected groups — with module middleware.
Route names (admin.*) are used in redirectByName() and Twig uri().
4. Authorization middleware
// Middlewares/AuthMiddleware.php — session check, admin role, ACTIVE status
public function process(...): ResponseInterface
{
if (!$this->auth->check()) {
return new RedirectResponse('/admin/login', HttpStatusCode::FOUND);
}
// ...
return $handler->handle($request);
}
The class is registered in DI automatically; League Route creates the instance through the container.
5. Controller and FormRequest
// Controllers/AdminController.php
public function login(LoginRequest $request): ResponseInterface
{
$loginDto = $request->toDto();
if ($loginDto && $this->auth->attempt($loginDto->email, $loginDto->password, $loginDto->remember)) {
return $this->response->redirectByName('admin.dashboard');
}
return $this->response->redirectByName('admin.login');
}
public function showLogin(): ResponseInterface
{
return $this->viewResponse->create('@auth-admin/login');
}
LoginRequest — validation rules + DTO; CSRF and global
HandleValidationExceptionMiddleware work like for any controller.
6. Views
{# Views/login.twig — namespace auth-admin, layout from App #}
{% extends '@dashboard/layouts/auth.twig' %}
<form method="post" action="{{ uri('admin.login.submit') }}">
<input type="hidden" name="_csrf_token" value="{{ csrf_token }}">
...
</form>
Module templates — @auth-admin/...; shared admin chrome —
@dashboard/... from resources/views/dashboard/.
7. Database
Migration in Database/Migrations/V2026_03_24_120000_CreateUsersTable.php, model UserModel, seeder UserSeeder.
php bin/console.php db:migrate
php bin/console.php db:seed -c "Concept\Components\AuthAdmin\Database\Seeders\UserSeeder"
For a single seeder specify the class full FQCN in quotes (option -c or
--class). Short names like UserSeeder do not work.
Without options, db:seed runs all registered seeders.
Paths from migrationPaths() and classes from seeders() are added to core registries on component boot.
8. Assets and CLI
php bin/console.php component:publish-assets
php bin/console.php component:list
More details — CLI and assets.
New component checklist
{Name}Component.phpimplementingComponentInterface- Entry in
config/components.php routes.php(if needed)- Controllers, Services, Models, Requests/Dto
- Views +
viewPaths()/viewContexts() - Migrations / seeders / commands / assets — via interface methods
db:migrate,component:publish-assets,route:list
Other reference components in the demo application: Acl (route interceptor + admin UI), SettingsManager (typed settings + Twig helpers), DebugBar (dev-only middleware and assets).