fabricio872/register-command
A small Laravel package that simplifies registering custom Artisan commands, letting you wire up command classes and keep your console tooling organized with minimal configuration.
This is a small single-maintainer Symfony bundle whose entire purpose is to expose a console command that creates a user record — typically for bootstrapping an environment where you have no web UI to sign up through yet. Treat it as a convenience wrapper, not infrastructure, and read the source before you depend on it (it is small enough to read in one sitting).
1. Install and register the bundle
composer require fabricio872/register-command
With Symfony Flex the bundle should be auto-registered into config/bundles.php. On older skeletons, add it manually:
// config/bundles.php
return [
// ...
Fabricio872\RegisterCommandBundle\RegisterCommandBundle::class => ['all' => true],
];
2. Find the actual command name first
The single most important first step is discovering what the bundle registered, because the command name and its arguments are the contract you will be scripting against:
bin/console list | grep -i register
bin/console help <command-name-from-above>
Read the help output end-to-end before running anything. Note which options are interactive prompts (usually hidden questions for password/email) and which are real flags — that determines whether the command is usable in CI at all.
3. Run it once against a throwaway database
bin/console <register-command> --env=dev
4. First real use case: seeding a staging/dev admin
The natural day-one use for this package is creating the first user in a freshly migrated database — a fresh Docker volume, a new staging environment, or a local make install. Instead of writing your own app:create-user command, you lean on this bundle to get an admin account in place so you can log in and start building.
Before you build any workflow on top of it, verify two things: which fields it actually persists (it may assume a specific User entity shape), and where it gets the password hasher from. If either assumption does not match your app, stop and write your own command — it is roughly 60 lines of code.
Wrap it, don't call it directly from scripts
Do not scatter bin/console <register-command> through Makefiles and CI YAML. Put one thin wrapper command in your app that either delegates to the bundle's command or replaces it, so you have a single seam to change when the bundle breaks (see Gotchas):
#[AsCommand(name: 'app:user:create', description: 'Create a user from CLI')]
class CreateUserCommand extends Command
{
protected function configure(): void
{
$this
->addArgument('email', InputArgument::REQUIRED)
->addOption('password', null, InputOption::VALUE_REQUIRED)
->addOption('admin', null, InputOption::VALUE_NONE)
->addOption('if-not-exists', null, InputOption::VALUE_NONE);
}
// ...
}
Calling the bundle's command from inside your own via $this->getApplication()->find('...')->run(...) is possible but brittle; prefer duplicating the small amount of logic into your wrapper and dropping the dependency once you outgrow it.
Seed script for local onboarding
The pattern that pays off most is a single idempotent bootstrap:
#!/usr/bin/env bash
set -euo pipefail
bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
bin/console app:user:create [email protected] --password="${ADMIN_PASSWORD}" --admin --if-not-exists
Run it from make install / docker compose up post-start hook. The --if-not-exists semantics matter: you want the script re-runnable on every container restart without duplicate-email errors blowing up the entrypoint.
CI pattern: non-interactive only
Console commands that use QuestionHelper with hidden input deadlock or fail in CI. Either the bundle exposes a --password-style option, or you feed input through stdin:
printf '%s\n%s\n' "$EMAIL" "$PASSWORD" | bin/console <register-command> --no-interaction
Verify locally with --no-interaction before trusting it in a pipeline. If the command only supports interactive prompts and has no flags, it is not CI-usable.
Pair it with your entity, not against it
If your User entity has required non-nullable fields beyond email/password (tenant, locale, createdAt without a default), the bundle will either fail or write a row your app cannot hydrate. Two options: make the extra columns nullable/defaulted at the DB level, or take over user creation yourself and confine the bundle to pure-dev environments.
Environment guard
Gate it in config/services.yaml so it can never run in production, or at minimum add an --env check in your wrapper. A command that can mint privileged accounts is a privilege-escalation vector if it ships to prod and is reachable by anyone with shell access.
Testing
In functional tests, do not shell out. Use CommandTester against your wrapper (or the bundle command) with a real kernel and a SQLite transaction so you avoid the shell-escaping and interactive-input problems entirely.
Vendor risk is the headline. One star, zero dependents, a single maintainer, and a last release in April 2024. That is not a dismissal — a small console command has a tiny blast radius — but you should pin the exact version in composer.json, avoid it in production paths, and be prepared to vendor the code into your project. The upside of a package this small is that forking it is a 30-minute task.
Read the source before adopting. With this low a signal-to-noise ratio in the ecosystem, the source is the documentation. Check specifically: does it assume a Doctrine EntityManager? What class does it instantiate for the user? Does it hardcode ROLE_USER? Does it call password_hash() directly instead of the UserPasswordHasherInterface? A raw password_hash() bypasses your configured encoder and will produce accounts that fail to authenticate if you use a custom hasher or bcrypt cost overrides.
Do not pass passwords as CLI arguments in shared environments. They land in shell history, ps output, and CI logs. Use an environment variable read inside the command, or stdin piping, and always mask the value in CI.
Hidden questions break both CI and --no-interaction. Symfony's Question with setHidden(true) requires a TTY. Symptom: the command hangs forever in Docker without -it, or throws RuntimeException: Aborted in CI. If you hit this, it is a hard blocker for automation — write your own command.
Bundle not auto-discovered. Non-Flex skeletons and any Symfony version mismatch will silently leave the command unregistered — bin/console list shows nothing and you'll waste time assuming a bad install. If composer require succeeded but no command appears, check config/bundles.php first, then composer dump-autoload.
Email uniqueness is usually enforced at the DB level, so errors surface as raw Doctrine\DBAL\Exception\UniqueConstraintViolationException rather than a friendly message. Wrap invocations in scripts with an existence check rather than relying on catch-and-continue.
Interactive prompts and long-running processes. If you call the command programmatically from a worker or a test kernel, remember the entity manager keeps stale state between invocations — clear() the EntityManager between user creations in loops, or you will get identity-map collisions on the unique email index.
Extension points are likely thin. Expect configuration limited to the user class and maybe the role default; anything else (custom fields, invitations, email verification) means writing your own command. Do not architect around the assumption that this bundle will grow with your requirements.
Debugging recipe. When the command fails opaquely: run with -vvv, check var/log/dev.log for the stack trace, and confirm the DB connection and migrations are current — a missing users table produces a confusing DBAL error, not a "run migrations" message.
Have an exit plan. Write your wrapper command so that swapping the bundle's internals for your own implementation is a one-file change. Given the maintenance profile, assume you will do this within a year, and structure the call sites so that day costs you an afternoon rather than a sprint.
How can I help you explore Laravel packages today?