mistaq
mistaq

Reputation: 385

CakePHP 3 - Conditionally creating newEntity for associated table

I have these three tables:

In the BookingsController, I have two functions:

When a user first submits a new Booking data entry, while a newEntity is created and saved for Bookings and Sessions, no newEntity is created and saved for Files. However, when a Booking/Session is being updated and confirmed, that is when a newEntity in Files is created the very first time. Since the Confirm function can be used many times, I use a conditional if statement to determine if an associated Files entry exists or not - if one exists, a newEntity for Files is created, patched and then saved. If not, it is just patched and saved.

In my Confirm function:

public function confirm($id = null)
{
    $booking = $this->Bookings->get($id,[
        'contain' => ['Sessions']
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
        $booking = $this->Bookings->patchEntity($booking, $this->request->data,[
            'contain' => ['Sessions']
        ]);
        $fileTable = TableRegistry::get('Files');
        $findFiles = $fileTable->find()->where([
            'session_id' => $session['id']
        ])->first();
        if($findFiles == null){
            $findFiles = $fileTable->newEntity();
            $findFiles = $fileTable->patchEntity($findFiles, $data);
            $findFiles->session_id = $booking['session']['id'];
            if($fileTable->save($findFiles)){

            } else {

            }
        } else {
            $findFiles = $filesTable->patchEntity($findFiles, $data);
            if($filesTable->save($findFiles)){

            } else {

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

However, when trying to use the Confirm function, I get a integrity constraint violation regarding the booking_id foreign key in the Sessions table. I've pinpointed that by removing all the conditional saving code regarding the Files table, the function works fine, however that means neither a newEntity for Files is created when needed.

The easier method is I think just including Files in the New function, but because some bookings could be cancelled, there could potentially be a lot of empty Files data entries.

Update: Including Model and Confirm function's View and form input.

Below is the View of the Confirm function in BookingsController:

<?= $this->Form->create($booking) ?>
<fieldset>
    <legend><?= __('Confirm Booking') ?></legend>
            <?php
        echo $this->Form->input('session.startdate', ['class'=>'form-control']);
        echo $this->Form->input('session.enddate', ['class'=>'form-control']);
        echo $this->Form->input('session.no_people', ['class'=>'form-control']);
    ?>
</fieldset>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>

And the Models for the individual tables.

Bookings:

public function initialize(array $config)
{
    parent::initialize($config);

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

    $this->addBehavior('Timestamp');

    $this->hasOne('Sessions', [
        'foreignKey' => 'booking_id'
    ]);
}
public function validationDefault(Validator $validator)
{
    $validator
        ->integer('id')
        ->allowEmpty('id', 'create');

    return $validator;
}

Sessions:

public function initialize(array $config)
{
    parent::initialize($config);

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

    $this->addBehavior('Timestamp');

    $this->belongsTo('Bookings', [
        'foreignKey' => 'booking_id',
        'joinType' => 'INNER'
    ]);
    $this->hasOne('Templates', [
        'foreignKey' => 'session_id'
    ]);
}

public function validationDefault(Validator $validator)
{
    $validator
        ->integer('id')
        ->allowEmpty('id', 'create');

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

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

    return $validator;
}

public function buildRules(RulesChecker $rules)
{
    $rules->add($rules->existsIn(['booking_id'], 'Bookings'));
    return $rules;
}

Files:

public function initialize(array $config)
{
    parent::initialize($config);

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

    $this->addBehavior('Timestamp');

    $this->belongsTo('Sessions', [
        'foreignKey' => 'session_id',
        'joinType' => 'INNER'
    ]);
}

public function validationDefault(Validator $validator)
{
    $validator
        ->integer('id')
        ->allowEmpty('id', 'create');

    $validator
        ->allowEmpty('link');

    $validator
        ->allowEmpty('name');

    return $validator;
}

public function buildRules(RulesChecker $rules)
{
    $rules->add($rules->existsIn(['session_id'], 'Sessions'));

    return $rules;
}

Upvotes: 1

Views: 1808

Answers (2)

mistaq
mistaq

Reputation: 385

The foreign key session_id was a required attribute (this was why Files couldn't ever save). I've changed up the function to the following:

public function confirm($id = null)
{
    $booking = $this->Bookings->get($id,[
        'contain' => ['Sessions', 'Sessions.Files']
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
        if($booking->session->file == null){ //checks for existing Files array
            $template = $this->Bookings->Sessions->Files->newEntity(); //creates new Files entity if one doesn't exist for the associated session
            $template->session_id = $booking->session->id; //fills in the foreign key with the currently active session primary key.
            if($this->Bookings->Sessions->Templates->save($template)){ //saves the File entity

            } else {

            }
        }
        $booking = $this->Bookings->patchEntity($booking, $this->request->data,[
            'contain' => ['Sessions', 'Sessions.Files']
        ]);
        $fileTable = TableRegistry::get('Files');
        $file = $fileTable->find('all',[
            'contain' => ['Sessions'],
            'conditions' => ['Sessions.booking_id' => $booking->id, 'Files.session_id' => $booking->session->id]
        ])->first();
        if($file->session_id != $data['session']['id']){ //checks for any changes in session_id
            $file->engineer_id = $data['session']['id']; //changes value to match if there isn't a match 
            $fileTable->save($template);
        }
        if ($this->Bookings->save($booking)) {
            $this->Flash->success(__('The booking has been saved.'));
            return $this->redirect(['action' => 'index']);
        } else {
            $this->Flash->error(__('The booking could not be saved. Please, try again.'));
        }
        $this->set(compact('booking'));
        $this->set('_serialize', ['booking']);
    }
}

Upvotes: 1

Derek
Derek

Reputation: 859

Something like this should work. Cake will change the query to an UPDATE if an id exists else uses an INSERT.

public function confirm($id = null)
{
    $booking = $this->Bookings->get($id, [
        'contain' => ['Sessions' => ['Files']]
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
        $booking = $this->Bookings->patchEntity($booking, $this->request->data, [
            'associated' => ['Sessions' => ['associated' => ['Files']]]
        ]);

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

Upvotes: 0

Related Questions