user1077915
user1077915

Reputation: 894

CAKEPHP 3.x - Error on entity update - missing fields claimed even when have values

I have an entity ( booking) for which I set some values manually and other values are set using patchEntity. But when it comes to save, I got an error stating that two required fields are missing.

This is what the entity debug looks like:

object(App\Model\Entity\Booking) {

    'provider_pk' => (int) 2,
    'date' => object(Cake\I18n\Time) {

        'time' => '2016-03-31T00:00:00+00:00',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'start' => object(Cake\I18n\Time) {

        'time' => '2016-03-31T08:00:00+00:00',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    'modules_count' => (int) 1,
    'requester_pk' => (int) 1,
    'remarks' => '',
    'status_id' => (int) 1,
    'end' => object(Cake\I18n\Time) {

        'time' => '2016-03-31T09:00:00+00:00',
        'timezone' => 'UTC',
        'fixedNowTime' => false

    },
    '[new]' => true,
    '[accessible]' => [
        '*' => true
    ],
    '[dirty]' => [
        'provider_pk' => true,
        'date' => true,
        'start' => true,
        'modules_count' => true,
        'requester_pk' => true,
        'remarks' => true,
        'status_id' => true,
        'end' => true
    ],
    '[original]' => [],
    '[virtual]' => [],
    '[errors]' => [
        'date' => [
            '_required' => 'This field is required'
        ],
        'start' => [
            '_required' => 'This field is required'
        ]
    ],
    '[invalid]' => [],
    '[repository]' => 'Bookings'

}

As you can see, the 'date', and 'start' fields have a value, but in the errors section, it shows that they are missing.

This is how I create the new entity ( $date and $start have the corresponding values):

    $booking = $this->Bookings->newEntity( [ 'provider_pk' => $provider_pk, 'date' => $date, 'start' => $start ] );

Then I use patchEntity, and before saving it, I manually set another field, and then try to save it like this:

   $booking = $this->Bookings->patchEntity($booking, $this->request->data);

    $booking->end = $end->modify( '+' . $booking->modules_count .  ' hours' );
    if ($this->Bookings->save($booking)) 
    {
        $this->Flash->success(__('The booking has been saved.'));
        return $this->redirect( ['action' => 'show' ] );
    } else ...

And then the $booking->errors() gives this:

[

    'date' => [
        '_required' => 'This field is required'
    ],
    'start' => [
        '_required' => 'This field is required'
    ]
]

As you can see the date and start fields are claimed to be required even when the originally have a value.

What am I doing wrong?

Here's the table model :

<?php
namespace App\Model\Table;

use App\Model\Entity\Booking;
use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Bookings Model
 *
 * @property \Cake\ORM\Association\BelongsTo $Statuses
 */
class BookingsTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('bookings');
        $this->displayField('id');
        $this->primaryKey('id');

        $this->belongsTo('Statuses', [
            'foreignKey' => 'status_id',
            'joinType' => 'INNER'
        ]);

        // ---------------------------------------------------------------------
        // customize for every project

        $this->hasMany( 'Aircrafts' );
        $this->belongsTo( 'Pilots', [ 'foreignKey' => 'requester_pk', 'joinType' => 'LEFT' ] );

    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmpty('id', 'create');

        $validator
            ->date('date')
            ->requirePresence('date', 'create')
            ->notEmpty('date');

        $validator
            ->dateTime('start')
             ->requirePresence('start', 'create')
            ->notEmpty('start');

        $validator
            ->dateTime('end')
             ->requirePresence('end', 'create')
            ->notEmpty('end');

        $validator
            ->integer('modules_count')
             ->requirePresence('modules_count', 'create')
            ->notEmpty('modules_count');

        $validator
            ->integer('requester_pk')
            ->requirePresence('requester_pk', 'create')
            ->notEmpty('requester_pk');

        $validator
            ->integer('provider_pk')
            ->allowEmpty('provider_pk');

        $validator
            ->integer('assistant_pk')
            ->allowEmpty('assistant_pk');

        $validator
            ->allowEmpty('remarks');

        return $validator;
    }

