LoginSignup
1
0

More than 3 years have passed since last update.

BakeしたModelを継承するModelを生成してBakableな環境を作る

Last updated at Posted at 2020-08-18

目的

開発の途中でテーブル定義が変更されることがよくある。(カラム追加とか)
その際に、CakePHPのBakeコマンドを再実行して、Model(Table,Entity)を最新化すると独自に記述したロジックが上書きされてしまうので、独自ロジックはBakeしたModelを継承したクラスに記述して、BakeしたModelを更新しても独自ロジックに影響しない状態にする。

※実運用に達していないので、参考程度と考えていただきたい。

ソースコード

bake用のModelを生成するコマンドとテンプレート

通常のbake modelを利用するとsrc/Model/Entity(またはTable)にModelがBakeされる。
ここにはBakeしたModelを継承したModelを配置したいので、ModelCommandを継承したCommandを作り、public $pathFragment = 'Model/Baked/';とすることで、`src/Model/Baked/Entity(またはTable)にModelをBakeするようにする。

src/Command/BakedModelCommand.php
<?php
declare(strict_types=1);

namespace App\Command;

use Bake\Command\ModelCommand;
use Bake\Utility\TemplateRenderer;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Core\Configure;
use Cake\ORM\Table;

class BakedModelCommand extends ModelCommand
{
    public $pathFragment = 'Model/Baked/';

    /**
     * {@inheritDoc}
     */
    public function bake(string $name, Arguments $args, ConsoleIo $io): void
    {
        parent::bake($name, $args, $io);
    }
}

上だけだと、Bakeのデフォルトのテンプレートを使われてしまい、BakeされたModelのnamespaceがデフォルトになってしまうので、vendor/cakephp/bake/templates/bake/Model配下のentity.twigtable.twigをコピーして、templates/plugin/Bake/Model配下に配置します。2ファイルのnamespaceを修正します。

bakeしたModelを継承するクラスを生成するコマンドとテンプレート

同じくModelCommandを継承しつつ、ModelCommandbakeTablebakeTableを元に出力先ディレクトリや利用するテンプレートを変更したbakeExtendedTablebakeExtendedEntityを作成した。
もっといい方法はないものか・・?
ModelCommandが出力先ディレクトリや利用するテンプレートを差し替えれるようにしておいてくれると継承しやすいのに。。)

src/Command/ExtendedModelCommand.php
<?php
declare(strict_types=1);

namespace App\Command;

use Bake\Command\ModelCommand;
use Bake\Utility\TemplateRenderer;
use Cake\Console\Arguments;
use Cake\Console\ConsoleIo;
use Cake\Core\Configure;
use Cake\ORM\Table;

class ExtendedModelCommand extends ModelCommand
{
    /**
     * {@inheritDoc}
     */
    public function bake(string $name, Arguments $args, ConsoleIo $io): void
    {
        $table = $this->getTable($name, $args);
        $tableObject = $this->getTableObject($name, $table);
        $data = $this->getTableContext($tableObject, $table, $name, $args, $io);
        $this->bakeExtendedTable($tableObject, $data, $args, $io);
        $this->bakeExtendedEntity($tableObject, $data, $args, $io);

    }

    public function bakeExtendedTable(Table $model, array $data, Arguments $args, ConsoleIo $io): void
    {
        if ($args->getOption('no-table')) {
            return;
        }

        $namespace = Configure::read('App.namespace');
        $pluginPath = '';
        if ($this->plugin) {
            $namespace = $this->_pluginNamespace($this->plugin);
        }

        $name = $model->getAlias();
        $entity = $this->_entityName($model->getAlias());
        $data += [
            'plugin' => $this->plugin,
            'pluginPath' => $pluginPath,
            'namespace' => $namespace,
            'name' => $name,
            'entity' => $entity,
            'associations' => [],
            'primaryKey' => 'id',
            'displayField' => null,
            'table' => null,
            'validation' => [],
            'rulesChecker' => [],
            'behaviors' => [],
            'connection' => $this->connection,
        ];

        $renderer = new TemplateRenderer($this->theme);
        $renderer->set($data);
        $out = $renderer->generate('Bake.Model/extended_table');

        $path = $this->getPath($args);
        $filename = $path . 'Table' . DS . $name . 'Table.php';
        $io->out("\n" . sprintf('Baking table class for %s...', $name), 1, ConsoleIo::QUIET);
        $io->createFile($filename, $out, false);

        // Work around composer caching that classes/files do not exist.
        // Check for the file as it might not exist in tests.
        if (file_exists($filename)) {
            require_once $filename;
        }
        $this->getTableLocator()->clear();

        $emptyFile = $path . 'Table' . DS . '.gitkeep';
        $this->deleteEmptyFile($emptyFile, $io);
    }

    public function bakeExtendedEntity(Table $model, array $data, Arguments $args, ConsoleIo $io): void
    {
        if ($args->getOption('no-entity')) {
            return;
        }
        $name = $this->_entityName($model->getAlias());

        $namespace = Configure::read('App.namespace');
        $pluginPath = '';
        if ($this->plugin) {
            $namespace = $this->_pluginNamespace($this->plugin);
            $pluginPath = $this->plugin . '.';
        }

        $data += [
            'name' => $name,
            'namespace' => $namespace,
            'plugin' => $this->plugin,
            'pluginPath' => $pluginPath,
            'primaryKey' => [],
        ];

        $renderer = new TemplateRenderer($this->theme);
        $renderer->set($data);
        $out = $renderer->generate('Bake.Model/extended_entity');

        $path = $this->getPath($args);
        $filename = $path . 'Entity' . DS . $name . '.php';
        $io->out("\n" . sprintf('Baking entity class for %s...', $name), 1, ConsoleIo::QUIET);
        $io->createFile($filename, $out, false);

        $emptyFile = $path . 'Entity' . DS . '.gitkeep';
        $this->deleteEmptyFile($emptyFile, $io);
    }
}

上記コマンドから呼び出すテンプレートが以下2つ。

templates/plugin/Bake/Model/extended_table.twig
{#
/**
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @since         2.0.0
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */
#}
{% set annotations = DocBlock.buildTableAnnotations(associations, associationInfo, behaviors, entity, namespace) %}
<?php
declare(strict_types=1);

namespace {{ namespace }}\Model\Table;

use {{ namespace }}\Model\Baked\Table\{{ name }}Table as BakedTable;
{% set uses = ['use Cake\\ORM\\Query;', 'use Cake\\ORM\\RulesChecker;', 'use Cake\\ORM\\Table;', 'use Cake\\Validation\\Validator;'] %}
{{ uses|join('\n')|raw }}

{{ DocBlock.classDescription(name, 'Model', annotations)|raw }}
class {{ name }}Table extends BakedTable
{
    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config): void
    {
        parent::initialize($config);
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator): Validator
    {
        parent::validationDefault($validator);

        return $validator;
    }

    /**
     * Returns a rules checker object that will be used for validating
     * application integrity.
     *
     * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
     * @return \Cake\ORM\RulesChecker
     */
    public function buildRules(RulesChecker $rules): RulesChecker
    {
        parent::buildRules($rules);

        return $rules;
    }

    /**
     * Returns the database connection name to use by default.
     *
     * @return string
     */
    public static function defaultConnectionName(): string
    {
        return '{{ connection }}';
    }
}


templates/plugin/Bake/Model/extended_entity.twig
{#
/**
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
 * @link          http://cakephp.org CakePHP(tm) Project
 * @since         2.0.0
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
 */
#}
<?php
declare(strict_types=1);

namespace {{ namespace }}\Model\Entity;

use {{ namespace }}\Model\Baked\Entity\{{ name }} as BakedEntity;

class {{ name }} extends BakedEntity
{
}

利用方法

bin/cake bake baked_model Xxxs
bin/cake bake extended_model Xxxs

感想

一旦はこれで開発してみて問題があれば修正する。
マニュアルに記載されているSimpleBakeCommand継承版も作ってみたが、引数のXxxsからファイル名やクラス名を生成するのが手間なので、ModelCommand継承にすることにした。
ただこの実装だとModelCommandに変更が入った時にコピペし直しが必要なので、もっといい方法がありそう。

参考

この考え方自体は以下を見て知った。
https://wp.tech-style.info/archives/1519
https://qiita.com/ymm1x/items/12ced37212636b09de19

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0