[Smarty] HTMLを出力する以外のSmarty活用法

## [Smarty](http://www.smarty.net/)とは
PHPのテンプレートエンジンです。 

テンプレートエンジンといえば、
MVCアーキテクチャのViewを担当していて
**「HTMLを出力するもの」** みたいなイメージが強いですが
**「条件に応じたテキストを吐き出す便利ツール」** として捉えたとき
**「PHP内部で記述してるSQLも分離できるんじゃね？」** と思い立ちました。

そこで、自作フレームワークにおいてSQLを分離させてみました。
まずは下記のように、smartyのテンプレートディレクトリパスにSQLファイルを管理するディレクトリを追加。

```php:ControllerBase.class.php
<?php
abstract class ControllerBase
{
    /* 一部抜粋 */
    protected $_smarty;

    private function _initializeTemplateEngine()
    {
        $this->_smarty = new Smarty();
        $this->_smarty->template_dir = array(
            SMARTY_TEMPLETE.DS.'sql',
            SMARTY_TEMPLETE.DS.'common',
            SMARTY_TEMPLETE.DS.$this->_controller.DS.$this->_action,
        );
        $this->_smarty->compile_dir = SMARTY_COMPILE.DS ;
        $this->_smarty->cache_dir   = SMARTY_CACHE.DS ;
        $this->_smarty->config_dir  = SMARTY_CONFIGS.DS ;
    }

    private function _setCollection()
    {
        $tables = Database::getTables(USE_DB);

        foreach ($tables as $v) {
            $this->_collection[$v] = CreateInstance::createModelInstance($v);

            /* （中略） */

            // テンプレートエンジンを利用する
            if (method_exists($this->_collection[$v], 'setTemplateEngine')) {
                $this->_collection[$v]->setTemplateEngine($this->_smarty);
            }
        }
    }

}
```

`` _initializeTemplateEngine() `` でSmartyのインスタンスを生成し、各種設定を行う。

`` _setCollection() `` で各モデルクラスのインスタンスを生成しつつ、_smarty のインスタンスをモデルでも共有する。

各モデルクラスは、ModelBase クラスを継承している（自作フレームワーク内の前提）ので
ModelBase クラスメソッドとして setTemplateEngine を定義する。

```php:ModelBase.class.php
<?php
abstract class ModelBase extends ModelCore
{
    /* 一部抜粋 */

    protected $_smarty;
    protected $_sqlParams;

    public function __construct()
    {
        $this->_sqlParams = array();
        parent::__construct();
    }

    /* （中略） */

    public function setTemplateEngine($_obj)
    {
        $this->_smarty = $_obj;
    }

    protected function _makeQuery($_class, $_function)
    {
        $this->_smarty->assign('_sqlParams', $this->_sqlParams);
        return $this->_smarty->fetch($_class.DS.$_function.'.sql');
    }
}
```

各モデルクラスでは、データを取得するメソッド `` getData() `` を下記のように定義。

```php:HogeFuga.class.php
<?php
class HogeFuga extends ModelBase
{
    public function getData($id)
    {
        // コメントからのご指摘 [#comment-d4c53ddaaa31e43d4ae8]
        // 入力されたパラメータには、適切なエスケープ処理を行うこと
        $this->_sqlParams['id'] = $id;
        $query = $this->_makeQuery(__CLASS__, __FUNCTION__);
        $row = $this->_result($query); // ModelCore 内で定義
        return $this->_formingResultRows($row); // ModelCore 内で定義
    }
}
```

最後に、SQLファイルを下記のように記述。

```sql:SMARTY_TEMPLETE.DS.'sql'.DS.'HogeFuga'.DS.'getData.sql'
SELECT * FROM {$_sqlParams._table} WHERE id={$_sqlParams.id}
```

この方法により、PHPスクリプト内部からSQL文が完全に分離でき
スクリプトの見通しがかなりスッキリしました。

細かい調整はここでは省略させていただきましたが
同一メソッド内で複数のクエリを発行したい場合などは、
smarty テンプレート内部で`` {if}{/if} `` などで分岐して記述することも可能です。
（個人的な目標は1メソッド1クエリー）

以上、ざざっと書いてしまいましたが、なにかのお役に立てば幸いです。



## 追記
SQLエスケープについて、下記をご参照ください。

* [PHPでデータベースに接続するときのまとめ](http://qiita.com/mpyw/items/b00b72c5c95aac573b71)
* [PHPとセキュリティの解説書12種類を読んでSQLエスケープの解説状況を調べてみた](http://blog.tokumaru.org/2013/12/php12sql.html)