Passed
Push — master ( 65aa29...bdaab5 )
by Thomas
13:33 queued 01:40
created

UuidExtension   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 131
Duplicated Lines 0 %

Importance

Changes 3
Bugs 0 Features 2
Metric Value
wmc 21
eloc 59
c 3
b 0
f 2
dl 0
loc 131
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
A updateCMSFields() 0 5 2
A UuidSegment() 0 13 3
A assignNewUuid() 0 16 3
A getByUuid() 0 27 6
A getUuidFormat() 0 14 5
A onBeforeWrite() 0 6 2
1
<?php
2
3
namespace LeKoala\Uuid;
4
5
use Ramsey\Uuid\Uuid;
6
use SilverStripe\ORM\DB;
7
use InvalidArgumentException;
8
use SilverStripe\Forms\FieldList;
9
use SilverStripe\Forms\ReadonlyField;
10
use SilverStripe\ORM\DataObject;
11
use Tuupola\Base62Proxy as Base62;
12
use SilverStripe\ORM\DataExtension;
13
use SilverStripe\ORM\DataObjectSchema;
14
15
class UuidExtension extends DataExtension
16
{
17
    const UUID_BINARY_FORMAT = 'binary';
18
    const UUID_STRING_FORMAT = 'string';
19
    const UUID_BASE62_FORMAT = 'base62';
20
21
    private static $db = [
0 ignored issues
show
introduced by
The private property $db is not used, and could be removed.
Loading history...
22
        "Uuid" => DBUuid::class,
23
    ];
24
    private static $indexes = [
0 ignored issues
show
introduced by
The private property $indexes is not used, and could be removed.
Loading history...
25
        "Uuid" => true,
26
    ];
27
28
    /**
29
     * Assign a new uuid to this record. This will overwrite any existing uuid.
30
     *
31
     * @param bool $check Check if the uuid is already taken
32
     * @return string The new uuid
33
     */
34
    public function assignNewUuid($check = true)
35
    {
36
        $uuid = Uuid::uuid4();
37
        if ($check) {
38
            $schema = DataObjectSchema::create();
39
            $table = $schema->tableForField(get_class($this->owner), 'Uuid');
40
            do {
41
                $this->owner->Uuid = $uuid->getBytes();
42
                // If we have something, keep checking
43
                $check = DB::prepared_query('SELECT count(ID) FROM ' . $table . ' WHERE Uuid = ?', [$this->owner->Uuid])->value() > 0;
44
            } while ($check);
45
        } else {
46
            $this->owner->Uuid = $uuid->getBytes();
47
        }
48
49
        return $this->owner->Uuid;
50
    }
51
52
    /**
53
     * Get a record by its uuid
54
     *
55
     * @param string $class The class
56
     * @param string $uuid The uuid value
57
     * @param string $format Any UUID_XXXX_FORMAT constant or string
58
     * @return DataObject|false The DataObject or false if no record is found or format invalid
59
     */
60
    public static function getByUuid($class, $value, $format = null)
61
    {
62
        // Guess format from value
63
        if ($format === null) {
64
            try {
65
                $format = self::getUuidFormat($value);
66
            } catch (InvalidArgumentException $ex) {
67
                $format = null;
68
            }
69
        }
70
        // Convert format to bytes for query
71
        switch ($format) {
72
            case self::UUID_BASE62_FORMAT:
73
                $uuid = Uuid::fromBytes(Base62::decode($value));
74
                break;
75
            case self::UUID_STRING_FORMAT:
76
                $uuid = Uuid::fromString($value);
77
                break;
78
            case self::UUID_BINARY_FORMAT:
79
                $uuid = Uuid::fromBytes($value);
80
                break;
81
            default:
82
                return false;
83
        }
84
        // Fetch the first record and disable subsite filter in a similar way as asking by ID
85
        $q = $class::get()->filter('Uuid', $uuid->getBytes())->setDataQueryParam('Subsite.filter', false);
86
        return $q->first();
87
    }
88
89
    /**
90
     * Guess uuid format based on strlen
91
     *
92
     * @param mixed $value
93
     * @return string
94
     * @throws InvalidArgumentException
95
     */
96
    public static function getUuidFormat($value)
97
    {
98
        $len = strlen((string)$value);
99
100
        if ($len == 36) {
101
            // d84560c8-134f-11e6-a1e2-34363bd26dae => 36 chars
102
            return self::UUID_STRING_FORMAT;
103
        } elseif ($len > 20 && $len < 24) {
104
            // 6a630O1jrtMjCrQDyG3D3O => 22 chars (in theory, because sometimes it's different)
105
            return self::UUID_BASE62_FORMAT;
106
        } elseif ($len == 16) {
107
            return self::UUID_BINARY_FORMAT;
108
        }
109
        throw new InvalidArgumentException("$value does not seem to be a valid uuid");
110
    }
111
112
    /**
113
     * Return a uuid suitable for an URL, like an URLSegment
114
     *
115
     * @return string
116
     */
117
    public function UuidSegment()
118
    {
119
        // assign on the fly
120
        if (!$this->owner->Uuid) {
121
            $uuid = $this->assignNewUuid();
122
            // Make a quick write without using orm
123
            if ($this->owner->ID) {
124
                $schema = new DataObjectSchema;
125
                $table = $schema->tableName(get_class($this->owner));
126
                DB::prepared_query("UPDATE $table SET Uuid = ? WHERE ID = ?", [$uuid, $this->owner->ID]);
127
            }
128
        }
129
        return $this->owner->dbObject('Uuid')->Base62();
130
    }
131
132
    public function updateCMSFields(FieldList $fields)
133
    {
134
        if (DBUuid::config()->show_cms_field) {
135
            $firstField = $fields->dataFieldNames()[0] ?? null;
136
            $fields->addFieldToTab('Root.Main', ReadonlyField::create('UuidNice', 'Uuid', $this->owner->dbObject('Uuid')->Nice()), $firstField);
137
        }
138
    }
139
140
    public function onBeforeWrite()
141
    {
142
        parent::onBeforeWrite();
143
144
        if (!$this->owner->Uuid) {
145
            $this->assignNewUuid();
146
        }
147
    }
148
}
149