首页 > 解决方案 > Laravel 7 自定义 Artisan 命令使用现有类抛出 BindingResolutionException

问题描述

我在Laravel 7中创建了一个命令,该命令在数据库/迁移和播种器内的文件夹中创建一些迁移。然后,它运行 dumpAutoloads() 以确保创建的迁移和播种器已在自动加载类映射中注册。然后,我的命令调用php artisan migrate命令并连续调用带有--class标志的php artisan db:seed命令,以仅对创建的播种机进行播种。

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

Illuminate\Contracts\Container\BindingResolutionException

Target class [StudentOperationTypesSeeder] does not exist

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

这让我觉得也许在命令过程完成之前自动加载类映射不会更新或其他什么......我真的不知道。

我的代码如下:

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();
    }
}

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

注意:我在TimeMachineGeneratorCommand.php的函数中调用的其他“时间机器”命令是我创建的其他自定义命令,它们实际上只是扩展了供应商的现有迁移命令并将存根更改为自定义命令。

标签: phpexceptionlaravel-7laravel-artisanlaravel-migrations

解决方案


我遵循了 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
    }

推荐阅读