Several issues regarding booleans (in Postgres)

Consider the following example. For customers there should be a field which indicates if the customer wants to receive a newsletter. This is modelled in a table in Postgres by using a boolean as followed:

CREATE TABLE customers
             (id integer
                 GENERATED ALWAYS AS IDENTITY,
              email_address text
                            NOT NULL,
              wants_newsletter boolean
                               NOT NULL
                               DEFAULT true,
              PRIMARY KEY (id),
              UNIQUE (email_address));

The controller looks like this:

<?php

namespace App\Controllers;

use GroceryCrud\Core\GroceryCrud;

class Customers extends BaseController
{
    protected function _getDbData()
    {
        $db = (new \Config\Database())->default;

        return ['adapter' => ['driver' => 'Pdo_Pgsql',
                              'host' => $db['hostname'],
                              'database' => $db['database'],
                              'username' => $db['username'],
                              'password' => $db['password'],
                              'charset' => 'utf8']];
    }

    protected function _getGroceryCrudEnterprise($bootstrap = true, $jquery = true)
    {
        $db = $this->_getDbData();

        $config = (new \Config\GroceryCrudEnterprise())->getDefaultConfig();

        $groceryCrud = new GroceryCrud($config, $db);

        $groceryCrud->setCsrfTokenName(csrf_token());
        $groceryCrud->setCsrfTokenValue(csrf_hash());

        return $groceryCrud;
    }

    protected function _output($output = null, $title = null)
    {
        $output->title = $title;

        if (isset($output->isJSONResponse)
            && $output->isJSONResponse) {
            header('Content-Type: application/json; charset=utf-8');
            echo $output->output;
            exit;
        }

        return view('view', (array)$output);
    }

    public function index()
    {
        $crud = $this->_getGroceryCrudEnterprise();

        $crud->setSubject('Customer', 'Customers');

        $crud->setTable('customers');

        $crud->columns(['email_address', 'wants_newsletter']);
        $crud->fields(['email_address', 'wants_newsletter']);
        $crud->fieldType('email_address', 'email');
        $crud->displayAs('email_address', 'E-Mail Address');
        $crud->displayAs('wants_newsletter', 'Wants Newsletter');
        $crud->requiredFields(['email_address', 'wants_newsletter']);
        $crud->uniqueFields(['email_address']);

        $crud->defaultOrdering('email_address');

        $crud->callbackAddForm(function ($data) {
            $data['wants_newsletter'] = true;

            return $data;
        });

        $output = $crud->render();

        return $this->_output($output, 'Customers');
    }
}

And that is the view:

<!DOCTYPE html>
<html>
<head>
    <title>
        <?php echo "Customers"; ?>
    </title>
	<meta charset="utf-8" />
    <?php foreach($css_files as $file): ?>
	<link type="text/css" rel="stylesheet" href="<?php echo $file; ?>" />
    <?php endforeach; ?>
</head>
<body>
    <div>
		<?php echo $output; ?>
    </div>
    <?php foreach($js_files as $file): ?>
    <script src="<?php echo $file; ?>">
    </script>
    <?php endforeach; ?>
</body>
</html>

We’re using Grocery Crud Enterprise 3.2.3 (i.e. currently the latest) in a CodeIgniter 4 project with the bootstrap v5 1.5.3 theme installed via Composer.

The issues:

(1) When editing a customer where wants_newsletter = true in the database, the form shows the toggle in the false state (
Wants Newsletter
).

(2) When using the quick search for the boolean there’s an error. From the logs I can gather that as operation ILIKE '%1%' is used when filtering for true and ILIKE '%0%' when filtering for false. Of course ILIKE is a text operator not defined for booleans and furthermore the literals have to be booleans (true/false) as well instead of strings with wildcarded integer representations. So the DBMS rightfully rejects the queries.

(3) The default value of the column in the database isn’t taken into account when creating a new customer. The form shows the toggle in the false state. Trying to explicitly set the value changing data[] within callbackAddForm() as shown doesn’t work either. I also tried $data['wants_newsletter'] = 1; to no avail though. Maybe the root cause here is the same as (1).

How to work around these issues? Or what am I doing wrong?

Hello @tcdev0 ,

From a quick look those seems like Grocery CRUD bugs with the boolean field. I will check it and let you know. Will try to fix the issues you have but if a solution is more difficult than that, maybe I will try to find a work-around for you.

I will keep you up to date on the investigation.

Regards
Johnny

Hello @tcdev0 ,

A quick update, so all 3 are bugs. So far, I have (1) and (3) resolved (basically the (3) was caused by (1) as you said) and now I am looking for the (2) which is trickier than I thought. Unfortunately no work-arounds for any of those. I will try to resolve all 3 and have a new version.

I will keep you up to date on that.

Regards
Johnny

Hello @tcdev0 ,

I am glad to inform you that all of the issues that you have referred to has been now fixed on the latest version 3.2.4, please keep in mind that for the default value (3) you should also use callbackAddForm for example in your case:

$crud->callbackAddForm(function ($data) {
    $data['wants_newsletter'] = '1';

    return $data;
});

You can update the version that you are using by following the wizard: User's Page Login

Please let me know if latest version fixed those issues for you.

Regards
Johnny

1 Like

Thanks
I’ve been waiting for this feature for a while. Every update required changing the GC core. I created a function, castAs(), in which I changed booleans to int and fields to dropdown.

    $crud->fieldType('supervisor','dropdown',['0' => 'No', '1' => 'Si']);
    $crud->fieldType('encuestador','dropdown',['0' => 'No', '1' => 'Si']);
    $crud->fieldType('coordinador','dropdown',['0' => 'No', '1' => 'Si']);
    $crud->fieldType('encuestador_asigna','dropdown',['0' => 'No', '1' => 'Si']);
    $crud->fieldType('supervisor_asigna','dropdown',['0' => 'No', '1' => 'Si']);

    $crud->castAs(['supervisor' => 'int']);
    $crud->castAs(['encuestador' => 'int']);
    $crud->castAs(['coordinador' => 'int']);
    $crud->castAs(['encuestador_asigna' => 'int']);
    $crud->castAs(['supervisor_asigna' => 'int']);

public function castAs($fieldName, $castAs = null)
{
if (is_array($fieldName)) {
foreach ($fieldName as $field => $castAs) {
$this->_cast[$field] = $castAs;
}
} elseif ($castAs !== null) {
$this->_cast[$fieldName] = $castAs;
}
return $this;
}

public function getCastAs() {
    return $this->_cast;
}

public function update($primaryKeyValue, $data, $tableName = null) {
foreach($this->_fieldTypes[$this->tableName] as $key => $value) {
if($value->defaultValue == ‘false’ || $value->defaultValue == ‘true’) {
$data[$key] = $data[$key] == ‘’ ? ‘f’ : $data[$key];
$data[$key] = $data[$key] == ‘0’ ? ‘f’ : $data[$key];
$data[$key] = $data[$key] == ‘1’ ? ‘t’ : $data[$key];
}
}