Laravel的包存放在vendor目錄下面。php
例如way,能夠是一個供應商代號,其目錄下面有一個generators目錄。app
在src目錄下面有Way/Generators目錄,裏面存放真正的代碼文件。提供一個GeneratorServiceProvider.php文件,實現了多個派生自ide
Illuminate\Console\Command類的命令。其中包括:
generate:view
generate:model
generate:controller
generate:migration
generate:seeder
generate:pivot
generate:resource
generate:scaffold
generate:publisher
其所有代碼以下:<?php namespace Way\Generators; use Illuminate\Support\ServiceProvider; use Way\Generators\Commands\ControllerGeneratorCommand; use Way\Generators\Commands\ModelGeneratorCommand; use Way\Generators\Commands\ResourceGeneratorCommand; use Way\Generators\Commands\SeederGeneratorCommand; use Way\Generators\Commands\PublishTemplatesCommand; use Way\Generators\Commands\ScaffoldGeneratorCommand; use Way\Generators\Commands\ViewGeneratorCommand; use Way\Generators\Commands\PivotGeneratorCommand; class GeneratorsServiceProvider extends ServiceProvider { /** * Indicates if loading of the provider is deferred. * * @var bool */ protected $defer = false; /** * Booting */ public function boot() { $this->package('way/generators'); } /** * Register the commands * * @return void */ public function register() { foreach([ 'Model', 'View', 'Controller', 'Migration', 'Seeder', 'Pivot', 'Resource', 'Scaffold', 'Publisher'] as $command) { $this->{"register$command"}(); } } /** * Register the model generator */ protected function registerModel() { $this->app['generate.model'] = $this->app->share(function($app) { $generator = $this->app->make('Way\Generators\Generator'); return new ModelGeneratorCommand($generator); }); $this->commands('generate.model'); } /** * Register the view generator */ protected function registerView() { $this->app['generate.view'] = $this->app->share(function($app) { $generator = $this->app->make('Way\Generators\Generator'); return new ViewGeneratorCommand($generator); }); $this->commands('generate.view'); } /** * Register the controller generator */ protected function registerController() { $this->app['generate.controller'] = $this->app->share(function($app) { $generator = $this->app->make('Way\Generators\Generator'); return new ControllerGeneratorCommand($generator); }); $this->commands('generate.controller'); } /** * Register the migration generator */ protected function registerMigration() { $this->app['generate.migration'] = $this->app->share(function($app) { return $this->app->make('Way\Generators\Commands\MigrationGeneratorCommand'); }); $this->commands('generate.migration'); } /** * Register the seeder generator */ protected function registerSeeder() { $this->app['generate.seeder'] = $this->app->share(function($app) { $generator = $this->app->make('Way\Generators\Generator'); return new SeederGeneratorCommand($generator); }); $this->commands('generate.seeder'); } /** * Register the pivot generator */ protected function registerPivot() { $this->app['generate.pivot'] = $this->app->share(function($app) { return new PivotGeneratorCommand; }); $this->commands('generate.pivot'); } /** * Register the resource generator */ protected function registerResource() { $this->app['generate.resource'] = $this->app->share(function($app) { $generator = $this->app->make('Way\Generators\Generator'); return new ResourceGeneratorCommand($generator); }); $this->commands('generate.resource'); } /** * register command for publish templates */ public function registerpublisher() { $this->app['generate.publish-templates'] = $this->app->share(function($app) { return new publishtemplatescommand; }); $this->commands('generate.publish-templates'); } /** * register scaffold command */ public function registerScaffold() { $this->app['generate.scaffold'] = $this->app->share(function($app) { return new ScaffoldGeneratorCommand; }); $this->commands('generate.scaffold'); } /** * Get the services provided by the provider. * * @return array */ public function provides() { return array(); } }
其中$this->commands('generate.scaffold')命令調用了ServiceProvider的commands.
public function commands($commands) { $commands = is_array($commands) ? $commands : func_get_args(); // To register the commands with Artisan, we will grab each of the arguments // passed into the method and listen for Artisan "start" event which will // give us the Artisan console instance which we will give commands to. $events = $this->app['events']; $events->listen('artisan.start', function($artisan) use ($commands) { $artisan->resolveCommands($commands); }); }
其中每個命令都是派生自GeneratorCommand類。函數
<?php namespace Way\Generators\Commands; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; class ControllerGeneratorCommand extends GeneratorCommand { <?php namespace Way\Generators\Commands; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Way\Generators\Parsers\MigrationNameParser; use Way\Generators\Parsers\MigrationFieldsParser; use Way\Generators\Generator; use Way\Generators\SchemaCreator; use Config; class MigrationGeneratorCommand extends GeneratorCommand { <?php namespace Way\Generators\Commands; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; class ModelGeneratorCommand extends GeneratorCommand { <?php namespace Way\Generators\Commands; use Illuminate\Support\Facades\File; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; class ViewGeneratorCommand extends GeneratorCommand {
咱們來看看GeneratorCommand的代碼:this
<?php namespace Way\Generators\Commands; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Illuminate\Console\Command; use Way\Generators\Filesystem\FileAlreadyExists; use Way\Generators\Generator; use Config; abstract class GeneratorCommand extends Command { /** * @var \Way\Generators\ModelGenerator */ protected $generator; /** * @param Generator $generator */ public function __construct(Generator $generator) { $this->generator = $generator; parent::__construct(); } /** * Fetch the template data * * @return array */ protected abstract function getTemplateData(); /** * The path where the file will be created * * @return mixed */ protected abstract function getFileGenerationPath(); /** * Get the path to the generator template * * @return mixed */ protected abstract function getTemplatePath(); /** * Compile and generate the file */ public function fire() { $filePathToGenerate = $this->getFileGenerationPath(); try { $this->generator->make( $this->getTemplatePath(), $this->getTemplateData(), $filePathToGenerate ); $this->info("Created: {$filePathToGenerate}"); } catch (FileAlreadyExists $e) { $this->error("The file, {$filePathToGenerate}, already exists! I don't want to overwrite it."); } } /** * Get a directory path either through a * command option, or from the configuration * * @param $option * @param $configName * @return string */ protected function getPathByOptionOrConfig($option, $configName) { if ($path = $this->option($option)) { return $path; } return Config::get("generators::config.{$configName}"); } /** * Get the console command options. * * @return array */ protected function getOptions() { return [ ['path', null, InputOption::VALUE_REQUIRED, 'Where should the file be created?'], ['templatePath', null, InputOption::VALUE_REQUIRED, 'The location of the template for this generator'] ]; } }
該類有三個虛函數spa
getTemplateData
getFileGenerationPath //建立的文件存放路徑
getTemplatePath //模板文件的路徑
而且最終調用了Generator類的make函數來生成相應的內容。
其所有代碼以下: code
<?php namespace Way\Generators; use Way\Generators\Filesystem\Filesystem; use Way\Generators\Compilers\TemplateCompiler; use Way\Generators\UndefinedTemplate; class Generator { /** * @var Filesystem */ protected $file; /** * @param Filesystem $file */ public function __construct(Filesystem $file) { $this->file = $file; } /** * Run the generator * * @param $templatePath * @param $templateData * @param $filePathToGenerate */ public function make($templatePath, $templateData, $filePathToGenerate) { // We first need to compile the template, // according to the data that we provide. $template = $this->compile($templatePath, $templateData, new TemplateCompiler); // Now that we have the compiled template, // we can actually generate the file. $this->file->make($filePathToGenerate, $template); } /** * Compile the file * * @param $templatePath * @param array $data * @param TemplateCompiler $compiler * @throws UndefinedTemplate * @return mixed */ public function compile($templatePath, array $data, TemplateCompiler $compiler) { return $compiler->compile($this->file->get($templatePath), $data); } }
/** * Register the view generator */ protected function registerView() { $this->app['generate.view'] = $this->app->share(function($app) { $generator = $this->app->make('Way\Generators\Generator'); return new ViewGeneratorCommand($generator); }); $this->commands('generate.view'); }
$this->app->make是從container中取出單一實例化的對象。對象
/** * Create directory tree for views, * and fire generator */ public function fire() { $directoryPath = dirname($this->getFileGenerationPath()); if ( ! File::exists($directoryPath)) { File::makeDirectory($directoryPath, 0777, true); } parent::fire(); }
/** * The path where the file will be created * * @return mixed */ protected function getFileGenerationPath() { $path = $this->getPathByOptionOrConfig('path', 'view_target_path'); $viewName = str_replace('.', '/', $this->argument('viewName')); return sprintf('%s/%s.blade.php', $path, $viewName); }
protected function getPathByOptionOrConfig($option, $configName) { if ($path = $this->option($option)) { return $path; } return Config::get("generators::config.{$configName}"); }
爲何咱們輸入php artisan generate:view xxxx.yyyy的時候,新建的文件yyyy.php會出如今app/views/xxxx下面?blog
就是由於下面這段代碼:get
$viewName = str_replace('.', '/', $this->argument('viewName'));
首先判斷path參數是否指定,若是沒有指定的話,那麼就從默認的Config裏面去取generators::config.view_target_path字段。
<?php return [ /* |-------------------------------------------------------------------------- | Where the templates for the generators are stored... |-------------------------------------------------------------------------- | */ 'model_template_path' => 'vendor/way/generators/src/Way/Generators/templates/model.txt', 'scaffold_model_template_path' => 'vendor/way/generators/src/Way/Generators/templates/scaffolding/model.txt', 'controller_template_path' => 'vendor/way/generators/src/Way/Generators/templates/controller.txt', 'scaffold_controller_template_path' => 'vendor/way/generators/src/Way/Generators/templates/scaffolding/controller.txt', 'migration_template_path' => 'vendor/way/generators/src/Way/Generators/templates/migration.txt', 'seed_template_path' => 'vendor/way/generators/src/Way/Generators/templates/seed.txt', 'view_template_path' => 'vendor/way/generators/src/Way/Generators/templates/view.txt', /* |-------------------------------------------------------------------------- | Where the generated files will be saved... |-------------------------------------------------------------------------- | */ 'model_target_path' => app_path('models'), 'controller_target_path' => app_path('controllers'), 'migration_target_path' => app_path('database/migrations'), 'seed_target_path' => app_path('database/seeds'), 'view_target_path' => app_path('views') ];
protected function getTemplatePath() { return $this->getPathByOptionOrConfig('templatePath', 'view_template_path'); }
從template文件中讀取相應的數據,並進行替換,而後再經過filesystem的make函數將數據寫入到文件裏面就OK.
<?php // Composer: "fzaninotto/faker": "v1.3.0" use Faker\Factory as Faker; class $CLASS$ extends Seeder { public function run() { $faker = Faker::create(); foreach(range(1, 10) as $index) { $MODEL$::create([ ]); } } }
/** * Fetch the template data * * @return array */ protected function getTemplateData() { $tableName = $this->getTableName(); return [ 'CLASS' => "{$tableName}TableSeeder", 'MODEL' => str_singular($tableName) ]; }
class Filesystem { /** * Make a file * * @param $file * @param $content * @throws FileAlreadyExists * @return int */ public function make($file, $content) { if ( $this->exists($file)) { throw new FileAlreadyExists; } return file_put_contents($file, $content); }
ViewGeneratorCommand::fire()
GeneratorCommand::fire()
ViewGeneratorCommand::getFileGenerationPath()
ViewGeneratorCommand::getTemplatePath
ViewGeneratorCommand::getTemplateData
Generator::make()
TemplateCompiler::compile()
FileSystem::make()