تكامل CakePHP لـ htmx.
إصدارات 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
وتحميل مكون Htmx في وظيفة initialize()
$ 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
. عند تحميل الصفحة، سوف نقوم بعرض 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. سيسمح لنا ذلك بتصفية النتائج في الوقت الفعلي وتحديث ترقيم الصفحات وفقًا لذلك. سنقوم بتغليف جسم الجدول الخاص بنا داخل viewBlock يسمى usersTable
وترقيم الصفحات الخاص بنا إلى كتلة pagination
. عند تحميل الصفحة، سنعرض كلاً من usersTable
و viewBlock pagination
.
// 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.