テンプレート

NetteはLatteテンプレートエンジンを使用しています。これはPHPで最も安全なテンプレートエンジンであり、同時に最も直感的なシステムでもあるためです。多くの新しいことを学ぶ必要はなく、PHPの知識といくつかのタグで十分です。

ページは通常、レイアウトテンプレートと特定のアクションのテンプレートから構成されます。これはレイアウトテンプレートの例です。{block}ブロックと{include}タグに注目してください:

<!DOCTYPE html>
<html>
<head>
	<title>{block title}My App{/block}</title>
</head>
<body>
	<header>...</header>
	{include content}
	<footer>...</footer>
</body>
</html>

そして、これはアクションテンプレートになります:

{block title}Homepage{/block}

{block content}
<h1>Homepage</h1>
...
{/block}

これは、レイアウトの{include content}の場所に挿入されるcontentブロックを定義し、レイアウトの{block title}を上書きするtitleブロックも再定義します。結果を想像してみてください。

テンプレートの検索

Presenterでどのテンプレートをレンダリングするかを指定する必要はありません。フレームワークはパスを自動的に推測し、記述の手間を省きます。

各Presenterが独自のディレクトリを持つディレクトリ構造を使用している場合は、アクション(またはビュー)の名前でこのディレクトリにテンプレートを配置するだけです。つまり、アクションdefaultにはテンプレートdefault.latteを使用します:

app/
└── Presentation/
    └── Home/
        ├── HomePresenter.php
        └── default.latte

Presenterが1つのディレクトリにまとめられ、テンプレートがtemplatesフォルダにある構造を使用している場合は、ファイルを<Presenter>.<view>.latteまたは<Presenter>/<view>.latteに保存します:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── Home.default.latte  ← 1番目のバリアント
        └── Home/
            └── default.latte   ← 2番目のバリアント

templatesディレクトリは、Presenterクラスを含むディレクトリと同じレベル、つまり1つ上のレベルに配置することもできます。

テンプレートが見つからない場合、Presenterはエラー404 – ページが見つかりませんで応答します。

$this->setView('jineView')を使用してビューを変更します。$this->template->setFile('/path/to/template.latte')を使用してテンプレートファイルを直接指定することもできます。

テンプレートが検索されるファイルは、可能なファイル名の配列を返すメソッドformatTemplateFiles()をオーバーライドすることで変更できます。

レイアウトテンプレートの検索

Netteはレイアウトファイルも自動的に検索します。

各Presenterが独自のディレクトリを持つディレクトリ構造を使用している場合は、レイアウトをPresenterのフォルダに配置します(そのPresenterに固有の場合)。または、複数のPresenterで共有されている場合は1つ上のレベルに配置します:

app/
└── Presentation/
    ├── @layout.latte           ← 共通レイアウト
    └── Home/
        ├── @layout.latte       ← Home presenter 専用
        ├── HomePresenter.php
        └── default.latte

Presenterが1つのディレクトリにまとめられ、テンプレートがtemplatesフォルダにある構造を使用している場合、レイアウトは次の場所にあると想定されます:

app/
└── Presenters/
    ├── HomePresenter.php
    └── templates/
        ├── @layout.latte       ← 共通レイアウト
        ├── Home.@layout.latte  ← Home 専用、1番目のバリアント
        └── Home/
            └── @layout.latte   ← Home 専用、2番目のバリアント

Presenterがモジュール内にある場合、モジュールのネストに応じて、さらに上のディレクトリレベルでも検索されます。

レイアウト名は$this->setLayout('layoutAdmin')を使用して変更でき、その場合、ファイル@layoutAdmin.latteにあると想定されます。$this->setLayout('/path/to/template.latte')を使用してレイアウトテンプレートファイルを直接指定することもできます。

$this->setLayout(false)またはテンプレート内の{layout none}タグを使用すると、レイアウト検索が無効になります。

レイアウトテンプレートが検索されるファイルは、可能なファイル名の配列を返すメソッドformatLayoutTemplateFiles()をオーバーライドすることで変更できます。

テンプレート内の変数

テンプレートに変数を渡すには、それらを$this->templateに書き込みます。その後、テンプレート内でローカル変数として利用できます:

$this->template->article = $this->articles->getById($id);

このようにして、任意の変数をテンプレートに簡単に渡すことができます。ただし、堅牢なアプリケーションを開発する場合、制限を設ける方が役立つ場合があります。たとえば、テンプレートが期待する変数のリストとその型を明示的に定義するなどです。これにより、PHPは型をチェックでき、IDEは正しく提案でき、静的解析はエラーを検出できます。

そして、そのようなリストをどのように定義するのでしょうか? 単純にクラスとそのプロパティの形式で定義します。Presenterと同様に名前を付けますが、最後にTemplateを付けます:

/**
 * @property-read ArticleTemplate $template
 */
class ArticlePresenter extends Nette\Application\UI\Presenter
{
}

class ArticleTemplate extends Nette\Bridges\ApplicationLatte\Template
{
	public Model\Article $article;
	public Nette\Security\User $user;

	// その他の変数
}

Presenterの$this->templateオブジェクトは、ArticleTemplateクラスのインスタンスになります。したがって、PHPは書き込み時に宣言された型をチェックします。そして、PHP 8.2以降では、存在しない変数への書き込みについても警告します。以前のバージョンでは、トレイトNette\SmartObjectを使用することで同じことが達成できます。

@property-readアノテーションはIDEと静的解析向けであり、これにより提案が機能します。「PhpStorm and code completion for $this⁠-⁠>⁠template」:https://blog.nette.org/en/phpstorm-and-code-completion-for-this-template を参照してください。

