URL リンクの作成

Nette でのリンクの作成は、指を差すのと同じくらい簡単です。狙いを定めるだけで、あとはフレームワークが全部やってくれます。ここでは次のことを扱います。

  • テンプレートやそのほかの場所でリンクを作る方法
  • 現在のページへのリンクを見分ける方法
  • 不正なリンクへの対処

双方向のルーティングのおかげで、あとで変わるかもしれない、あるいは組み立てが面倒なアプリケーションの URL を、テンプレートやコードに直接書き込む必要はもうありません。リンクにはプレゼンターとアクションを指定し、必要なパラメータを渡すだけで、URL はフレームワークが生成します。実のところ、関数を呼ぶのとよく似ています。きっと気に入るでしょう。

プレゼンターのテンプレートで

リンクを作るのはたいていテンプレートの中で、そこでは n:href 属性が頼もしい助けになります。

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

HTML の属性 href の代わりに n:属性n:href を使っていることに注目してください。その値は、href 属性の場合のような URL ではなく、プレゼンターとアクションの名前です。

リンクをクリックすることは、簡単にいえば ProductPresenter::renderShow() メソッドを呼ぶようなものです。そしてそのシグネチャにパラメータがあれば、引数を付けて呼べます。

<a n:href="Product:show $product->id, $product->slug">商品の詳細</a>

名前付きパラメータも渡せます。次のリンクは、値 en を持つパラメータ lang を渡します。

<a n:href="Product:show $product->id, lang: en">商品の詳細</a>

ProductPresenter::renderShow() メソッドのシグネチャに $lang がなければ、$lang = $this->getParameter('lang')プロパティでその値を取得できます。

パラメータが配列に入っているなら、... 演算子で展開できます。

{var $args = [$product->id, lang => en]}
<a n:href="Product:show, ...$args">商品の詳細</a>

いわゆる永続パラメータもリンクに自動的に渡されます。

n:href 属性は HTML の <a> タグにとても便利です。リンクをそれ以外の場所、たとえば文章の中に出したい場合は {link} を使います。

URL は次のとおりです: {link Home:default}

コードの中で

プレゼンターでリンクを作るには link() メソッドを使います。

$url = $this->link('Product:show', $product->id);

パラメータは配列としても渡せ、その中で名前付きパラメータも指定できます。

$url = $this->link('Product:show', [$product->id, 'lang' => 'en']);

リンクは LinkGeneratorとその link() メソッドを使えば、プレゼンターなしでも作れます。

ときには、いまリンクを作りつつ、実際の URL の生成はあとにしたいことがあります。そのために lazyLink() メソッドがあり、Nette\Application\UI\Link オブジェクトを返します。利点は、このオブジェクトをたとえばテンプレートへ渡し、描かれる前に setParameter() メソッドでそのパラメータをまだ調整できることです。URL そのものは、オブジェクトが文字列に変換されるときにはじめて組み立てられます。

$link = $this->lazyLink('Product:show', $id);
// ...
echo $link; // URL はここではじめて生成されます

プレゼンターへのリンク

リンクの行き先がプレゼンターとアクションなら、次の構文になります。

