Subclassing Migrator not working for namespaced mi

2019-08-14 17:48发布

问题:

I have some namespaced migrations, and I can't get past the Class Not Found errors due to namespacing. In an earlier question, Antonio Carlos Ribeiro stated:

Laravel migrator doesn't play nice with namespaced migrations. Your best bet in this case is to subclass and substitute the Migrator class, like Christopher Pitt explains in his blog post: https://medium.com/laravel-4/6e75f99cdb0.

I have tried doing so (followed by composer dump-autoload, of course), but am continuing to receive Class Not Found errors. I've got the project files set up as

inetpub
|--appTruancy
   |--database
      |--2015_04_24_153942_truancy_create_districts.php
      |--MigrationsServiceProvider.php
      |--Migrator.php

The migration file itself is as follows:

<?php

namespace Truancy;

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class TruancyCreateDistricts extends Migration {

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('districts', function($table) {
            $table->string('id')->unique()->primary()->nullable(false);
            $table->string('district');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('districts');
    }

}

Migrator.php is as follows:

namespace Truancy;

use Illuminate\Database\Migrations\Migrator as Base;

class Migrator extends Base{
  /**
   * Resolve a migration instance from a file.
   *
   * @param string $file
   * @return object
   */
  public function resolve($file)
  {
    $file = implode("_", array_slice(explode("_", $file), 4));

    $class = "Truancy\\" . studly_case($file);

    return new $class;
  }
}

MigrationServiceProvider.php is as follows:

<?php

namespace Truancy;

use Illuminate\Support\ServiceProvider;

class TruancyServiceProvider extends ServiceProvider{
  public function register()
  {
    $this->app->bindShared(
      "migrator",
      function () {
        return new Migrator(
          $this->app->make("migration.repository"),
          $this->app->make("db"),
          $this->app->make("files")
        );
      }
    );
  }
}

The lines generated in autoload_classmap.php are as expected:

'Truancy\\Migrator' => $baseDir . '/appTruancy/database/migrations/Migrator.php',
'Truancy\\TruancyCreateDistricts' => $baseDir . '/appTruancy/database/migrations/2015_04_24_153942_truancy_create_districts.php',
'Truancy\\TruancyServiceProvider' => $baseDir . '/appTruancy/database/migrations/MigrationsServiceProvider.php'

I'm calling php artisan migrate --path="appTruancy/database/migrations" and I receive the error:

PHP Fatal error:  Class 'TruancyCreateDistricts' not found in
C:\inetpub\laravel\vendor\laravel\framework\src\Illuminate\Database
\Migrations\Migrator.php on line 297

I know I must be doing something dumb (my instinct is $class = "Truancy\\" . studly_case($file); in Migrator.php is wrong), but I can't unscrew this lightbulb. The migrate command is obviously successfully finding my migrations file, and the correct classname is in the classmap, so it has to be somewhere in the process of resolving the classname itself from the file, which the subclass and substitution is supposed to address. Any suggestions as to where I've gone wrong?

回答1:

Ok, I've gotten this working. It turns out that the Medium.com article assumes you'd just know where to put the files he talks about, which I didn't. I've made several changes, and now everything is working correctly:

  1. I created a new appTruancy\providers subfolder, and add it to composer.json
  2. I moved both Migrator.php and MigrationServiceProvider.php into the new folder
  3. I changed the namespace in both of those files to Truancy\Providers to match the directory structure
  4. I added 'Truancy\Providers\MigrationsServiceProvider' to the providers array in appTruancy\config\app.php
  5. I added a \ in front of Schema in the migration file to reference the base namespace.
  6. I ran dump-autoload to update the classmap

This is one of those cases where I'm not 100% certain that all of the changes were required, but the layout does make sense so I'm happy with it. So, in a nutshell, if you're trying to namespace your migrations, you need to subclass the Migrator class as described in the Medium.com article listed above, but you then need to register the service provider in config\app, making sure the class names in both files are consistent.