PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Sunday, January 23, 2022

[FIXED] CakePHP 3.x unique validation not working for saving multiple records

 January 23, 2022     cakephp, cakephp-3.0, validation     No comments   

Issue

I have a Questions table which has a validation like :

$validator
    ->notEmpty('title')
    ->add('title', [
        'unique' => [
            'rule' => [
                    'validateUnique', 
                     ['scope' => ['subject_id', 'chapter_id']]
            ],
            'provider' => 'table'
        ]
    ]);

I want to save following records into my table at a time.

Array
(
    [0] => Array
        (
            [subject_id] => 1
            [chapter_id] => 4
            [title] => What is a .ctp file used for in CakePHP?
        )
    [1] => Array
        (
            [subject_id] => 1
            [chapter_id] => 4
            [title] => What is a .ctp file used for in CakePHP?
        )
)

I try to save it using saveMany() method. It save both records i.e. validation not working. I also try following code for transactional() method instead of saveMany() method, but validation also not working.

$entities = $this->Questions->newEntities($records);
$this->Questions->connection()->transactional(function () use ($entities) {
    foreach ($entities as $entity) {
       $this->Questions->save($entity);
    }
});

My validation works fine if I save the records one by one using save() method or my records already saved in database. Why my unique validation not working for saveMany() and also for transactional() for duplicate new entities?


Solution

Validation happens before saving

Validation happens before saving, so that behavior is to be expected, given that the rule looks up the database, and not the request data (it would only have access to one set of data at the time anyways), ie, no matter how many datasets are being tested, none of the submitted ones will be saved yet, and therefore validation will pass unless a matching record already exists in the database.

So either create/patch and save all entities one by one in a custom transaction (and don't forget to add some proper failure checks),

$this->Questions->connection()->transactional(function () {
    foreach ($this->request->data() as $set) {
        $entity = $this->Questions->newEntity($set); // < validaton is being applied there
        if (!$this->Questions->save($entity)) { // < not there
            return false;
        }
    }
    return true;
});

or use application rules instead.

Application rules are being applied in the saving process

Application rules are being applied in the actual saving process, ie upon calling Table::save(), so to avoid the hassle of using custom transactions, and generally to have a last line of defense, use them instead of/additionally to validation.

// QuestionsTable class

public function buildRules(\Cake\ORM\RulesChecker $rules)
{
    $rules->add($rules->isUnique(['title', 'subject_id', 'chapter_id']));

    // ...

    return $rules;
}

See also

  • Cookbook > Database Access & ORM > Database Basics > Using Transactions
  • Cookbook > Database Access & ORM > Validation > Applying Application Rules
  • Cookbook > Database Access & ORM > Validation > Creating Unique Field Rules
  • Cookbook > Database Access & ORM > Validation > Validation vs. Application Rules


Answered By - ndm
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing