そのせいでコントローラが複雑になってしまったり、コンフィグ化して管理場所が深い位置になってしまったり、スッキリと解決するのがなかなか難しい問題だと思います。共通化に失敗すれば、苦痛にしかなりません。
最近、LaravelのBladeテンプレートを用いて、その辺りがかなり解決できた(気がしている)ので、その例のメモです。Bladeテンプレートでなくても、継承式のテンプレートエンジンであれば、似たような事はできるんだろうと思います。
以下、Laravel 4.2を用いていますが、バージョンはたぶんあまり関係ありません。
1) 全体のレイアウトを管理するビューファイルを作成する
views/layouts/default.blade.php
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> @yield('append_css') @yield('append_js') </head> <body> <section id="content"> <h2>content</h2> @yield('content') </section> <section id="content2"> <h2>content2</h2> @yield('content2') </section> <section id="content3"> <h2>content3</h2> @yield('content3') </section> </body> </html>このレイアウトファイルは、各画面用のビューファイルで @extends('layouts.default') されます。ポイントは @yield() している箇所です。それぞれの使い方は以下です。
@yield('append_css') ... 画面単位で使用するCSSを追加する領域
@yield('append_js') ... 画面単位で使用するJSを追加する領域
@yield('content') ... コンテンツ領域
@yield('content2') ... コンテンツ領域2
@yield('content3') ... コンテンツ領域3
この例では、3つのコンテンツ領域を縦に並べているだけですが、実際にはヘッダ領域になったり、サイドナビ領域になったり、メイン領域になったりするイメージです。このビューファイルは、レイアウトの大枠しか知りません。
2) 画面用のビューファイルを作成する
views/contents/index/index.blade.php
@extends('layouts.default') @section('content') @include('contents/top/_foo') @stop @section('content2') @include('contents/top/_bar') @stop @section('content3') @include('contents/top/_baz') @stopそれぞれのコンテンツ領域に、後述する細分化されたビューファイルを @include() して展開しています。このビューファイルは、使用するレイアウトと、各コンテンツ領域でどのコンテンツを使用するかしか知りません。
3) 各コンテンツのビューファイルを作成する
views/contents/index/_foo.blade.php
<h3>foo</h3> <?php append_css('assets/css/contents/foo.css'); append_js('assets/js/contents/foo.js'); ?>views/contents/index/_bar.blade.php
<h3>bar</h3> <?php append_css('assets/css/contents/bar.css'); append_js('assets/js/contents/bar.js'); ?>views/contents/index/_baz.blade.php
<h3>baz</h3> <?php append_css('assets/css/contents/baz.css'); append_js('assets/js/contents/baz.js'); ?>これらのビューファイルは、自身のコンテンツ内容しか知りません。それぞれ内容は適当ですが、ポイントは append_css() と append_js() です。これは自作の関数で、以下になります。
<?php if (! function_exists('append_css')) { /** * @param mixed $styles */ function append_css($styles) { static $appended = []; if (is_scalar($styles)) { $styles = [$styles]; } foreach ($styles as $style) { if (in_array($style, $appended)) { continue; } $appended[] = $style; View::startSection('append_css'); echo HTML::style($style); View::appendSection(); } } } if (! function_exists('append_js')) { /** * @param mixed $scripts */ function append_js($scripts) { static $appended = []; if (is_scalar($scripts)) { $scripts = [$scripts]; } foreach ($scripts as $script) { if (in_array($script, $appended)) { continue; } $appended[] = $script; View::startSection('append_js'); echo HTML::script($script); View::appendSection(); } } }@section('append_js')
{{ HTML::script('assets/js/contents/bar.js'); }}
@append
の方法で append すると、他ビューで同名のファイルが append された時、重複して読み込まれてしまいます。その防止策として、上記のように関数化しています。
これで、細分化されたコンテンツ単位で、依存するJSやCSSファイルが明確になりました。そのコンテンツが不要になれば、セットで削除するだけです。尚、他のビュー用のJSと連携する場合は、独自イベントを介するのが好ましいと思います。(Backbone.js的な考え方かも。)チーム作業時にコンフリクトしにくくなるメリットもあります。
デメリットは、コンテンツファイルが増えるとJSやCSSもファイル単位で増えるので、その結果、読み込みファイル数が多くなることです。JSは要らない場合もありますが、CSSはどんどん増えるはずなので、似たような考え方でSass等で管理した方が良いかもしれません。(実際には別の事情でそうしています。)
以下、参考までに、Sassを用いた簡単な管理例です。
A) 各コンテンツ用のSCSSファイル (上記の3と対) を作成する
B) 画面単位のSCSSファイル (上記の2と対) を作成して A を @import する
C) 画面単位のビューファイル (上記の2) で B のCSSファイルを append_css() する
D) 当然、各コンテンツのビューファイルでは append_css() しない
全体的に大切なことは、とりあえず適切な単位でファイル分割しておいて、各所で好きに組み合わせて使えること。でしょうか。
No comments:
Post a Comment