Welcome Guest, Not a member yet? Register   Sign In
A check if model has anything to update
#1

After one of CodeIgniter updates I started getting CodeIgniter\Database\Exceptions\DataException ("There is no data to update."). That is caused by the commit b5b6ddfa3fb8 ("Add additional empty checks after field protection.").

My problem is that \CodeIgniter\Entity::hasChanged() doesn't take into account model's $allowedFields. It means that if:
  • $allowedFields doesn't include one of fields used in Entity
  • [or] Entity has some extra properties set

then hasChanged() will return true BUT BaseModel will throw "There is no data to update."

Could we have some way of checking if BaseModel:Confusedave() / BaseModel::update() is safe to call (won't throw an exception)?

Example:
Code:
<?php
namespace App\Entities;

class Foo extends \CodeIgniter\Entity
{
    protected $casts = [
        'id' => 'integer',
        'name' => 'string',
    ];
}
Code:
<?php
namespace App\Models;

class FooModel extends \CodeIgniter\Model
{
    protected $table = 'foo';
    protected $primaryKey = 'id';
    protected $returnType = '\App\Entities\Foo';
    protected $allowedFields = [ 'name' ];
}
Code:
$fooModel = new \App\Models\FooModel();

$foo = $fooModel->find(1);

var_dump($foo->toArray());

/* Emulate POST data */
$post = [
    'name' => 'John',  /* <input type="text" name="name" /> */
    'agree' => '1',    /* <input type="checkbox" name="agree" value="1" required /> */
    'submit' => 'Save', /* <input type="submit" name="submit" value="Save" /> */
];
$foo->fill($post);

var_dump($foo->toArray());

if (!$foo->hasChanged()) {
    die('Nothing to change');
}

if (!$fooModel->save($foo)) {
    die('Failed to save');
}

die('Success');

Current result:
Code:
array(2) {
  ["id"]=>
  int(1)
  ["name"]=>
  string(4) "John"
}
array(4) {
  ["id"]=>
  int(1)
  ["name"]=>
  string(4) "John"
  ["agree"]=>
  string(1) "1"
  ["submit"]=>
  string(4) "Save"
}
Code:
CodeIgniter\Database\Exceptions\DataException
There is no data to update.
Reply
#2

$allowedFields has only name, and name is not changed.
So There is no data to update.
Reply
#3

(This post was last modified: 04-17-2022, 09:37 PM by rmilecki.)

(04-17-2022, 06:09 PM)kenjis Wrote: $allowedFields has only name, and name is not changed.
So There is no data to update.
You misunderstood me. I understand what and why happens. I'm looking for a solution.
  1. Entity doesn't know about $allowedFields so its ::hasChanged() is unreliable for this case
  2. Model doesn't have anything like ::hasChanged($entity)
It means that current there is no way to check if save() will have anything to update OR not (& will throw an exception).
Reply
#4

(This post was last modified: 04-17-2022, 11:12 PM by kenjis.)

> $allowedFields doesn't include one of fields used in Entity

Do you mean `agree` is a database field?
If so, and you want to save it, you need to add it in  $allowedFields.
Reply
#5

(04-17-2022, 11:12 PM)kenjis Wrote: > $allowedFields doesn't include one of fields used in Entity

Do you mean `agree` is a database field?
If so, and you want to save it, you need to add it in  $allowedFields.

No. Database table has only "id" and "name" fields.

Remaining POST data ("agree" & "submit") are some extra values that should be filtered out.
Reply
#6

Entity class is simply a class that represents a single database row.
You should fill only database fields.

Why do you need to fill "agree" & "submit"?
Reply
#7

(04-17-2022, 11:46 PM)kenjis Wrote: Entity class is simply a class that represents a single database row.
I know.

(04-17-2022, 11:46 PM)kenjis Wrote: You should fill only database fields.
That means I need to manually filter POST data. It means more code, more logic, some code duplication. Model already knows allowed fields and has filtering code. I'd expect to get some helper so I don't have to handle checking for changes on my own.

(04-17-2022, 11:46 PM)kenjis Wrote: Why do you need to fill "agree" & "submit"?

  1. I need to make sure user agreed with site rules
  2. My form has "Save" and "Delete" buttons so I need "submit" name for submit input and "remove" for the other.
  3. There are actually even more extra fields in my form as I also have "foo_bars" table. So my POST actually looks like:
    Code:
    [
        'name' => 'John',
        'bars' => [
            '0' => 'red',
            '1' => 'green',
            '2' => 'blue',
        ],
        'agree' => '1',
        'submit' => 'Save',
    ];
Reply
#8

Entity represents a single database row.
So I must say you should not fill properties that does not exist in database field.
And a form is not a database table.

You could write your own classes (Entity and/or Model) that meet your needs.
Reply
#9

(04-18-2022, 04:31 PM)kenjis Wrote: Entity represents a single database row.
So I must say you should not fill properties that does not exist in database field.
And a form is not a database table.
CodeIgniter's documentation says something different, please take a look at Using Entity Classes → Entity Usage → Filling Properties Quickly

Quote from manual:
Quote:when saving through the model, only the fields in $allowedFields will actually be saved to the database, so you can store additional data on your entities without worrying much about stray fields getting saved incorrectly

Does it mean documentation isn't accurate there?
Reply
#10

(This post was last modified: 04-19-2022, 03:35 AM by kenjis.)

I don't agree with the way of the user guide.
I think It is bad practice. I think you should fill only database fields.

But the description seems to be correct.

In your case, $allowedFields has only name, and name is not changed.
So there is no data to update, so stray fields are not saved.
Reply




Theme © iAndrew 2016 - Forum software by © MyBB