laravel 開発日記 第11回 ~ カスタムコマンドの作成(スキャフォールド Scaffold)前編 ~
前回、シンプルなCRUDアプリを作りましたが、
なかなか、作業数が多く、骨が折れました。
ということで、今回は、コマンド1発でCRUDアプリを作る
カスタムコマンドを作りたいと思います。
ちなみに、こういうのって開発の世界では、
スキャフォールド(Scaffold)っていうらしいですね。
cakePHPとか、RubyOnRailsではよくつかわれている模様。
ちなみにlaravelにもすでにあったりします。
■PHPフレームワークLaravelやってみた
http://www.webopixel.net/php/824.html
今回は、お勉強もかねて自前で作ります。
なお、完成したファイル一式を以下にアップしてます。
参考まで。
■myapp
ダウンロード
では、始めます。
まず、laravelのartisan開発について。
■Laravel 5.0.0 Artisan開発(公式)
http://readouble.com/laravel/5/0/0/ja/commands.html
■コマンドラインアプリケーション(ララ帳)
コマンドラインアプリケーション
カスタムコマンド作成にあたり、以下2ファイルを修正します。
・/app/Console/Commands/MyAppCommand.php (新規)
・/app/Console/Kernel.php (修正)
まずは、簡単なKernel.phpから。
【作業ファイル:/app/Console/Kernel.php】 protected $commands = [ 'App\Console\Commands\Inspire', 'App\Console\Commands\MyAppCommand', //追加 ];
カスタムコマンドとして呼び出すファイルを記述します。
で、コマンド本体のMyAppCommand.php。
長いので、順を追ってみていきます。
【作業ファイル:/app/Console/Commands/MyAppCommand.php】 <?php namespace App\Console\Commands; use Illuminate\Console\Command; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Input\InputArgument; use Illuminate\Support\Str;//(1)
(1)ですが、文字列操作用のAPIがあるので使いました。
■Str(laravel5 api)
http://laravel.com/api/5.0/Illuminate/Support/Str.html
protected $appName; //snake_case /* Str::camel($this->appName); //lowerCamel Str::studly($this->appName); //UpperCamel Str::plural($this->appName); //snake_cases Str::camel(Str::plural($this->appName)); //lowerCamels Str::studly(Str::plural($this->appName)); //UpperCamels */
$appNameにスネークケースをいれて、
各ローワーキャメル、アッパーキャメル、またその複数形も取得してます。
そのあと、public function fire()は、コマンド処理本体なので、ちょっとおいときます。
protected function getArguments() { return [ ['name', InputArgument::REQUIRED, 'entry app name with snake case'], ]; } protected function getOptions() { return [ ['create', 'c', InputOption::VALUE_NONE, 'create my App.', null], ['delete', 'd', InputOption::VALUE_NONE, 'delete my App.', null], ]; }
アーギュメントとオプションの定義。
php artisan [カスタムコマンド名] [アーギュメント] [–オプション]
※ショートコードの時は[-オプション]
で、呼び出します。
今回、アーギュメントにはアプリ名をスネークケースで、
また、オプションは、–create(-c)で作成、–delete(-d)で削除するように設定しました。
では、飛ばしていた処理本体に戻ります。
public function fire() { //入力値の取得 $this->appName = $this->argument('name'); //appNameがスネークケースかどうか、チェックする preg_match('/[a-z0-9_]*/', $this->appName, $result); if( $this->appName !== $result[0] ) //小文字アルファベット、半角英数字、「_」以外の文字を含むとき { $this->error('スネークケースで入力してください。'); exit(); } if( !$this->option('create') && !$this->option('delete') ) { $this->error('create または delete オプションをつけてください。'); exit(); }
最初に入力値の取得とチェックを行ってます。
・appNameがスネークケースか?
・-c または、-d オプションのどちらかがついているか?
//クリエイトモード、デリートモードの切り替え if($this->option('create')) { //クリエートモード /* ---------------------------------------- (1)modelの作成(migrationも自動作成) -------------------------------------------*/ $model_path = './app/'. Str::studly($this->appName). '.php'; if( !file_exists($model_path) ) //モデルが無いとき { $this->call('make:model', ['name' => Str::studly($this->appName)]); //modelにマスアサインメント追加 $model_text = file_get_contents( $model_path ); $model_text = str_replace("//", 'protected $fillable = [\'name\'];'. "\n\t//", $model_text); file_put_contents($model_path, $model_text); } $migration_files = glob("./database/migrations/*". Str::plural($this->appName). "_table.php"); if(count($migration_files) == 1) //該当マイグレーションファイルが一つある場合 { //migrationにnameカラム追加 $migration_path = $migration_files[0]; $migration_text = file_get_contents( $migration_path ); $migration_text = str_replace( '$table->increments(\'id\');', '$table->increments(\'id\');'. "\n\t\t\t". '$table->text(\'name\');', $migration_text ); file_put_contents($migration_path, $migration_text); }
クリエートモードのときの処理。
まずは、model作成です。
$this->call('artisan[コマンド]’, ['アーギュメント’ => '[値]’]);
で、artisan コマンドを呼出ししてます。
基本、ファイル操作をするときは、ファイルの有無を確認してから作業してます。
平行してデリートモードの処理も書いていってます。
コード書いては、クリエート⇒デリートを繰り返しテストしながら、すすめます。
/* ---------------------------------------- (2)ルーティング修正 -------------------------------------------*/ $routes_path = './app/Http/routes.php'; $routes_text = file_get_contents( $routes_path ); $routes_src = 'Route::resource(\''. Str::camel(Str::plural($this->appName)). '\', \''. Str::studly(Str::plural($this->appName)). 'Controller\');'; if( !stristr($routes_text, $routes_src) ) //ルート内に追加対象がない場合 { $routes_text = $routes_text. "\n". $routes_src; file_put_contents($routes_path, $routes_text); $this->info('routes rewrited successfully.'); }
次は、ルーティング
挿入箇所、ちょっと悩みましたが、最後に追加してます。
/* ---------------------------------------- (3)リクエスト作成 -------------------------------------------*/ $request_path = './app/Http/Requests/'. Str::studly($this->appName). 'Request.php'; if( !file_exists($request_path) ) //リクエストが無いとき { $this->call('make:request', ['name' => Str::studly($this->appName). 'Request']); //Requestの修正 $request_text = file_get_contents( $request_path ); $request_text = str_replace( "public function authorize()\n\t{\n\t\treturn false;", "public function authorize()\n\t{\n\t\treturn true;", $request_text ); $request_text = str_replace( "//", "'name' => 'required',". "\n\t//", $request_text ); file_put_contents($request_path, $request_text); }
リクエスト修正。
これも置換方法がいまいち・・・
artisanコマンドで生成されるファイルのフォーマットが変わったら、作り直しになりますね・・・。
/* ---------------------------------------- (4)ルートサービスプロバイダ修正 -------------------------------------------*/ $routes_service_provider_path = './app/Providers/RouteServiceProvider.php'; $routes_service_provider_text = file_get_contents( $routes_service_provider_path ); $routes_service_provider_src = '$router->model(\''. Str::camel(Str::plural($this->appName)). '\', \'App\\'. Str::studly($this->appName). '\');'; if( !stristr($routes_service_provider_text, $routes_service_provider_src) ) //ルート内に追加対象がない場合 { $routes_service_provider_text = str_replace( 'parent::boot($router);'. "\n\t\t", 'parent::boot($router);'. "\n\t\t$routes_service_provider_src\n\t\t", $routes_service_provider_text ); file_put_contents($routes_service_provider_path, $routes_service_provider_text); $this->info('routes service provicer rewrited successfully.'); }
ルートサービスプロバイダで、モデルとルートを関連付けます。
/* ---------------------------------------- (5)コントローラー作成(テンプレートから) -------------------------------------------*/ $controller_path = './app/Http/Controllers/'. Str::studly(Str::plural($this->appName)). 'Controller.php'; $controller_template_path = './app/Http/Controllers/MyAppTemplateController.php'; if( !file_exists($controller_path) ) //リクエストが無いとき { $this->call('make:controller', ['name' => Str::studly(Str::plural($this->appName)). 'Controller']); //controllerの修正 $controller_text = file_get_contents( $controller_template_path ); $controller_text = str_replace( "[[[lc]]]", Str::camel($this->appName), $controller_text ); $controller_text = str_replace( "[[[uc]]]", Str::studly($this->appName), $controller_text ); $controller_text = str_replace( "[[[lcs]]]", Str::camel(Str::plural($this->appName)), $controller_text ); $controller_text = str_replace( "[[[ucs]]]", Str::studly(Str::plural($this->appName)), $controller_text ); file_put_contents($controller_path, $controller_text); }
コントローラーは、
/app/Http/Controllers/MyAppTemplateController.php
に以下リプレイスをかけたもので上書きしてます。
[[[lc]]] ⇒ ローワーキャメルのアプリ名に。
[[[uc]]] ⇒ アッパーキャメルのアプリ名に。
[[[lcs]]] ⇒ ローワーキャメル(複数)のアプリ名に。
[[[ucs]]] ⇒ アッパーキャメル(複数)のアプリ名に。
/* ---------------------------------------- (6)ビュー作成(テンプレートから) -------------------------------------------*/ $view_path = './resources/views/'. Str::camel(Str::plural($this->appName)); $view_template_path = './resources/views/myAppTemplate'; $file_names = array( 'index' => 'index.blade.php', 'show' => 'show.blade.php', 'createAndEdit' => 'createAndEdit.blade.php', ); if( !file_exists($view_path) ) { mkdir( $view_path, 0777, true ); foreach ($file_names as $key => $value) { copy( $view_template_path. '/'. $value, $view_path. '/'. $value ); $view_text = file_get_contents( $view_path. '/'. $value ); $view_text = str_replace( "[[[lc]]]", Str::camel($this->appName), $view_text ); $view_text = str_replace( "[[[uc]]]", Str::studly($this->appName), $view_text ); $view_text = str_replace( "[[[lcs]]]", Str::camel(Str::plural($this->appName)), $view_text ); $view_text = str_replace( "[[[ucs]]]", Str::studly(Str::plural($this->appName)), $view_text ); file_put_contents($view_path. '/'. $value, $view_text); } $this->info('view file copied successfully.'); }
ビューも、コントローラー同様。
/resources/views/myAppTemplate
をリプレイスして、コピーするだけです。
これで、スキャフォールドコマンド「myapp」が完成しました。
モデル制作手順としては、
(1)コンソールから
php artisan myapp [アプリ名] -c
(2)マイグレーション、モデル、リクエストに、追加したいカラム項目を追加。
(3)マイグレーション
php artisan migrate
で、シンプルなCRUDアプリの完成です。
いろいろ、改造の余地はあると思うので、
それぞれ、自分の好みを入れつつ、作ってみてください。
ではでは。
Discussion
New Comments
No comments yet. Be the first one!