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: 123 で Product: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
に向かいます。影響を受けるのは相対リンクだけで、コロンで始まる絶対リンクと現在のプレゼンターへのリンク(this、show)は変わりません。
{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 を単に home、Admin:Dashboard:default を admin
と呼ぶ、といった具合です。
別名は設定の 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 を投げます。