htmx 用の CakePHP の統合。
サポートされている CakePHP バージョン >= 4.x および 5.x。
cd
アプリ フォルダーのルート ( composer.json
ファイルがある場所) に移動し、次のコマンドを実行します。
composer require zunnu/cake-htmx
次に、CakePHP のコンソールを使用してプラグインをロードします。
./bin/cake plugin load CakeHtmx
htmx をインストールするには、ドキュメントを参照してください。
現在、主要な機能は Htmx コンポーネント内にラップされています。コンポーネントをロードするには、 src/Controller/AppController.php
変更し、 initialize()
関数で Htmx コンポーネントをロードする必要があります。
$ this -> loadComponent ( ' CakeHtmx.Htmx ' );
ディテクタを使用して、リクエストが Htmx であるかどうかを確認できます。
$ this -> getRequest ()-> is ( ' htmx ' ) // Always true if the request is performed by Htmx
$ this -> getRequest ()-> is ( ' boosted ' ) // Indicates that the request is via an element using hx-boost
$ this -> getRequest ()-> is ( ' historyRestoreRequest ' ) // True if the request is for history restoration after a miss in the local history cache
コンポーネントを使用すると、リクエストに関するより具体的な詳細を確認できます。
$ this -> Htmx -> getCurrentUrl (); // The current URL of the browser
$ this -> Htmx -> getPromptResponse (); // The user response to an hx-prompt
$ this -> Htmx -> getTarget (); // The id of the target element if it exists
$ this -> Htmx -> getTriggerName (); // The name of the triggered element if it exists
$ this -> Htmx -> getTriggerId (); // The id of the triggered element if it exists
redirect
Htmx は、 HX-Redirect
ヘッダーを持つ応答を受信したときに、クライアント側のリダイレクトをトリガーできます。
$ this -> Htmx -> redirect ( ' /somewhere-else ' );
clientRefresh
Htmx は、 HX-Refresh
ヘッダーを持つ応答を受信すると、ページのリロードをトリガーします。 clientRefresh
、そのような応答を送信できるようにするカスタム応答です。 Htmx はコンテンツを無視するため、引数は必要ありません。
$ this -> Htmx -> clientRefresh ();
stopPolling
ポーリング トリガーを使用する場合、Htmx は特別な HTTP ステータス コード 286 を持つ応答を検出するとポーリングを停止します。 stopPolling
はそのステータス コードを持つカスタム応答です。
$ this -> Htmx -> stopPolling ();
残りの利用可能なヘッダーについてはドキュメントを参照してください。
$ this -> Htmx -> location ( $ location ) // Allows you to do a client-side redirect that does not do a full page reload
$ this -> Htmx -> pushUrl ( $ url ) // pushes a new url into the history stack
$ this -> Htmx -> replaceUrl ( $ url ) // replaces the current URL in the location bar
$ this -> Htmx -> reswap ( $ option ) // Allows you to specify how the response will be swapped
$ this -> Htmx -> retarget ( $ selector ); // A CSS selector that updates the target of the content update to a different element on the page
さらに、 addTrigger
メソッドを使用してクライアント側イベントをトリガーできます。
$ this -> Htmx
-> addTrigger ( ' myEvent ' )
-> addTriggerAfterSettle ( ' myEventAfterSettle ' )
-> addTriggerAfterSwap ( ' myEventAfterSwap ' );
イベントとともに詳細を渡したい場合は、2 番目の引数を使用して本文を送信できます。文字列または配列をサポートします。
$ this -> Htmx -> addTrigger ( ' myEvent ' , ' Hello from myEvent ' )
-> addTriggerAfterSettle ( ' showMessage ' , [
' level ' => ' info ' ,
' message ' => ' Here is a Message '
]);
複数のイベントをトリガーする場合は、これらのメソッドを複数回呼び出すことができます。
$ this -> Htmx
-> addTrigger ( ' trigger1 ' , ' A Message ' )
-> addTrigger ( ' trigger2 ' , ' Another Message ' )
すべてのリクエストに CSRF トークンを追加するには、以下のコードをレイアウトに追加します。
document.body.addEventListener( ' htmx:configRequest ' , (event) => {
event.detail.headers[ ' X-CSRF-Token ' ] = " <?= $ this -> getRequest ()->getAttribute('csrfToken') ?> " ;
})
setBlock()
関数を使用すると、レンダリングされる可能性のある他のブロックを削除しながら、特定のブロックをレンダリングできます。これは、ビューの一部のみを更新する必要がある場合に特に便利です。
$ this -> Htmx -> setBlock ( ' userTable ' );
addBlock()
関数を使用すると、レンダリングするブロックのリストに特定のブロックを追加できます。
$ this -> Htmx -> addBlock ( ' userTable ' );
addBlocks()
関数を使用すると、レンダリングするブロックのリストに複数のブロックを追加できます。
$ this -> Htmx -> addBlocks ([ ' userTable ' , ' pagination ' ]);
Htmx は、 hx-swap-oop
で複数の部分応答を返すことにより、複数のターゲットの更新をサポートします。例のようにテーブルを操作している場合は、 Users index search functionality with pagination update
例を参照してください。追加が必要な場合があります
< script type = "text/javascript" >
htmx.config.useTemplateFragments = true;
</ script >
テンプレートまたはレイアウト内。
この例では、Htmx を使用してユーザーのインデックスの検索機能を実装し、結果を動的にフィルターします。テーブル本体をusersTable
という viewBlock 内にラップします。ページが読み込まれると、 usersTable
viewBlock がレンダリングされます。
// Template/Users/index.php
<?= $ this -> Form -> control ( ' search ' , [
' label ' => false ,
' placeholder ' => __ ( ' Search ' ),
' type ' => ' text ' ,
' required ' => false ,
' class ' => ' form-control input-text search ' ,
' value ' => ! empty ( $ search ) ? $ search : '' ,
' hx-get ' => $ this -> Url -> build ([ ' controller ' => ' Users ' , ' action ' => ' index ' ]),
' hx-trigger ' => " keyup changed delay:200ms " ,
' hx-target ' => " #search-results " ,
' templates ' => [
' inputContainer ' => ' <div class="col-10 col-md-6 col-lg-5">{{content}}</div> '
]
]); ?>
<table id="usersTable" class="table table-hover table-white-bordered">
<thead>
<tr>
<th scope="col"> <?= ' id ' ?> </th>
<th scope="col"> <?= ' Name ' ?> </th>
<th scope="col"> <?= ' Email ' ?> </th>
<th scope="col"> <?= ' Modified ' ?> </th>
<th scope="col"> <?= ' Created ' ?> </th>
<th scope="col" class="actions"> <?= ' Actions ' ?> </th>
</tr>
</thead>
<tbody id="search-results">
<?php $ this -> start ( ' usersTable ' ); ?>
<?php foreach ( $ users as $ user ): ?>
<tr>
<td> <?= $ user -> id ?> </td>
<td> <?= h ( $ user -> name ) ?> </td>
<td> <?= h ( $ user -> email ) ?> </td>
<td> <?= $ user -> modified ?> </td>
<td> <?= $ user -> created ?> </td>
<td class="actions">
<?= $ this -> Html -> link ( ' Edit ' ,
[
' action ' => ' edit ' ,
$ user -> id
],
[
' escape ' => false
]
); ?>
<?= $ this -> Form -> postLink ( ' Delete ' ,
[
' action ' => ' delete ' ,
$ user -> id
],
[
' confirm ' => __ ( ' Are you sure you want to delete user {0}? ' , $ user -> email ),
' escape ' => false
]
); ?>
</td>
</tr>
<?php endforeach ; ?>
<?php $ this -> end (); ?>
<?php echo $ this -> fetch ( ' usersTable ' ); ?>
</tbody>
</table>
out コントローラーでは、リクエストが Htmx であるかどうかを確認し、そうである場合はusersTable
viewBlock のみをレンダリングします。
// src/Controller/UsersController.php
public function index ()
{
$ search = null ;
$ query = $ this -> Users -> find ( ' all ' );
if ( $ this -> request -> is ( ' get ' )) {
if (! empty ( $ this -> request -> getQueryParams ())) {
$ data = $ this -> request -> getQueryParams ();
if ( isset ( $ data [ ' search ' ])) {
$ data = $ data [ ' search ' ];
$ conditions = [
' OR ' => [
' Users.id ' => ( int ) $ data ,
' Users.name LIKE ' => ' % ' . $ data . ' % ' ,
' Users.email LIKE ' => ' % ' . $ data . ' % ' ,
],
];
$ query = $ query -> where ([ $ conditions ]);
$ search = $ data ;
}
}
}
$ users = $ query -> toArray ();
$ this -> set ( compact ( ' users ' , ' search ' ));
if ( $ this -> getRequest ()-> is ( ' htmx ' )) {
$ this -> viewBuilder ()-> disableAutoLayout ();
// we will only render the usersTable viewblock
$ this -> Htmx -> setBlock ( ' usersTable ' );
}
}
この例では、Htmx を使用してユーザーのインデックスの動的検索機能を実装します。これにより、結果をリアルタイムでフィルタリングし、それに応じてページネーションを更新できるようになります。テーブル本体をusersTable
という viewBlock 内にラップし、ページネーションからpagination
ブロックに移動します。ページが読み込まれると、 usersTable
とpagination
viewBlock の両方がレンダリングされます。
// Template/Users/index.php
<?= $ this -> Form -> control ( ' search ' , [
' label ' => false ,
' placeholder ' => __ ( ' Search ' ),
' type ' => ' text ' ,
' required ' => false ,
' class ' => ' form-control input-text search ' ,
' value ' => ! empty ( $ search ) ? $ search : '' ,
' hx-get ' => $ this -> Url -> build ([ ' controller ' => ' Users ' , ' action ' => ' index ' ]),
' hx-trigger ' => ' keyup changed delay:200ms ' ,
' hx-target ' => ' #search-results ' ,
' hx-push-url ' => ' true ' ,
' templates ' => [
' inputContainer ' => ' <div class="col-10 col-md-6 col-lg-5">{{content}}</div> '
]
]); ?>
<table id="usersTable" class="table table-hover table-white-bordered">
<thead>
<tr>
<th scope="col"> <?= ' id ' ?> </th>
<th scope="col"> <?= ' Name ' ?> </th>
<th scope="col"> <?= ' Email ' ?> </th>
<th scope="col"> <?= ' Modified ' ?> </th>
<th scope="col"> <?= ' Created ' ?> </th>
<th scope="col" class="actions"> <?= ' Actions ' ?> </th>
</tr>
</thead>
<tbody id="search-results">
<?php $ this -> start ( ' usersTable ' ); ?>
<?php foreach ( $ users as $ user ): ?>
<tr>
<td> <?= $ user -> id ?> </td>
<td> <?= h ( $ user -> name ) ?> </td>
<td> <?= h ( $ user -> email ) ?> </td>
<td> <?= $ user -> modified ?> </td>
<td> <?= $ user -> created ?> </td>
<td class="actions">
<?= $ this -> Html -> link ( ' Edit ' ,
[
' action ' => ' edit ' ,
$ user -> id
],
[
' escape ' => false
]
); ?>
<?= $ this -> Form -> postLink ( ' Delete ' ,
[
' action ' => ' delete ' ,
$ user -> id
],
[
' confirm ' => __ ( ' Are you sure you want to delete user {0}? ' , $ user -> email ),
' escape ' => false
]
); ?>
</td>
</tr>
<?php endforeach ; ?>
<?php $ this -> end (); ?>
<?php echo $ this -> fetch ( ' usersTable ' ); ?>
</tbody>
</table>
// pagination
<?php $ this -> start ( ' pagination ' ); ?>
<nav aria-label="Page navigation" id="pagination">
<ul class="pagination justify-content-center">
<?php $ this -> Paginator -> setTemplates ([
' prevActive ' => ' <li class="page-item pagination-previous"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
' prevDisabled ' => ' <li class="page-item disabled pagination-previous"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
' number ' => ' <li class="page-item"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
' current ' => ' <li class="page-item active"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
' nextActive ' => ' <li class="page-item pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
' nextDisabled ' => ' <li class="page-item disabled pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
' first ' => ' <li class="page-item pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
' last ' => ' <li class="page-item pagination-next"><a class="page-link" hx-get="{{url}}" hx-target="#search-results" hx-push-url="true" href="#">{{text}}</a></li> ' ,
]); ?>
<?= $ this -> Paginator -> first ( ' <i class="fas fa-angles-left"></i> ' , [ ' escape ' => false ]) ?>
<?= $ this -> Paginator -> prev ( ' <i class="fas fa-chevron-left"></i> ' , [ ' escape ' => false ]) ?>
<?= $ this -> Paginator -> numbers ([ ' first ' => 1 , ' last ' => 1 , ' modulus ' => 3 ]) ?>
<?= $ this -> Paginator -> next ( ' <i class="fas fa-chevron-right"></i> ' , [ ' escape ' => false ]) ?>
<?= $ this -> Paginator -> last ( ' <i class="fas fa-angles-right"></i> ' , [ ' escape ' => false ]) ?>
</ul>
</nav>
<?php $ this -> end (); ?>
<?= $ this -> fetch ( ' pagination ' ); ?>
out コントローラーでは、リクエストが Htmx であるかどうかを確認し、そうである場合はusersTable
viewBlock のみをレンダリングします。
// src/Controller/UsersController.php
public function index ()
{
$ search = null ;
$ query = $ this -> Users -> find ( ' all ' );
if ( $ this -> request -> is ( ' get ' )) {
if (! empty ( $ this -> request -> getQueryParams ())) {
$ data = $ this -> request -> getQueryParams ();
if ( isset ( $ data [ ' search ' ])) {
$ data = $ data [ ' search ' ];
$ conditions = [
' OR ' => [
' Users.id ' => ( int ) $ data ,
' Users.name LIKE ' => ' % ' . $ data . ' % ' ,
' Users.email LIKE ' => ' % ' . $ data . ' % ' ,
],
];
$ query = $ query -> where ([ $ conditions ]);
$ search = $ data ;
}
}
}
$ this -> paginate [ ' limit ' ] = 200 ;
$ users = $ this -> paginate ( $ query );
$ this -> set ( compact ( ' users ' , ' search ' ));
if ( $ this -> getRequest ()-> is ( ' htmx ' )) {
$ this -> viewBuilder ()-> disableAutoLayout ();
// render users table and pagination blocks
$ this -> Htmx -> addBlock ( ' usersTable ' )-> addBlock ( ' pagination ' );
}
}
MITライセンスに基づいてライセンスされています。