0

I'm currently working on finalizing an upgrade from CakePHP 3.3 to 4.4. I now have the same problem in a few places, which all are the same SQLSTATE error:

SQLSTATE[HY000]: General error: 1364 Field 'cert_request' doesn't have a default value

The code looks currently like this:

Controller

$cert = $this->Certs->newEmptyEntity();
$data = $this->request->getData();

if(array_key_exists('certificate', $data) && !is_null($data['certificate'])) {
    $certificate = $data['certificate'];
    $cert_string = $certificate;
    $certificated = openssl_x509_parse($certificate);
} else {
    if ($this->request->is('post')) {
        $data['effective_date'] = date('Y-m-d H:i', strtotime($data['effective_date']));
        $data['expiry_date'] = date('Y-m-d H:i', strtotime($data['expiry_date']));

        $cert = $this->Certs->patchEntity($cert, $data);

        // Database Error happens here during save()
        if ($this->Certs->save($cert)) {
            $this->Flash->success(__('The cert has been saved.'));
            return $this->redirect(['action' => 'index']);

        } else {
            $this->Flash->error(__('The cert could not be saved. Please, try again.'));
        }
    }
}

Mind, that $data does indeed not contain 'cert_request' and other fields related to this problem.

Model/Table

namespace App\Model\Table;

use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Certs Model
 *
 * @property \Cake\ORM\Association\BelongsTo $Requests
 * @property \Cake\ORM\Association\BelongsTo $CertTemplates
 * @property \Cake\ORM\Association\BelongsTo $CertStatuses
 * @property \Cake\ORM\Association\BelongsTo $Users
 *
 * @mixin \Cake\ORM\Behavior\TimestampBehavior
 */
class CertsTable extends Table
{
    public function initialize(array $config): void
    {
        parent::initialize($config);

        $this->setTable('certs');
        $this->setDisplayField('id');
        $this->setPrimaryKey('id');

        $this->addBehavior('Timestamp');

        $this->belongsTo('CertTemplates', [
            'foreignKey' => 'cert_template_id',
            'joinType' => 'INNER'
        ]);
        $this->belongsTo('CertStatuses', [
            'foreignKey' => 'cert_status_id',
            'joinType' => 'INNER'
        ]);
        $this->belongsTo('Users', [
            'foreignKey' => 'user_id'
        ]);
        $this->hasMany('CertAlerts', [
            'foreignKey' => 'cert_id'
        ]);
    }

    public function validationDefault(Validator $validator): \Cake\Validation\Validator
    {
        $validator
            ->integer('id')
            ->allowEmptyFor('id', Validator::EMPTY_ALL, Validator::WHEN_CREATE)
            ->add('id', 'unique', ['rule' => 'validateUnique', 'provider' => 'table']);

        $validator
            ->dateTime('revocation_date')
            ->allowEmptyDatetime('revocation_date');

        $validator
            ->dateTime('effective_revocation_date')
            ->allowEmptyDatetime('effective_revocation_date');

        $validator
            ->allowEmptyString('revocation_reason');

        $validator
            ->requirePresence('common_name', 'create')
            ->notEmptyString('common_name');

        $validator
            ->requirePresence('cert_template_id', 'create')
            ->notEmptyString('cert_template_id');

        $validator
            ->requirePresence('requester_name', 'create')
            ->notEmptyString('requester_name');

        $validator
            ->requirePresence('requester_email', 'create')
            ->notEmptyString('requester_email');

        $validator
            ->allowEmptyString('budget_zi');

        $validator
            ->allowEmptyString('billable');

        $validator
            ->allowEmptyString('key_csr');

        $validator
            ->allowEmptyString('cert_request');

        $validator
            ->allowEmptyString('subject');

        $validator
            ->allowEmptyString('alternative_name');

        $validator
            ->allowEmptyString('serial_no');

        $validator
            ->allowEmptyString('cert');

        $validator
            ->allowEmptyString('ticket');

        $validator
            ->dateTime('request_submission_date')
            ->allowEmptyDatetime('request_submission_date');

        $validator
            ->dateTime('effective_date')
            ->requirePresence('effective_date', 'create')
            ->notEmptyDatetime('effective_date');

        $validator
            ->dateTime('expiry_date')
            ->requirePresence('expiry_date', 'create')
            ->notEmptyDatetime('expiry_date');

        $validator
            ->requirePresence('country', 'create')
            ->notEmptyString('country');

        $validator
            ->requirePresence('organization', 'create')
            ->notEmptyString('organization');

        $validator
            ->requirePresence('organization_unit', 'create')
            ->notEmptyString('organization_unit');

        $validator
            ->requirePresence('city', 'create')
            ->notEmptyString('city');

        $validator
            ->requirePresence('state', 'create')
            ->notEmptyString('state');

        $validator
            ->allowEmptyString('issued_email');

        $validator
            ->allowEmptyString('san');

        $validator
            ->allowEmptyString('remarks');

       return $validator;
    }

    public function buildRules(RulesChecker $rules): \Cake\ORM\RulesChecker
    {
        $rules->add($rules->isUnique(['id']));
        $rules->add($rules->existsIn(['cert_template_id'], 'CertTemplates'));
        $rules->add($rules->existsIn(['cert_status_id'], 'CertStatuses'));
        $rules->add($rules->existsIn(['user_id'], 'Users'));
        $rules->add($rules->existsIn(['cert_id'], 'CertAlerts'));

        return $rules;
    }
}

Now I did add the following code which resolved the problem, but feels very much like a hack than an actual solution

if (!array_key_exists('cert_request', $data)) { $data['cert_request'] = ''; }
if (!array_key_exists('ip_address', $data)) { $data['ip_address'] = ''; }

Now the fields do indeed not have a default value in the DB, but I think in the version which is currently in prod (PHP 5.4 and CakePHP 3.3) it just adds empty strings when the values are not present.

I've been reading through the migration notes and didn't find something that would explain why this behavior changed or perhaps it works differently.

There are parts that make it hard to debug (The application was developed on prod relying on external authentication which is not available locally), which is why I might've missed something. To be honest any idea what the problem could be would help.

dyXur
  • 3
  • 1
  • Any reason why you can't just go ahead and add default values for these in the database schema? – Greg Schmidt Aug 31 '23 at 18:10
  • @GregSchmidt That would be possible, but I would like to keep most things as is before I start the refactoring. Also I have not figured out why this was done this way in the first place and would hate to get unexpected results because of a change like this. On a side not it would also just be nice to know why it works on 5.4/3.3 and not on 8.2/4.4 – dyXur Aug 31 '23 at 22:29

0 Answers0