Installation:
composer require danilovl/web-command-bundle
Add to config/bundles.php:
Danilovl\WebCommandBundle\WebCommandBundle::class => ['all' => true],
Configure (config/packages/web_command.yaml):
web_command:
api_prefix: 'api/commands'
console_path: '%kernel.project_dir%/bin/console'
async: true
memory_limit: '512M'
time_limit: 3600
First Use Case:
curl -X POST http://your-app/api/commands/run \
-H "Content-Type: application/json" \
-d '{"command": "cache:clear"}'
curl http://your-app/api/commands/status/{job_id}
Synchronous Execution:
$response = $client->request('POST', '/api/commands/run', [
'json' => ['command' => 'app:generate:key']
]);
Asynchronous Execution (Messenger):
config/packages/web_command.yaml:
commands:
'app:export:data':
async: true
voter: App\Voter\ExportVoter
// Start job
$jobId = $response->toArray()['job_id'];
// Check status later
$status = $client->get("/api/commands/status/{$jobId}");
Parameterized Commands:
commands:
'app:send:email':
parameters:
- name: 'recipient'
type: 'string'
required: true
{
"command": "app:send:email",
"parameters": {"recipient": "user@example.com"}
}
EasyAdmin Integration:
config/packages/easy_admin.yaml:
easy_admin:
entities:
Danilovl\WebCommandBundle\Entity\Command:
class: Danilovl\WebCommandBundle\Entity\Command
list: [id, name, async, enabled]
/admin/commands to manage allowed commands.Laravel-Specific:
ConsoleApplication in App\Kernel:
use Danilovl\WebCommandBundle\Console\LaravelApplication;
protected function createConsoleApplication(): LaravelApplication
{
return new LaravelApplication($this);
}
Command entity for custom fields (e.g., laravel_log_channel).Authentication:
Route::middleware(['auth:sanctum'])->post('/api/commands/run');
Logging:
web_command:
logger: monolog
Async Configuration:
config/packages/messenger.yaml:
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
web_command.async: true and Messenger is installed (composer require symfony/messenger).Command Whitelisting:
config/packages/web_command.yaml under commands are allowed.web_command:
validate_commands: false # Use with caution!
Memory/Time Limits:
memory_limit or time_limit may fail silently.var/log/web_command.log for execution errors.EasyAdmin Conflicts:
composer require easycorp/easy-admin-bundle) or disable dashboard:
web_command:
dashboard: false
API Errors:
curl -v -X POST http://your-app/api/commands/run -d '...'
422 Unprocessable Entity (invalid parameters) or 403 Forbidden (missing voter).Async Jobs:
SELECT * FROM web_command_job WHERE status = 'failed';
php bin/console messenger:retry-all
Custom Voters:
CommandVoterInterface to restrict access:
use Danilovl\WebCommandBundle\Security\CommandVoterInterface;
class AdminVoter implements CommandVoterInterface
{
public function supports(string $command, $subject): bool
{
return str_starts_with($command, 'app:admin:');
}
public function vote($subject, string $command, array $parameters): bool
{
return auth()->user()->isAdmin();
}
}
commands:
'app:admin:user:create':
voter: App\Voter\AdminVoter
Command History:
Job entity to store custom metadata:
// src/Entity/ExtendedJob.php
use Danilovl\WebCommandBundle\Entity\Job;
class ExtendedJob extends Job
{
#[ORM\Column(nullable: true)]
private ?string $userAgent = null;
}
Job repository to use your extended class.Webhooks:
failed/succeeded events:
use Symfony\Component\Messenger\Event\WorkerFailedEvent;
$eventDispatcher->addListener(WorkerFailedEvent::class, function (WorkerFailedEvent $event) {
Log::error('Command failed', ['job' => $event->getEnvelope()->getMessage()]);
});
Laravel Artisan Proxy:
ConsoleApplication to support Laravel’s Artisan commands:
use Danilovl\WebCommandBundle\Console\LaravelApplication;
use Symfony\Component\Console\Application;
class CustomApplication extends LaravelApplication
{
protected function getDefaultCommands(): array
{
return array_merge(parent::getDefaultCommands(), [
new \Artisan\Commands\MakeControllerCommand(),
]);
}
}
How can I help you explore Laravel packages today?