カスタム関数の作成

Latte のテンプレートに独自のヘルパー関数を簡単に追加できます。式の中から直接 PHP のロジックを呼び出して計算したり、サービスにアクセスしたり、動的なコンテンツを生成したりしながら、テンプレートを簡潔で強力なまま保てます。

関数とは何か

Latte の関数を使うと、テンプレートの式({...})の中で呼べる関数のセットを拡張できます。Latte のテンプレートの中でだけ使える独自の PHP 関数だと考えてください。これにはいくつかの利点があります。

手軽さ: ヘルパーのロジック(計算、書式づけ、アプリケーションのデータへのアクセスなど)を定義し、PHP で strlen()date() を呼ぶのと同じように、シンプルで見慣れた関数の構文でテンプレートから直接呼び出せます。

{var $userInitials = initials($userName)} {* 例: 'J. D.' *}

{if hasPermission('article', 'edit')}
    <a href="...">編集</a>
{/if}

グローバルスコープを汚さない: PHP で本物のグローバル関数を定義する場合と違い、Latte の関数はテンプレートのレンダリングという文脈の中にしか存在しません。テンプレート専用のヘルパーでグローバルな PHP の名前空間を散らかす必要がありません。

アプリケーションのロジックとの統合: Latte の関数の背後にある PHP の callable は何でもかまいません。クロージャでも、静的メソッドでも、インスタンスメソッドでもよいのです。つまりテンプレートの関数は、(クロージャなら)変数を取り込むことで、(オブジェクトなら)依存性注入によって、アプリケーションのサービス、データベース、設定など必要なロジックに簡単にアクセスできます。上の hasPermission の例はまさにそれを示していて、裏では認可サービスを呼んでいるのでしょう。

ネイティブ関数の上書き(任意): ネイティブの PHP 関数と同じ名前で Latte の関数を定義することもできます。テンプレートの中では、あなたの独自の版が代わりに呼ばれます。テンプレート専用の振る舞いを与えたり、一貫した処理を保証したり(たとえば strlen を常にマルチバイト安全にする)するのに便利です。混乱を招かないよう、この機能は慎重に使ってください。

既定では、Latte はすべてのネイティブ PHP 関数の呼び出しを許可します(サンドボックスで制限されていない限り)。カスタム関数は、この組み込みのライブラリをプロジェクト固有のニーズで補うものです。

値をひとつ変換するだけなら、カスタムフィルタのほうが自然かもしれません。

関数の作成と登録

フィルタと同じく、カスタム関数を定義して登録する方法はいくつかあります。

addFunction() による直接登録

最も簡単なのは、Latte\Engine オブジェクトの addFunction() を使う方法です。関数名(テンプレートに現れる名前)と、それに対応する PHP の callable を渡します。

$latte = new Latte\Engine;

// シンプルなヘルパー関数
$latte->addFunction('initials', function (string $name): string {
	preg_match_all('#\b\w#u', $name, $m);
	return implode('. ', $m[0]) . '.';
});

テンプレートでの使い方:

{var $userInitials = initials($userName)}

テンプレートでの関数の引数は、同じ順序でそのまま PHP の callable に渡されます。型宣言、既定値、可変長引数(...)といった PHP の機能は期待どおりに動きます。

拡張による登録

整理と再利用のしやすさのために、関数は Latte Extensionの中で登録しましょう。単純でないアプリケーションや共有ライブラリでは、これが推奨される方法です。

namespace App\Templating;

use Latte\Extension;
use Nette\Security\Authorizator;

class MyLatteExtension extends Extension
{
	public function __construct(
		// Authorizator サービスが存在すると仮定します
		private Authorizator $authorizator,
	) {
	}

	public function getFunctions(): array
	{
		// メソッドを Latte の関数として登録します
		return [
			'hasPermission' => $this->hasPermission(...),
		];
	}

	public function hasPermission(string $resource, string $action): bool
	{
		return $this->authorizator->isAllowed($resource, $action);
	}
}

// 登録($container が DIC を保持していると仮定)
$extension = $container->getByType(MyLatteExtension::class);
$latte = new Latte\Engine;
$latte->addExtension($extension);

この方法は、Latte で定義した関数がオブジェクトのメソッドに支えられ、そのメソッドがさらにアプリケーションの依存性注入コンテナやファクトリで管理された独自の依存関係を持てることを、はっきりと示しています。整理された状態を保ちながら、テンプレートのロジックをアプリケーションの中核につなげられます。

属性を使ったクラスによる関数

フィルタと同じく、関数も #[Latte\Attributes\TemplateFunction] 属性を使ってテンプレートパラメータのクラスのメソッドとして定義できます。

use Latte\Attributes\TemplateFunction;

class TemplateParameters
{
	public function __construct(
		public string $userName,
		// ほかのパラメータ...
	) {}

	// このメソッドはテンプレートで {initials(...)} として使えるようになります
	#[TemplateFunction]
	public function initials(string $name): string
	{
		preg_match_all('#\b\w#u', $name, $m);
		return implode('. ', $m[0]) . '.';
	}
}

// オブジェクトをテンプレートに渡します
$params = new TemplateParameters(userName: 'John Doe', /* ... */);
$latte->render('template.latte', $params);

パラメータのオブジェクトがテンプレートに渡されると、Latte はこの属性の付いたメソッドを自動的に見つけて登録します。テンプレートでの関数名はメソッド名と同じです。

{* パラメータクラスで定義された関数を使う *}
{var $inits = initials($userName)}

コンテキストに応じた関数は?

フィルタと違い、関数は FilterInfo オブジェクトを受け取りません。ただし関数はテンプレートの文脈にアクセスできます。最初のパラメータの型宣言が Latte\Runtime\Template であれば、Latte は現在のテンプレートオブジェクトを自動的に渡します。組み込みの hasBlock()hasTemplate() 関数は、まさにこの方法で実装されています。

バージョン: 3.x