====== Apricot フロントエンド ====== --- //[[http://www.y2sunlight.com|y2sunlight]] 2020-07-29// [[apricot:usage:ja|Apricot ドキュメント に戻る]] 目次 * [[apricot:usage:ja:features|Apricot 特徴と概要]] * [[apricot:usage:ja:config|Apricot 配置と構成]] * [[apricot:usage:ja:errors-logging|Apricot ログとエラー処理]] * [[apricot:usage:ja:http|Apricot リクエストとレスポンス]] * Apricot フロントエンド * [[apricot:usage:ja:database|Apricot データベース]] * [[apricot:usage:ja:model|Apricot モデルとサービス]] * [[apricot:usage:ja:middleware|Apricot ミドルウェア]] * [[apricot:usage:ja:controller|Apricot コントローラ]] * [[apricot:usage:ja:validation|Apricot バリデーション]] * [[apricot:usage:ja:provider|Apricot サービスプロバイダー]] * [[apricot:usage:ja:authentication|Apricot ユーザ認証]] * [[apricot:usage:ja:utility|Apricot ユーティリティ]] ---- ===== HTMLテンプレート ===== ==== テンプレートエンジン ==== Apricotでは、テンプレートエンジンにLaravelと同じBladeを使用しています。実際に使用しているライブラリは [[https://github.com/EFTEC/BladeOne|BladeOne]] です。BladeOneは、Blade のスタンドアロンバージョンです。 テンプレートの詳しい使用方法については、以下のドキュメントを参照して下さい。 * https://github.com/EFTEC/BladeOne --- BladeOne * https://readouble.com/laravel/5.8/ja/blade.html --- laravel5.8のblade \\ ==== レンダリング ==== Apricotでは、BladeOneを直接使う代わりに、それをラップしてシングルトンにしたViewクラスを使用します。ViewシングルトンはBladeOneと同じメソッド使用できますが、Apricotで主に使用するのはrun()メソッドだけです。 使用法: **View::{メソッド}** ^メソッド^機能^ |string run(string $view, array $variables = [])|テンプレートエンジンの実行| * $view --- テンプレート名 * $variables --- テンプレート変数 * return値 --- HTMLテキストを返す Apricotのアプリケーションでは、直接Viewシングルトンを使用する代わりに、ボイラープレート''render()'' を使って、HTMLをレンダリングします( 即ち、render()の内部でView::run()が使用されています )。以下はユーザコントローラのindexアクションの例です。 public function index() { $users = $this->user->findAll(); return render("user.index", ["users"=>$users]); } render() の第1引数にはHTMLテンプレート名、第2引数にはテンプレート変数を[変数名 ⇒ 値]の形で渡します。 テンプレートファイルは、''/your-project/assets/view/'' に配置します。一般的にテンプレートファイルは、''/your-project/assets/view/{path}/{name}.blade.php'' として保存します。''{path}'' はサブディレクトリ下に配置したい場合のオプションで、コントローラ名に関連した名前を付けるのが一般的でしょう。''{name}''はテンプレート名です。上例のテンプレートファイルは、''/your-project/assets/view/user/index.blade.php'' に配置し、テンプレート名は ''user.index'' になります。 以下は、テンプレートファイル ''index.blade.php'' からの抜粋です。render()メソッドで与えらてたテンプレート変数 $users を使って各要素( $user )を取り出し、ユーザデータをレンダリングしています。 @foreach($users as $user) id}/edit")}}"> {{ $user->id }} {{ $user->account }} {{ $user->email }} {{ ViewHelper::formatDatetime($user->created_at) }} @endforeach === render()関数の実装 === render() 関数は、Apricotコア部分下の ''helpser/boilerplates.php'' の中で以下のように実装されています。 /** * Renders HTML via the given template and returns a response object. * * @param string $view Template name * @param array $variables An array of template variables * @return \Apricot\Foundation\Response\RenderResponse */ function render(string $view=null, array $variables=[]):Apricot\Foundation\Response\RenderResponse { $variables['errors'] = errors(); $html = isset($view) ? Apricot\View::run($view, $variables) : null; return new Apricot\Foundation\Response\RenderResponse($html); } render() 関数の2つの引数は View シングルトンのrun()メソッドに渡わたされ、テンプレートを介してHTMLをレンダリングし、その結果を使ってレンダーレスポンスを生成して返しています。ボイラープレート [[#テンプレート変数_errors|errors()]] については次項を参照して下さい。 \\ ==== テンプレート変数 $errors ==== ''$errors'' は ''render()'' ボイラープレートによって自動的に作られるテンプレート変数です。''$errors'' は ''errors()'' ボイラープレートによって取得されます。 // テンプレート変数 $errors ['errors' => errors()] ''errors()'' ボイラープレートはフラッシュに保存されている[[apricot:usage:ja:errors-logging#エラーバッグ]]を返します(このフラッシュデータの名前は 'errors' です)。''$errors'' はテンプレートの中で次のようにして使用されます: @if($errors->count()) @foreach($errors as $key=>$value) {{$value}}
@endforeach @endif
\\ ==== ビューヘルパー ==== Aprocotのアプリ部分では、ヘルパークラスを ''/your-project/app/Helpers'' に配置しています。 テンプレート内で使用するヘルパークラスはビューヘルパーと呼ばれ、以下は、初期実装されている ViewHelperクラス で、文字列の日付をフォーマットするメソッドだけが実装されています。必要に応じて、ここにメソッドを追加して下さい。 {{fa>folder-open-o}} ** /your-project/app/Helpers ** \\ ==== クラスエイリアス ==== Apricotでは、シングルトンとビューヘルパーのエイリアスを作っています。これによって、完全修飾クラス名を短くコーディングできます。クラスエイリアスの設定については、「[[apricot:usage:ja:config#クラスエイリアス|Apricot 配置と構成]]」を参照して下さい。 以下はテンプレート内でのクラスエイリアスの使用例です。この例では、フラッシュにキーがmsgのデータがあればそれを出力しています。 @if(Flash::has('msg'))
{{Flash::get('msg')}}
@endif
\\ ==== テンプレートファイル ==== テンプレートファイルは、''/your-project/assets/views/'' の下に配置し、ファイル名は ''.blade.php'' で終わる必要があります。例えば、''/your-project/assets/views/{path}/{name}.blade.php'' として保存した場合、テンプレート名は、''{path}.{name}'' になります。 Apricotのアプリ部分として提供しているテンプレートファイルの構成を以下に示します。 your-project | ├── assets | ├── views | | | | | ├── error [エラーページ用] | | | | | | | ├── layout.blade.php [エラーページ用のマスターレイアウト] | | | └── exception.blade.php [集約例外コントローラ用] | | | | | ├── user [ユーザコントローラ用] | | | | | | | ├── create.blade.php [ユーザ新規登録ページ] | | | ├── edit.blade.php [ユーザ編集ページ] | | | └── index.blade.php [ユーザリストページ] | | | | | ├── home.blade.php [ホームコントローラ用] | | ├── layout.blade.php [通常ページ用のマスターレイアウト] | | ├── login.blade.php [ログイン認証コントローラ用] | | └── stub.blade.php [スタブコントローラ用] 1つのコントローラーが複数のHTMLテンプレートを持っている場合、ユーザコントローラのように、サブディレクトリ―にはコントローラ名に関連した名前付けを推奨します。上のユーザ新規登録ページのテンプレート名は ''user.create'' です。 ''layout.blade.php'' ログイン認証とエラーページ以外のマスターレイアウトとして使用されます(次項参照)。 \\ ==== マスターレイアウト ==== 通常のアプリケーションでは、ページの全体的なレイアウトの中に個別のレイアウトを表示します。この全体的なレイアウトをマスターレイアウトと呼びます。以下は、マスターレイアウトのテンプレート ( layout.blade.php ) の例です。 {{fa>folder-open-o}} ** /your-project/assets/views ** {{-- This is an example of master layout --}} {{__('messages.app.title')}} {!! DebugBar::renderHead() !!} @stack('scripts')

@yield('title')

@yield('content') {{-- error --}} @if($errors->count())
@foreach($errors as $key=>$value) {{$value}}
@endforeach
@endif
{{env('APP_NAME')}} © 2020 y2sunlight
{!! DebugBar::render() !!}
マスターレイアウトの例に従ってBladeによるテンプレートの書き方を確認してみましょう。 === コメント === Bladeのコメントは ''{{-- --}}'' 表記を使います。このコメント表記はHTMLを出力しません。 {{-- This is an example of master layout --}} === データ表示 === Bladeからデータを表示するには、''{{ }}'' 表記を使います。これは自動的にhtmlspecialchars()を呼び出して、HTMLをエスケープしXSS攻撃を防ぎます。以下は、ヘルパー関数 ''__()'' による翻訳文字列とテンプレート変数の表示例です。 {{__('messages.app.title')}} {{$value}}
HTMLをエスケープしたくない場合は以下のように、''{!! !!}'' 表記を使います。これは [[apricot:usage:ja:errors-logging#デバッグ出力]] 用のヘッダーと本体部分をレンダリングしている例です。 {!! DebugBar::renderHead() !!} {!! DebugBar::render() !!} === セクションの宣言 === Bladeでは、@で始まるディレクティブと呼ばれる構文があります。''@yield'' と ''@section'' ディレクティブは対で使用されます。親テンプレートの @yield で名前付きのセクションを宣言し、子テンプレートの@sectionでそこに表示するHTMLを定義します。以下の例では、マスターレイアウトでのタイトルとコンテンツセクションを宣言しています。@sectionの例は、次項を参照して下さい。 @yield('title') @yield('content') === スタックの宣言 === ''@stack'' と ''@push'' ディレクティブは、テンプレートにスタック構造を与えます。親テンプレートの @stackで名前付きのスタックを宣言し、子テンプレートの@pushでそこに表示するHTMLをプッシュします。''@yield'' - ''@section'' との違いは、必要に応じてHTMLを何度もプッシュできます。以下の例では、マスターレイアウトのHEAD要素の中にスクリプトのスタックを宣言しています。@pushの例は、次項を参照して下さい。 @stack('scripts') === 制御構文 === PHPの制御構造と同様に、Blade でも ''@if''、''@switch''、''@for''、''@foreach''、そして ''@while'' の制御構文が使用できます。以下は、@if と @foreach の例です。これはエラー表示用のテンプレート変数からエラー情報を表示している例です。 @if($errors->count())
@foreach($errors as $key=>$value) {{$value}}
@endforeach
@endif
=== PHPコード === @phpディレクティブを使えば、任意のPHPコードを実行できます。 @php /* Do something. */ @endphp \\ ==== テンプレートの継承 ==== 前項のマスターテンプレートを継承した例を以下に示します。これはユーザコントローラーのcreateアクションで、ユーザの新規登録を行うページです。 {{fa>folder-open-o}} ** /your-project/assets/views/user ** {{-- Parent layout --}} @extends('layout') {{-- Additional script --}} @push('scripts') @endpush {{-- title --}} @section('title', __('messages.user.create.title')) {{--content --}} @section('content')
@csrf {{-- account --}}
{{-- Show other input elements here --}}
@endsection
上の例に従ってBladeによるテンプレートの書き方を確認してみましょう。 === テンプレートの継承 === マスターテンプレートを継承するには、@extendsディレクティブを使用します。このディレクティブを使えばマスターテンプレートの中に個別のコンテンツを表示することができます。 @extends('layout') === スタックへのプッシュ === マスターテンプレートで宣言されている@stackに個別のコンテンツをプッシュするには、@pushディレクティブを使います。下の例では、マスターテンプレートのHEAD要素内に個別のJavascriptを差し込んでいます。 @push('scripts') @endpush === セクションの定義 === マスターテンプレートで宣言されている@yieldに個別のコンテンツを表示するには、@sectionディレクティブを使います。以下は個別のタイトルを表示する例です。 @section('title', __('messages.user.create.title')) また、複数行のセクションを定義するには以下の構文を使います。 @section('content') {!-- Display the form element here. --} @endsection === CSRF対策 === @csrfは、CSRF対策用のトークンフィールドを出力するカスタムディレクティブです。@csrfの作り方については「[[#Bladeのセットアップ]]」を参照して下さい。
@csrf {!-- Display input elements here. --}
\\ ===== Bladeの設定 ===== ==== Bladeの設定ファイル ==== Bladeの設定ファイルは、''bladeone.setting.php'' です。 {{fa>folder-open-o}} ** your-project/config/setting ** env('VIEW_TEMPLATE',assets_dir('views')), 'compile_path' => env('VIEW_CACHE',var_dir('cache/views')), 'mode' => \eftec\bladeone\BladeOne::MODE_AUTO, ]; * template_path --- HTMLテンプレートファイルのパス(初期設定値は assets/views) * compile_path --- コンパイル後のHTMLファイルのパス(初期設定値は var/cache/views) * mode --- 実行モード(初期設定値は MODE_AUTO(=0)) 実行モードが MODE_AUTO の場合、テンプレートはPHPへコンパイルされ、変更があるまで ''compile_path'' で指定された場所にキャッシュされ続けます。強制的に再コンパイルしたい場合は、キャッシュをクリアして下さい。 実行モードには以下の種類があるます: * MODE_AUTO(=0) --- テンプレートが変更されている場合のみ、コンパイルします。 * MODE_SLOW(=1) --- ファイルは常にコンパイルされます。 * MODE_FAST(=2) --- ファイルは常にコンパイルされません。 * MODE_DEBUG(=5) --- ファイルは常にコンパイルされ、ファイル名は識別可能です。 \\ ==== Bladeのセットアップ ==== Bladeには以下のセットアップファイルが存在します。 {{fa>folder-open-o}} ** /your-project/config/setup ** "; }); // @csrf directive Apricot\View::directive('csrf', function() { $name = Apricot\Foundation\Security\CsrfToken::CSRF_KEY; return ''; }); return true; // Must return true on success }; このセットアップファイルの目的は、HTMLテンプレートに新しいカスタムディレクティブを追加することです。ここでは以下のディレクティブを追加しています。 === @now === 現在時刻を表示するディレクティブです。これはカスタムディレクティブのサンプルです。 === @csrf === CSRF対策用のトークンフィールドを出力するディレクティブです。セッションからCSRFトークンを取得して、hidden タイプの input 要素の中に設定しています。 \\ ===== Bladeの拡張 ===== Apricotで使用しているテンプレートエンジン [[https://github.com/EFTEC/BladeOne|BladeOne]] には、オプションの拡張ライブラリーとして ''BladeOneHtml'' があります。このライブラリーを使用すると、HTMLフォームが簡単に綺麗に作成できます。 例: @form() @input(type="text" name="myform" value=$myvalue) @button(type="submit" value="Send") @endform() インストール方法や使い方は、以下を参照して下さい: * https://github.com/eftec/BladeOneHtml \\ ===== 多言語化 ===== Apricotは多言語をサポートしてます。これはアプリケーションで各言語毎にメッセージをファイルを作成することによって行い、このファイルの事を[[#言語ファイル]]と呼びます。 アプリケーションの実行時に、Apricotがどの言語を選択するかは、利用者がブラウザで使用している言語とアプリケーションで準備されている言語ファイルによって自動的に決定されます。デフォルトの言語は、環境変数 APP_LANG で指定してます。この変数が指定されていない場合は英語になります。 \\ ==== 言語ファイル ==== 言語ファイルは ''/your-project/assets/lang/{言語コード}/'' に配置されます。言語コードは、[[https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes|ISO 639-1]]で定義された2文字のコードです。言語ファイル名は各言語共通で、内容を同じキーで定義しなければなりません。Apricotの初期実装では以下のディレクトリーと言語ファイルが用意されています。 your-project [プロジェクトディレクトリー] | ├── assets | | | ├── lang/ | | | | | ├── en [英語] | | | ├── auth.php [ログイン認証画面用] | | | ├── messages.php [一般的なメッセージ] | | | └── vlucas.valitron.php [バリデーション用] | | | | | ├── ja [日本語] | | | ├── auth.php | | | ├── messages.php | | | └── vlucas.valitron.php メッセージの識別は、ドット表記のキーで行います。最初のキーは、言語ファイルの ''.php'' を除いたベース名です。キーの残りの部分は、それに続いてドットで区切られた階層が続きます。 以下は、言語ファイル(messages.php)の例です。このファイル内のメッセージを取得するドット表記のキーは ''messages.home.title'' と ''messages.home.msg_hello'' です。後者のメッセージには '':account'' という名前の付いた1つのパラメータを含んでいます。 {{fa>folder-open-o}} ** /your-project/assets/lang/en ** [ 'title'=>env('APP_NAME'), 'msg_hello'=>'Hello, :account !', ], ]; \\ ==== 言語メッセージの取得 ==== === Langクラス === Langクラスはドット表記で指定されたキーから各言語用のメッセージを取得するシングルトンです。このクラスは ''Apricot\Foundation\Translation'' クラスをシングルトンにしたものです。 使用法: ** Lang::{メソッド} ** ^メソッド^機能^ |string getLangCode()|言語コード(ISO 639-1)の取得| |bool has(string $key)|キーの存在確認| |string get(string $key, array $params = [])|言語テキストの取得| 以下は、ホームコントローラのindexアクションです。この例では、前項の例の言語ファイル( messages.php )から 'messages.home.msg_hello' のキーを持つメッセージを取得した後、それをテンプレートに渡しHTMLをレンダリングしています。 public function index() { $message = Lang::get('messages.home.msg_hello', [':account'=>AuthUser::getUser()->account]); return render('home',['message'=>$message]); } === ボイラープレート === ''Lang::get()'' メソッドを使用する代わりに ''__()'' ボイラープレートを使って以下のようにもコーディングできます。 public function index() { $message = __('messages.home.msg_hello', [':account'=>AuthUser::getUser()->account]); return render('home',['message'=>$message]); } > この関数名は __ です。2つ並んだアンダースコアはPythonプログラマーの間では ''dunders'' (double underscoreの意) と呼ばれ特別なクラス内メンバに付加されますが、ここではそのような意味はなくLaravelと同じ関数名にしました。 \\ ===== アセットファイルのバージョニング ===== 通常のブラウザでは、静的なアセットファイル( ''.js'' , ''.css'' または画像ファイル)がキャッシュされる場合があります。これは、アプリケーションのアセットファイルを変更した場合に問題が発生するかもしれません。これを防ぐ手段をキャッシュ・バスティング( ''Cache Busting'' )と呼びます。 簡単なキャッシュ・バスティングの方法としては、アセットファイルのURLに現在日時をクエリー文字列として付加する方法があります。 しかし、この方法では、アクセスする度にブラウザがアセットファイルを取得してしまい、ブラウザのキャッシュ機構が上手く機能しません。従って、現在日時の代わりにアプリケーションのバージョン番号を付加すれば、この問題を解決することができます。これがアセットファイルのバージョニングです。 Apricotではバージョニング用のボイラープレート [[apricot:usage:ja:utility#urlとパス|url_ver()]] 関数が用意さており、上のコードを以下のように変更することで、アセットファイルのバージョニングができます。 アプリケーションのバージョン番号は、[[apricot:usage:ja:config#環境設定|環境変数]] ''APP_VERSION'' から取得します。従って、アセットファイルのバージョニングを使用している場合は、アセットファイルを変更したら必ず ''APP_VERSION'' も変更する必要があります。 \\