Framework: Commands: Using $this->argument() doesn't work in the constructor() method.

Created on 23 May 2019  路  5Comments  路  Source: laravel/framework

  • Laravel Version: 5.8.17
  • PHP Version: PHP 7.3.4-1+ubuntu16.04.1+deb.sury.org+3 (cli) (built: Apr 10 2019 10:50:34) ( NTS )
  • Database Driver & Version: Mariadb-server-10.3

Description:

When passing arguments to a command, you cannot access the arguments in the constructor method

Steps To Reproduce:

php artisan make:command TestArguments

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class TestArguments extends Command
{
    protected $variable;

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:test {variable=0}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Testing arguments in Constructor method';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
        $this->variable = $this->argument('variable');
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //
    }
}

php artisan command:test

Running command will output:

   Symfony\Component\Debug\Exception\FatalThrowableError  : Call to a member function getArgument() on null

  at /var/www/entry_system_customdomain/vendor/laravel/framework/src/Illuminate/Console/Command.php:274
    270|         if (is_null($key)) {
    271|             return $this->input->getArguments();
    272|         }
    273|
  > 274|         return $this->input->getArgument($key);
    275|     }
    276|
    277|     /**
    278|      * Get all of the arguments passed to the command.

  Exception trace:

  1   Illuminate\Console\Command::argument("variable")
      /var/www/entry_system_customdomain/app/Console/Commands/TestArguments.php:33

  2   App\Console\Commands\TestArguments::__construct()
      [internal]:0

  Please use the argument -v to see more details.

Most helpful comment

Why would you not want to be able to access arguments within the constructor method?

Because Laravel uses Symfony and that is simply how the Symfony console works. Like in most code, the constructor is used to "set up" things, not "run" things. So you can change/set _how_ the command behaves but you don't run it. And unless you run it, you don't have access to any of what you want.

This never worked, this is not expected to work. IMHO there's no bug here.

All 5 comments

Not possible, Laravel need to construct the command during the bootstrap process because it need to know the command signature. Only when the command is executed then you can access the argument/option

As the output described,

Call to a member function getArgument() on null

$this->input variable is null. (Illuminate\Console\Command class)

$this->input is set when Command::run() is executed.

    public function run(InputInterface $input, OutputInterface $output)
    {
...
        return parent::run(
            $this->input = $input, $this->output
        );
    }

But, that happens after TestArguments::__construct() is executed.

If you inserted echo, print into these functions as debug, you could understand very easily.

Is this then by design? Why would you not want to be able to access arguments within the constructor method?

Why would you not want to be able to access arguments within the constructor method?

Because Laravel uses Symfony and that is simply how the Symfony console works. Like in most code, the constructor is used to "set up" things, not "run" things. So you can change/set _how_ the command behaves but you don't run it. And unless you run it, you don't have access to any of what you want.

This never worked, this is not expected to work. IMHO there's no bug here.

You can make a constructor facade: a function that is just a normal function, but it is called immediately in the handle method. That way it can act as a constructor for you and handle things related to the argument provided in the command.

Think of it like the 2nd constructor.

public function constructor_facade()
{
    // Do stuff with $this->argument('id');
}

public function handle()
{
    $this->constructor_facade();

    // Do the rest of your command
}

Was this page helpful?
0 / 5 - 0 ratings

Related issues

felixsanz picture felixsanz  路  3Comments

lzp819739483 picture lzp819739483  路  3Comments

JamborJan picture JamborJan  路  3Comments

progmars picture progmars  路  3Comments

ghost picture ghost  路  3Comments