Jose Jimenez
Software Architect & Developer
> >

Simplify Your Laravel Project Installation with One Custom Command

Published in Laravel, PHP, Database on May 30, 2023

Overview

This article will demonstrate how to create a command to install your Laravel project. The command will ensure that the application is consistent across all team members by executing all the necessary steps to get the application up and running.

Introduction

When working with a Laravel project, you don't want to complicate your life by requiring your team members to run different commands to get your application working. Instead, creating one command that executes everything needed ensures that everything is consistent within everyone's application.

Creating the Command

To create the command, start by running the following command:

art make:command System/InstallCommand

This will create the command under app/Console/Commands/System. You can create additional directories inside the Commands directory to keep your application more organized. For example, you could lump all system:* commands under the System/ directory.

Here is the code for the InstallCommand:

<?php

declare(strict_types=1);

namespace App\Console\Commands\System;

use App;
use Illuminate\Console\Command;

class InstallCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'system:install';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Install the application on a non-production environment.';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle(): int
    {
        // Check that the application you are installing has the proper .env configuration
        if (config('app.name') !== 'myappname') {
            $this->error('Application name is not myappname.');

            return Command::FAILURE;
        }
        
        // Check if this command is being run in production
        if ($this->isRunningInProduction()) {
            $this->error('Cannot run this command in production.');

            return Command::FAILURE;
        }

        // Create symbolic links
        $this->info('Create Symbolic links.');
        $this->call('storage:link', ['--force' => true]);

        // Clear cached bootstrap files
        $this->clearCommands();

        // Migrate and seed a fresh database to work with
        $this->info('Migrate and seed a fresh database to work with.');
        $this->call('migrate:fresh', ['--seed' => true]);

        // Display success message
        $this->line('[Installation is now complete]', 'info');

        return Command::SUCCESS;
    }

    /**
     * Check if this command is being run in production
     *
     * @return bool
     */
    protected function isRunningInProduction(): bool
    {
        return App::isProduction() === true;
    }

    /**
     * Clear cached bootstrap files
     */
    protected function clearCommands(): void
    {
        $this->info('Clearing cached bootstrap files.');

        // Clear cached bootstrap files such as optimize and queue
        collect([
            'optimize' => fn () => $this->callSilent('optimize:clear') == 0,
            'queue' => fn () => $this->callSilent('queue:clear') == 0,
        ])->each(fn ($task, $description) => $this->command->task($description, $task));

        $this->newLine();
    }
}

Explanation

When you run the system:install command, several things happen:

  1. The command checks to make sure that the application you are installing has the proper .env configuration. This code checks that config('app.name') matches myappname. This initial check ensures that your environment variables match.
if (config('app.name') !== 'myappname') {
    $this->error('Application name is not myappname.');

    return Command::FAILURE;
}
  1. The command checks whether it is being run in production, to avoid accidentally installing your application on a live server. This method should have more complex logic, check to make sure that specific critical configuration variables hold non-production values in a way to not divulge any sensitive information.
if ($this->isRunningInProduction()) {
    $this->error('Cannot run this command in production.');

    return Command::FAILURE;
}
  1. The command creates the storage links configured:
$this->call('storage:link', ['--force' => true]);
  1. Your custom command runs optimize:clear, which as of Laravel 10 clears events, views, cache, route, config, and compiled by default. In the code we added an additional item during an app install such as queue:clear, you can check your packages to add additional clear commands.
$this->clearCommands();
  1. The command runs migrate:fresh with the seed, to drop and create the tables from a fresh state. This allows you to work between separate branches without any issues that are introduced with rollbacks.
$this->call('migrate:fresh', ['--seed' => true]);
  1. The command displays a message and sends the success signal to instruct the machine running this command that everything installed correctly and fire off any additional commands as needed.
$this->line('[Installation is now complete]', 'info');

By following these steps and creating a consistent installation process, you can save yourself and your team members time and unnecessary hassle when working on your Laravel project.