Laravel Console Toolkit

Overview

Laravel Console Toolkit

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads PHP Version

Header Image

This package makes it even easier to write maintainable and expressive Artisan commands, with argument/option casting, validation and autoAsk. Also, it lets you define your arguments/options with simple properties and attributes for better ide support and static analysis. And all this with a single trait.

🤯 Features

All the features:

Support Name Description
Laravel Features Supports everything laravel can do
Attribute Syntax Use PHP-Attributes to automatically define your inputs based on types
Casting Automatically cast your inputs to Enums, Models, Objects or anything you want
Validation Use the Laravel Validator to validate the inputs from the console
Auto Ask If the user provides an invalid value toolkit will ask again for a valid value without the need to run the command again
Negatable Options Options can be specified as opposites: --dry or --no-dry
Option required Value Options can have required values

💜 Support me

Visit my blog on https://bitbench.dev or follow me on Social Media Twitter @bitbench Instagram @bitbench.dev

📦 Installation

You can install the package via composer:

composer require thettler/laravel-console-toolkit

🔧 Usage

🗯️ Before you use this package you should already have an understanding of Artisan Commands. You can read about them here.

A Basic Command

To use the Toolkit you simply need to add the UsesConsoleToolkit trait inside your command.

Then add the Thettler\LaravelConsoleToolkit\Attributes\ArtisanCommand to the class to specify the name and other things like description, help, and so on.

The ArtisanCommand requires the name parameter to be set. This will be the name of the Command which you can use to call it from the commandline.


namespace App\Console\Commands;

use Illuminate\Console\Command;
use Thettler\LaravelConsoleToolkit\Concerns\UsesConsoleToolkit;

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;
    
    public function handle()
    {
    }
}

And call it like:

php artisan basic
Traditional Syntax

    

namespace App\Console\Commands;

use Illuminate\Console\Command;

class BasicCommand extends Command
{
    protected $signature = 'basic';

    public function handle()
    {
    }
}

Descriptions, Help and Hidden Commands

If you want to add a description, a help comment or mark the command as hidden, you can specify this on the ArtisanCommand Attribute like this:

#[ArtisanCommand(
    name: 'basic',
    description: 'Some useful description.',
    help: 'Some helpful text.',
    hidden: true
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    ...
}

I like to use named arguments for a more readable look.

Traditional Syntax

...
class BasicCommand extends Command
{
    protected $signature = 'basic';

    protected $description = 'Some useful description.';

    protected $help = 'Some helpful text.';
    
    protected $hidden = true;
    ...
}

Defining Input Expectations

The basic workflow to add an argument or option is always to add a property and decorate it with an Attribute. #[Option] if you want an option and #[Argument] if you want an argument. The property will be hydrated with the value from the command line, so you can use it like any normal property inside your handle() method.

More about that in the following sections. ⬇️

The property will only be hydrated inside the handle() method. Keep that in mind.

Arguments

To define Arguments you create a property and add the Argument attribute to it. The property will be hydrated with the value from the command line, so you can use it like any normal property inside your handle() method.

...
use \Thettler\LaravelConsoleToolkit\Attributes\Argument;

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected string $myArgument;
    
    public function handle() {
        $this->line($this->myArgument);
    }
}

call it like:

php artisan basic myValue
# Output:
# myValue
Traditional Syntax

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument}';
    
    public function handle() {
        $this->line($this->argument('myArgument'));
    }
}

Array Arguments

You can also use arrays in arguments, simply typehint the property as array.

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected array $myArray;
    
    public function handle() {
        $this->line(implode(', ', $this->myArray));
    }
}

Call it like:

php artisan basic Item1 Item2 Item3 
# Output
# Item1, Item2, Item3 
Traditional Syntax

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument*}';
    
    public function handle() {
        $this->line($this->argument('myArgument'));
    }
}

Optional Arguments

Of course, you can use optional arguments as well. To achieve this you simply make the property nullable.

ℹ️ This works with array as well but the property won't be null but an empty array instead

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected ?string $myArgument;
    
    ...
}
Traditional Syntax

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument?}';
    
    ...
}

If your argument should have a default value, you can assign a value to the property which will be used as default value.

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected string $myArgument = 'default';
    
    ...
}
Traditional Syntax

class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument=default}';
    
    ...
}

Argument Description

You can set a description for arguments as parameter on the Argument Attribute.

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument(
        description: 'Argument Description'
    )]
    protected string $myArgument;
    
    ...
}
Traditional Syntax

...
class BasicCommand extends Command
{
    protected $signature = 'basic {myArgument: Argument Description}';
    
    ...
}

If you have more than one argument the order inside the class will also be the order on the commandline

Options

