Using CakePHP 3.5.3
Hi,
// WHAT I HAVE
I have a users index view which displays all the relevant users in the database. At the top of the page I have a select list which allows the user to select the deactivate option and on each users row I have a checkbox.
// WHAT I'M TRYING TO DO
The user can check the checkboxes they want then select the deactivate option from the select list and click submit. The submit sends the script to my actions method in my users controller. In the actions method I check to see if the deactivate option has been selected and if it hasn't I send a message back to the user to that effect. After this I check to see if a checkbox has been checked and if none have I send a message back to the user to that effect. If at least one checkbox has been checked and the deactivate option has been selected I then identify which checkboxes have been checked and update the database to deactivate for those choosen users.
// ON USERS INDEX VIEW I DECLARE MY CHECKBOX AS BELOW
<?= $this->Form->checkbox("checkboxes[]", ['value' => $user->id]); ?>
// ACTIONS METHOD
public function actions()
{
if (empty($this->request->getData('action'))) {
$this->Flash->sys_error('', [
'key' => 'message',
'params' => [
'p1' => __('Please select an action!')
]
]);
// Send to index action for page load
$this->setAction('index');
}
else {
$checkboxArray = $this->request->getData('checkboxes');
foreach($checkboxArray as $key => $val):
echo 'key is ' . $key . "<br />";
echo 'val is ' . $val . "<br />";
if (empty($val)) {
unset($checkboxArray[$key]);
}
endforeach;
if (empty($checkboxArray)) {
$this->Flash->sys_error('', [
'key' => 'message',
'params' => [
'p1' => __('Please select a user!')
]
]);
// Send to index action for page load
$this->setAction('index');
}
else {
foreach($this->request->getData('checkboxes') as $userID):
if ($userID != 0) {
// UPDATE THE DATABSE
echo 'in update the database ' . '<hr />';
}
endforeach;
// Send to index action for page load
$this->setAction('index');
}
}
}
Even though I'm not 100% sure if this the best or correct way to do this it does work except for the following error which occurs on the checkbox in the users index view each time I invoke $this->setAction('index');
The stacktrace from the log is as follows:
Notice (8): Array to string conversion in [C:\xampp\htdocs\app\vendor \cakephp\cakephp\src\View\Widget\CheckboxWidget.php, line 81] Request URL: /users/actions Referer URL: https://localhost/app/users Trace: Cake\Error\BaseErrorHandler::handleError() - CORE\src\Error\BaseErrorHandler.php, line 153 Cake\View\Widget\CheckboxWidget::_isChecked() - CORE\src\View\Widget\CheckboxWidget.php, line 81 Cake\View\Widget\CheckboxWidget::render() - CORE\src\View\Widget\CheckboxWidget.php, line 51 Cake\View\Helper\FormHelper::widget() - CORE\src\View\Helper\FormHelper.php, line 2775 Cake\View\Helper\FormHelper::checkbox() - CORE\src\View\Helper\FormHelper.php, line 1570 include - APP/Template\Users\index.ctp, line 193 Cake\View\View::_evaluate() - CORE\src\View\View.php, line 1196 Cake\View\View::_render() - CORE\src\View\View.php, line 1157 Cake\View\View::render() - CORE\src\View\View.php, line 765 Cake\Controller\Controller::render() - CORE\src\Controller\Controller.php, line 623 Cake\Http\ActionDispatcher::_invoke() - CORE\src\Http\ActionDispatcher.php, line 125 Cake\Http\ActionDispatcher::dispatch() - CORE\src\Http\ActionDispatcher.php, line 93 Cake\Http\BaseApplication::__invoke() - CORE\src\Http\BaseApplication.php, line 103 Cake\Http\Runner::__invoke() - CORE\src\Http\Runner.php, line 65 Cake\Http\Middleware\CsrfProtectionMiddleware::__invoke() - CORE\src\Http\Middleware\CsrfProtectionMiddleware.php, line 106 Cake\Http\Runner::__invoke() - CORE\src\Http\Runner.php, line 65 Cake\I18n\Middleware\LocaleSelectorMiddleware::__invoke() - CORE\src\I18n\Middleware\LocaleSelectorMiddleware.php, line 62 Cake\Http\Runner::__invoke() - CORE\src\Http\Runner.php, line 65 Cake\Routing\Middleware\RoutingMiddleware::__invoke() - CORE\src\Routing\Middleware\RoutingMiddleware.php, line 107 Cake\Http\Runner::__invoke() - CORE\src\Http\Runner.php, line 65 Cake\Routing\Middleware\AssetMiddleware::__invoke() - CORE\src\Routing\Middleware\AssetMiddleware.php, line 88 Cake\Http\Runner::__invoke() - CORE\src\Http\Runner.php, line 65 Cake\Error\Middleware\ErrorHandlerMiddleware::__invoke() - CORE\src\Error\Middleware\ErrorHandlerMiddleware.php, line 95 Cake\Http\Runner::__invoke() - CORE\src\Http\Runner.php, line 65 DebugKit\Middleware\DebugKitMiddleware::__invoke() - ROOT\vendor\cakephp\debug_kit\src\Middleware\DebugKitMiddleware.php, line 52 Cake\Http\Runner::__invoke() - CORE\src\Http\Runner.php, line 65 Cake\Http\Runner::run() - CORE\src\Http\Runner.php, line 51 Cake\Http\Server::run() - CORE\src\Http\Server.php, line 81 [main] - ROOT\webroot\index.php, line 40
// SOLUTION
I have managed to eliminate this error if I declare my checkbox as below:
// FROM
<?= $this->Form->checkbox("checkboxes[]", ['value' => $user->id]); ?>
// TO
<?= $this->Form->checkbox("checkboxes[$user->id]", ['value' => user->id]); ?>
// MY QUESTION
But this means I'm replacing the keys in the array with the user id and I don't know if this is considered as OK or if there's a better more professional way of doing it.
Any help would be greatly appreciated. Z.
@SamHecquet - Thanks for the edit, I'll know for next time how to format the stacktrace.
@Derek - As requested see below - I won't include the whole page as a lot of it I believe is unnecessary but if you want more than I have posted just let me know.
// Select list from the element
<!-- Start form -->
<?php
// Declare individual page forms
if ($page === 'usersPage') {
// Users form
echo $this->Form->create(null, [
'url' => ['controller' => 'Users', 'action' => 'actions'],
'novalidate' => true
]);
}
<?php
// Declare individual page select lists
if ($page === 'usersPage') {
// Users form
$options = [
'' => '--Select Action--',
'a' => 'Deactivate User/s'
];
}
else{
header ("Location: ".$this->Url->build(["controller" => "Pages", "action" => "display", 'blank']));
exit(0);
}
$this->Form->label('action', '');
echo $this->Form->input('action', [
'type' => 'select',
'options' => $options,
'class' => 'ns-typ-2 ix-fa',
'label' => false
]);
?>
<?= $this->Form->submit(__('Go'), [
'class' => 'submit-ix-go'
]);
?>
// INDEX USERS
<!-- Headings -->
<div class="grid-ix-1 ix-r-top">
<!-- Checkbox -->
<div class="ix-usr-checkbox">
<div class="ix-h">
<div class="ix-h-cb">
<input type="checkbox" onClick="toggle(this)" >
</div>
</div>
</div>
<!-- Role -->
<div class="ix-usr-role">
<div class="ix-h">
<h2 class="ix-h2"><?= $this->Paginator->sort('Users.role', __('Role')) ?></h2>
</div>
</div>
</div>
<!-- Cells-->
foreach ($users as $user): ?>
<div class="grid-ix-1">
<!-- Checkbox -->
<div class="ix-usr-checkbox">
<div class="ix-c-cb">
<?= $this->Form->checkbox('checkboxes[]', ['value' => $user->id]); ?>
</div>
</div>
<!-- Role -->
<div class="ix-usr-role">
<div class="ix-usr-role-text"><?= h($user->role) ?></div>
</div>
</div>
<?php endforeach; ?>
<!-- Form ends -->
<?= $this->Form->end() ?>
@Derek - Please see the multiple checkbox issue.
When I used the solution from Dave the multiple checkbox selection worked on page load but not after I invoked the below:
// Send to index action for page load
$this->setAction('index');
I fixed this by redirecting instead of using setAction as below:
// Redirect to index page
return $this->redirect(['action' => 'index']);
But with your solution the multiple checkbox selection functionality did not work on page load or after any of the redirects. I use the following code to achieve this.
// Javascript
// Check and uncheck all check boxes on index pages
function toggle(source) {
checkboxes = document.getElementsByName('checkboxes[]');
for(var i=0, n=checkboxes.length;i<n;i++) {
checkboxes[i].checked = source.checked;
}
}
// And then the heading checkbox is:
<input type="checkbox" onClick="toggle(this)" >
// And of course the cells checkbox
<?= $this->Form->checkbox('checkboxes.' . $k, ['value' => $user->id]); ?>
If you help with the javascript great but if not if you advise that a solution can be found I'm happy to post in the stackoverflow javascript section for a solution which would mean I could go with your solution and not edit the framework code.
Thanks in advance for any help or advice. Z