Laravel Packages

Laravel的包存放在vendor目錄下面。php

image

例如way,能夠是一個供應商代號,其目錄下面有一個generators目錄。app

image

在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

1. GeneratorSeriveProvider

 
其所有代碼以下:
 
 
 
 
 
<?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 {

2. 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函數來生成相應的內容。

3. Generator

其所有代碼以下: 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);
    }

}

4. generator:view 是如何執行的?

4.1 ServiceProvider Register

/**
     * 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中取出單一實例化的對象。對象

4.2 ViewGeneratorCommand fire

4.2.1 getFileGenerationPath

/**
     * 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字段。

image

<?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')

];

4.2.2 getTemplatePath

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

5. 流程圖

ViewGeneratorCommand::fire()
GeneratorCommand::fire()
ViewGeneratorCommand::getFileGenerationPath()
ViewGeneratorCommand::getTemplatePath
ViewGeneratorCommand::getTemplateData
Generator::make()
TemplateCompiler::compile()
FileSystem::make()
相關文章
相關標籤/搜索