サードパーティライブラリを使用しないPHPテンプレートの継承

Webアプリケーションを開発するとき、HTMLページのレンダリングに関する問題が必ず発生します。 通常、これらの問題はテンプレートエンジン(PHP自体または何らかのテンプレートパーサー)によって解決されます。 アプリケーションが大きく、ページに多くのブロックが含まれている場合、テンプレートの複雑さは劇的に増大する可能性があり、開発者はテンプレートを使用した作業を簡素化したいと考えています。 さまざまな手法が使用されますが、通常、これはテンプレート内の繰り返しブロックの割り当てと、テンプレート継承を含むそれらの正しい分解です。



Djangoでテンプレートを継承する方法が気に入っています。 アイデアはシンプルです-基本的なテンプレートがあり、コンテンツブロックが選択されています。これはページによって異なります。 ページをレンダリングするときに、ベーステンプレートをベースとして使用し、必要なブロックのみを再定義するように指定できます。 プロジェクトに同じタイプのページが多数ある場合は、メインテンプレートを継承する中間テンプレートを作成し、そのデータを再定義できます。 熟練した設計を使用すると、繰り返されるコードの量を無効にすることができ、設計を変更するときに作業が楽になります。



このアプローチは、私とDjango開発者だけでなく、SymfonyフレームワークとTwigテンプレートエンジンの作成者であるFabien Potencierにも好まれているようです。 Twigには、テンプレートをネイティブPHPクラスにコンパイルする、データフィルタリング、組み込みループなど、多くの興味深い機能があります。 -一般に、これらすべてには最新のテンプレートエンジンが搭載されているはずです。 しかし、最も興味深いのは、前述のテンプレートの継承です。 公式ドキュメントの例を次に示します。



基本テンプレート:



<!DOCTYPE html> <html> <head> {% block head %} <link rel="stylesheet" href="style.css" /> <title>{% block title %}{% endblock %} - My Webpage</title> {% endblock %} </head> <body> <div id="content">{% block content %}{% endblock %}</div> <div id="footer"> {% block footer %} © Copyright 2011 by <a href="http://domain.invalid/">you</a>. {% endblock %} </div> </body> </html>
      
      







子テンプレート:



 {% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ parent() }} <style type="text/css"> .important { color: #336699; } </style> {% endblock %} {% block content %} <h1>Index</h1> <p class="important"> Welcome on my awesome homepage. </p> {% endblock %}
      
      







基本テンプレートでは、メインブロックが定義され、子では、それらの再定義の例が定義されています。 ソースコードを掘り下げると、この継承もネイティブに実装されていることがわかりました(「コンパイル済み」クラスを継承することによって)。 素晴らしいアイデア! すべては問題ありませんが、単純ではありますが、PHPとは異なるテンプレート構文を学習する必要があるため、多少混乱しました。 非ネイティブテンプレートに対する私の嫌悪感はSmarty(継承もある)から始まり、今日まで大きな変化はありませんでした。



サンフランシスコの開発者であるアダム・ショーは、このアイデアに非常に近づきました。 明らかに、彼は私と同じように、テンプレートの構文を試すのが好きではなく、単純な名前のTemplate Inheritanceライブラリを思いつきました。 私はそれに同意します。 彼は何を提供していますか? 公式のドキュメントから例をもう一度見てみましょう。



基本テンプレート:



 <?php require_once 'ti.php' ?> <html> <body> <h1> <?php startblock('title') ?> <?php endblock() ?> </h1> <div id='article'> <?php startblock('article') ?> <?php endblock() ?> </div> </body> </html>
      
      







子テンプレート:



 <?php include 'base.php' ?> <?php startblock('title') ?> This is the title <?php endblock() ?> <?php startblock('article') ?> This is the article <?php endblock() ?>
      
      







構文は自然で、ブロックは明示的に強調表示され、ライブラリをベーステンプレートに接続して忘れました。 それだけです 著者は、これをバッファとスタックで行ったと言っています(ネストされたブロックが可能です)。 コードは非常に興味深いものですが、グローバル変数の存在に満ちています。 他に何が望まれていますか?



ここで、私たちの物語の主要なトピックに行き着きます。 しかし、PHP自体がベーステンプレートのブロックをオーバーライドできますか? 私はかなり思います! 参照:



基本的なテンプレートは次のとおりです。



 <!DOCTYPE HTML> <html lang="ru-RU"> <head> <title><?php echo isset($title) ? $title : ''; ?></title> <meta charset="UTF-8"> </head> <body> <div class="content"> <?php if(isset($content)){echo $content}else{ ?> Default content <?php }?> </div> <div class="sidebar"> <?php if(isset($sidebar)){echo $sidebar}else{ ?> Default sidebar <?php }?> </div> </body> </html>
      
      







ここでは、テンプレートの3ブロックで、コンテンツを格納する対応する変数の存在が確認され、スコープに存在する場合はテンプレートに出力でき、存在しない場合はデフォルトでコンテンツが表示されます。 これらの変数は、子テンプレートでのみ再定義できます。 そして、ここに彼は:



 <?php if(!isset($content)){ ob_start(); ?> <h1> </h1> <?php $content = ob_get_clean();} ?> <?php require 'baseTemplate.php'; ?>
      
      







この例は、$ content変数が以前に設定されていない場合にオーバーライドします。 これは、このテンプレートを継承してコンテンツブロックを再定義できるようにするために行われます。 その考えは明確だと思います。 ライブラリは必要ありません-このスタイルでテンプレートを書くだけで満足です。



もちろん、ここにはいくつかの欠点がありました。 第一に、これはブロックを定義および再定義するための非常に簡潔な構文ではありません。つまり、ネイティブ性に対する報復です。 第二に、子テンプレートで親ブロックコードを取得できません。 第三に、実際のHTMLコードの前にテンプレートを含めるこの方法では、ブロック間のインデントにより一定数のスペースが形成される場合があります。 ここで、テンプレートをバッファに接続し、コンテンツをフィルタリングすることをお勧めします。 これは多くのフレームワークで行われます:



 function render($pathToTemplate, $data) { extract($data); ob_start(); require $pathToTemplate; return trim(ob_get_clean()); }
      
      







この関数は、$データ配列から取得した変数置換を使用して、$ pathToTemplateファイルからテンプレート出力を返します。 抽出-すべての人向けではありません-実行する必要はありませんが、$データを直接参照します。 コンテンツから出力する前に、先頭と末尾のスペースが削除されます。 テンプレートでは、ロジックとプレゼンテーションの分離、およびPHPの原則に違反することなく、良心でできることをすべて実行できます。 たとえば、状況に応じて、1つまたは別のベースファイルを接続します。



以上です。 継承を実装するには、上記の方法のいずれかを使用できます。他の方法があると確信しています。 この記事が誰かがコードを少し良くするのを助けてくれたら嬉しいです。



All Articles