独自オプションを作成してShellを実装してみた

## 環境
- *PHP 7.2.6*
- *CakePHP 3.6.13*

## やりたい事
- *DBの値更新*
    - **オプション無しの場合**
        - 対象カラムの値全更新
    - **オプション有りの場合**
        - 対象カラムの指定箇所のみ更新

## Shellクラス作成
- CakePHPの**bake**コマンドでShellクラス作成

```terminal
$ bin/cake bake shell sample


Creating file /vagrant/yourmystar.jp/src/Shell/SampleShell.php
Wrote `/vagrant/yourmystar.jp/src/Shell/SampleShell.php`

Baking test case for App\Shell\SampleShell ...

Creating file /vagrant/yourmystar.jp/tests/TestCase/Shell/SampleShellTest.php
Wrote `/vagrant/yourmystar.jp/tests/TestCase/Shell/SampleShellTest.php` 
```
- 下記のようなShellが作成される

<img width="871" alt="スクリーンショット 2019-03-05 16.18.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/267738/024cdb49-6f08-93e6-a157-20a95a9b0593.png">

- **getOptionParser**
    - オプションを定義
        - 参考：CakePHP 3.7 Red Velvet Cookbook[オプションパーサー](https://book.cakephp.org/3.0/ja/console-and-shells/option-parsers.html)

- **main**
    - 実行処理を定義

## 実装

```php
<?php
namespace App\Shell;

use Cake\Console\Shell;

/**
 * Sample shell command.
 */
class SampleShell extends Shell
{

    /**
     * Manage the available sub-commands along with their arguments and help
     *
     * @see http://book.cakephp.org/3.0/en/console-and-shells.html#configuring-options-and-generating-help
     *
     * @return \Cake\Console\ConsoleOptionParser
     */
    public function getOptionParser()
    {
        $parser = parent::getOptionParser();

        $parser->addOption('target_id', [
            'help' => '更新対象のIDを指定する'
        ]);

        return $parser;
    }

    public function initialize()
    {
        parent::initialize();
        $this->loadModel('Hoge');
    }

    /**
     * main() method.
     *
     * @return bool|int|null Success or error code.
     */
    public function main()
    {
        $offset = 0;
        $executionDate = date('Y-m-d H:i:s');

        $this->out($executionDate . " [Start]");

        while (true) {
            $samples = $this->Hoge
                ->find()
                ->offset($offset)
                ->limit(100);

            if (isset($this->params['target_id'])) {
                $samples->where(['id IN' => explode(',', $this->params['target_id'])]);
            }

            $samples = $samples->toArray();

            if (empty($samples)) {
                break;
            }

            foreach ($samples as $sample) {
                $sample->flg = 1;
                $this->Hoge->save($sample);
                $this->out('Update Id：' . $sample->id);
            }

            $offset += 100;
        }

        $this->out($executionDate . " [End]");
    }
}
```

## 解説
- *getOptionParser*
    - **addOption**メソッド
        - オプションを定義する
        - 参考：[オプションの利用](https://book.cakephp.org/3.0/ja/console-and-shells/option-parsers.html#id5)
- DBからデータ取得
    - **Hoge**Tableからデータを100件ずつ取得
    - オプションが指定された場合、**where句**に指定される
        - [**explode**](http://php.net/manual/ja/function.explode.php)でIDの複数指定を可能にしている
- カラムの値更新
    - **flg**カラムの値を**1**に更新
- offsetの値追加
    - 100件ずつ取得するので、次の100を取得するために`$offset += 100;`をする

※**変数名は基本「仮」なのでご了承下さい。**

## 実行結果
- **通常実行**

```terminal
$ bin/cake sample
2019-03-05 20:37:52 [Start]
Update Id：1
Update Id：2
・
・
・
2019-03-05 20:37:52 [End]
```
- **オプション指定実行**

```terminal
$ bin/cake sample --target_id=1
2019-03-05 20:39:51 [Start]
Update Id：1
2019-03-05 20:39:51 [End]
```

- **オプション複数指定実行**

```terminal
$ bin/cake sample --target_id=1,2,3
2019-03-05 20:39:51 [Start]
Update Id：1
Update Id：2
Update Id：3
2019-03-05 20:39:51 [End]
```

- **help**表示

```terminal
$ bin/cake sample --help
Usage:
cake sample [-h] [-q] [--target_id] [-v]

Options:

--help, -h       Display this help.
--quiet, -q      Enable quiet output.
--target_id      更新対象のIDを指定する // addOptionメソッドで追加したオプション
--verbose, -v    Enable verbose output.
```

# 簡単！！！

## おまけ
Twitterやってます！外部のエンジニアの方ともどんどん繋がりたいと考えていますので、是非フォローして頂ければと思います！[@Tatsuo96](https://twitter.com/Tatsuo96)
ブログ始めました！
https://note.mu/tatsuo_iriyama