Laravel5でヘルパーメソッドを実装してみた

# はじめに
ドキュメントやコード読んだりしながら分かったことをまとめました。
URLをよしなに生成してくれる便利なassetというヘルパーメソッドがあるので、
これを試しにちょっとカスタマイズしてみたいと思います。

# 作ってみる
メソッドを実装したphpファイルを作ってrequireします。では順を追って説明します。
（requireする以外にcomposerを利用する方法も試したので、そちらもチョロっと紹介したいとます。）

## helpers.php
僕の場合はHelpersというディレクトリを作って、その下にhelpers.phpというのを書きました。

```app/Helpers/helpers.php
<?php

if ( ! function_exists('assetEx'))
{
  /**
   * assetの処理に加えて、ランダムな値をクエリストリングとして付加する。
   *
   * @param  string  $file
   * @param  bool    $secure
   * @return string
   */
  function assetEx($file, $secure = false)
  {
      static $refresher = null;

      if (is_null($refresher))
      {
        $refresher = mt_rand();
      }
      return app('url')->asset($file, $secure) . '?' . $refresher;
  }
}
```
[^1]ここで紹介する例はぜーんぜん実用的ではないのでご注意ください。


## requireする
~~僕はとりあえず public/index.phpでautoloadしている手前でさっき作ったhelpers.phpをrequireしました。~~
**(追記：ここだとユニットテスト走らせるときとか読み込んでくれないのでbootstrap/index.phpのほうが良いと同僚に指定されました。なるほど確かに！）**

```public/index.php

    << 省略 >>
/*
|--------------------------------------------------------------------------
| Register The Original Auto Loader
|--------------------------------------------------------------------------
*/

require __DIR__.'/../app/Helpers/helpers.php';


/*
|--------------------------------------------------------------------------
| Register The Auto Loader
|--------------------------------------------------------------------------
|
| Composer provides a convenient, automatically generated class loader for
| our application. We just need to utilize it! We'll simply require it
| into the script here so that we don't have to worry about manual
| loading any of our classes later on. It feels nice to relax.
|
*/

require __DIR__.'/../bootstrap/autoload.php';

    << 省略 >>
```

ちなみにLaravelで実装されているヘルパーと同名のメソッドを作りたい場合はautoload前でrequireする必要があります。

## 使う
こんな感じで使えるようになります。

テンプレート

```
<img src="{{ assetEx("hoge.jp/fuga.png") }}">
```

生成されるHTML

```
<img src="http://hoge.jp/fuga.png?3801342084329384">
```

## requireじゃなくてcomposerを使う
別の方法としてcomposerで読み込む方法もあります。requireするのとどっちがスマートかは運用ポリシーとかで変わってくると思うので、よく考えて選択したいところです。

composer.json のautoload下にfilesとして指定します。

```composer.json
  <<省略>>
    "autoload": {
        "classmap": [
            "database"
        ],
        "files": [
            "app/Helpers/helpers.php"
        ],
        "psr-4": {
            "App\\": "app/"
        }
    },
  <<省略>>
```

そしたらcomposer update！

```
$ composer update app/Helpers/helpers.php
```

これでrequireしたときと同じように使えるようになります。

# ヘルパークラスを作る
ヘルパーメソッドを実装するのは簡単なんですが、クラスじゃないからテストが書きづらいんですよね。
実際Laravelで実装されているヘルパーを見てみると、メソッド自体は非常にシンプルになっていました。
デフォルトでこの2ファイルがcomposerで読み込まれています。
・vendor/laravel/framework/src/Illuminate/Support/helpers.php
・vendor/laravel/framework/src/Illuminate/Foundation/helpers.php

## ヘルパーメソッドをシンプルに
というわけで、僕もヘルパーメソッド自体はシンプルにしたい。
こんな感じにします。

```app/Helpers/helpers.php
<?php

if ( ! function_exists('assetEx'))
{
  /**
   * assetの処理に加えて、ランダムな値をクエリストリングとして付加する。
   *
   * @param  string  $file
   * @param  bool    $secure
   * @return string
   */
  function assetEx($file, $secure = false)
  {
    return app('AppUrl')->assetEx($file, $secure);
  }
}
```

## ヘルパークラスを実装する
assetが実装されているクラス名をパクってUrlGeneratorというクラスを作ります。

```app/Helpers/UrlGenerator.php
<?php namespace App\Helpers;

class UrlGenerator {

  /**
   * assetの処理に加えて、ランダムな値をクエリストリングとして付加する。
   *
   * @param  string  $path
   * @param  bool  $secure
   * @return string
   */
  public function assetEx($path, $secure = false)
  {
      static $refresher = null;

      if (is_null($refresher))
      {
        $refresher = mt_rand();
      }
      return app('url')->asset($path, $secure) . '?' . $refresher;
  }
}
```

## aliasesの設定
ヘルパーメソッドにて app('AppUrl') って感じでクラスを呼び出すには config/app.php の aliases にこんな感じで追記します。

```config/app.php
  'aliases' => [

    << 省略 >>

    'AppUrl'    => 'App\Helpers\UrlGenerator',
  ],
```

以上、ヘルパーの実装方法でした！

# まとめ
ヘルパーメソッドの実装自体は大したことなかったんですが、ユニットテストを書くためにLaravelのFacadeあたりのソースを追ったりは僕には大変でした。（テストの書き方については後日またまとめたい）
でもおかげでファサードパターンというデザパタを知る機会にもなって面白かったです。


[^1]: 実際にはproductionやdevelopなど、環境によって参照先を変えたりランダムなクエリストリングもリクエスト毎で変わるのではなくElixirちっくにしたりしました。