テンプレートでも提案の贅沢を楽しむことができます。PhpStormにLatteプラグインをインストールし、テンプレートの先頭にクラス名を指定するだけです。詳細は「Latte: 型システムの使い方」:https://blog.nette.org/cs/latte-jak-na-typovy-system の記事を参照してください:

{templateType App\Presentation\Article\ArticleTemplate}
...

これはコンポーネントのテンプレートでも機能します。命名規則に従い、たとえばコンポーネントFifteenControlに対してテンプレートクラスFifteenTemplateを作成するだけです。

$templateを別のクラスのインスタンスとして作成する必要がある場合は、createTemplate()メソッドを使用します:

public function renderDefault(): void
{
	$template = $this->createTemplate(SpecialTemplate::class);
	$template->foo = 123;
	// ...
	$this->sendTemplate($template);
}

デフォルト変数

Presenterとコンポーネントは、いくつかの便利な変数を自動的にテンプレートに渡します:

  • $basePath はルートディレクトリへの絶対URLパスです(例:/eshop
  • $baseUrl はルートディレクトリへの絶対URLです(例:http://localhost/eshop
  • $userユーザーを表すオブジェクトです
  • $presenter は現在のPresenterです
  • $control は現在のコンポーネントまたはPresenterです
  • $flashes は関数 flashMessage() によって送信されたメッセージの配列です

独自のテンプレートクラスを使用している場合、これらの変数はプロパティを作成すれば渡されます。

リンクの作成

テンプレートでは、他のPresenterとアクションへのリンクは次のように作成されます:

<a n:href="Product:show">製品詳細</a>

属性 n:href はHTMLタグ <a> に非常に便利です。リンクを他の場所、たとえばテキスト内に出力したい場合は、{link} を使用します:

アドレスは: {link Home:default}

詳細については、URLリンクの作成の章を参照してください。

カスタムフィルタ、タグなど

Latteテンプレートシステムは、カスタムフィルタ、関数、タグなどで拡張できます。これは、render<View>またはbeforeRender()メソッドで直接行うことができます:

public function beforeRender(): void
{
	// フィルタの追加
	$this->template->addFilter('foo', /* ... */);

	// または Latte\Engine オブジェクトを直接設定
	$latte = $this->template->getLatte();
	$latte->addFilterLoader(/* ... */);
}

Latteバージョン3では、より高度な方法として、各Webプロジェクト用にextensionを作成する方法が提供されています。そのようなクラスの簡単な例:

namespace App\Presentation\Accessory;

final class LatteExtension extends Latte\Extension
{
	public function __construct(
		private App\Model\Facade $facade,
		private Nette\Security\User $user,
		// ...
	) {
	}

	public function getFilters(): array
	{
		return [
			'timeAgoInWords' => $this->filterTimeAgoInWords(...),
			'money' => $this->filterMoney(...),
			// ...
		];
	}

	public function getFunctions(): array
	{
		return [
			'canEditArticle' =>
				fn($article) => $this->facade->canEditArticle($article, $this->user->getId()),
			// ...
		];
	}

	// ...
}

設定を使用して登録します:

latte:
	extensions:
		- App\Presentation\Accessory\LatteExtension

翻訳

多言語アプリケーションをプログラミングしている場合、テンプレート内の一部のテキストを異なる言語で出力する必要があるでしょう。Nette Frameworkはこの目的のために、翻訳インターフェースNette\Localization\Translatorを定義しています。これにはtranslate()という1つのメソッドがあります。これはメッセージ$message(通常は文字列)と任意の追加パラメータを受け取ります。タスクは翻訳された文字列を返すことです。 Netteにはデフォルトの実装はありません。Componetteで見つけることができるいくつかの既製のソリューションから、ニーズに合わせて選択できます。それらのドキュメントで、トランスレータの設定方法を学びます。

テンプレートには、渡してもらうトランスレータをsetTranslator()メソッドで設定できます:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator);
}

あるいは、トランスレータは設定を使用して設定することもできます:

latte:
	extensions:
		- Latte\Essential\TranslatorExtension(@Nette\Localization\Translator)

その後、トランスレータは、たとえばフィルタ|translateとして使用でき、translate()メソッドに渡される追加パラメータ(foo, barを参照)も含みます:

<a href="basket">{='カート'|translate}</a>
<span>{$item|translate}</span>
<span>{$item|translate, foo, bar}</span>

またはアンダースコアタグとして:

<a href="basket">{_'カート'}</a>
<span>{_$item}</span>
<span>{_$item, foo, bar}</span>

テンプレートの一部を翻訳するには、ペアタグ{translate}があります(Latte 2.11以降、以前は{_}タグが使用されていました):

<a href="order">{translate}注文{/translate}</a>
<a href="order">{translate foo, bar}注文{/translate}</a>

トランスレータは通常、テンプレートのレンダリング中に実行時に呼び出されます。ただし、Latteバージョン3では、テンプレートのコンパイル中にすべての静的テキストを翻訳できます。これにより、各文字列が一度だけ翻訳され、結果の翻訳がコンパイル済み形式に書き込まれるため、パフォーマンスが節約されます。キャッシュディレクトリには、言語ごとに複数のコンパイル済みバージョンのテンプレートが作成されます。これを行うには、言語を2番目のパラメータとして指定するだけです:

protected function beforeRender(): void
{
	// ...
	$this->template->setTranslator($translator, $lang);
}

静的テキストとは、たとえば {_'hello'}{translate}hello{/translate} のようなものを意味します。{_$foo} のような非静的テキストは、引き続き実行時に翻訳されます。

バージョン: 4.0