Difference between #16 and #19 of
UUID instead of an auto-increment integer for ID with Active Record


Title unchanged

UUID instead of an auto-increment integer for ID with Active Record

Category unchanged


Yii version unchanged


Tags unchanged

mysql,active record,REST,UUID

Content changed

> I have a dream ... > I am happy to join with you today in what will go down in history as the > greatest demonstration of
 bad design of Active Record.

I have an API. It's built with a RESTful extension over Active Record, and some endpoints provide PUT methods to upload files. By a REST design we create an entity with `POST /video` first, and then upload a video file with `PUT /video/{id}/data`.
//check if value is a valid UUID
['id','match', 'pattern'=>'/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i'], // convert UUID from text value to binary and store the text value in a private variable,
// this is a workaround for lack of mapping in active record
['id','filter','skipOnError' => true, 'filter' => function($uuid) {
if ($this->idText === NULL && $this->getIsNewRecord()){
//the filter did not convert ID to binary yet, return the data from input
strtoupper($this->getAttribute('id')); } //ID is converted return strtoupper($this->idText ?? $this->getAttribute('id_text')); } ``` Active Record does not callWhen we call the `$model->id` property we need the `getter method if attributes contain the property. It should not be this way, so I return the default component behavior and make ID returned the right wayId()` executed. But Active Record base class overrides Yii compoent default behavior and does not call a getter method of an object if a property is a field in a table. So I override the magic getter. From the other hand, the firsta regexp valiator I wrote calls `$model->id`, triggering the getter before the UUID is saved to the private property so I ne. I check if the object is newly created to serve the text value from usor validator.
Note the `strtoupper()` call: client may send UUID in both upper and low cases, but aft
er iunput.
It is strange
acking from binary we will have a value in upper case. I received different string values before storing data to DB and after fetching it. Convert the textual UUID value to an upper or lower case everywhere to avoid problems.
It looks weird
to mutate data in a validator, but I found this is the onlybest way. I belive I shouldn't use `beforeSave()` callback to set the binary value for generating SQL, and return the text value back in `afterSave()` - supporting this code would be a classic hell like `#define true false;`.

Step 4. Define the mapping for output

Step 5. Use Object Relation Mapping in Yii 3 when it's available and write
simple mapping instead.mapping instead of these hacks.
P.S. A couple of helper functions.
namespace common\helpers;
class UUIDHelper
    const UUID_REGEXP = '/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i';
    public static function string2bin(string $uuid): string
        return pack("H*", str_replace('-', '', $uuid));
    public static function bin2string(string $binary): string
        return strtolower(join("-", unpack("H8time_low/H4time_mid/H4time_hi/H4clock_seq_hi/H12clock_seq_low", $binary)));
    public static function isUUID(string $uuid): bool
        return (bool)preg_match(self::UUID_REGEXP,$uuid);
4 0
Viewed: 22 495 times
Version: 2.0
Category: How-tos
Written by: grigori
Last updated by: samdark
Created on: Nov 25, 2019
Last updated: 7 months ago
Update Article


View all history