Search code examples
phpcakephpcakephp-3.0uuid

Saving records with a given uuid


I want to save a bunch of static records in my database with a given uuid, this is for testing purposes, so that on every system the application starts with the exact same dataset.

When inserting with SQL this is no problem but I wanted to use the CakePHP way ( I use a migrations file for this, but that does not matter).

The problem is that I give cake a data array like this and save it:

$data = [
    ['id' => '5cedf79a-e4b9-f235-3d4d-9fbeef41c7e8', 'name' => 'test'],
    ['id' => 'c2bf879c-072c-51a4-83d8-edbf2d97e07e', 'name' => 'test2']
];

$table = TableRegistry::get('My_Model');

$entities = $table->newEntities($data, [
        'accessibleFields' => ['*' => true],
        'validate' => false
    ]);

array_map([$table, 'save'], $entities );

Everything saves, but all my items have been given a different uuid, If I debug a record after saving it shows the original uuid in the entity

'new' => false,
'accessible' => [
    '*' => true
],    
'properties' => [
    'id' => '6b4524a8-4698-4297-84e5-5160f42f663b',
    'name' => 'test',
],
'dirty' => [],
'original' => [
    'id' => '5cedf79a-e4b9-f235-3d4d-9fbeef41c7e8'
],

So why does cake generate a new uuid for me? and how do I prevent it


Solution

  • This doesn't work because primary keys are unconditionally being generated before the insert operation, see

    https://github.com/cakephp/cakephp/blob/3.0.0/src/ORM/Table.php#L1486-L1490

    // ...
    
    $id = (array)$this->_newId($primary) + $keys;
    $primary = array_combine($primary, $id);
    $filteredKeys = array_filter($primary, 'strlen');
    $data = $filteredKeys + $data;
    
    // ...
    
    $statement = $this->query()->insert(array_keys($data))
      ->values($data)
      ->execute();
    
    // ...
    

    Currently the UUID type is the only type that implements generating IDs, so providing custom IDs works with other types.

    You can workaround this by for example overriding the _newId() method in your table so that it returns null, which effectively results in the existing primary key not being overwritten.

    protected function _newId($primary)
    {
        // maybe add some conditional logic here
        // in case you don't want to be required
        // to always manually provide a primary
        // key for your insert operations
    
        return null;
    }