To use options in your commands you use the Options Attribute. If you have set a typehint of boolean it will be false if the option was not set and true if it was set.

use \Thettler\LaravelConsoleToolkit\Attributes\Option;

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option]
    protected bool $myOption;
    
    public function handle() {
        dump($this->myOption);
    }
}

Call it like:

php artisan basic --myOption
# Output
# true
php artisan basic
# Output
# false
Traditional Syntax

class BasicCommand extends Command
{
    protected $signature = 'basic {--myOption}';
    
    public function handle() {
        dump($this->option('myOption'));
    }
}

Value Options

You can add a value to an option if you type hint the property with something different as bool. This will automatically make it to an option with a value. If your typehint is not nullable the option will have a required value. This means the option can only be used with a value.

Wont work --myoption works --myoption=myvalue

If you want to make the value optional simply make the type nullable or assign a value to the property

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option]
    protected string $requiredValue; // if the option is used the User must specify a value  
    
    #[Option]
    protected ?string $optionalValue; // The value is optional

    #[Option]
    protected string $defaultValue = 'default'; // The option has a default value

    #[Option]
    protected array $array; // an Array Option 

    #[Option]
    protected array $defaultArray = ['default1', 'default2']; // an Array Option with default
    ...
}

Call it like:

php artisan basic --requiredValue=someValue --optionalValue --array=Item1 --array=Item2
Traditional Syntax

class BasicCommand extends Command
{
    // requiredValue is not possible
    // defaultArray is not possible
    protected $signature = 'basic {--optionalValue=} {--defaultValue=default} {--array=*}';
   
   ...
}

Option Description

You can set a description for an option on the Option Attribute.

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option(
        description: 'Option Description'
    )]
    protected bool $option;
    ...
}
Traditional Syntax

class BasicCommand extends Command
{
    protected $signature = 'basic {--option: Option Description}';
}

Option Shortcuts

You can set a shortcut for an option on the Option Attribute.

⚠️ Be aware that a shortcut can only be one char long

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option(
        shortcut: 'Q'
    )]
    protected bool $option;
    ...
}

Call it like:

php artisan basic -Q
Traditional Syntax

class BasicCommand extends Command
{
    protected $signature = 'basic {--Q|option}';
}

Negatable Options

You can make option negatable by adding the negatable parameter to the Option Attribute. Now the option accepts either the flag (e.g. --yell) or its negation (e.g. --no-yell).

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Option(
        negatable: true
    )]
    protected bool $yell;
    
    public function handle(){
       dump($this->yell); // true if called with --yell
       dump($this->yell); // false if called with --no-yell
    }
}

Call it like:

php artisan basic --yell
php artisan basic --no-yell

Enum Types

It is also possible to type Arguments or Options as Enum. The Package will automatically cast the input from the commandline to the typed Enum. If you use BackedEnums you use the value of the case and if you have a non backed Enum you use the name of the case.

enum Enum
{
    case A;
    case B;
    case C;
}

enum IntEnum: int
{
    case A = 1;
    case B = 2;
    case C = 3;
}

enum StringEnum: string
{
    case A = 'String A';
    case B = 'String B';
    case C = 'String C';
}
    #[Argument]
    protected Enum $argEnum;

    #[Argument]
    protected StringEnum $argStringEnum;

    #[Argument]
    protected IntEnum $argIntEnum;

    #[Option]
    protected Enum $enum;

    #[Option]
    protected StringEnum $stringEnum;

    #[Option]
    protected IntEnum $intEnum;
php artisan enum B "String B" 2 --enum=B --stringEnum="String B" --intEnum=2

Input alias

By default, the input name used on the commandline will be same as the property name. You can change this with the as parameter on the Option or Argument Attribute. This can be handy if you have conflicting property names or want a more expressive api for your commands.

⚠️ If you use the ->option() syntax you need to specify the alias name to get the option.

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument(
        as: 'alternativeArgument'
    )]
    protected string $myArgument;
 
    #[Option(
        as: 'alternativeName'
    )]
    protected bool $myOption;
    
    public function handle(){
       dump($this->myArgument);
       dump($this->myOption);
    }
}

Call it like:

php artisan basic something --alternativeName

Special Default values

If you want to use some objects with casts as default values you can use the configureDefauls() method on the command to set default values.

#[ArtisanCommand(
    name: 'basic',
)]
class BasicCommand extends Command
{
    use UsesConsoleToolkit;

    #[Argument]
    protected BandModel $band;

    public function configureDefaults(): void {
        $this->band = BandModel::find('2');
    }
    
    public function handle(){
       dump($this->band); // The Band with id 2
    }
}

Casts

Cast can be specified on Arguments and Options. You can either provide a class-string of a caster to use or an instance of the caster. This is helpful to configure the caster via the constructor.

