htmx용 CakePHP 통합.
지원되는 CakePHP 버전 >= 4.x 및 5.x.
앱 폴더의 루트( composer.json
파일이 있는 위치)로 cd
이동하고 다음 명령을 실행합니다.
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 ' );
이벤트와 함께 세부정보를 전달하려면 두 번째 인수를 사용하여 본문을 보낼 수 있습니다. 문자열이나 배열을 지원합니다.
$ 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>
외부 컨트롤러에서는 요청이 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 ' ); ?>
외부 컨트롤러에서는 요청이 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 라이선스에 따라 라이선스가 부여됩니다.