0
votes

I was wondering if you could help me with the problem that I found a few hours ago and I have not been able to solve it. I happen to be trying to develop a small package in Laravel. This package will contain several commands, but for now I need those commands to inherit some methods and properties from a parent class. I have defined the following structure in my project:

package
├── composer.json
└── src
    ├── Commands
    │   └── ACommand.php
    │   └── BCommand.php
    ├── Package.php
    └── PackageServiceProvider.php

I register the commands inside the PackageServiceProvider as follows:

<?php

namespace Author\Package;

use Illuminate\Support\ServiceProvider;

class PackageServiceProvider extends ServiceProvider
{
    /**
     * The commands to be registered.
     *
     * @var array
     */
    protected $commands = [
        \Author\Package\Commands\ACommand::class,
        \Author\Package\Commands\BCommand::class,
    ];

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        $this->commands($this->commands);
    }
}

Let's take the ACommand command as an example, it contains the following very basic structure to be brief:

<?php

namespace Author\Package\Commands;

use Author\Package\Package;
use Illuminate\Console\Command;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

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

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle(Package $package)
    {
        return 0;
    }
}

As you can see, in the handle() method I do dependency injection to the Package class. This is the content of the Package class:

<?php

namespace Author\Package;

use Illuminate\Console\Concerns\InteractsWithIO;

class Package
{
    use InteractsWithIO;

    public function __construct()
    {
        $this->info("Hello World!");
    }
}

when I run the command:

php artisan command:name

I get the following error in the terminal:

Call to a member function writeln() on null

What other kinds of things have I tried?

  1. Create a base command and extend the other commands to that class, it would be as follows:
package
├── composer.json
└── src
    ├── Commands
    │   └── ACommand.php
    │   └── BCommand.php
    │   └── BaseCommand.php
    └── PackageServiceProvider.php

Let's take the ACommand command as an example here:

<?php

namespace Author\Package\Commands;

use Illuminate\Console\Command;

class ACommand extends BaseCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:name';

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

...

the content of BaseCommand in this case I have left it like this:

<?php

namespace Author\Package\Commands;

use Author\Package\Package;
use Illuminate\Console\Command;

abstract class BaseCommand extends Command
{
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();

        $this->info("Hello World!");
    }
}

and I get the same error as a result:

Call to a member function writeln() on null
  • Also setting the BaseCommand class as concrete and not as abstract I get the same error.

Basically for now what I need is to do almost all my logic from the __construct() method in a parent class .. and leave the result in a property. Then from a child class, access that property, since all my commands are going to use that property .. but each one of them is going to do something different with that property.. But it happens that I have not been able to execute statements as I would in a simple command, even when the parent class extends from the class: Illuminate\Console\Command.

As you can see in the example I am basically trying to display a message in the terminal called from the parent class when the command is executed. But I have not succeeded. I have not done Dependency Injection from the __construct() method for the following reason:

All artisan command constructors are fired when running any artisan command...what?

Change artisan console to only fire constructors for the command being called

As you can see, when Dependency Injection is done in the __construct() method of a command, it is executed in any other command, that is why the documentation advises to do this Dependency Injection in the handle() method.

Command Structure

I have also read something here, but it has not served me much:

Laravel command cannot call $this->info() in child class

Please if someone expert on the subject could help me. Basically I am trying here, to call methods of the child class (ACommand) from the parent class (Package). I would appreciate any help in advance.

1

1 Answers

3
votes

Try set output before $this->info(..)

...
$this->output = new \Symfony\Component\Console\Output\ConsoleOutput();
$this->info()
...

The output does not appear to be set because your writeln error is probably in src/Illuminate/Console/Concerns/InteractsWithIO.php:

    public function line($string, $style = null, $verbosity = null)
    {
        $styled = $style ? "<$style>$string</$style>" : $string;

        $this->output->writeln($styled, $this->parseVerbosity($verbosity));
    }