Model Cast

The Toolkit provides a cast for eloquent models out of the box. So if you typehint an eloquent model toolkit will try to match the console input to the primary key of the model and fetches it from the database.

    #[Argument]
    protected BandModel $band;
    
    public function handle(){
        $this->band // Well be an instance of BandModel 
    }

If you want to change the column that will be used to match the input to the database, load relations or only select specific columns you can use the manual cast like this:

    #[Argument(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\ModelCaster(
            findBy: 'name',
            select: ['id', 'name']
            with: ['songs']
        )
    )]
    protected BandModel $band;
    
    public function handle(){
        $this->band // Will be an instance of BandModel 
    }

Enum Cast

The enum cast will automatically cast every typed enum to this enum. But you can also manually specify it like so.

    #[Argument(
        cast: \Thettler\LaravelConsoleToolkit\Casts\EnumCaster::class
    )]
    protected Enum $argEnum;

    #[Option(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\EnumCaster(Enum::class)
    )]
    protected Enum $enum;

Array Cast

If you have an array and want to cast all its values to a specific type you can use the ArrayCaster. It expects a caster and a specific type:

    #[Argument(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\ArrayCaster(
            caster: \Thettler\LaravelConsoleToolkit\Casts\EnumCaster::class, 
            type: StringEnum::class
        )
    )]
    protected array $enumArray;

    #[Option(
        cast: new \Thettler\LaravelConsoleToolkit\Casts\ArrayCaster(
            caster: \Thettler\LaravelConsoleToolkit\Casts\EnumCaster::class, 
            type: StringEnum::class
        )
    )]
    protected array $enumArray2;

Custom Casts

It's also possible to define your own casts. To do so you need to create a class that implements the Caster Interface.

Let's have a look at small UserCast that allows to simply use the id of a user model on the command line and automatically fetch the correct user from the database:



class UserCast implements Caster
{
    /**
    * This method deals with the conversion from the default value to a value the console understand so only 
    * basic return types are allowed  
    * 
    * @param mixed $value The default value if one is present
    * @param string $type The type is a string representation of the type of the property 
    * @param \ReflectionProperty $property The property reflection itself for more control
    * @return int|float|array|string|bool|null
     */
    public function from(mixed $value, string $type, \ReflectionProperty $property): int|float|array|string|bool|null
    {
        if ($value instanceof Band){
            return $value->getKey();
        }
        
        throw new Exception(self::class . ' can only be used with type '. Band::class)
    }

    /**
     * This method deals with the conversion from console input to property value 
     * 
     * @param  mixed  $value The Value from the command line
     * @param  class-string
    
       $type The type is a string representation of the type of the property 
    
     * @param  \ReflectionProperty  $property The property reflection itself for more control
     * @return mixed
     */
    public function to(mixed $value, string $type, \ReflectionProperty $property)
    {
        return $type::find($value);
    }
}

Now you can use this cast ether locally on an attribute or register it globally for automatic casting like this in your AppServiceProvider

    /** Uses the UserCaster everytime the User class is typehint on an Argument or Option */
    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::addCast(UserCaster::class, User::class);

    /** Uses the UserCaster everytime the User or MasterUser class is typehint on an Argument or Option */
    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::addCast(UserCaster::class, [User::class, MasterUser::class]);

    /** Uses the UserCaster everytime the callable returns true */
    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::addCast(
        UserCaster::class,
        fn (mixed $value, ReflectionProperty $property): bool  =>  is_subclass_of($property->getType()->getName(), User::class);
    );

Validation

You can also use the normal laravel validation rules to validate the input.

    #[Argument(
        validation: ['max:5']
    )]
    protected string $validated;

If you want custom messages you need to use the Validation object

    #[Argument(
        validation: new \Thettler\LaravelConsoleToolkit\Transfers\Validation(
            rules: ['max:5']
            messages: [
                'max' => 'This is way to much!'
            ]   
        )
    )]
    protected string $validated;

Auto Ask

By default, Auto Ask is enabled. Every time a command is called with an input that fails validation or is required but not specified the command automatically asks the user to enter a (new) value. If the type is an enum it will give the user choice with all the enum values.

If you want to disable this behavior you can do it locally:

    #[Argument(
        autoAsk: false
    )]
    protected string $dontAsk;

or globally in your AppServiceProvider:

    \Thettler\LaravelConsoleToolkit\ConsoleToolkit::enableAutoAsk(false);

🤖 Testing

composer test

📖 Changelog

Please see CHANGELOG for more information on what has changed recently.

👼 Contributing

Please see CONTRIBUTING for details.

🔒 Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

©️ Credits

📚 License

The MIT License (MIT). Please see License File for more information.

