レガシーフレームワーク symfony1.4.9 メンテナンス・サルベージ

枯れたレガシーフレームワークは安定して収益を生むので、リプレースの必要をさほど感じないんですよね。
その結果、メモ書きでもいいから参考になる資料がどんどん枯れてきて、調べても調べても出てこないから「ウキャ〜！:scream:」ってなりませんか？なりますよねぇ。

ちなみに、`symfony`は1まで。`Symfony`てのは2以上になります。このフレームワークを開発している会社の社長がVer.2開発途中での社長自らの語った言葉のなかでVer.2以降はアッパーキャメルになると。知ってました？こういうのも枯れちゃうので書いときます。

「新しいぶどう酒は新しい革袋に」という格言がありますが、対抗勢力は得てして「古いのは旨い！」ってことで離れなられないものです。
やがて酸化しビネガー（酢）にしまうのに、いつまでもこだわってしまう。やはり旨いから…なんでしょうが、、、

#Doctrineが古いのでファインダーの扱いがorz...

通常、

```php:NG
$searchParams = array(
    'pref'     => '北海道',
    'postcode' => '0100110',
);
$r = $this->getTable()->findOneBy($searchParams);
```
で行けそうに思いますよね。`Symfony`ならいけます。ググってもそういうのしか出ません。でも`symfony`ではNGのバージョンがあります。その場合、

```php:ok
$searchParams = array(
    '0100110',
);
$r = $this->getTable()->findOneByPostcode($searchParams);
```
になります。先程のNGケースのような複数パラメータではどうするか？

```php:ok
$searchParams = array(
    '北海道',
    '0100110',
);
$r = $this->getTable()->findOneByPrefAndPostcode($searchParams);
```
`And`で繋いでいく使い方になるのです。でもこれお察しの通り、郵便番号情報の更新なので、

- 郵便番号
- 都道府県名よみ
- 市区町村名よみ
- 丁目番地よみ
- 都道府県名
- 市区町村名
- 丁目番地
- 他にも色々...

とかを`And`で繋いでいくとメンテナンス性の悪い呪文のようになってしまいます。しかも順番が違うと動かなかったりヘンテコになります。

じゃ、どうするか？ `$this->getTable()->createQuery()`を使って実装するのはどうでしょう。

```php:プレースホルダー
$searchParams = array(
    '北海道',
    '0100110',
);
$r = $this->getTable()->createQuery()->where('`pref`=? AND `postcode`=?', $searchParams);
```

でもこれでは数多くの検索条件が加わった時のメリットを感じられません。それでバインド使ってみましょう。

```php:バインド
$searchParams = array(
    'pref'     => '北海道',
    'postcode' => '0100110',
);
$r = $this->getTable()->createQuery()->where('`pref`=:pref AND `postcode`=:postcode', $searchParams);
```
いかがです？これだと、検索条件が加わってもササッとメンテナンスできそうでしょ。

#新しいテーブルを追加して動かす
##スキーマ定義
まず`schema.yml`に追加したいテーブルの定義を書く。
##マイグレーションファイルの自動生成
続いて

```php:symfony
./symfony doctrine:generate-migrations-diff
```
を実行。これでスキーマ差分が自動生成され、`lib/migration/doctrine/`配下にマイグレーションファイルが出力されます。
##テーブル自動生成
ファイル末尾にバージョンナンバーがついているので、[verson number]にその数を入れて
` ./symfony doctrine:migrate [verson number]`
番号を入れなければ、上から順に実行していきます。
ちなみにやっぱりやめ、戻したければ、
` ./symfony doctrine:migrate [verson number] --down`
これでテーブルは消えます。
##基底クラスの生成
そして` ./symfony doctrine:build-model`を実行。基底クラス諸諸を作成してくれます。
BaseHogeFuga
HogeFuga
HogeFugaTable
こんなかんじのものを３つ。

##フォームの生成
忘れそうなのがこれ。スキーマ定義でモデル編集用フォームで使われるウィジェットやバリデーターを生成しなければ。
ORMにDoctrineを使用しているならこうですね。

```php:symfony
./symfony doctrine:build-forms
```
`./form/doctrine/`配下に１つ、`./form/doctrine/base`配下に１つ生成されます。

