PHP
laravel5.4

LaravelでViewCreatorを使ってPC/SPのビューを切り替える

概要

レスポンシブ対応できない状況があるが、同じURLでPC/SPのビューを切り替えたいといった場合の対応方法をメモしておきます。

環境

Laravel 5.4
※ 5.3以下では動作確認していないのでエラーが出る場合は適時修正が必要かも?

やり方の検討

  1. 各ビューを呼び出す部分で調整するような仕組みを作る
  2. ミドルウェアでビューの検索もとのパスを切り替える
  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で作ります。

エージェントの判定

スマホかどうかの判定が必要なので、下記ライブラリを導入する。

https://github.com/jenssegers/Laravel-Agent

$ composer require jenssegers/agent

Laravel 5.4のため、下記の設置ではproviderの方がエラーとなった。
※ 原因 : share()メソッドが5.4ではなくなっているみたい・・・

config/app.php
    'providers' => [
        'Jenssegers\Agent\AgentServiceProvider', // <= 追記
    ],
    'aliases' => [
        'Agent' => 'Jenssegers\Agent\Facades\Agent',  // <= 追記
    ],

そのため、providerについてはこちらで上書きする。

app/Providers/AgentServiceProvider.php
<?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());
        });
    }
}

動作確認

app/Http/Controllers/TopController.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()
    {
        // サービスプロバイダ経由
        $agent = app('agent');
        var_dump($agent->isMobile());

        // ファサード経由
        var_dump(Agent::isMobile());
    }
}

ViewCreator作成

「app/Http/ViewCreators/」フォルダを作って下記クラスを配置する。

app/Http/ViewCreators/ViewSwitchCreator.php
<?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を全てのビューに適用されるよう登録する。

app/Providers/ViewCreatorServiceProvider.php
<?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にプロバイダを登録する。

config/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

動作確認してみる。

app/Http/Controllers/TopController.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向けのビューが表示される (レスポンシブ対応ページのため)

以上です。

参考サイト