概要
レスポンシブ対応できない状況があるが、同じ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向けのビューが表示される (レスポンシブ対応ページのため)
以上です。