https://symfony.com/legacy/doc/gentle-introduction/1_4/ja/10-forms
ん？🤔↑ これ読み込んだらスキーマーベースで生成したフォームの第一引数にモデルのオブジェクト渡すことになってる。
さらに謎が深まるんだけど。。。
[フォームのシリアライズ](https://symfony.com/legacy/doc/forms/1_4/ja/11-doctrine-integration#chapter_11_8bc0aadd2c4e9667add8b3c40f6875ebf38739f1)
その深い謎の究明にも取り組まね。その前に、浅めの沼からからどうぞ。
#フォームのメンテナンス
いい記事を見つけたので、この[リンク](https://qiita.com/shotets/items/8343167452d42d67b93e)を辿ってみてください。

#sfForm()を継承して作ったカスタムフォーム動作の謎
フォームは謎いわ〜。
フォームに入力して送信、バリデーションでエラーになって戻ってきたら全部消えてるってのはダメシステム。
だから初期値を設定して出力させたいのがフォーム実装の普通ですね。それで、次のように実装して…

```php:symfony1.4
class myCustomForm extends BaseForm
{
  const NAME = 'my_custom';
  public configure()
  {
    $this->widgetSchema->setNameFormat(self::NAME.'[%s]');
    $this->setWidget(
      'service',
      new sfWidgetFormInputText(
        array(
          'default' => $this->getOption('service')
        ),
        array(
           'value'  => $this->getOption('service')
        )
      )
    );
  }
}
```
カスタムフォームを出力するクラスを実装したのですが、それを使う側でこのように実装すると…

```php:symfony1.4action
public function executeMyHome(sfWebRequest $request)
{
    $service = $request->getParameter('service');
    $default = array(
      'service' => $service,
    );
    $params = $request->getParameter(myCustomForm::NAME);
    $form = new myCustomForm($default);
    $this->form = $form;
}
```
これだと$defaultの値が反映されて出力されません。このように出力されます。

```html
<input type="text" name="my_custom[service]" class="my_custom_service">
```

`value`が出力されて欲しいのです😓。それでハマりましたorz...

##解決策①
アクション側で渡す位置をずらす。

```php:symfony1.4解決①
public function executeMyHome(sfWebRequest $request)
{
    $service = $request->getParameter('service');
    $default = array(
      'service' => $service,
    );
    $params = $request->getParameter(myCustomForm::NAME);
    $form = new myCustomForm(null, $default);
    $this->form = $form;
}
```
わかりますか？ `new myCustomForm(null, $default);`として値を設定します。

##解決策②
フォーム側で受け取り方を、

```php:symfony1.4
class myCustomForm extends BaseForm
{
  const NAME = 'my_custom';
  public configure()
  {
    $this->widgetSchema->setNameFormat(self::NAME.'[%s]');
    $this->setWidget(
      'service',
      new sfWidgetFormInputText(
        array(
          'default' => $this->getOption('service')
        ),
        array(
           //'value'  => $this->getOption('service')
        )
      )
    );
  }
}
```
このように修正。うけとるようになりました。

```html
<input value="＄serviceの値が入るよ" type="text" name="my_custom[service]" class="my_custom_service">
```
でもこの記事書いた後で、なんかわかってきました。
`myCustomForm($option, $attribute)`という扱いで、

```php5:カスタムフォームクラスの一部

    // to:
    $this->setWidget(
      'mailTo',
      new sfWidgetFormInputText(
        array(), // $option側が適用される
        array()  // $attribute側が適用される
      )
    );
```
こういう構造になっているんですね。

じゃぁ疑問、みんなどうしてるのかな？初期値の設定を。
前半のarray()でdefaultで組むべきなのか？後半のvalueで組むべきなのか…

う〜謎い。コメントくだされ


#参考記事
[ななうぇぶのブログ](http://php-sql-gdgd.jugem.jp/?cid=12) 
[doctrine::migrate](http://symfony.com/legacy/doc/reference/1_4/ja/16-Tasks#chapter_16_sub_doctrine_migrate)
[symfony1.4開発の為の参考資料](https://qiita.com/tigersun2000/items/b977e3b9724d0967ba57)