Presenter
NetteでPresenterとテンプレートを作成する方法について学びます。読み終えた後、あなたは知っているでしょう:
- Presenterがどのように機能するか
- パーシステントパラメータとは何か
- テンプレートがどのようにレンダリングされるか
すでに知っているように、PresenterはWebアプリケーションの特定のページ(ホームページ、eコマースの製品、ログインフォーム、サイトマップフィードなど)を表すクラスです。アプリケーションは1つから数千のPresenterを持つことができます。他のフレームワークでは、コントローラーとも呼ばれます。
通常、Presenterという用語は、Webインターフェースの生成に適したNette\Application\UI\Presenterクラスの子孫を意味し、この章の残りの部分で扱います。一般的な意味では、PresenterはNette\Application\IPresenterインターフェースを実装する任意のオブジェクトです。
Presenterのライフサイクル
Presenterのタスクは、リクエストを処理し、応答(HTMLページ、画像、リダイレクトなど)を返すことです。
したがって、最初にリクエストが渡されます。これは直接のHTTPリクエストではなく、ルーターの助けを借りてHTTPリクエストが変換されたNette\Application\Requestオブジェクトです。Presenterはリクエストの処理をこれから示す他のメソッドに賢く委任するため、通常はこのオブジェクトに直接触れることはありません。
この図は、存在する場合に上から下に順に呼び出されるメソッドのリストを表しています。これらのメソッドのいずれも存在する必要はなく、単一のメソッドを持たない完全に空のPresenterを持ち、それに基づいて単純な静的Webサイトを構築できます。
__construct()
コンストラクタは、オブジェクト作成時に呼び出されるため、Presenterのライフサイクルには完全には属しません。しかし、その重要性のために記載しています。コンストラクタ(injectメソッドとともに)は、依存関係を渡すために使用されます。
Presenterは、アプリケーションのビジネスロジックを処理したり、データベースへの書き込みや読み取りを行ったり、計算を実行したりすべきではありません。これらは、モデルと呼ばれるレイヤーのクラスの仕事です。例えば、ArticleRepository
クラスは記事の読み込みと保存を担当するかもしれません。Presenterがそれを使用できるようにするには、依存性注入を使用して渡してもらいます。
startup()
リクエストを受信するとすぐに startup()
メソッドが呼び出されます。プロパティの初期化、ユーザー権限の検証などに使用できます。メソッドは常に親
parent::startup()
を呼び出す必要があります。
action<Action>(args...)
render<View>()
メソッドに似ています。render<View>()
は後でレンダリングされる特定のテンプレートのデータを準備することを目的としていますが、action<Action>()
ではテンプレートのレンダリングとは無関係にリクエストが処理されます。例えば、データが処理され、ユーザーがログインまたはログアウトされ、などが行われ、その後別の場所にリダイレクトされます。
重要なのは、action<Action>()
が render<View>()
より前に呼び出されるため、そこで将来のイベントの流れを変更できることです。つまり、レンダリングされるテンプレートと呼び出される
render<View>()
メソッドを setView('jineView')
を使用して変更できます。
メソッドにはリクエストからのパラメータが渡されます。パラメータに型を指定することが可能であり、推奨されます。例えば
actionShow(int $id, ?string $slug = null)
– パラメータ id
が欠落している場合、または整数でない場合、Presenterは404エラーを返し、動作を終了します。
handle<Signal>(args...)
このメソッドは、コンポーネントに関する章で学ぶ、いわゆるシグナルを処理します。これは主にコンポーネントとAJAXリクエストの処理を目的としています。
メソッドには、action<Action>()
の場合と同様に、型チェックを含むリクエストからのパラメータが渡されます。
beforeRender()
beforeRender
メソッドは、その名前が示すように、各 render<View>()
メソッドの前に呼び出されます。共通のテンプレート設定、レイアウトへの変数の受け渡しなどに使用されます。
render<View>(args...)
後続のレンダリングのためにテンプレートを準備し、データを渡すなどの場所です。
メソッドには、action<Action>()
の場合と同様に、型チェックを含むリクエストからのパラメータが渡されます。
afterRender()
afterRender
メソッドは、名前が再び示すように、各 render<View>()
メソッドの後に呼び出されます。これはむしろ例外的に使用されます。
shutdown()
Presenterのライフサイクルの最後に呼び出されます。
先に進む前に、良いアドバイス。ご覧のとおり、Presenterは複数のアクション/ビューを処理できます。つまり、複数の
render<View>()
メソッドを持つことができます。ただし、1つまたはできるだけ少ないアクションを持つPresenterを設計することをお勧めします。
応答の送信
Presenterの応答は通常、HTMLページを含むテンプレートのレンダリングですが、ファイルの送信、JSON、または別のページへのリダイレクトなども可能です。
ライフサイクルのいつでも、次のいずれかのメソッドを使用して応答を送信し、同時にPresenterを終了できます。
redirect()
,redirectPermanent()
,redirectUrl()
,forward()
はリダイレクトしますerror()
はエラーのためにPresenterを終了しますsendJson($data)
はPresenterを終了し、JSON形式でデータを送信しますsendTemplate()
はPresenterを終了し、すぐにテンプレートをレンダリングしますsendResponse($response)
はPresenterを終了し、カスタム応答を送信しますterminate()
は応答なしでPresenterを終了します
これらのメソッドのいずれも呼び出さない場合、Presenterは自動的にテンプレートのレンダリングに進みます。なぜですか?なぜなら、99%の場合、テンプレートをレンダリングしたいので、Presenterはこの動作をデフォルトと見なし、作業を楽にしたいからです。
リンクの作成
Presenterには link()
メソッドがあり、これを使用して他のPresenterへのURLリンクを作成できます。最初のパラメータはターゲットのPresenterとアクションであり、その後に渡される引数が続きます。引数は配列として指定できます。
テンプレートでは、他のPresenterとアクションへのリンクは次のように作成されます。
単に実際のURLの代わりに、既知の Presenter:action
ペアを記述し、必要に応じてパラメータを指定します。トリックは n:href
にあり、これはこの属性がLatteによって処理され、実際のURLが生成されることを示します。Netteでは、URLについて考える必要はまったくなく、Presenterとアクションについて考えるだけです。
詳細については、URLリンクの作成の章を参照してください。
リダイレクト
別のPresenterに移動するには、redirect()
および forward()
メソッドを使用します。これらはlink()メソッドと非常によく似た構文を持っています。
forward()
メソッドは、HTTPリダイレクトなしで即座に新しいPresenterに移動します。
HTTPコード302(または現在のリクエストメソッドがPOSTの場合は303)を持ついわゆる一時的なリダイレクトの例:
HTTPコード301を持つ永続的なリダイレクトは、次のように実現できます。
アプリケーション外の別のURLにリダイレクトするには、redirectUrl()
メソッドを使用します。2番目のパラメータとしてHTTPコードを指定できます。デフォルトは302(または現在のリクエストメソッドがPOSTの場合は303)です。
リダイレクトは、いわゆるサイレント終了例外 Nette\Application\AbortException
をスローすることで、Presenterの動作を即座に終了します。
リダイレクトの前に、フラッシュメッセージ、つまりリダイレクト後にテンプレートに表示されるメッセージを送信できます。
フラッシュメッセージ
これらは通常、何らかの操作の結果を通知するメッセージです。フラッシュメッセージの重要な特徴は、リダイレクト後もテンプレートで利用できることです。表示後もさらに30秒間有効です。例えば、転送エラーのためにユーザーがページを更新した場合でも、メッセージはすぐには消えません。
flashMessage()
メソッドを呼び出すだけで、Presenterがテンプレートへの受け渡しを処理します。最初のパラメータはメッセージのテキストであり、オプションの2番目のパラメータはそのタイプ(error、warning、infoなど)です。flashMessage()
メソッドは、フラッシュメッセージのインスタンスを返し、これに追加情報を追加できます。
これらのメッセージは、テンプレートでは $flashes
変数で stdClass
オブジェクトとして利用できます。これらには
message
(メッセージテキスト)、type
(メッセージタイプ)プロパティが含まれ、前述のユーザー情報を含むこともできます。例えば、次のようにレンダリングします。
404エラーなど
リクエストを満たせない場合、例えば表示したい記事がデータベースに存在しないなどの理由で、error(?string $message = null, int $httpCode = 404)
メソッドを使用して404エラーをスローします。
エラーのHTTPコードは2番目のパラメータとして渡すことができます。デフォルトは404です。メソッドは
Nette\Application\BadRequestException
例外をスローするように機能し、その後 Application
は制御をエラーPresenterに渡します。これは、発生したエラーを通知するページを表示するタスクを持つPresenterです。
エラーPresenterの設定は、application設定で行われます。
JSONの送信
JSON形式でデータを送信し、Presenterを終了するアクションメソッドの例:
リクエストパラメータ
Presenterおよび各コンポーネントは、HTTPリクエストからパラメータを取得します。その値は
getParameter($name)
または getParameters()
メソッドで取得できます。値は文字列または文字列の配列であり、基本的にはURLから直接取得された生のデータです。
より便利にするために、プロパティを介してパラメータにアクセスすることをお勧めします。#[Parameter]
属性でマークするだけです。
プロパティにはデータ型(例:string
)を指定することをお勧めします。Netteはそれに基づいて値を自動的にキャストします。パラメータ値は検証することもできます。
リンクを作成するときに、パラメータの値を直接設定できます。
パーシステントパラメータ
パーシステントパラメータは、異なるリクエスト間で状態を維持するために使用されます。その値は、リンクをクリックした後も同じままです。セッションデータとは異なり、URLで転送されます。そして、これは完全に自動的に行われるため、link()
や n:href
で明示的に指定する必要はありません。
使用例は?多言語アプリケーションがあるとします。現在の言語は、常にURLの一部である必要があるパラメータです。しかし、すべてのリンクでそれを指定するのは非常に面倒です。そこで、それをパーシステントパラメータ
lang
にし、自動的に転送されるようにします。素晴らしい!
Netteでパーシステントパラメータを作成するのは非常に簡単です。パブリックプロパティを作成し、属性でマークするだけです。(以前は
/** @persistent */
が使用されていました)
$this->lang
が例えば 'en'
の値を持つ場合、link()
または
n:href
を使用して作成されたリンクも lang=en
パラメータを含みます。そして、リンクをクリックした後も、再び
$this->lang = 'en'
になります。
プロパティにはデータ型(例:string
)を指定することをお勧めします。また、デフォルト値を指定することもできます。パラメータ値は検証できます。
パーシステントパラメータは、通常、特定のPresenterのすべてのアクション間で転送されます。複数のPresenter間で転送するには、次のいずれかで定義する必要があります。
- Presenterが継承する共通の祖先で
- Presenterが使用するトレイトで:
リンクを作成するときに、パーシステントパラメータの値を変更できます。
または、リセットすることもできます。つまり、URLから削除します。その後、デフォルト値を取ります。
インタラクティブコンポーネント
Presenterには組み込みのコンポーネントシステムがあります。コンポーネントは、Presenterに挿入する独立した再利用可能なユニットです。フォーム、データグリッド、メニューなど、繰り返し使用する意味のあるものであれば何でもかまいません。
コンポーネントがどのようにPresenterに挿入され、その後使用されるのでしょうか?それはコンポーネントの章で学びます。ハリウッドと共通点があることさえ発見するでしょう。
そして、どこでコンポーネントを入手できますか?Componetteページでは、オープンソースコンポーネントや、フレームワーク周辺のコミュニティのボランティアによってここに配置されたNette用の他の多くのアドオンを見つけることができます。
深く掘り下げる
この章でこれまで見てきたことで、おそらく完全に十分でしょう。以下の行は、Presenterについて深く掘り下げ、すべてを知りたい人のためのものです。
パラメータの検証
URLから受け取ったリクエストパラメータとパーシステントパラメータの値は、loadState()
メソッドによってプロパティに書き込まれます。また、プロパティで指定されたデータ型と一致するかどうかもチェックし、一致しない場合は404エラーで応答し、ページは表示されません。
パラメータは、ユーザーがURLで簡単に上書きできるため、決して盲目的に信用しないでください。例えば、このようにして言語
$this->lang
がサポートされている言語の中にあるかどうかを検証します。適切な方法は、前述の
loadState()
メソッドをオーバーライドすることです。
リクエストの保存と復元
Presenterが処理するリクエストはNette\Application\Requestオブジェクトであり、Presenterの
getRequest()
メソッドによって返されます。
現在のリクエストはセッションに保存したり、逆にそこから復元してPresenterに再度実行させたりすることができます。これは、例えばユーザーがフォームに入力していてログインが期限切れになった場合に便利です。データを失わないように、ログインページにリダイレクトする前に現在のリクエストを
$reqId = $this->storeRequest()
を使用してセッションに保存します。これは短い文字列の形式でその識別子を返し、それをログインPresenterにパラメータとして渡します。
ログイン後、$this->restoreRequest($reqId)
メソッドを呼び出します。これはセッションからリクエストを取得し、それにフォワードします。メソッドは、リクエストが現在ログインしているユーザーと同じユーザーによって作成されたことを検証します。別のユーザーがログインした場合、またはキーが無効な場合、何もしませんでプログラムは続行します。
以前のページに戻る方法のガイドをご覧ください。
カノニカル化
Presenterには、より良いSEO(インターネットでの検索エンジンの最適化)に貢献する本当に素晴らしい機能が1つあります。異なるURLで重複コンテンツが存在するのを自動的に防ぎます。特定のターゲットに複数のURLアドレス(例:/index
と
/index?page=1
)がある場合、フレームワークはそのうちの1つをプライマリ(カノニカル)として決定し、HTTPコード301を使用して他のアドレスをそれにリダイレクトします。これにより、検索エンジンはページを2回インデックス付けせず、ページランクを希釈しません。
このプロセスはカノニカル化と呼ばれます。カノニカルURLは、ルーターによって生成されるURLであり、通常はコレクション内の最初の一致するルートです。
カノニカル化はデフォルトで有効になっており、$this->autoCanonicalize = false
を介して無効にできます。
AJAXまたはPOSTリクエストの場合、データが失われたり、SEOの観点から付加価値がなかったりするため、リダイレクトは発生しません。
カノニカル化は、canonicalize()
メソッドを使用して手動で呼び出すこともできます。このメソッドには、link()
メソッドと同様に、Presenter、アクション、およびパラメータが渡されます。リンクを作成し、現在のURLアドレスと比較します。異なる場合は、生成されたリンクにリダイレクトします。
イベント
Presenterのライフサイクルの一部として呼び出される
startup()
、beforeRender()
、shutdown()
メソッドに加えて、自動的に呼び出されるように他の関数を定義することもできます。Presenterはいわゆるイベントを定義し、そのハンドラを
$onStartup
、$onRender
、$onShutdown
配列に追加します。
$onStartup
配列のハンドラは startup()
メソッドの直前に呼び出され、次に
$onRender
は beforeRender()
と render<View>()
の間に呼び出され、最後に
$onShutdown
は shutdown()
の直前に呼び出されます。
応答
Presenterが返す応答は、Nette\Application\Responseインターフェースを実装するオブジェクトです。多くの準備された応答が利用可能です。
- Nette\Application\Responses\CallbackResponse – コールバックを送信します
- Nette\Application\Responses\FileResponse – ファイルを送信します
- Nette\Application\Responses\ForwardResponse – forward()
- Nette\Application\Responses\JsonResponse – JSONを送信します
- Nette\Application\Responses\RedirectResponse – リダイレクト
- Nette\Application\Responses\TextResponse – テキストを送信します
- Nette\Application\Responses\VoidResponse – 空の応答
応答は sendResponse()
メソッドを使用して送信されます。
#[Requires]
を使用したアクセス制限
#[Requires]
属性は、Presenterとそのメソッドへのアクセスを制限するための高度なオプションを提供します。HTTPメソッドの指定、AJAXリクエストの要求、同一オリジン(same
origin)への制限、およびフォワーディング経由のアクセスのみに使用できます。属性は、Presenterクラスと個々の
action<Action>()
、render<View>()
、handle<Signal>()
、createComponent<Name>()
メソッドの両方に適用できます。
これらの制限を指定できます。
- HTTPメソッドについて:
#[Requires(methods: ['GET', 'POST'])]
- AJAXリクエストの要求:
#[Requires(ajax: true)]
- 同一オリジンからのアクセスのみ:
#[Requires(sameOrigin: true)]
- フォワード経由のアクセスのみ:
#[Requires(forward: true)]
- 特定のアクションへの制限:
#[Requires(actions: 'default')]
詳細については、Requires属性の使用方法のガイドをご覧ください。
HTTPメソッドのチェック
NetteのPresenterは、各受信リクエストのHTTPメソッドを自動的に検証します。このチェックの理由は主にセキュリティです。標準では、GET
、POST
、HEAD
、PUT
、DELETE
、PATCH
メソッドが許可されています。
例えば OPTIONS
メソッドを追加で許可したい場合は、#[Requires]
属性を使用します(Nette Application v3.2以降)。
バージョン3.1では、検証は checkHttpMethod()
で行われます。これは、リクエストで指定されたメソッドが $presenter->allowedMethods
配列に含まれているかどうかを判断します。メソッドを追加するには、次のようにします。
OPTIONS
メソッドを許可する場合、その後Presenter内で適切に処理する必要があることを強調することが重要です。このメソッドは、いわゆるプリフライトリクエストとしてよく使用されます。これは、CORS(Cross-Origin
Resource
Sharing)ポリシーの観点からリクエストが許可されているかどうかを判断する必要がある場合に、ブラウザが実際のリクエストの前に自動的に送信します。メソッドを許可しても正しい応答を実装しない場合、不整合や潜在的なセキュリティ問題につながる可能性があります。