Eloquent は Laravel の用意したデータベースに対応するクラスの枠組みです。その特性上、データベース中のテーブルに対する操作を色々やりやすい他、テーブル中のメタ情報を元に様々なことができます。このメタ情報は Eloquent に生えている getConnection メソッドから呼び出せる Doctrine によってアクセスでき、これを元に色々できます。
Doctrine: PHP Open Source Project
【PHP】DBを直接扱うためのパッケージDoctrineの紹介 – 株式会社シーポイントラボ | 浜松のシステム・RTK-GNSS開発
この Doctrine を使うとあるEloquentの指すテーブルに対応したコードを編集しやすく、メタプログラミングの大きな助けになります。
例えばdeleted_at カラムの有無で SoftDelete トレイトの使用を自動で記述できます。
モデル中の deleted_at カラムに対応する softDelete trait の use を追加する Artisan コマンドのソースコード
<?php namespace App\Console\Commands\ForDevelop; use App\Console\BaseCommand; use App\Models\Eloquents\BaseEloquent; class AppendSoftDeleteToModel extends BaseCommand { /** * The name and signature of the console command. * * @var string */ protected $signature = 'dev:append-model-soft-delete {classNamePath}'; /** * The console command description. * * @var string */ protected $description = 'モデル中の deleted_at カラムに対応する softDelete trait の use を追加'; /** * Execute the console command. * * @return mixed */ public function handle(): void { /** @var BaseEloquent $modelNamePath */ $modelNamePath = $this->argument('classNamePath'); /** @var BaseEloquent $modelInstance */ $modelInstance = new $modelNamePath(); $deletedAtColumn = $modelInstance->getConnection()->getDoctrineColumn($modelInstance->getTable(), 'deleted_at'); if (! $deletedAtColumn) { $this->error($modelInstance->getTable().'.deleted_at カラムが見つかりませんでした。'); return; } if (in_array('Illuminate\Database\Eloquent\SoftDeletes', class_uses($modelInstance), true)) { $this->error('既に SoftDeletes は use されています'); return; } $modelFilePath = config('infyom.laravel_generator.path.model').DIRECTORY_SEPARATOR.class_basename($modelNamePath).'.php'; $content = file_get_contents($modelFilePath); $replacedContent = $this->appender($content); file_put_contents($modelFilePath, $replacedContent); } private function appender(string $content): string { $newContent = preg_replace('/(class [^{]*{)/', "$1\nuse SoftDeletes;", $content); return preg_replace('/(namespace \S+;\n)/', "$1\nuse Illuminate\Database\Eloquent\SoftDeletes;", $newContent); } }
例えば各カラムの型とコメントを元にルールとルールに対応するラベルを作れます。
モデル中の public static function rules() メソッドの返り値のキーに対応する rules 用のラベルメソッド rulesAttributes を追加する Artisan コマンドのソースコード
<?php namespace App\Console\Commands\ForDevelop; use App\Console\BaseCommand; use App\Models\Eloquents\BaseEloquent; class AppendRuleAttributesToModel extends BaseCommand { /** * The name and signature of the console command. * * @var string */ protected $signature = 'dev:append-model-rule-attributes {classNamePath}'; /** * The console command description. * * @var string */ protected $description = 'モデル中の public static function rules() メソッドの返り値のキーに対応する attributes メソッドを追加'; /** * Execute the console command. * * @return mixed */ public function handle(): void { /** @var BaseEloquent $modelNamePath */ $modelNamePath = $this->argument('classNamePath'); $rules = $modelNamePath::rules(); $ruleKeys = array_keys($rules); $nameToCommentMap = $this->getCommentMapFromTable(new $modelNamePath()); $attributeMap = []; foreach ($ruleKeys as $ruleKey) { $attributeMap[$ruleKey] = $nameToCommentMap[$ruleKey] ?? ''; } $modelFilePath = config('infyom.laravel_generator.path.model').DIRECTORY_SEPARATOR.class_basename($modelNamePath).'.php'; $content = file_get_contents($modelFilePath); $replacedContent = $this->replacer($content, $attributeMap); file_put_contents($modelFilePath, $replacedContent); } /** * @param BaseEloquent $model * @return array {columnName => comment}[] */ protected function getCommentMapFromTable(BaseEloquent $model): ?array { $table = $model->getConnection()->getTablePrefix().$model->getTable(); $schema = $model->getConnection()->getDoctrineSchemaManager(); $database = null; if (strpos($table, '.')) { [$database, $table] = explode('.', $table); } $columns = $schema->listTableColumns($table, $database); $nameToCommentMap = []; foreach ($columns as $column) { $nameToCommentMap[$column->getName()] = $column->getComment(); } return $nameToCommentMap; } protected function replacer(string $content, array $attributeMap): string { $attributeMapStr = ''; foreach ($attributeMap as $key => $attribute) { $attributeMapStr .= " '{$key}' => '{$attribute}',\n"; } $appendFunctionStr = <<<EOF public static function ruleAttributes(): array { return [ {$attributeMapStr} ]; } EOF; return preg_replace('/}\s*\z/', $appendFunctionStr."\n}\n", $content); } }
例の様なオレオレコードジェネレータをサクッと作れる他、 hoge-generator の様な名前を冠したコード生成ライブラリ多くのがこの Doctrine を用いて便利なコードを実現しています。その様なライブラリのコードを読むとメタプログラミングの技術を吸収でき、手打ちやコピペ抜きにコーディングの大半を済ませられるようになります。