├── .github └── workflows │ └── php.yml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml.dist ├── src ├── Config │ ├── Services.php │ └── Uuid.php ├── Exceptions │ └── UuidModelException.php ├── Language │ └── en │ │ └── UuidModel.php ├── Uuid.php ├── UuidCast.php ├── UuidEntity.php └── UuidModel.php └── tests ├── UuidEntityTest.php ├── UuidModelTest.php ├── UuidTest.php └── _support ├── Database ├── Migrations │ └── 2020-05-13-062216_Uuid.php └── Seeds │ └── UuidSeeder.php ├── Project1Entity.php ├── Project1Model.php ├── Project2Entity.php └── Project2Model.php /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: PHP Tests 2 | 3 | on: 4 | push: 5 | branches: 6 | - develop 7 | pull_request: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | 13 | tests: 14 | runs-on: ubuntu-22.04 15 | if: "!contains(github.event.head_commit.message, '[ci skip]')" 16 | name: PHP ${{ matrix.php-versions }} 17 | 18 | services: 19 | mysql: 20 | image: mysql:5.7 21 | env: 22 | MYSQL_ALLOW_EMPTY_PASSWORD: yes 23 | MYSQL_DATABASE: codeigniter_uuid 24 | ports: 25 | - 3306 26 | options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 27 | 28 | strategy: 29 | matrix: 30 | php-versions: ['8.1', '8.2', '8.3'] 31 | 32 | steps: 33 | - name: Checkout 34 | uses: actions/checkout@v2 35 | 36 | - name: Setup PHP, with composer and extensions 37 | uses: shivammathur/setup-php@v2 38 | with: 39 | php-version: ${{ matrix.php-versions }} 40 | tools: composer, pecl 41 | coverage: xdebug 42 | env: 43 | COMPOSER_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | 45 | - name: Get composer cache directory 46 | id: composer-cache 47 | run: | 48 | echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT 49 | 50 | - name: Cache composer dependencies 51 | uses: actions/cache@v4 52 | with: 53 | path: ${{ steps.composer-cache.outputs.dir }} 54 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 55 | restore-keys: ${{ runner.os }}-composer- 56 | 57 | - name: Install dependencies 58 | run: composer update --ansi --no-interaction 59 | 60 | - name: Test with phpunit 61 | run: vendor/bin/phpunit --coverage-text 62 | env: 63 | database.tests.port: ${{ job.services.mysql.ports[3306] }} 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #------------------------- 2 | # Operating Specific Junk Files 3 | #------------------------- 4 | 5 | # OS X 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # OS X Thumbnails 11 | ._* 12 | 13 | # Windows image file caches 14 | Thumbs.db 15 | ehthumbs.db 16 | Desktop.ini 17 | 18 | # Recycle Bin used on file shares 19 | $RECYCLE.BIN/ 20 | 21 | # Windows Installer files 22 | *.cab 23 | *.msi 24 | *.msm 25 | *.msp 26 | 27 | # Windows shortcuts 28 | *.lnk 29 | 30 | # Linux 31 | *~ 32 | 33 | # KDE directory preferences 34 | .directory 35 | 36 | # Linux trash folder which might appear on any partition or disk 37 | .Trash-* 38 | 39 | #------------------------- 40 | # Environment Files 41 | #------------------------- 42 | # These should never be under version control, 43 | # as it poses a security risk. 44 | .env 45 | .vagrant 46 | Vagrantfile 47 | 48 | #------------------------- 49 | # Temporary Files 50 | #------------------------- 51 | writable/cache/* 52 | !writable/cache/index.html 53 | 54 | writable/logs/* 55 | !writable/logs/index.html 56 | 57 | writable/session/* 58 | !writable/session/index.html 59 | 60 | writable/uploads/* 61 | !writable/uploads/index.html 62 | 63 | writable/debugbar/* 64 | 65 | php_errors.log 66 | 67 | #------------------------- 68 | # Test Files 69 | #------------------------- 70 | tests/coverage* 71 | 72 | # Don't save phpunit under version control. 73 | phpunit 74 | 75 | #------------------------- 76 | # Composer 77 | #------------------------- 78 | vendor/ 79 | composer.lock 80 | 81 | #------------------------- 82 | # IDE / Development Files 83 | #------------------------- 84 | 85 | # Modules Testing 86 | _modules/* 87 | 88 | # phpenv local config 89 | .php-version 90 | 91 | # Jetbrains editors (PHPStorm, etc) 92 | .idea/ 93 | *.iml 94 | 95 | # Netbeans 96 | nbproject/ 97 | build/ 98 | nbbuild/ 99 | dist/ 100 | nbdist/ 101 | nbactions.xml 102 | nb-configuration.xml 103 | .nb-gradle/ 104 | 105 | # Sublime Text 106 | *.tmlanguage.cache 107 | *.tmPreferences.cache 108 | *.stTheme.cache 109 | *.sublime-workspace 110 | *.sublime-project 111 | .phpintel 112 | /api/ 113 | 114 | # Visual Studio Code 115 | .vscode/ 116 | 117 | /results/ 118 | /phpunit*.xml 119 | /.phpunit.*.cache 120 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## [Unreleased] 5 | 6 | ## [1.0.1] - 2021-05-03 7 | 8 | ### Fixed 9 | - Fixed inserting `null` value when UUID field should be translated into byte form. Thanks to [@yassinedoghri](https://github.com/yassinedoghri) 10 | 11 | ## [1.0.0] - 2021-04-24 12 | - Official release 13 | 14 | ### Changed 15 | - Due to changes to the CodeIgniter Model class, we updated methods we use as well. 16 | - Added `UuidCast` class. 17 | 18 | ## [1.0.0-beta3] - 2021-04-02 19 | - Third beta release 20 | 21 | ### Fixed 22 | - Due to fixes made to the CodeIgniter Database class, we had to fix the check for the query result. 23 | 24 | ## [1.0.0-beta2] - 2020-08-16 25 | - Second beta release 26 | 27 | ### Changed 28 | - Due to changes to the CodeIgniter Model class, we can now extend its model. 29 | - Added support for `useAutoIncrement` feature. 30 | 31 | ## [1.0.0-beta1] - 2020-05-10 32 | - First beta realease 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Michal Sniatala 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeIgniter 4 UUID [![PHP Tests](https://github.com/michalsn/codeigniter4-uuid/actions/workflows/php.yml/badge.svg)](https://github.com/michalsn/codeigniter4-uuid/actions/workflows/php.yml) 2 | This package make it easy to work with UUIDs in Codeigniter 4. It provide four classes to make that possible: `Uuid`, `UuidModel`, `UuidEntity` and `UuidCast`. This implementation is tighly coupled with [Ramsey\Uuid](https://github.com/ramsey/uuid). 3 | 4 | ## Installation via composer 5 | 6 | composer require michalsn/codeigniter4-uuid 7 | 8 | ## Manual installation 9 | 10 | Download this repo and then enable it by editing **app/Config/Autoload.php** and adding the **Michalsn\UuidModel** 11 | namespace to the **$psr4** array. For example, if you copied it into **app/ThirdParty**: 12 | 13 | ```php 14 | APPPATH . 'Config', 18 | APP_NAMESPACE => APPPATH, 19 | 'App' => APPPATH, 20 | 'Michalsn\Uuid' => APPPATH . 'ThirdParty/codeigniter4-uuid/src', 21 | ]; 22 | ``` 23 | 24 | ## Versions 25 | 26 | | CodeIgniter version | This package version | 27 | |---------------------|----------------------| 28 | | `>= 4.5` | `>= 1.1` | 29 | | `< 4.5` | `< 1.1` | 30 | 31 | ## How to use it 32 | 33 | In general, using `UuidModel` and `UuidEntity` is no much different than using the original classes provided with CodeIgniter 4 framework. We just have some additional config options. There is a good chance that you will not need to use `Uuid` class at all, because most of the things that happens are already automated. 34 | 35 | ### Uuid 36 | 37 | Working with `Uuid` class is really simple: 38 | 39 | ```php 40 | uuid4(); 45 | // will assign UUID4 as string 46 | $string = $uuid4->toString(); 47 | // will assign UUID4 as byte string 48 | $byte_string = $uuid4->getBytes(); 49 | ``` 50 | 51 | If you have any additional configuration options to set to a specific UUID version then you can do it via config file. 52 | 53 | ### UuidModel 54 | 55 | UUID fields are always returned as a `string` even if we store them in byte format in the database. This decision was made because of the convenience of use. We don't have to worry about field type or conversion of the data. 56 | 57 | Parameter | Default value | Description 58 | --------- | ------------- | ----------- 59 | `$uuidVersion` | `uuid4` | Defines the UUID version to use. 60 | `$uuidUseBytes` | `true` | Defines if the UUID should be stored in byte format in the database. This is recommended since will allow us to save over half the space. Also, it's quite easy to use, because we always translate UUID to a string form when retrieving the data or to a byte form when we are saving it. 61 | `$uuidFields` | `['id']` | Defines the fields that will be treated as UUID. By default we assume it will be a primary key, but it can be any field or fields you want. 62 | 63 | Now, let's see a simple example, how to use `UuidModel` in your code. In example below, there are no additional changes except that our model extends `UuidModel`. The primary key will be stored as UUID4 in the byte format in the database. 64 | 65 | ```php 66 | 'required|min_length[3]', 86 | 'description' => 'required', 87 | ]; 88 | } 89 | 90 | ``` 91 | 92 | Now, here is an example where we will use the UUID but not as a primary key. 93 | 94 | ```php 95 | 'required', 117 | 'name' => 'required|min_length[3]', 118 | 'description' => 'required', 119 | ]; 120 | } 121 | 122 | ``` 123 | 124 | ### UuidEntity 125 | 126 | Using the `UuidEntity` is only required if we store UUID fields in the byte format. In other case there are no benefits over original `Entity` class. The same as in the `UuidModel`, by default we assume that only primary key will have the UUID type. 127 | 128 | Parameter | Default value | Description 129 | --------- | ------------- | ----------- 130 | `$uuids` | `['id']` | Defines the fields that will be treated as UUID. By default we assume it will be a primary key, but it can be any field or fields you want. 131 | 132 | Now let's see a two examples which will match those for models that were previously shown. 133 | 134 | ```php 135 | null, 145 | 'name' => null, 146 | 'description' => null, 147 | 'created_at' => null, 148 | 'updated_at' => null, 149 | 'deleted_at' => null, 150 | ]; 151 | } 152 | ``` 153 | 154 | ```php 155 | null, 167 | 'category_id' => null, 168 | 'name' => null, 169 | 'description' => null, 170 | 'created_at' => null, 171 | 'updated_at' => null, 172 | 'deleted_at' => null, 173 | ]; 174 | } 175 | ``` 176 | 177 | And that pretty much it. No more changes are needed. 178 | 179 | ### UuidCast 180 | 181 | **NOTE: Don't use this casting class if you intend to use the `UuidModel` class.** 182 | 183 | Since CodeIgniter now allows the use of custom cast classes in Entities, we've also added our own casting class. This casting class enables the UUID to be converted from byte to string during data retrieval and reverse on data setting. Normally you will not use the cast function as the transition from strings to bytes is done in the `UuidModel` class. With that in mind, you should only use this cast feature when you want to work with the Entity without using `UuidModel` class later. 184 | 185 | This is a simple example of how we would use our casting class with Entity. 186 | 187 | ```php 188 | 'uuid', 196 | ]; 197 | 198 | protected $castHandlers = [ 199 | 'uuid' => 'Michalsn\Uuid\UuidCast', 200 | ]; 201 | } 202 | ``` 203 | 204 | ## Limitations 205 | 206 | For now this class doesn't support SQLite3 database when you want to strore UUIDs in a byte format. 207 | 208 | ## Supported UUID versions 209 | 210 | * Version 1: Time-based - `uuid1` 211 | * Version 2: DCE Security - `uuid2` 212 | * Version 3: Name-based (MD5) - `uuid3` 213 | * Version 4: Random - `uuid4` 214 | * Version 5: Name-based (SHA-1) - `uuid5` 215 | * Version 6: Ordered-Time - `uuid6` 216 | * Version 7: Ordered-Time Random - `uuid7` 217 | 218 | ## License 219 | 220 | The MIT License (MIT). Please see [License File](LICENSE) for more information. 221 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "michalsn/codeigniter4-uuid", 3 | "description": "UUID package for CodeIgniter 4 with support for Model and Entity.", 4 | "keywords": [ 5 | "codeigniter4", "uuid", "model", "entity" 6 | ], 7 | "homepage": "https://github.com/michalsn/codeigniter4-uuid", 8 | "authors": [ 9 | { 10 | "name": "michalsn", 11 | "homepage": "https://github.com/michalsn", 12 | "role": "Developer" 13 | } 14 | ], 15 | "license": "MIT", 16 | "repositories": [ 17 | { 18 | "type": "vcs", 19 | "url": "https://github.com/codeigniter4/CodeIgniter4" 20 | } 21 | ], 22 | "require": { 23 | "php": ">=8.1", 24 | "ramsey/uuid": "^4.7" 25 | }, 26 | "minimum-stability": "dev", 27 | "require-dev": { 28 | "phpunit/phpunit" : "8.5.*", 29 | "codeigniter4/codeigniter4": "dev-develop" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "Michalsn\\Uuid\\": "src" 34 | } 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "Tests\\Support\\": "tests/_support" 39 | } 40 | }, 41 | "scripts": { 42 | "test": "vendor/bin/phpunit", 43 | "post-update-cmd": [ 44 | "@composer dump-autoload" 45 | ] 46 | } 47 | } -------------------------------------------------------------------------------- /phpunit.xml.dist: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests 15 | 16 | 17 | 18 | 19 | 20 | ./src 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /src/Config/Services.php: -------------------------------------------------------------------------------- 1 | null, 20 | 'clockSequence' => null 21 | ]; 22 | 23 | //-------------------------------------------------------------------- 24 | // UUID version 2 optional config 25 | //-------------------------------------------------------------------- 26 | // See more: https://uuid.ramsey.dev/en/latest/rfc4122/version2.html 27 | 28 | public $uuid2 = [ 29 | 'localDomain' => null, 30 | 'localIdentifier' => null, 31 | 'nodeProvider' => null, 32 | 'clockSequence' => null 33 | ]; 34 | 35 | //-------------------------------------------------------------------- 36 | // UUID version 3 optional config 37 | //-------------------------------------------------------------------- 38 | // See more: https://uuid.ramsey.dev/en/latest/rfc4122/version3.html 39 | 40 | public $uuid3 = [ 41 | 'ns' => null, 42 | 'name' => null 43 | ]; 44 | 45 | //-------------------------------------------------------------------- 46 | // UUID version 5 optional config 47 | //-------------------------------------------------------------------- 48 | // See more: https://uuid.ramsey.dev/en/latest/rfc4122/version5.html 49 | 50 | public $uuid5 = [ 51 | 'ns' => null, 52 | 'name' => null 53 | ]; 54 | 55 | //-------------------------------------------------------------------- 56 | // UUID version 6 optional config 57 | //-------------------------------------------------------------------- 58 | // See more: https://uuid.ramsey.dev/en/stable/rfc4122/version6.html 59 | 60 | public $uuid6 = [ 61 | 'nodeProvider' => null, 62 | 'clockSequence' => null 63 | ]; 64 | } -------------------------------------------------------------------------------- /src/Exceptions/UuidModelException.php: -------------------------------------------------------------------------------- 1 | 'The "{0}" is incorrect UUID version.', 6 | 'incorrectValueInUuidFields' => 'The primary key "{0}"" should not be in the "uuidFields" list if you are not using the auto-increment feature.', 7 | ]; -------------------------------------------------------------------------------- /src/Uuid.php: -------------------------------------------------------------------------------- 1 | config = $config; 32 | } 33 | 34 | /** 35 | * UUID Version 1 36 | * 37 | * @param Hexadecimal|int|string|null $nodeProvider A 48-bit number representing the hardware address; this number may be represented as an integer or a hexadecimal string 38 | * @param int $clockSequence A 14-bit number used to help avoid duplicates that could arise when the clock is set backwards in time or if the node ID changes 39 | * 40 | * @return \Ramsey\Uuid\UuidInterface 41 | */ 42 | public function uuid1($nodeProvider = null, ?int $clockSequence = null) 43 | { 44 | return RamseyUuid::uuid1( 45 | $nodeProvider ?? $this->config->uuid1['nodeProvider'], 46 | $clockSequence ?? $this->config->uuid1['clockSequence'] 47 | ); 48 | } 49 | 50 | /** 51 | * UUID Version 2 52 | * 53 | * @param int $localDomain The local domain to use when generating bytes, according to DCE Security 54 | * @param IntegerObject|null $localIdentifier The local identifier for the given domain; this may be a UID or GID on POSIX systems, if the local domain is person or group, or it may be a site-defined identifier if the local domain is org 55 | * @param Hexadecimal|null $node A 48-bit number representing the hardware address 56 | * @param int|null $clockSequence A 14-bit number used to help avoid duplicates that could arise when the clock is set backwards in time or if the node ID changes (in a version 2 UUID, the lower 8 bits of this number are replaced with the domain). 57 | * 58 | * @return \Ramsey\Uuid\UuidInterface 59 | */ 60 | public function uuid2( 61 | int $localDomain, 62 | ?IntegerObject $localIdentifier = null, 63 | ?Hexadecimal $nodeProvider = null, 64 | ?int $clockSequence = null) 65 | { 66 | return RamseyUuid::uuid2( 67 | $localDomain ?? $this->config->uuid2['localDomain'], 68 | $localIdentifier ?? $this->config->uuid2['localIdentifier'], 69 | $nodeProvider ?? $this->config->uuid2['nodeProvider'], 70 | $clockSequence ?? $this->config->uuid2['clockSequence'] 71 | ); 72 | } 73 | 74 | /** 75 | * UUID Version 3 76 | * 77 | * @param string|UuidInterface $ns The namespace (must be a valid UUID) 78 | * @param string $name The name to use for creating a UUID 79 | * 80 | * @return \Ramsey\Uuid\UuidInterface 81 | */ 82 | public function uuid3($ns, string $name) 83 | { 84 | return RamseyUuid::uuid3( 85 | $ns ?? $this->config->uuid3['ns'], 86 | $name ?? $this->config->uuid3['name'] 87 | ); 88 | } 89 | 90 | /** 91 | * UUID Version 4 92 | * 93 | * @return \Ramsey\Uuid\UuidInterface 94 | */ 95 | public function uuid4() 96 | { 97 | return RamseyUuid::uuid4(); 98 | } 99 | 100 | /** 101 | * UUID Version 5 102 | * 103 | * @param string|UuidInterface $ns The namespace (must be a valid UUID) 104 | * @param string $name The name to use for creating a UUID 105 | * 106 | * @return \Ramsey\Uuid\UuidInterface 107 | */ 108 | public function uuid5($ns, string $name) 109 | { 110 | return RamseyUuid::uuid5( 111 | $ns ?? $this->config->uuid5['ns'], 112 | $name ?? $this->config->uuid5['name'] 113 | ); 114 | } 115 | 116 | /** 117 | * UUID Version 6 118 | * 119 | * @param Hexadecimal|null $node A 48-bit number representing the hardware address 120 | * @param int $clockSequence A 14-bit number used to help avoid duplicates that could arise when the clock is set backwards in time or if the node ID changes 121 | * 122 | * @return \Ramsey\Uuid\UuidInterface 123 | */ 124 | public function uuid6( 125 | ?Hexadecimal $nodeProvider = null, 126 | ?int $clockSequence = null 127 | ) 128 | { 129 | return RamseyUuid::uuid6( 130 | $nodeProvider ?? $this->config->uuid6['nodeProvider'], 131 | $clockSequence ?? $this->config->uuid6['clockSequence'] 132 | ); 133 | } 134 | 135 | /** 136 | * UUID Version 7 137 | * 138 | * @param \DateTimeImmutable|null $dateTimeImmutable To use an existing date and time to generate a version 7 UUID, you may pass a \DateTimeInterface instance to the uuid7() method. 139 | * 140 | * @return \Ramsey\Uuid\UuidInterface 141 | */ 142 | public function uuid7(?\DateTimeImmutable $dateTimeImmutable = null) : \Ramsey\Uuid\UuidInterface 143 | { 144 | return RamseyUuid::uuid7($dateTimeImmutable); 145 | } 146 | 147 | /** 148 | * From string to UUID object 149 | * 150 | * @param string $uuid 151 | * 152 | * @return \Ramsey\Uuid\UuidInterface 153 | */ 154 | public function fromString(string $uuid) 155 | { 156 | return RamseyUuid::fromString($uuid); 157 | } 158 | 159 | /** 160 | * From byte string to UUID object 161 | * 162 | * @param string $bytes 163 | * 164 | * @return \Ramsey\Uuid\UuidInterface 165 | */ 166 | public function fromBytes(string $bytes) 167 | { 168 | return RamseyUuid::fromBytes($bytes); 169 | } 170 | 171 | /** 172 | * From 128-bit integer string to UUID object 173 | * 174 | * @param string $integer 175 | * 176 | * @return \Ramsey\Uuid\UuidInterface 177 | */ 178 | public function fromInteger(string $integer) 179 | { 180 | return RamseyUuid::fromInteger($integer); 181 | } 182 | 183 | /** 184 | * Creates a UUID from a DateTimeInterface instance 185 | * 186 | * @return \Ramsey\Uuid\UuidInterface 187 | */ 188 | public function fromDateTime( 189 | DateTimeInterface $dateTime, 190 | ?Hexadecimal $nodeProvider = null, 191 | ?int $clockSequence = null 192 | ) 193 | { 194 | return RamseyUuid::fromDateTime($dateTime, $nodeProvider, $clockSequence); 195 | } 196 | 197 | /** 198 | * Is valid UUID 199 | * 200 | * @param string $uuid 201 | * 202 | * @return bool 203 | */ 204 | public function isValid(string $uuid) 205 | { 206 | return RamseyUuid::isValid($uuid); 207 | } 208 | } -------------------------------------------------------------------------------- /src/UuidCast.php: -------------------------------------------------------------------------------- 1 | fromBytes($value)->toString(); 22 | } 23 | 24 | return $value; 25 | } 26 | 27 | /** 28 | * Set 29 | * 30 | * @param mixed $value Data 31 | * @param array $params Additional param 32 | * 33 | * @return mixed 34 | */ 35 | public static function set($value, array $params = []) 36 | { 37 | if (ctype_print($value)) 38 | { 39 | $value = service('uuid')->fromString($value)->getBytes(); 40 | } 41 | 42 | return $value; 43 | } 44 | } -------------------------------------------------------------------------------- /src/UuidEntity.php: -------------------------------------------------------------------------------- 1 | original = $this->attributes; 27 | 28 | if (! empty($this->uuids) && ! empty($this->attributes)) 29 | { 30 | // Load Uuid service 31 | $uuidObj = service('uuid'); 32 | 33 | // Loop through the UUID array fields 34 | foreach ($this->uuids as $uuid) 35 | { 36 | // Check if field is in byte format 37 | if (isset($this->attributes[$uuid]) && ! ctype_print($this->attributes[$uuid])) 38 | { 39 | $this->original[$uuid] = $uuidObj->fromBytes($this->attributes[$uuid])->toString(); 40 | } 41 | } 42 | } 43 | 44 | return $this; 45 | } 46 | 47 | /** 48 | * Magic method to all protected/private class properties to be easily set, 49 | * either through a direct access or a `setCamelCasedProperty()` method. 50 | * 51 | * Examples: 52 | * 53 | * $this->my_property = $p; 54 | * $this->setMyProperty() = $p; 55 | * 56 | * @param string $key 57 | * @param null $value 58 | * 59 | * @return $this 60 | * @throws \Exception 61 | */ 62 | public function __set(string $key, $value = null) 63 | { 64 | // Check if field is uuid field and in byte format 65 | if (! empty($this->uuids) && in_array($key, $this->uuids) && ! ctype_print($value)) 66 | { 67 | $value = service('uuid')->fromBytes($value)->toString(); 68 | } 69 | 70 | return parent::__set($key, $value); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/UuidModel.php: -------------------------------------------------------------------------------- 1 | uuidVersion, config('Uuid')->supportedVersions)) 58 | { 59 | throw UuidModelException::forIncorrectUuidVersion($this->uuidVersion); 60 | } 61 | 62 | // When we are not using auto-increment feature, 63 | // the primary key shouldn't be in the uuidFields list 64 | if (! $this->useAutoIncrement && in_array($this->primaryKey, $this->uuidFields)) 65 | { 66 | throw UuidModelException::forIncorrectValueInUuidFields($this->primaryKey); 67 | } 68 | 69 | // Load Uuid service 70 | $this->uuid = service('uuid'); 71 | 72 | parent::__construct($db, $validation); 73 | } 74 | 75 | //-------------------------------------------------------------------- 76 | // UUID HELPER METHODS 77 | //-------------------------------------------------------------------- 78 | 79 | /** 80 | * Prepare UUID results - transform if needed. 81 | * 82 | * @param array|object $row Row 83 | * @param string $returnType Return type 84 | * 85 | * @return void; 86 | */ 87 | protected function convertUuidFieldsToStrings($results, string $returnType = 'array') 88 | { 89 | if (empty($this->uuidFields) || $this->uuidUseBytes === false) 90 | { 91 | return $results; 92 | } 93 | 94 | if (is_object($results) || empty($results[0]))//! is_array($results)) 95 | { 96 | return $this->convertUuidFieldToString($results, $returnType); 97 | } 98 | 99 | foreach ($results as &$row) 100 | { 101 | $row = $this->convertUuidFieldToString($row, $returnType); 102 | } 103 | 104 | return $results; 105 | } 106 | 107 | /** 108 | * Prepare UUID row - transform if needed. 109 | * 110 | * @param array|object $row Row 111 | * @param string $returnType Return type 112 | * 113 | * @return void; 114 | */ 115 | protected function convertUuidFieldToString($row, string $returnType = 'array') 116 | { 117 | if (empty($this->uuidFields) || $this->uuidUseBytes === false) 118 | { 119 | return $row; 120 | } 121 | 122 | foreach ($this->uuidFields as $field) 123 | { 124 | if ($returnType === 'array') 125 | { 126 | if (empty($row[$field])) 127 | { 128 | continue; 129 | } 130 | 131 | $row[$field] = $this->uuid->fromBytes($row[$field])->toString(); 132 | } 133 | else 134 | { 135 | if (empty($row->{$field})) 136 | { 137 | continue; 138 | } 139 | 140 | $row->{$field} = $this->uuid->fromBytes($row->{$field})->toString(); 141 | } 142 | } 143 | 144 | return $row; 145 | } 146 | 147 | /** 148 | * Convert UUID primary key to bytes if needed 149 | * 150 | * @param array|string $key Key or array of keys to convert 151 | * 152 | * @return array|string 153 | */ 154 | protected function convertUuidPrimaryKeyToBytes($key = null) 155 | { 156 | if (! in_array($this->primaryKey, $this->uuidFields) || $this->uuidUseBytes === false) 157 | { 158 | return $key; 159 | } 160 | 161 | if (is_array($key)) 162 | { 163 | foreach ($key as &$val) 164 | { 165 | $val = $this->uuid->fromString($val)->getBytes(); 166 | } 167 | } 168 | elseif (! empty($key)) 169 | { 170 | $key = $this->uuid->fromString($key)->getBytes(); 171 | } 172 | 173 | return $key; 174 | } 175 | 176 | /** 177 | * Convert UUID to bytes if needed 178 | * 179 | * @param array $results Result array to convert 180 | * 181 | * @return array|string|null 182 | */ 183 | protected function convertUuidFieldsToBytes($results) 184 | { 185 | if (empty($this->uuidFields) || $this->uuidUseBytes === false) 186 | { 187 | return $results; 188 | } 189 | 190 | if (empty($results[0])) 191 | { 192 | return $this->convertUuidFieldToByte($results); 193 | } 194 | 195 | foreach ($results as &$row) 196 | { 197 | $row = $this->convertUuidFieldToByte($row); 198 | } 199 | 200 | return $results; 201 | } 202 | 203 | /** 204 | * Convert UUID to bytes if needed 205 | * 206 | * @param array $row Row array to convert 207 | * 208 | * @return array|string|null 209 | */ 210 | protected function convertUuidFieldToByte($row) 211 | { 212 | if (empty($this->uuidFields) || $this->uuidUseBytes === false) 213 | { 214 | return $row; 215 | } 216 | 217 | foreach ($this->uuidFields as $field) 218 | { 219 | if (empty($row[$field])) 220 | { 221 | continue; 222 | } 223 | 224 | $row[$field] = $this->uuid->fromString($row[$field])->getBytes(); 225 | } 226 | 227 | return $row; 228 | } 229 | 230 | //-------------------------------------------------------------------- 231 | // CRUD & FINDERS 232 | //-------------------------------------------------------------------- 233 | 234 | /** 235 | * Fetches the row of database from $this->table with a primary key 236 | * matching $id. This methods works only with dbCalls 237 | * This methods works only with dbCalls 238 | * 239 | * @param boolean $singleton Single or multiple results 240 | * @param array|integer|string|null $id One primary key or an array of primary keys 241 | * 242 | * @return array|object|null The resulting row of data, or null. 243 | */ 244 | protected function doFind(bool $singleton, $id = null) 245 | { 246 | // Convert UUID fields to byte if needed 247 | $id = $this->convertUuidPrimaryKeyToBytes($id); 248 | 249 | $result = parent::doFind($singleton, $id); 250 | // Convert UUID fields from byte if needed 251 | $result = $this->convertUuidFieldsToStrings($result, $this->tempReturnType); 252 | 253 | return $result; 254 | } 255 | 256 | /** 257 | * Fetches the column of database from $this->table 258 | * This methods works only with dbCalls 259 | * 260 | * @param string $columnName Column Name 261 | * 262 | * @return array|null The resulting row of data, or null if no data found. 263 | */ 264 | protected function doFindColumn(string $columnName) 265 | { 266 | $result = parent::doFindColumn($columnName); 267 | 268 | // Convert UUID fields from byte if needed 269 | if (in_array($columnName, $this->uuidFields) && $this->uuidUseBytes === true) 270 | { 271 | $result = $this->convertUuidFieldsToStrings($result, 'array'); 272 | } 273 | 274 | return $result; 275 | } 276 | 277 | /** 278 | * Works with the current Query Builder instance to return 279 | * all results, while optionally limiting them. 280 | * This methods works only with dbCalls 281 | * 282 | * @param integer $limit Limit 283 | * @param integer $offset Offset 284 | * 285 | * @return array 286 | */ 287 | protected function doFindAll(int $limit = null, int $offset = 0) 288 | { 289 | $result = parent::doFindAll($limit, $offset); 290 | // Convert UUID fields from byte if needed 291 | $result = $this->convertUuidFieldsToStrings($result, $this->tempReturnType); 292 | 293 | return $result; 294 | } 295 | 296 | /** 297 | * Returns the first row of the result set. Will take any previous 298 | * Query Builder calls into account when determining the result set. 299 | * This methods works only with dbCalls 300 | * 301 | * @return array|object|null 302 | */ 303 | protected function doFirst() 304 | { 305 | $builder = $this->builder(); 306 | 307 | if ($this->tempUseSoftDeletes) 308 | { 309 | $builder->where($this->table . '.' . $this->deletedField, null); 310 | } 311 | elseif ($this->useSoftDeletes && empty($builder->QBGroupBy) && $this->primaryKey) 312 | { 313 | $builder->groupBy($this->table . '.' . $this->primaryKey); 314 | } 315 | 316 | // Search when UUID6 or UUID7 is used as primary key 317 | if (empty($builder->QBOrderBy) && in_array($this->primaryKey, $this->uuidFields) && $this->uuidUseBytes === false && in_array($this->uuidVersion, ['uuid6', 'uuid7'])) 318 | { 319 | $builder->orderBy($this->table . '.' . $this->primaryKey, 'asc'); 320 | } 321 | // Search when other UUID is used as a primary key 322 | elseif (empty($builder->QBOrderBy) && in_array($this->primaryKey, $this->uuidFields) && $this->useTimestamps === true) 323 | { 324 | $builder->orderBy($this->table . '.' . $this->createdField, 'asc'); 325 | } 326 | // Some databases, like PostgreSQL, need order 327 | // information to consistently return correct results. 328 | elseif ($builder->QBGroupBy && empty($builder->QBOrderBy) && $this->primaryKey) 329 | { 330 | $builder->orderBy($this->table . '.' . $this->primaryKey, 'asc'); 331 | } 332 | 333 | $result = $builder->limit(1, 0)->get()->getFirstRow($this->tempReturnType); 334 | 335 | // Convert UUID fields from byte if needed 336 | return $this->convertUuidFieldsToStrings($result, $this->tempReturnType); 337 | } 338 | 339 | /** 340 | * Inserts data into the current table. 341 | * This methods works only with dbCalls 342 | * 343 | * @param array $data Data 344 | * 345 | * @return Query|boolean 346 | */ 347 | protected function doInsert(array $data) 348 | { 349 | $escape = $this->escape; 350 | $this->escape = []; 351 | 352 | // Require non empty primaryKey when 353 | // not using auto-increment feature 354 | if (! $this->useAutoIncrement && empty($data[$this->primaryKey])) 355 | { 356 | throw DataException::forEmptyPrimaryKey('insert'); 357 | } 358 | 359 | $builder = $this->builder(); 360 | 361 | if (! empty($this->uuidFields)) 362 | { 363 | if (in_array($this->primaryKey, $this->uuidFields) && empty($data[$this->primaryKey])) 364 | { 365 | $primaryVal = $this->uuid->{$this->uuidVersion}(); 366 | 367 | if ($this->uuidUseBytes === true) 368 | { 369 | $builder->set($this->primaryKey, $primaryVal->getBytes()); 370 | } 371 | else 372 | { 373 | $builder->set($this->primaryKey, $primaryVal->toString()); 374 | } 375 | } 376 | } 377 | 378 | // Must use the set() method to ensure to set the correct escape flag 379 | foreach ($data as $key => $val) 380 | { 381 | // Convert UUID fields if needed 382 | if ($val && in_array($key, $this->uuidFields) && $this->uuidUseBytes === true) 383 | { 384 | $val = ($this->uuid->fromString($val))->getBytes(); 385 | } 386 | 387 | $builder->set($key, $val, $escape[$key] ?? null); 388 | } 389 | 390 | $result = $builder->insert(); 391 | 392 | // If insertion succeeded then save the insert ID 393 | if ($result) 394 | { 395 | if (! $this->useAutoIncrement) 396 | { 397 | $this->insertID = $data[$this->primaryKey]; 398 | } 399 | else 400 | { 401 | if (in_array($this->primaryKey, $this->uuidFields)) 402 | { 403 | $this->insertID = empty($primaryVal) ? $data[$this->primaryKey] : $primaryVal->toString(); 404 | } 405 | else 406 | { 407 | $this->insertID = $this->db->insertID(); 408 | } 409 | } 410 | } 411 | 412 | return $result; 413 | } 414 | 415 | /** 416 | * Compiles batch insert strings and runs the queries, validating each row prior. 417 | * This methods works only with dbCalls 418 | * 419 | * @param array|null $set An associative array of insert values 420 | * @param boolean|null $escape Whether to escape values and identifiers 421 | * @param integer $batchSize The size of the batch to run 422 | * @param boolean $testing True means only number of records is returned, false will execute the query 423 | * 424 | * @return integer|boolean Number of rows inserted or FALSE on failure 425 | */ 426 | protected function doInsertBatch(?array $set = null, ?bool $escape = null, int $batchSize = 100, bool $testing = false) 427 | { 428 | if (is_array($set)) 429 | { 430 | foreach ($set as &$row) 431 | { 432 | // Require non empty primaryKey when 433 | // not using auto-increment feature 434 | if (! $this->useAutoIncrement && empty($row[$this->primaryKey])) 435 | { 436 | throw DataException::forEmptyPrimaryKey('insertBatch'); 437 | } 438 | 439 | // Add primary key and convert to bytes if needed 440 | if (! empty($this->uuidFields)) 441 | { 442 | foreach ($this->uuidFields as $field) 443 | { 444 | if ($field === $this->primaryKey) 445 | { 446 | if ($this->uuidUseBytes === true) 447 | { 448 | $row[$field] = ($this->uuid->{$this->uuidVersion}())->getBytes(); 449 | } 450 | else 451 | { 452 | $row[$field] = ($this->uuid->{$this->uuidVersion}())->toString(); 453 | } 454 | } 455 | else 456 | { 457 | if ($this->uuidUseBytes === true && ! empty($row[$field])) 458 | { 459 | $row[$field] = ($this->uuid->fromString($row[$field]))->getBytes(); 460 | } 461 | } 462 | } 463 | } 464 | } 465 | } 466 | 467 | return $this->builder()->testMode($testing)->insertBatch($set, $escape, $batchSize); 468 | } 469 | 470 | /** 471 | * Updates a single record in $this->table. 472 | * This methods works only with dbCalls 473 | * 474 | * @param integer|array|string|null $id ID 475 | * @param array|null $data Data 476 | * 477 | * @return boolean 478 | */ 479 | protected function doUpdate($id = null, $data = null): bool 480 | { 481 | $escape = $this->escape; 482 | $this->escape = []; 483 | 484 | $builder = $this->builder(); 485 | 486 | if ($id) 487 | { 488 | $id = $this->convertUuidPrimaryKeyToBytes($id); 489 | $builder = $builder->whereIn($this->table . '.' . $this->primaryKey, $id); 490 | } 491 | 492 | // Must use the set() method to ensure to set the correct escape flag 493 | foreach ($data as $key => $val) 494 | { 495 | // Convert UUID fields if needed 496 | if ($val && in_array($key, $this->uuidFields) && $this->uuidUseBytes === true) 497 | { 498 | $val = ($this->uuid->fromString($val))->getBytes(); 499 | } 500 | 501 | $builder->set($key, $val, $escape[$key] ?? null); 502 | } 503 | 504 | return $builder->update(); 505 | } 506 | 507 | /** 508 | * Compiles an update string and runs the query 509 | * This methods works only with dbCalls 510 | * 511 | * @param array|null $set An associative array of update values 512 | * @param string|null $index The where key 513 | * @param integer $batchSize The size of the batch to run 514 | * @param boolean $returnSQL True means SQL is returned, false will execute the query 515 | * 516 | * @return mixed Number of rows affected or FALSE on failure 517 | * 518 | * @throws DatabaseException 519 | */ 520 | protected function doUpdateBatch(array $set = null, string $index = null, int $batchSize = 100, bool $returnSQL = false) 521 | { 522 | foreach ($set as &$row) 523 | { 524 | // Convert UUID fields if needed 525 | if (! empty($this->uuidFields) && $this->uuidUseBytes === true) 526 | { 527 | foreach ($this->uuidFields as $field) 528 | { 529 | if (! empty($row[$field])) 530 | { 531 | $row[$field] = ($this->uuid->fromString($row[$field]))->getBytes(); 532 | } 533 | } 534 | } 535 | } 536 | 537 | return $this->builder()->testMode($returnSQL)->updateBatch($set, $index, $batchSize); 538 | } 539 | 540 | /** 541 | * Deletes a single record from $this->table where $id matches 542 | * the table's primaryKey 543 | * This methods works only with dbCalls 544 | * 545 | * @param integer|string|array|null $id The rows primary key(s) 546 | * @param boolean $purge Allows overriding the soft deletes setting. 547 | * 548 | * @return string|boolean 549 | * 550 | * @throws DatabaseException 551 | */ 552 | protected function doDelete($id = null, bool $purge = false) 553 | { 554 | // Convert UUID pk to byte if needed 555 | $id = $this->convertUuidPrimaryKeyToBytes($id); 556 | return parent::doDelete($id, $purge); 557 | } 558 | 559 | } -------------------------------------------------------------------------------- /tests/UuidEntityTest.php: -------------------------------------------------------------------------------- 1 | uuid = new Uuid($config); 17 | } 18 | 19 | public function testFilledConstruction() 20 | { 21 | $data = [ 22 | 'id' => $this->uuid->fromString('bb0ed7fc-888d-11ea-b597-0800273b4cc5')->getBytes(), 23 | 'name' => 'Sample name', 24 | 'description' => 'Sample description', 25 | ]; 26 | 27 | $p1 = new Project1Entity($data); 28 | $this->assertEquals('bb0ed7fc-888d-11ea-b597-0800273b4cc5', $p1->id); 29 | $this->assertEquals('Sample name', $p1->name); 30 | $this->assertEquals('Sample description', $p1->description); 31 | 32 | $this->assertTrue($p1->hasChanged('id')); 33 | $this->assertTrue($p1->hasChanged('name')); 34 | $this->assertTrue($p1->hasChanged('description')); 35 | } 36 | 37 | public function testFillWithSyncOriginal() 38 | { 39 | $p1 = new Project1Entity(); 40 | 41 | $data = [ 42 | 'id' => $this->uuid->fromString('bb0ed7fc-888d-11ea-b597-0800273b4cc5')->getBytes(), 43 | 'name' => 'Sample name', 44 | 'description' => 'Sample description', 45 | ]; 46 | 47 | $p1->fill($data)->syncOriginal(); 48 | 49 | $this->assertEquals('bb0ed7fc-888d-11ea-b597-0800273b4cc5', $p1->id); 50 | $this->assertEquals('Sample name', $p1->name); 51 | $this->assertEquals('Sample description', $p1->description); 52 | 53 | $this->assertFalse($p1->hasChanged('id')); 54 | $this->assertFalse($p1->hasChanged('name')); 55 | $this->assertFalse($p1->hasChanged('description')); 56 | } 57 | 58 | public function testFilledConstructionWithReadyData() 59 | { 60 | $data = [ 61 | 'id' => 'bb0ed7fc-888d-11ea-b597-0800273b4cc5', 62 | 'name' => 'Sample name', 63 | 'description' => 'Sample description', 64 | ]; 65 | 66 | $p1 = new Project1Entity($data); 67 | $this->assertEquals('bb0ed7fc-888d-11ea-b597-0800273b4cc5', $p1->id); 68 | $this->assertEquals('Sample name', $p1->name); 69 | $this->assertEquals('Sample description', $p1->description); 70 | } 71 | 72 | public function testNonDefaultUuidField() 73 | { 74 | $data = [ 75 | 'id' => 1, 76 | 'category_id' => $this->uuid->fromString('bb0ed7fc-888d-11ea-b597-0800273b4cc5')->getBytes(), 77 | 'name' => 'Sample name', 78 | 'description' => 'Sample description', 79 | ]; 80 | 81 | $p2 = new Project2Entity($data); 82 | $this->assertEquals(1, $p2->id); 83 | $this->assertEquals('bb0ed7fc-888d-11ea-b597-0800273b4cc5', $p2->category_id); 84 | $this->assertEquals('Sample name', $p2->name); 85 | $this->assertEquals('Sample description', $p2->description); 86 | } 87 | } -------------------------------------------------------------------------------- /tests/UuidModelTest.php: -------------------------------------------------------------------------------- 1 | 'Sample name', 25 | 'description' => 'Sample description', 26 | ]; 27 | 28 | $projectId = $projectModel->insert($data); 29 | 30 | $result = $projectModel->find($projectId); 31 | unset($result['created_at'], $result['updated_at'], $result['deleted_at']); 32 | 33 | $expected = [ 34 | 'id' => $projectId, 35 | 'name' => $data['name'], 36 | 'description' => $data['description'], 37 | ]; 38 | 39 | $this->assertEquals($expected, $result); 40 | } 41 | 42 | public function testUpdateWithUuidPrimaryKey() 43 | { 44 | $projectModel = new Project1Model(); 45 | $config = new \Michalsn\Uuid\Config\Uuid(); 46 | $uuid = new Uuid($config); 47 | 48 | $row = $projectModel->update('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', ['name' => 'updated']); 49 | 50 | $expected = [ 51 | 'id' => $uuid->fromString('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394')->getBytes(), 52 | 'name' => 'updated', 53 | 'description' => 'Description 1', 54 | ]; 55 | 56 | $this->seeInDatabase('projects1', $expected); 57 | } 58 | 59 | public function testDeleteWithUuidPrimaryKey() 60 | { 61 | $projectModel = new Project1Model(); 62 | $config = new \Michalsn\Uuid\Config\Uuid(); 63 | $uuid = new Uuid($config); 64 | 65 | $row = $projectModel->delete('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394'); 66 | 67 | $expected1 = [ 68 | 'id' => $uuid->fromString('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394')->getBytes(), 69 | 'deleted_at' => null, 70 | ]; 71 | 72 | $expected2 = [ 73 | 'id' => $uuid->fromString('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394')->getBytes(), 74 | ]; 75 | 76 | $this->dontSeeInDatabase('projects1', $expected1); 77 | $this->seeInDatabase('projects1', $expected2); 78 | } 79 | 80 | public function testDeleteWithoutSoftDeleteWithUuidPrimaryKey() 81 | { 82 | $projectModel = new Project1Model(); 83 | $config = new \Michalsn\Uuid\Config\Uuid(); 84 | $uuid = new Uuid($config); 85 | 86 | $this->setPrivateProperty($projectModel, 'useSoftDeletes', false); 87 | 88 | $row = $projectModel->delete('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394'); 89 | 90 | $expected = [ 91 | 'id' => $uuid->fromString('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394')->getBytes(), 92 | ]; 93 | 94 | $this->dontSeeInDatabase('projects1', $expected); 95 | } 96 | 97 | public function testFindWithUuidPrimaryKey() 98 | { 99 | $projectModel = new Project1Model(); 100 | 101 | $row = $projectModel->find('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394'); 102 | 103 | $expected = [ 104 | 'id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 105 | 'name' => 'Name 1', 106 | 'description' => 'Description 1', 107 | ]; 108 | 109 | unset($row['created_at'], $row['updated_at'], $row['deleted_at']); 110 | 111 | $this->assertEquals($expected, $row); 112 | } 113 | 114 | public function testFindAllWithUuidPrimaryKey() 115 | { 116 | $projectModel = new Project1Model(); 117 | 118 | $results = $projectModel->orderBy('created_at')->findAll(); 119 | 120 | $expected = [ 121 | [ 122 | 'id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 123 | 'name' => 'Name 1', 124 | 'description' => 'Description 1', 125 | ], 126 | [ 127 | 'id' => 'c2912425-c3aa-7774-4dc2-aac28654c3a1', 128 | 'name' => 'Name 2', 129 | 'description' => 'Description 2', 130 | ], 131 | [ 132 | 'id' => 'c38ac295-c2b9-c384-c398-c28a4416c2b5', 133 | 'name' => 'Name 3', 134 | 'description' => 'Description 3', 135 | ], 136 | ]; 137 | 138 | foreach ($results as &$row) 139 | { 140 | unset($row['created_at'], $row['updated_at'], $row['deleted_at']); 141 | } 142 | 143 | $this->assertEquals($expected, $results); 144 | } 145 | 146 | public function testFirstWithUuidPrimaryKey() 147 | { 148 | $projectModel = new Project1Model(); 149 | 150 | $row = $projectModel->first(); 151 | 152 | $expected = [ 153 | 'id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 154 | 'name' => 'Name 1', 155 | 'description' => 'Description 1', 156 | ]; 157 | 158 | unset($row['created_at'], $row['updated_at'], $row['deleted_at']); 159 | 160 | $this->assertEquals($expected, $row); 161 | } 162 | 163 | public function testInsertBatchWithUuidPrimaryKey() 164 | { 165 | $projectModel = new Project1Model(); 166 | 167 | $inserts = [ 168 | [ 169 | 'name' => 'Name 4', 170 | 'description' => 'Description 4', 171 | ], 172 | [ 173 | 'name' => 'Name 5', 174 | 'description' => 'Description 5', 175 | ], 176 | [ 177 | 'name' => 'Name 6', 178 | 'description' => 'Description 6', 179 | ], 180 | ]; 181 | 182 | $results = $projectModel->insertBatch($inserts); 183 | 184 | $this->assertEquals(3, $results); 185 | 186 | $this->seeNumRecords(6, 'projects1', ['deleted_at' => null]); 187 | } 188 | 189 | public function testUpdateBatchWithUuidPrimaryKey() 190 | { 191 | $projectModel = new Project1Model(); 192 | 193 | $updates = [ 194 | [ 195 | 'id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 196 | 'name' => 'Name 1 updated', 197 | 'description' => 'Description 1', 198 | ], 199 | [ 200 | 'id' => 'c2912425-c3aa-7774-4dc2-aac28654c3a1', 201 | 'name' => 'Name 2 updated', 202 | 'description' => 'Description 2', 203 | ], 204 | [ 205 | 'id' => 'c38ac295-c2b9-c384-c398-c28a4416c2b5', 206 | 'name' => 'Name 3 updated', 207 | 'description' => 'Description 3', 208 | ], 209 | ]; 210 | 211 | $results = $projectModel->updateBatch($updates, 'id'); 212 | 213 | $this->assertEquals(3, $results); 214 | 215 | $this->seeInDatabase('projects1', ['name' => 'Name 1 updated']); 216 | $this->seeInDatabase('projects1', ['name' => 'Name 2 updated']); 217 | $this->seeInDatabase('projects1', ['name' => 'Name 3 updated']); 218 | } 219 | 220 | // Projects2 221 | public function testInsertWithoutUuidPrimaryKey() 222 | { 223 | $projectModel = new Project2Model(); 224 | 225 | $data = [ 226 | 'category_id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 227 | 'name' => 'Sample name', 228 | 'description' => 'Sample description', 229 | ]; 230 | 231 | $projectId = $projectModel->insert($data); 232 | 233 | $result = $projectModel->find($projectId); 234 | unset($result['created_at'], $result['updated_at'], $result['deleted_at']); 235 | 236 | $expected = [ 237 | 'id' => (string) $projectId, 238 | 'category_id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 239 | 'name' => $data['name'], 240 | 'description' => $data['description'], 241 | ]; 242 | 243 | $this->assertEquals($expected, $result); 244 | } 245 | 246 | public function testUpdateWithoutUuidPrimaryKey() 247 | { 248 | $projectModel = new Project2Model(); 249 | $config = new \Michalsn\Uuid\Config\Uuid(); 250 | $uuid = new Uuid($config); 251 | 252 | $row = $projectModel->update(1, [ 253 | 'name' => 'updated', 'category_id' => 'c2912425-c3aa-7774-4dc2-aac28654c3a1' 254 | ]); 255 | 256 | $expected = [ 257 | 'id' => '1', 258 | 'category_id' => $uuid->fromString('c2912425-c3aa-7774-4dc2-aac28654c3a1')->getBytes(), 259 | 'name' => 'updated', 260 | 'description' => 'Description 1', 261 | ]; 262 | 263 | $this->seeInDatabase('projects2', $expected); 264 | } 265 | 266 | public function testInsertNullWithoutUuidPrimaryKey() 267 | { 268 | $projectModel = new Project2Model(); 269 | 270 | $data = [ 271 | 'category_id' => null, 272 | 'name' => 'Sample name', 273 | 'description' => 'Sample description', 274 | ]; 275 | 276 | $projectId = $projectModel->insert($data); 277 | 278 | $result = $projectModel->find($projectId); 279 | unset($result['created_at'], $result['updated_at'], $result['deleted_at']); 280 | 281 | $expected = [ 282 | 'id' => (string) $projectId, 283 | 'category_id' => null, 284 | 'name' => $data['name'], 285 | 'description' => $data['description'], 286 | ]; 287 | 288 | $this->assertEquals($expected, $result); 289 | } 290 | 291 | public function testUpdateNullWithoutUuidPrimaryKey() 292 | { 293 | $projectModel = new Project2Model(); 294 | $config = new \Michalsn\Uuid\Config\Uuid(); 295 | $uuid = new Uuid($config); 296 | 297 | $row = $projectModel->update(1, [ 298 | 'name' => 'updated', 'category_id' => null 299 | ]); 300 | 301 | $expected = [ 302 | 'id' => '1', 303 | 'category_id' => null, 304 | 'name' => 'updated', 305 | 'description' => 'Description 1', 306 | ]; 307 | 308 | $this->seeInDatabase('projects2', $expected); 309 | } 310 | 311 | public function testDeleteWithoutUuidPrimaryKey() 312 | { 313 | $projectModel = new Project2Model(); 314 | $config = new \Michalsn\Uuid\Config\Uuid(); 315 | $uuid = new Uuid($config); 316 | 317 | $row = $projectModel->delete(1); 318 | 319 | $expected1 = [ 320 | 'id' => '1', 321 | 'category_id' => $uuid->fromString('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394')->getBytes(), 322 | 'deleted_at' => null, 323 | ]; 324 | 325 | $expected2 = [ 326 | 'category_id' => $uuid->fromString('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394')->getBytes(), 327 | ]; 328 | 329 | $this->dontSeeInDatabase('projects2', $expected1); 330 | $this->seeInDatabase('projects2', $expected2); 331 | } 332 | 333 | public function testDeleteWithoutSoftDeleteWithoutUuidPrimaryKey() 334 | { 335 | $projectModel = new Project2Model(); 336 | 337 | $this->setPrivateProperty($projectModel, 'useSoftDeletes', false); 338 | 339 | $row = $projectModel->delete(1); 340 | 341 | $expected = [ 342 | 'id' => 1, 343 | ]; 344 | 345 | $this->dontSeeInDatabase('projects2', $expected); 346 | } 347 | 348 | public function testFindWithoutUuidPrimaryKey() 349 | { 350 | $projectModel = new Project2Model(); 351 | 352 | $row = $projectModel->find(1); 353 | 354 | $expected = [ 355 | 'id' => '1', 356 | 'category_id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 357 | 'name' => 'Name 1', 358 | 'description' => 'Description 1', 359 | ]; 360 | 361 | unset($row['created_at'], $row['updated_at'], $row['deleted_at']); 362 | 363 | $this->assertEquals($expected, $row); 364 | } 365 | 366 | public function testFindAllWithoutUuidPrimaryKey() 367 | { 368 | $projectModel = new Project2Model(); 369 | 370 | $results = $projectModel->orderBy('created_at')->findAll(); 371 | 372 | $expected = [ 373 | [ 374 | 'id' => '1', 375 | 'category_id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 376 | 'name' => 'Name 1', 377 | 'description' => 'Description 1', 378 | ], 379 | [ 380 | 'id' => '2', 381 | 'category_id' => 'c2912425-c3aa-7774-4dc2-aac28654c3a1', 382 | 'name' => 'Name 2', 383 | 'description' => 'Description 2', 384 | ], 385 | [ 386 | 'id' => '3', 387 | 'category_id' => 'c38ac295-c2b9-c384-c398-c28a4416c2b5', 388 | 'name' => 'Name 3', 389 | 'description' => 'Description 3', 390 | ], 391 | ]; 392 | 393 | foreach ($results as &$row) 394 | { 395 | unset($row['created_at'], $row['updated_at'], $row['deleted_at']); 396 | } 397 | 398 | $this->assertEquals($expected, $results); 399 | } 400 | 401 | public function testFirstWithoutUuidPrimaryKey() 402 | { 403 | $projectModel = new Project2Model(); 404 | 405 | $row = $projectModel->first(); 406 | 407 | $expected = [ 408 | 'id' => '1', 409 | 'category_id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 410 | 'name' => 'Name 1', 411 | 'description' => 'Description 1', 412 | ]; 413 | 414 | unset($row['created_at'], $row['updated_at'], $row['deleted_at']); 415 | 416 | $this->assertEquals($expected, $row); 417 | } 418 | 419 | public function testInsertBatchWithoutUuidPrimaryKey() 420 | { 421 | $projectModel = new Project2Model(); 422 | 423 | $inserts = [ 424 | [ 425 | 'category_id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 426 | 'name' => 'Name 4', 427 | 'description' => 'Description 4', 428 | ], 429 | [ 430 | 'category_id' => 'c2912425-c3aa-7774-4dc2-aac28654c3a1', 431 | 'name' => 'Name 5', 432 | 'description' => 'Description 5', 433 | ], 434 | [ 435 | 'category_id' => 'c38ac295-c2b9-c384-c398-c28a4416c2b5', 436 | 'name' => 'Name 6', 437 | 'description' => 'Description 6', 438 | ], 439 | ]; 440 | 441 | $results = $projectModel->insertBatch($inserts); 442 | 443 | $this->assertEquals(3, $results); 444 | 445 | $this->seeNumRecords(6, 'projects2', ['deleted_at' => null]); 446 | } 447 | 448 | public function testUpdateBatchWithoutUuidPrimaryKey() 449 | { 450 | $projectModel = new Project2Model(); 451 | $config = new \Michalsn\Uuid\Config\Uuid(); 452 | $uuid = new Uuid($config); 453 | 454 | $updates = [ 455 | [ 456 | 'id' => '1', 457 | 'category_id' => 'c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394', 458 | 'name' => 'Name 1 updated', 459 | 'description' => 'Description 1', 460 | ], 461 | [ 462 | 'id' => '2', 463 | 'category_id' => 'c2912425-c3aa-7774-4dc2-aac28654c3a1', 464 | 'name' => 'Name 2 updated', 465 | 'description' => 'Description 2', 466 | ], 467 | [ 468 | 'id' => '3', 469 | 'category_id' => 'c38ac295-c2b9-c384-c398-c28a4416c2b5', 470 | 'name' => 'Name 3 updated', 471 | 'description' => 'Description 3', 472 | ], 473 | ]; 474 | 475 | $results = $projectModel->updateBatch($updates, 'id'); 476 | 477 | $this->assertEquals(3, $results); 478 | 479 | $this->seeInDatabase('projects2', [ 480 | 'name' => 'Name 1 updated', 481 | 'category_id' => $uuid->fromString('c2b8c2a8-2fc3-a2c3-bf22-4d2ec2b6c394')->getBytes(), 482 | ]); 483 | $this->seeInDatabase('projects2', [ 484 | 'name' => 'Name 2 updated', 485 | 'category_id' => $uuid->fromString('c2912425-c3aa-7774-4dc2-aac28654c3a1')->getBytes(), 486 | ]); 487 | $this->seeInDatabase('projects2', [ 488 | 'name' => 'Name 3 updated', 489 | 'category_id' => $uuid->fromString('c38ac295-c2b9-c384-c398-c28a4416c2b5')->getBytes(), 490 | ]); 491 | } 492 | 493 | } -------------------------------------------------------------------------------- /tests/UuidTest.php: -------------------------------------------------------------------------------- 1 | config = new \Michalsn\Uuid\Config\Uuid(); 17 | } 18 | 19 | public function testUuid1() 20 | { 21 | $this->uuid = new Uuid($this->config); 22 | 23 | $uuid = $this->uuid->uuid1('0800200c9a66', 0x1669); 24 | $this->assertInstanceOf(\Ramsey\Uuid\Lazy\LazyUuidFromString::class, $uuid); 25 | $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime()); 26 | $this->assertEquals(2, $uuid->getVariant()); 27 | $this->assertEquals(1, $uuid->getVersion()); 28 | $this->assertEquals(5737, $uuid->getClockSequence()); 29 | $this->assertSame('8796630719078', $uuid->getNode()); 30 | $this->assertEquals('9669-0800200c9a66', substr($uuid->toString(), 19)); 31 | } 32 | 33 | public function testUuid3() 34 | { 35 | $this->uuid = new Uuid($this->config); 36 | 37 | $uuid = $this->uuid->uuid3(\Ramsey\Uuid\Uuid::NIL, '0'); 38 | $this->assertInstanceOf(\Ramsey\Uuid\Lazy\LazyUuidFromString::class, $uuid); 39 | 40 | $this->assertEquals('19826852-5007-3022-a72a-212f66e9fac3', $uuid->toString()); 41 | } 42 | 43 | public function testUuid4() 44 | { 45 | $this->uuid = new Uuid($this->config); 46 | 47 | $uuid = $this->uuid->uuid4(); 48 | $this->assertInstanceOf(\Ramsey\Uuid\Lazy\LazyUuidFromString::class, $uuid); 49 | $this->assertEquals(2, $uuid->getVariant()); 50 | $this->assertEquals(4, $uuid->getVersion()); 51 | } 52 | 53 | public function testUuid5() 54 | { 55 | $uuid = '5f890891-ea7c-5669-ac4e-65767fe71a93'; 56 | $ns = \Ramsey\Uuid\Uuid::NAMESPACE_DNS; 57 | $name = 'codeigniter.com'; 58 | 59 | $this->uuid = new Uuid($this->config); 60 | 61 | $uobj1 = $this->uuid->uuid5($ns, $name); 62 | $uobj2 = $this->uuid->uuid5($this->uuid->fromString($ns), $name); 63 | 64 | $this->assertEquals(2, $uobj1->getVariant()); 65 | $this->assertEquals(5, $uobj1->getVersion()); 66 | $this->assertEquals($this->uuid->fromString($uuid)->toString(), $uobj1); 67 | $this->assertEquals((string) $uobj1, $uuid); 68 | $this->assertTrue($uobj1->equals($uobj2)); 69 | } 70 | 71 | public function testUuid6() 72 | { 73 | $this->uuid = new Uuid($this->config); 74 | 75 | $uuid = $this->uuid->uuid6(new \Ramsey\Uuid\Type\Hexadecimal('0800200c9a66'), 0x1669); 76 | $this->assertInstanceOf(\Ramsey\Uuid\Lazy\LazyUuidFromString::class, $uuid); 77 | $this->assertInstanceOf(DateTimeInterface::class, $uuid->getDateTime()); 78 | $this->assertSame(2, $uuid->getVariant()); 79 | $this->assertSame(6, $uuid->getVersion()); 80 | $this->assertSame('1669', $uuid->getClockSequenceHex()); 81 | $this->assertSame('0800200c9a66', $uuid->getNodeHex()); 82 | $this->assertSame('9669-0800200c9a66', substr($uuid->toString(), 19)); 83 | } 84 | 85 | public function testUuid7() 86 | { 87 | $this->uuid = new Uuid($this->config); 88 | 89 | $uuid = $this->uuid->uuid7(new \DateTimeImmutable('@281474976710.655')); 90 | $this->assertInstanceOf(\Ramsey\Uuid\Lazy\LazyUuidFromString::class, $uuid); 91 | $this->assertInstanceOf(DateTimeImmutable::class, $uuid->getDateTime()); 92 | $this->assertSame(2, $uuid->getVariant()); 93 | $this->assertSame(7, $uuid->getVersion()); 94 | } 95 | 96 | public function testFromString() 97 | { 98 | $this->uuid = new Uuid($this->config); 99 | 100 | $uuid = $this->uuid->fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66'); 101 | $this->assertInstanceOf(\Ramsey\Uuid\Lazy\LazyUuidFromString::class, $uuid); 102 | $this->assertEquals('ff6f8cb0-c57d-11e1-9b21-0800200c9a66', $uuid->toString()); 103 | } 104 | 105 | public function testFromBytes() 106 | { 107 | $this->uuid = new Uuid($this->config); 108 | 109 | $uuid = $this->uuid->fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66'); 110 | $bytes = $uuid->getBytes(); 111 | 112 | $fromBytesUuid = $this->uuid->fromBytes($bytes); 113 | 114 | $this->assertTrue($uuid->equals($fromBytesUuid)); 115 | } 116 | 117 | public function testFromInteger() 118 | { 119 | $this->uuid = new Uuid($this->config); 120 | 121 | $uuid = $this->uuid->fromString('ff6f8cb0-c57d-11e1-9b21-0800200c9a66'); 122 | $integer = $uuid->getInteger()->toString(); 123 | 124 | $fromIntegerUuid = $this->uuid->fromInteger($integer); 125 | 126 | $this->assertTrue($uuid->equals($fromIntegerUuid)); 127 | } 128 | 129 | public function testFromDateTime() 130 | { 131 | $this->uuid = new Uuid($this->config); 132 | 133 | $uuid = $this->uuid->fromString('ff6f8cb0-c57d-11e1-8b21-0800200c9a66'); 134 | $dateTime = $uuid->getDateTime(); 135 | 136 | $fromDateTimeUuid = $this->uuid->fromDateTime($dateTime, new \Ramsey\Uuid\Type\Hexadecimal('0800200c9a66'), 2849); 137 | 138 | $this->assertTrue($uuid->equals($fromDateTimeUuid)); 139 | } 140 | 141 | public function testIsValid() 142 | { 143 | $this->uuid = new Uuid($this->config); 144 | 145 | $result = $this->uuid->isValid('ff6f8cb0-c57d-11e1-8b21-0800200c9a66'); 146 | $this->assertTrue($result); 147 | 148 | $result = $this->uuid->isValid('ff6f8cb0-0800200c9a66'); 149 | $this->assertFalse($result); 150 | } 151 | } -------------------------------------------------------------------------------- /tests/_support/Database/Migrations/2020-05-13-062216_Uuid.php: -------------------------------------------------------------------------------- 1 | forge->addField([ 13 | 'id' => [ 14 | 'type' => 'BINARY', 15 | 'constraint' => 16, 16 | ], 17 | 'name' => [ 18 | 'type' => 'VARCHAR', 19 | 'constraint' => '50', 20 | ], 21 | 'description' => [ 22 | 'type' => 'VARCHAR', 23 | 'constraint' => '200', 24 | ], 25 | 'created_at' => ['type' => 'datetime', 'null' => true], 26 | 'updated_at' => ['type' => 'datetime', 'null' => true], 27 | 'deleted_at' => ['type' => 'datetime', 'null' => true], 28 | ]); 29 | $this->forge->addKey('id', false, true); 30 | $this->forge->createTable('projects1'); 31 | 32 | // Projects2 table 33 | $this->forge->addField([ 34 | 'id' => [ 35 | 'type' => 'INT', 36 | 'constraint' => 11, 37 | 'auto_increment' => true, 38 | ], 39 | 'category_id' => [ 40 | 'type' => 'BINARY', 41 | 'constraint' => 16, 42 | 'null' => true, 43 | ], 44 | 'name' => [ 45 | 'type' => 'VARCHAR', 46 | 'constraint' => '50', 47 | ], 48 | 'description' => [ 49 | 'type' => 'VARCHAR', 50 | 'constraint' => '200', 51 | ], 52 | 'created_at' => ['type' => 'datetime', 'null' => true], 53 | 'updated_at' => ['type' => 'datetime', 'null' => true], 54 | 'deleted_at' => ['type' => 'datetime', 'null' => true], 55 | ]); 56 | $this->forge->addKey('id', true); 57 | $this->forge->createTable('projects2'); 58 | } 59 | 60 | //-------------------------------------------------------------------- 61 | 62 | public function down() 63 | { 64 | $this->forge->dropTable('projects1'); 65 | $this->forge->dropTable('projects2'); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/_support/Database/Seeds/UuidSeeder.php: -------------------------------------------------------------------------------- 1 | '¸¨/âÿ"M.¶Ô!–Yx4', 14 | 'name' => 'Name 1', 15 | 'description' => 'Description 1', 16 | 'created_at' => date('Y-m-d H:i:s', strtotime('now')), 17 | 'updated_at' => date('Y-m-d H:i:s', strtotime('now')), 18 | ], 19 | [ 20 | 'id' => '‘$%êwtMª†Táÿ4ªÊÉ', 21 | 'name' => 'Name 2', 22 | 'description' => 'Description 2', 23 | 'created_at' => date('Y-m-d H:i:s', strtotime('+1 minute')), 24 | 'updated_at' => date('Y-m-d H:i:s', strtotime('+1 minute')), 25 | ], 26 | [ 27 | 'id' => 'ʕ¹Ä؊Dµ§°ïÞ®h', 28 | 'name' => 'Name 3', 29 | 'description' => 'Description 3', 30 | 'created_at' => date('Y-m-d H:i:s', strtotime('+2 minutes')), 31 | 'updated_at' => date('Y-m-d H:i:s', strtotime('+2 minutes')), 32 | ], 33 | ]; 34 | 35 | $builder = $this->db->table('projects1'); 36 | 37 | foreach ($inserts as $insert) 38 | { 39 | $builder->insert($insert); 40 | } 41 | 42 | // Project 2 43 | $inserts = [ 44 | [ 45 | 'category_id' => '¸¨/âÿ"M.¶Ô!–Yx4', 46 | 'name' => 'Name 1', 47 | 'description' => 'Description 1', 48 | 'created_at' => date('Y-m-d H:i:s', strtotime('now')), 49 | 'updated_at' => date('Y-m-d H:i:s', strtotime('now')), 50 | ], 51 | [ 52 | 'category_id' => '‘$%êwtMª†Táÿ4ªÊÉ', 53 | 'name' => 'Name 2', 54 | 'description' => 'Description 2', 55 | 'created_at' => date('Y-m-d H:i:s', strtotime('+1 minute')), 56 | 'updated_at' => date('Y-m-d H:i:s', strtotime('+1 minute')), 57 | ], 58 | [ 59 | 'category_id' => 'ʕ¹Ä؊Dµ§°ïÞ®h', 60 | 'name' => 'Name 3', 61 | 'description' => 'Description 3', 62 | 'created_at' => date('Y-m-d H:i:s', strtotime('+2 minutes')), 63 | 'updated_at' => date('Y-m-d H:i:s', strtotime('+2 minutes')), 64 | ], 65 | ]; 66 | 67 | $builder = $this->db->table('projects2'); 68 | 69 | foreach ($inserts as $insert) 70 | { 71 | $builder->insert($insert); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /tests/_support/Project1Entity.php: -------------------------------------------------------------------------------- 1 | null, 11 | 'name' => null, 12 | 'description' => null, 13 | ]; 14 | } 15 | -------------------------------------------------------------------------------- /tests/_support/Project1Model.php: -------------------------------------------------------------------------------- 1 | 'required|min_length[3]', 21 | 'description' => 'required', 22 | ]; 23 | } -------------------------------------------------------------------------------- /tests/_support/Project2Entity.php: -------------------------------------------------------------------------------- 1 | null, 13 | 'category_id' => null, 14 | 'name' => null, 15 | 'description' => null, 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /tests/_support/Project2Model.php: -------------------------------------------------------------------------------- 1 | 'required|min_length[3]', 23 | 'description' => 'required', 24 | ]; 25 | } --------------------------------------------------------------------------------