    /**
     * Returns a rules checker object that will be used for validating
     * application integrity.
     *
     * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
     * @return \Cake\ORM\RulesChecker
     */
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->existsIn(['status_id'], 'Statuses'));
        return $rules;
    }
}

And here's the controller method :

/** form for booking confirmation
*
* @param string|null $id cell id.
* @return \Cake\Network\Response|null Redirects to bookings / show .
**/

public function confirmBooking()
{
    if( isset( $this->request->query['id']) )
    {
        $id = $this->request->query['id'];

        $token = strpos( $id, '-' );

        $day = substr( $id, $token + 1 , 2 );
        $month = substr( $id, $token + 3 , 2 );
        $year = substr( $id, $token + 5 , 4 );
        $hour = substr( $id, $token + 9 , 2 );
        $minutes = substr( $id, $token + 11, 2 );

        $date = Time::createFromTimestamp( mktime( 0, 0, 0, $month, $day, $year ) );
        $start = Time::createFromTimestamp( mktime( $hour, $minutes, 0, $month, $day, $year ) );
        $end = Time::createFromTimestamp( mktime( $hour + 1, $minutes, 0, $month, $day, $year ) );
        $provider_pk = substr( $id, 1, $token - 1 );
    }

    $aircrafts =  $this->Bookings->Aircrafts->find( 'all', [ 'order' => 'registration' ] );
    $this->set( 'aircrafts', $aircrafts );

    // ---------------------------------------------------------------------
    // depending on add or update

    if( isset( $this->request->query['booking_id'] ) )
        $booking = $this->Bookings->get($this->request->query['booking_id'], [ 'contain' => [ 'Pilots' ] ] );
    else
    {
        $booking = $this->Bookings->newEntity( [ 'provider_pk' => $provider_pk, 'date' => $date, 'start' => $start, 'end' => $end, 'modules_count' => 1 ] );
    }

    if ($this->request->is('post') || ( $this->request->is('put') &&  isset( $this->request->query['booking_id'] ) ) ) {

        debug( $this->request->data );

        $booking = $this->Bookings->patchEntity($booking, $this->request->data);

        $booking->end = $end->hour( $this->request->data[ 'end_value' ][ 'hour' ] );

        $booking->modules_count = $booking->end->diff( $booking->start )->format('%h');


        if ($this->Bookings->save($booking)) 
        {
            $this->Flash->success(__('The booking has been saved.'));
            return $this->redirect( ['action' => 'show' ] );
        } else 
        {
            $this->Flash->error(__('The booking could not be saved. Please, try again.')  );
            return $this->redirect(['action' => 'show']);
        }
    }

    $statuses = $this->Bookings->Statuses->find('list', ['limit' => 200]);
    $assistants = $this->Bookings->Pilots->find( 'list' , [ 'conditions' => [ 'is_instructor =' => true ], 'fields' => [ 'id', 'firstname' ] ] );        
    $providers = $this->Bookings->Aircrafts->find( 'list' , [ 'fields' => [ 'id', 'registration' ] ] );        
    $requesters = $this->Bookings->Pilots->find( 'list' , [ 'fields' => [ 'id', 'lastname' ] ] );        

    $this->set(compact('booking', 'statuses'));

    $this->set('_serialize', ['booking']);
    $this->set( 'assistants', $assistants );
    $this->set( 'providers', $providers );
    $this->set( 'requesters', $requesters );
}        

Upvotes: 0

Views: 2307

Answers (1)

user1077915
user1077915

Reputation: 894

I think I found the problem. The issue was the "requirePresence" rule in the validator. Since the field is not obtained through the form ( it is set as default by the controller ), it is not present in the array passed. Am I right ?

Regards. Facundo.

Upvotes: 1

Related Questions