[//] [[[[:]module:]presenter:]action | this] [#fragment]

この形式は、すべての Latte のタグと、リンクを扱うすべてのプレゼンターのメソッド、つまり n:href{link}{plink}link()lazyLink()isLinkCurrent()redirect()redirectPermanent()forward()canonicalize()、そして LinkGeneratorで使えます。ですから例で n:href を使っていても、そこにはこれらのどの関数が来てもかまいません。

基本の形は Presenter:action です。

<a n:href="Home:default">トップページ</a>

現在のプレゼンターのアクションへリンクするなら、その名前は省けます。

<a n:href="default">トップページ</a>

行き先のアクションが default なら省けますが、コロンは残さなければなりません。

<a n:href="Home:">トップページ</a>

リンクはほかのモジュールも指せます。ここでは、入れ子のサブモジュールからの相対と、絶対とが区別されます。原理はディスクのパスと同じで、スラッシュの代わりにコロンを使うだけです。現在のプレゼンターが Front モジュールの一部だとすると、次のように書きます。

<a n:href="Shop:Product:show">Front:Shop:Product:show へのリンク</a>
<a n:href=":Admin:Product:show">Admin:Product:show へのリンク</a>

特別な場合が自分自身へのリンクで、行き先に this を指定します。

<a n:href="this">更新</a>

ハッシュ記号 # のあとのいわゆるフラグメントで、ページの特定の部分を指せます。

<a n:href="Home:#main">Home:default とフラグメント #main へのリンク</a>

フラグメントは # のキーを持つ引数として動的に設定することもできます。その値は自動的にエンコードされ、行き先に指定されたフラグメントより優先されます。

$this->link('Home:default', ['#' => $fragment]);

絶対パス

link()n:href で生成されるリンクは常に絶対パス(つまり / で始まるもの)ですが、https://domain のようにプロトコルとドメインを含む絶対 URL ではありません。

絶対 URL を生成するには、先頭にスラッシュを 2 つ足します(たとえば n:href="//Home:")。あるいは $this->absoluteUrls = true を設定して、プレゼンターが絶対リンクだけを生成するように切り替えられます。

テンプレートでは |absoluteUrl フィルタを使って、相対パスを絶対パスに変換することもできます。

現在のページへのリンク

行き先 this は現在のページへのリンクを作ります。

<a n:href="this">更新</a>

同時に、action<Action>() または(action<Action>() が定義されていなければ)render<View>() メソッドのシグネチャに書かれたすべてのパラメータが引き継がれます。ですから id: 123Product:show のページにいるなら、this へのリンクもこのパラメータを渡します。

もちろん、パラメータを直接指定することもできます。

<a n:href="this refresh: 1">更新</a>

isLinkCurrent() 関数は、リンクの行き先が現在のページと同じかどうかを調べます。たとえばテンプレートでリンクを見分けるのに使えます。

パラメータは link() メソッドと同じですが、具体的なアクションの代わりにワイルドカード * も使えます。これは指定したプレゼンターの任意のアクションを意味します。

{if !isLinkCurrent('Admin:login')}
	<a n:href="Admin:login">ログイン</a>
{/if}

<li n:class="isLinkCurrent('Product:*') ? active">
	<a n:href="Product:">...</a>
</li>

ひとつの要素の中で n:href と組み合わせる場合は、短い形が使えます。

<a n:class="isLinkCurrent() ? active" n:href="Home:">...</a>

ワイルドカード * はアクションの代わりにだけ使え、プレゼンターの代わりには使えません。

特定のモジュールやそのサブモジュールにいるかどうかを判定するには、isModuleCurrent(moduleName) メソッドを使います。

<li n:class="isModuleCurrent('Forum:Users') ? active">
	<a n:href="Product:">...</a>
</li>

リンクの基準の変更

既定では、相対リンクは現在のプレゼンターから導かれます。これは {linkBase} で変えられます。

{linkBase Admin:Dashboard}
<a n:href="Product:show">商品の詳細</a>

このリンクは Admin:Dashboard:Product:show に向かいます。影響を受けるのは相対リンクだけで、コロンで始まる絶対リンクと現在のプレゼンターへのリンク(thisshow)は変わりません。

{linkBase} はテンプレート全体に適用され、とくにレイアウトのテンプレートで役立ちます。呼び出し元のプレゼンターに関係なく、一貫したリンクを保証してくれるからです。 このタグはテンプレートの先頭に置かなければならず、さもないと CompileException を投げます。

シグナルへのリンク

リンクの行き先はプレゼンターとアクションだけでなく、シグナルでもかまいません(handle<Signal>() メソッドを呼びます)。その場合の構文は次のとおりです。

[//] [sub-component:]signal! [#fragment]

シグナルは感嘆符で見分けられます。

<a n:href="click!">シグナル</a>

サブコンポーネント(やそのサブコンポーネント)のシグナルへのリンクも作れます。

<a n:href="componentName:click!">シグナル</a>

コンポーネントの中のリンク

コンポーネントは、周りのプレゼンターと何のつながりも持つべきでない独立した再利用可能な部品なので、ここではリンクの働きが少し違います。Latte の属性 n:href とタグ {link}、そしてコンポーネントの link() などのメソッドは、リンクの行き先を常にシグナル名とみなします。ですから感嘆符を付ける必要さえありません。

<a n:href="click">アクションではなくシグナル</a>

コンポーネントのテンプレートからプレゼンターへリンクしたい場合は、{plink} タグを使います。

<a href={plink Home:default}>トップ</a>

あるいはコードの中で

$this->getPresenter()->link('Home:default')

別名

Presenter:action の組に、覚えやすい別名を割り当てると便利なことがあります。たとえばトップページの Front:Home:default を単に homeAdmin:Dashboard:defaultadmin と呼ぶ、といった具合です。

別名は設定application › aliases キーで定義します。

application:
    aliases:
        home: Front:Home:default
        admin: Admin:Dashboard:default
        sign: Front:Sign:in

リンクの中ではアットマークを付けて書きます。たとえば次のようにです。

<a n:href="@admin">管理画面</a>

redirect() など、リンクを扱うすべてのメソッドでも使えます。

不正なリンク

不正なリンクを作ってしまうことがあります。存在しないプレゼンターを指している、行き先のメソッドのシグネチャが受け取るより多くのパラメータを渡している、あるいは行き先のアクションに対して URL を生成できない場合です。不正なリンクをどう扱うかは、プレゼンターの $this->invalidLinkMode で設定します。次の値(定数)の組み合わせを取れます。

  • Presenter::InvalidLinkSilent – 静かなモード。URL として文字 # を返します
  • Presenter::InvalidLinkWarning – E_USER_WARNING の警告が出ます。本番モードでは記録されますが、スクリプトの実行は止まりません
  • Presenter::InvalidLinkTextual – 目に見える警告。エラーをリンクに直接書き出します
  • Presenter::InvalidLinkException – InvalidLinkException を投げます

既定の設定は、本番モードでは InvalidLinkWarning、開発モードでは InvalidLinkWarning | InvalidLinkTextual です。本番環境の InvalidLinkWarning はスクリプトを止めませんが、警告は記録されます。開発環境では Tracyがそれを捕まえてブルースクリーンを表示します。InvalidLinkTextual は、#error: の文字で始まるエラーメッセージを URL として返すことで働きます。そうしたリンクを一目で見つけられるよう、CSS に次を足してください。

a[href^="#error:"] {
	background: red;
	color: white;
}

開発環境で警告を出したくない場合は、設定で直接抑制できます。

application:
	silentLinks: true

LinkGenerator

link() メソッドと同じ快適さで、しかしプレゼンターなしでリンクを作るにはどうすればよいでしょうか。そのためにあるのが Nette\Application\LinkGeneratorです。

LinkGenerator はサービスで、コンストラクタで受け取り、その link() メソッドでリンクを作れます。

プレゼンターとの違いがひとつあります。LinkGenerator はすべてのリンクを絶対 URL として直接作ります。さらに「現在のプレゼンター」がないので、行き先にアクション名だけを指定する link('default') はできませんし、モジュールへの相対パスも使えません。

不正なリンクは常に Nette\Application\UI\InvalidLinkException を投げます。

バージョン: 4.x