Comments
Releases(0.1.1)
Owner
Tobi
Tobi
Console - The Console component eases the creation of beautiful and testable command line interfaces.

Console Component The Console component eases the creation of beautiful and testable command line interfaces. Sponsor The Console component for Symfon

Symfony 9.4k Jan 7, 2023
Display your Laravel routes in the console, but make it pretty. 😎

Pretty Routes for Laravel Display your Laravel routes in the console, but make it pretty. ?? Installation You can install the package via composer: co

Alex 630 Dec 30, 2022
Customized loading ⌛ spinner for Laravel Artisan Console.

Laravel Console Spinner Laravel Console Spinner was created by Rahul Dey. It is just a custom Progress Bar inspired by icanhazstring/symfony-console-s

Rahul Dey 71 Oct 26, 2022
The Hoa\Console library.

Hoa is a modular, extensible and structured set of PHP libraries. Moreover, Hoa aims at being a bridge between industrial and research worlds. Hoa\Con

Hoa 366 Dec 14, 2022
ReactPHP Shell, based on the Symfony Console component.

Pecan Event-driven, non-blocking shell for ReactPHP. Pecan (/pɪˈkɑːn/) provides a non-blocking alternative to the shell provided in the Symfony Consol

Michael Crumm 43 Sep 4, 2022
It's like Tailwind CSS, but for the console.

Tailcli allows building unique, beautiful command-line applications, using tailwind classes. It's like Tailwind CSS, but for the console. Installation

Nuno Maduro 1.8k Jan 7, 2023
Supercharge your Symfony console commands!

zenstruck/console-extra A modular set of features to reduce configuration boilerplate for your commands: /** * Creates a user in the database. * *

Kevin Bond 29 Nov 19, 2022
🤖 GitHub Action to run symfony console commands.

Symfony Console GitHub Action Usage You can use it as a Github Action like this: # .github/workflows/lint.yml name: "Lint" on: pull_request: push

Nucleos 3 Oct 20, 2022
Console component from Zend Framework

zend-console Repository abandoned 2019-12-31 This repository has moved to laminas/laminas-console. Zend\Console is a component to design and implement

Zend Framework 47 Mar 16, 2021
Simple and customizable console log output for CLI apps.

Console Pretty Print Simple and customizable console log output for CLI apps. Highlights Simple installation (Instalação simples) Very easy to customi

William Alvares 3 Aug 1, 2022
A package built for lumen that ports most of the make commands from laravel.

A package built for lumen that ports most of the make commands from laravel. For lumen v5.1, but will most likely work for 5.2 as well. I haven't tested. If you have requests, let me know, or do it yourself and make a pull request

Michael Bonds 22 Mar 8, 2022
Backup your laravel database by a simple artisan command

Backup your laravel database by a simple artisan command This package will allow you to backup your laravel app database and you can also choose to se

Mohammed Omer 23 Feb 10, 2022
A CLI starter pack for developing a package with Laravel 5

Laravel PackMe Laravel PackMe is a project starter pack which combine all basic stuff (src, tests) in order to develop a package for Laravel 5.*. It t

Pierre Tondereau 63 Dec 29, 2021
Extendable Interactive Make Command for Laravel

Extendable Interactive Make Command for Laravel Make your life easier with interactive make command! Installation composer require hydreflab/laravel-m

Hydref Lab 37 Nov 29, 2022
Helper commands for Laravel Beyond Crud

About beyond-crud-helpers This package has many helper commands for the Laravel BEyond CRUD project by Spatie Installation composer require --dev tarr

null 4 Mar 8, 2022
Laravel Feature Flags allows instant, zero-deployment toggling of application features.

Dynamic Feature Flags for Laravel Laravel Feature Flags allows instant, zero-deployment toggling of application features. The state of each feature fl

Coding Labs 35 Oct 4, 2022
Twill is an open source CMS toolkit for Laravel that helps developers rapidly create a custom admin console that is intuitive, powerful and flexible. /// Chat with us and others on Spectrum: https://spectrum.chat/twill

About Twill Twill is an open source Laravel package that helps developers rapidly create a custom CMS that is beautiful, powerful, and flexible. By st

AREA 17 3k Jan 6, 2023
Laravel Console Toolkit

This Package provides some usefully console features like the attribute syntax for arguments and options, validation, auto ask and casting.

Tobi 29 Nov 29, 2022
Enter-to-the-Matrix-with-Symfony-Console - Reproduction of the "Matrix characterfall" effect with the Symfony Console component.

Enter to the Matrix (with Symfony Console) Reproduction of the "Matrix characterfall" effect with the Symfony Console component. Run Clone the project

Yoan Bernabeu 23 Aug 28, 2022