概要
レスポンシブ対応できない状況があるが、同じURLでPC/SPのビューを切り替えたいといった場合の対応方法をメモしておきます。
環境
Laravel 5.4
※ 5.3以下では動作確認していないのでエラーが出る場合は適時修正が必要かも?
やり方の検討
- 各ビューを呼び出す部分で調整するような仕組みを作る
- ミドルウェアでビューの検索もとのパスを切り替える
- 各ビュー作成時に対応する
1. 各ビューを呼び出す部分で調整するような仕組みを作る
やり方を変えたい場合に、呼び出し方法自体が変わった時には
全てのコントロールのViewの呼び出し箇所の修正が必要になりそうなので却下。
2. ミドルウェアでビューの検索対象フォルダのパスを切り替える
viewsフォルダが一番上の階層からきっちり別れる。
明確にPS/SPのビューが一番上の階層から別れていて見やすい
フォルダのパスは複数定義でき、優先順位の設定が可能なので、
スマホになければPCと行ったようにスマホのテンプレートが無くてもエラーにならない。
ただし、フォルダが完全に別なのでSPのビューを作り忘れが発生しやすい。
3. 各ビュー作成時に対応する
各ビューを作成時(includeなどのビューも含む)に実行されるので
実行回数が多くなることが気になる
ただし、以下のような細かな調整ができそう。
・フォルダごと階層を分けず、ファイル名で分ける (例: pc -> index.blade.php, sp -> index_sp.blade.php)
・レスポンシブ対応ページの混在(スマホのビューを作らない場合、PCが読み込む)
・スマホのビュー作成を強制させるよう500番エラーにすることも可能
・PCのみのページを作る場合は、スマホは404 not foundにすることも可能
以上のことから③のやり方でやってみる。
構築方法
Laravelのビューには、composerとcreatorという機能があるようです。
実行タイミングが違うようですね。
- creator: viewインスタンスが作成されるとき
- composer: viewインスタンスからviewがコンパイル、結合されて文字列に変換されるとき
ビュー自体を切り替えるのでcreatorの方が良さそうです。
ということで、creatorで作ります。
エージェントの判定
スマホかどうかの判定が必要なので、下記ライブラリを導入する。
$ composer require jenssegers/agent
Laravel 5.4のため、下記の設置ではproviderの方がエラーとなった。
※ 原因 : share()メソッドが5.4ではなくなっているみたい・・・
    'providers' => [
        'Jenssegers\Agent\AgentServiceProvider', // <= 追記
    ],
    'aliases' => [
        'Agent' => 'Jenssegers\Agent\Facades\Agent',  // <= 追記
    ],
そのため、providerについてはこちらで上書きする。
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider,
    Jenssegers\Agent\Agent;
class AgentServiceProvider extends ServiceProvider
{
    /**
     * プロバイダのローディングを遅延させるかどうか
     *
     * @var bool
     */
    protected $defer = false;
    /**
     * 全アプリケーションサービスの初期起動処理
     *
     * @return void
     */
    public function boot()
    {
        //
    }
    /**
     * コンテナへの結合登録
     *
     * @return  void
     */
    public function register()
    {
        $this->app->singleton('agent', function ($app) {
            return new Agent($app['request']->server->all());
        });
    }
}
動作確認
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Agent;
class TopController extends Controller
{
    /**
     * TOPページ
     *
     * URI : GET /
     *
     * @access  public
     * @return  string
     */
    public function index()
    {
        // サービスプロバイダ経由
        $agent = app('agent');
        var_dump($agent->isMobile());
        // ファサード経由
        var_dump(Agent::isMobile());
    }
}
ViewCreator作成
「app/Http/ViewCreators/」フォルダを作って下記クラスを配置する。
<?php
namespace App\Http\ViewCreators;
use Illuminate\View\View,
    Illuminate\Support\Facades\View as V,
    Illuminate\View\ViewName,
    Illuminate\View\FileViewFinder,
    Agent;
/**
 * スマホのビューに自動で切り替える
 *
 * @package App\Http\ViewCreators
 */
class ViewSwitchCreator
{
    /**
     * ビュー差し替え対象外とするフォルダ
     *
     * @var array
     */
    private $excluded_folder = ['layouts', 'shared'];
    /**
     * ビュー名の接尾語
     *
     * @var string
     */
    private $view_name_suffix = '_sp';
    /**
     * 新しいプロフィールコンポーザの生成
     */
    public function __construct()
    {
        //
    }
    /**
     * データをビューと結合
     *
     * @param  View  $view
     * @return void
     */
    public function create(View $view)
    {
        // スマホではない場合はスキップ
        if (Agent::isMobile() !== true) {
            return;
        }
        // スマホ向けのビューの場合はスキップ
        $name = $view->getName();
        if ($name && preg_match('|'.$this->view_name_suffix.'$|', $name) !== 0) {
            return;
        }
        // 除外フォルダの場合はスキップ
        list($dir) = explode('.', $name);
        if (in_array($dir, $this->excluded_folder)) {
            return;
        }
        // ビューを変更する
        $name = $name.$this->view_name_suffix;
        // ビューの存在チェック
        // ※ レスポンシブ対応ページも作れるように500エラーにはしない
        if (v::exists($name)) {
            $finder = v::getFinder();
            $view->setPath(
                $finder->find(
                    ViewName::normalize($name)
                )
            );
        }
    }
}
ViewCreator登録
サービスプロバイダを使ってViewCreatorを全てのビューに適用されるよう登録する。
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View,
    Illuminate\Support\ServiceProvider,
    App\Http\ViewCreators as V;
class ViewCreatorServiceProvider extends ServiceProvider
{
    /**
     * 全アプリケーションサービスの初期起動処理
     *
     * @return void
     */
    public function boot()
    {
        View::creator('*', V\ViewSwitchCreator::class);
    }
    /**
     * コンテナ結合の登録
     *
     * @return void
     */
    public function register()
    {
        //
    }
}
app.phpにプロバイダを登録する。
    'providers' => [
        App\Providers\ViewCreatorServiceProvider::class, // <= 追記
    ],
ビューファイル作成
resources/views/フォルダ配下にファイルをそれぞれVIEWファイルを作成する。
┗ resources
   ┣ assets
   ┣ lang
   ┗ views
      ┣ errors
      ┣ layouts                             // このフォルダは差し替え対象外 (レイアウト)
      ┃  ┣ app.blade.php
      ┃  ┗ app_sp.blade.php
      ┣ shared                               // このフォルダは差し替え対象外 (各共有パーツ)
      ┃  ┣ header.blade.php
      ┃  ┣ header_sp.blade.php
      ┃  ┣ footer.blade.php
      ┃  ┗ footer_sp.blade.php
      ┗ top
         ┣ index.blade.php
         ┗ index_sp.blade.php
動作確認してみる。
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Agent;
class TopController extends Controller
{
    /**
     * TOPページ
     *
     * URI : GET /
     *
     * @access  public
     * @return  string
     */
    public function index()
    {
        return view('top.index');
    }
}
※ スマホ向けのビューを作らなければ、自動的にPC向けのビューが表示される (レスポンシブ対応ページのため)
以上です。