Laravel 7 自定义 Artisan 命令抛出现有 class 的 BindingResolutionException

Laravel 7 custom Artisan command throws BindingResolutionException with existing class

我在 Laravel 7 中执行命令,在 database/migrations 和播种机中的文件夹中创建一些迁移。然后,它 运行s dumpAutoloads() 以确保创建的迁移和播种器已在自动加载 classmap 中注册。然后,我的命令调用 php artisan migrate 命令并连续调用 php artisan db:seed 命令--class 标志仅对创建的播种器进行播种。

在调用 db:seed 命令之前一切 运行 都很好。确实创建了播种机,但它一直向我抛出下一个异常:

Illuminate\Contracts\Container\BindingResolutionException

Target class [StudentOperationTypesSeeder] does not exist

这显然只是一个例子,但我已经检查过创建的Seeder的名称与异常显示的名称完全相同并且匹配。此外,在向我抛出此异常之后,我 运行 自己 db:seed --class=StudentOperationTypesSeeder 命令并且它有效!

这让我觉得也许自动加载 classmap 直到命令过程完成或其他什么才更新...我真的不知道。

我的代码如下:

TimeMachineGeneratorCommand.php

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;

class TimeMachineGeneratorCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'time-machine:generate {table_name}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Generates the tables and structure of a time machine for the given table.';

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

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        // Convierte a snake case singular el nombre de la entidad.
        $entity = Str::singular(
            Str::snake(class_basename($this->argument('table_name')))
        );

        // Revisa que el nombre del modelo esté en el formato adecuado. 
        $modelName = Str::singular(
            Str::studly($this->argument('table_name'))
        );

        $seederClassName = "{$modelName}OperationTypesSeeder";

        // Crea las migraciones para la estructura de la máquina del tiempo.
        $this->createMigrations($entity);

        // Genera el Seeder del los operation types básicos.
        $this->createSeeder($seederClassName);

        // Para asegurarse que las migrations y el seeder está registrada en 
        // los class loaders se ejecuta el dump-autoload.
        $this->line("<fg=yellow>Dumping Autoloads...</>");
        $this->laravel->composer->dumpAutoloads();
        $this->info("Autoloads dumped.");

        // Ejecuta las migraciones.
        $this->call('migrate', [
            '--path' => '/database/migrations/'.Str::plural(Str::snake($modelName)).'/'
        ]);
        // Ejecuta el seeder recién creado.
        $this->call('db:seed', [
            '--class' => $seederClassName
        ]);
        
        (...) // <-- Some other code that isn't executed because of the exception
    }

    /**
     * Genera los archivos de código de las migraciones necesarias
     * para la estructura de la máquina del tiempo.
     */
    private function createMigrations($entity)
    {
        // Genera la migración para los tipos de operación de la entidad.
        $this->call('time-machine:operation-type-migration', [
            'entity' => $entity
        ]);

        // Genera la migración para los logs de la entidad.
        $this->call('time-machine:log-migration', [
            'entity' => $entity
        ]);

        // Genera la migración para los logs de la entidad.
        $this->call('time-machine:required-fields-migration', [
            'entity' => $entity
        ]);
    }

    /**
     * Crea el seeder de operationt types basado en el 
     * template diseñado para la máquina del tiempo.
     *
     * @param  string | $seederClassName | El nombre de la clase para el seeder.
     * @return void.
     */
    private function createSeeder($seederClassName)
    {
        $this->call('time-machine:seeder', [
            'name' => $seederClassName
        ]);

        // Agrega el seeder recién creado al DatabaseSeeder.php. 
        $this->updateDatabaseSeeder($seederClassName);
    }

    /**
     * Agrega el seeder recién creado al DatabaseSeeder.php.
     *
     * @var    $seederClassName.
     * @return void.
     */
    private function updateDatabaseSeeder($seederClassName)
    {
        $filePath = base_path().'\database\seeds\DatabaseSeeder.php';

        // Lee el archivo del DataBaseSeeder.
        $seeder = file_get_contents($filePath);

        // Si el seeder no ha sido agregado ya al DatabaseSeeder.php...
        if(preg_match('/$this\-\>call\('.$seederClassName.'\:\:class\)\;/', $seeder) == 0) {
            // Agrega el seeder recién creado.
            $newContent = preg_replace(
                '/public function run\(\)\s*\{/',
    "public function run()
    {
        $this->call({$seederClassName}::class);", 
                
                $seeder, 1
            );
            
            // Guarda el contenido del archivo.
            file_put_contents($filePath, $newContent);

            $this->info('Seeder added to DatabaseSeeder.php.');
        } else {
            $this->error('Seeder is already in DataBaseSeeder.php.');
        }
    }
}

此外,这是我的 composer.json 的自动加载部分(我读到可能有一些东西或其他东西,我找不到适合我的解决方案无论如何问题)。

    "autoload": {
        "psr-4": {
            "App\": "app/"
        },
        "classmap": [
            "database/seeds",
            "database/factories"
        ]
    },

StudentOperationTypesSeeder.php

<?php

use Illuminate\Database\Seeder;

class StudentOperationTypesSeeder extends Seeder
{
    /**
     * Run the database seeds.
     *
     * @return void
     */
    public function run()
    {
        $studentOperationType = new \App\StudentOperationType();
        $studentOperationType->description = "Created";
        $studentOperationType->save();

        $studentOperationType = new \App\StudentOperationType();
        $studentOperationType->description = "Updated";
        $studentOperationType->save();

        $studentOperationType = new \App\StudentOperationType();
        $studentOperationType->description = "Deleted";
        $studentOperationType->save();
    }
}

请帮忙。该命令成功生成了迁移和播种器,然后 运行 进行了迁移,一切都完全按预期工作,除了播种器,我还没有找到原因。

注:另外"time-machine"命令是我在[=的函数中调用的62=] 是我创建的其他自定义命令,它们实际上只是扩展供应商的现有迁移命令并将存根更改为自定义命令。

我遵循了 lagbox 建议的方法并找到了一些有用的方法。

我在调用 bd:seed 命令之前添加了下一行代码,显然是在生成播种器的行之后。

include base_path()."\database\seeds\".$seederClassName.".php";

执行此操作后,播种器被正确执行,整个命令按预期工作。

最终的句柄方法如下所示。

/**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        // Convierte a snake case singular el nombre de la entidad.
        $entity = Str::singular(
            Str::snake(class_basename($this->argument('table_name')))
        );

        // Revisa que el nombre del modelo esté en el formato adecuado. 
        $modelName = Str::singular(
            Str::studly($this->argument('table_name'))
        );

        $seederClassName = "{$modelName}OperationTypesSeeder";

        // Crea las migraciones para la estructura de la máquina del tiempo.
        $this->createMigrations($entity);

        // Genera el Seeder del los operation types básicos.
        $this->createSeeder($seederClassName);

        // Para asegurarse que las migrations y el seeder está registrada en 
        // los class loaders se ejecuta el dump-autoload.
        $this->line("<fg=yellow>Dumping Autoloads...</>");
        $this->laravel->composer->dumpAutoloads();
        $this->info("Autoloads dumped.");

        // Ejecuta las migraciones.
        $this->call('migrate', [
            '--path' => '/database/migrations/'.Str::plural(Str::snake($modelName)).'/'
        ]);

        include base_path()."\database\seeds\".$seederClassName.".php";

        // Ejecuta el seeder recién creado.
        $this->call('db:seed', [
            '--class' => $seederClassName
        ]);
        
        (...) // <-- Some other code that isn't executed because of the exception
    }