アンダースコアを含むテーブル名をFuelPHPのfromdbタスクでmodelを生成した時にディレクトリーが作成されてしまうのが嫌な場合


## 記事を書くきっかけ

表題の通りで、例えばfizz_buzz_fizzbuzzという名前のテーブルのmodelを` fromdb`で生成すると、` fuel/app/classes/model/fizz/buzz/fizzbuzz.php`というファイルが生成され、クラス名は` Model_Fizz_Buzz_Fizzbuzz`となる。

これを、` fuel/app/classes/model/fizzbuzzfizzbuzz.php`というファイルが生成され、クラス名は` Model_Fizzbuzzfizzbuzz`となるようにしたかった。

## 対応にあたって

同じ事で悩んだ方で、生成されたファイルを` mv`して` rm`して` bootstrap.php`で` Autoloader::add_classes`する解決策があったが、` fromdb`の便利さを享受したかったため、この方法は見送った。FuelPHPなので、ファイル名にアンダースコアを使用したくなかった、というのも見送った事の遠因ではある。

` fromdb`が利用している` packages/oil/classes/generate.php`の` model`の処理で、ファイル名を決定する際に、アンダースコアとハイフンをdirectory separatorに置換するため、前述のような状態となる。

` generate.php`も` fromdb.php`も修正したくなかったので、` fromdb`を元にした` fromdbunderscore`を作成した。

## コード

_使用方法は基本的にfromdbと同じです（ヘルプ変更していません...）_

1. ` --singular`を指定しないと動作しないようにした。
1. ` --crud`が指定された場合動作しないようにした。
1. ` -us-model-dir'で``` fuel/app/classes/model```の下にディレクトリーを指定出来るようにした。
1. ` -us-base-model`で基底クラスを指定出来るようにした。
1. 動作未検証なので` -no-migration`を必ず指定する事。

```php:fuel/app/tasks/fromdbunderscore.php
<?php

namespace Fuel\Tasks;

require_once(PKGPATH . 'oil/tasks/fromdb.php');

class Fromdbunderscore extends Fromdb
{
  const ORIGINAL_PARENT_CLASS_NAME  = '\Orm\Model';

  private static $_model_dir = '';
  private static $_parent_class_name = '';

  public static function crud($tables = '')
  {
    \Cli::write('fromdbunderscore does not support crud.', 'red');
    exit();
  }

  public static function model($tables = '')
  {
    // do we have any tables defined?
    if (empty($tables))
    {
      // do we want to generate for all tables?
      if ( ! \Cli::option('all', false))
      {
        \Cli::write('No table names specified to generate a model on.', 'red');
        exit();
      }

      // get the list of all available tables
      try
      {
        $list = \DB::list_tables(null, \Cli::option('db', null));
      }
      catch (\FuelException $e)
      {
        \Cli::write('The database driver configured does not support listing tables. Please specify them manually.', 'red');
        exit();
      }

      $prefix = \DB::table_prefix();
      $migration = \Config::get('migrations.table', 'migration');

      $tables = array();

      // create the table list
      foreach ($list as $table)
      {
        // strip any defined table prefix from the table name
        if ( ! empty($prefix) and strpos($table, $prefix) === 0)
        {
          $table = substr($table, strlen($prefix));
        }

        // skip the migration table
        $table == $migration or $tables[] = $table;
      }
    }

    // make sure we have an array to work with
    is_array($tables) or $tables = explode(',', $tables);

    static::_preprocess_for_generate();

    // generate for each table defined
    foreach ($tables as $table)
    {
      static::generate_model_under_score($table);
    }
  }

  protected static function _preprocess_for_generate()
  {
    if(empty(\Cli::option('singular', ''))){
      \Cli::write('You must specify the -singular option.', 'red');
      exit();
    }

    static::$_model_dir = \Cli::option('us-model-dir', '');

    static::$_parent_class_name = empty(\Cli::option('us-base-model', '')) ?
      static::ORIGINAL_PARENT_CLASS_NAME : \Cli::option('us-base-model');
  }

  protected static function generate_model_under_score($table)
  {
    // start with an empty list
    \Oil\Generate::$create_files = array();

    $args = static::arguments($table);

    // underscores are erased from table name
    $table_name_which_has_not_underscores = str_replace('_', '', $args[0]);
    $args[0] = $table_name_which_has_not_underscores;

    // and generate, but doesn't create a file.
    call_user_func('Oil\Generate::model', $args, false);

    $filename = \Oil\Generate::$create_files[0]['path'];

    // restore a table name in generated content
    $search = array(sprintf('$_table_name = \'%s\'', $table_name_which_has_not_underscores));
    $replace = array(sprintf('$_table_name = \'%s\'', $table));

    // normalize a class name in generated content
    $search[] = sprintf('Model_%s', ucfirst($table_name_which_has_not_underscores));
    $replace[] = sprintf('Model_%s', ucfirst(\Inflector::camelize($table)));

    // replace parent class name if neededed
    if(!empty(static::$_parent_class_name)){
      $search[] = static::ORIGINAL_PARENT_CLASS_NAME;
      $replace[] = static::$_parent_class_name;
    }

    $contents = str_replace($search, $replace, \Oil\Generate::$create_files[0]['contents']);

    $type = \Oil\Generate::$create_files[0]['type'];

    $model_dir = empty(static::$_model_dir) ?
      dirname($filename) : sprintf('%s/%s', dirname($filename), static::$_model_dir);
    is_dir($model_dir) or mkdir($model_dir, 0755, TRUE);

    $filename = sprintf('%s/%s', $model_dir, basename($filename));

    // easy way
    \Cli::write("\tCreating $type: $filename", 'green');
    @file_put_contents($filename, $contents);
    @chmod($filename, 0666);
  }
}
```