├── .github └── workflows │ └── php.yml ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── HasUuid.php └── UuidPivot.php └── tests ├── HasUuidTest.php ├── Models ├── Category.php ├── Post.php └── Tag.php └── TestCase.php /.github/workflows/php.yml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: [push] 3 | jobs: 4 | router: 5 | name: Laravel UUID 6 | runs-on: ${{ matrix.operating-system }} 7 | strategy: 8 | fail-fast: false 9 | matrix: 10 | operating-system: [ubuntu-latest] 11 | php-versions: ['7.4', '8.0'] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Setup PHP, with composer and extensions 17 | uses: shivammathur/setup-php@v2 18 | with: 19 | php-version: ${{ matrix.php-versions }} 20 | 21 | - name: Get Composer Cache Directory 22 | id: composer-cache 23 | run: | 24 | echo "::set-output name=dir::$(composer config cache-files-dir)" 25 | 26 | - uses: actions/cache@v2 27 | with: 28 | path: ${{ steps.composer-cache.outputs.dir }} 29 | key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} 30 | restore-keys: | 31 | ${{ runner.os }}-composer- 32 | 33 | - name: Install dependencies 34 | if: steps.composer-cache.outputs.cache-hit != 'true' 35 | run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader 36 | 37 | - name: Run test suite 38 | run: composer run-script test 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor/ 2 | .idea/ 3 | composer.lock 4 | .phpunit.result.cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 James Hemery 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 | # laravel-uuid 2 | Eloquent UUID Trait for Laravel 5.7 and above. 3 | 4 | ⚠️ This package will no longer be maintained. [The HasUuids feature has been added in Laravel 9.](https://laravel.com/docs/9.x/eloquent#uuid-and-ulid-keys) 5 | 6 | [![Github Actions](https://img.shields.io/github/workflow/status/JamesHemery/laravel-uuid/Continuous%20Integration.svg?style=for-the-badge)](https://github.com/JamesHemery/laravel-uuid/actions?query=workflow%3A%22Continuous+Integration%22) 7 | [![Total Downloads](https://img.shields.io/packagist/dt/jamesh/laravel-uuid.svg?style=for-the-badge)](https://packagist.org/packages/jamesh/laravel-uuid) 8 | [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg?style=for-the-badge)](https://raw.githubusercontent.com/JamesHemery/laravel-uuid/master/LICENSE) 9 | 10 | The HasUuid Trait will add behavior to creating and saving Eloquent events for generate an Uuid. 11 | 12 | ## Installation 13 | 14 | composer require jamesh/laravel-uuid 15 | 16 | ## Usage 17 | 18 | #### In your migrations 19 | 20 | ```php 21 | Schema::create('users', function (Blueprint $table) { 22 | $table->uuid('id')->primary(); // Create CHAR(36) 23 | $table->string('name'); 24 | $table->string('email')->unique(); 25 | $table->timestamp('email_verified_at')->nullable(); 26 | $table->string('password'); 27 | $table->rememberToken(); 28 | $table->timestamps(); 29 | }); 30 | ``` 31 | 32 | #### In your models 33 | 34 | ```php 35 | 2 | 12 | 13 | 14 | tests 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/HasUuid.php: -------------------------------------------------------------------------------- 1 | keyType = 'string'; 46 | $model->incrementing = false; 47 | 48 | if (!$model->getKey()) { 49 | $model->{$model->getKeyName()} = (string)Str::uuid(); 50 | } 51 | }); 52 | 53 | // Set original if someone try to change UUID on update/save existing model 54 | static::saving(function (Model $model) { 55 | $original_id = $model->getOriginal('id'); 56 | if (!is_null($original_id) && $model->isLockedUuid) { 57 | if ($original_id !== $model->id) { 58 | $model->id = $original_id; 59 | } 60 | } 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/UuidPivot.php: -------------------------------------------------------------------------------- 1 | 'My awesome post']); 18 | $this->assertMatchesRegularExpression(self::UUID_REGEX, $post->id); 19 | 20 | $found = Post::first(); 21 | $this->assertMatchesRegularExpression(self::UUID_REGEX, $found->id); 22 | } 23 | 24 | public function test_find() 25 | { 26 | $post = Post::create(['title' => 'My awesome post']); 27 | 28 | $found = Post::find($post->id); 29 | 30 | $this->assertEquals($post->id, $found->id); 31 | $this->assertEquals('My awesome post', $found->title); 32 | } 33 | 34 | public function test_delete() 35 | { 36 | $post = Post::create(['title' => 'My awesome post']); 37 | 38 | $this->assertCount(1, Post::all()); 39 | 40 | Post::find($post->id)->delete(); 41 | 42 | $this->assertCount(0, Post::all()); 43 | } 44 | 45 | public function test_has_many() 46 | { 47 | $category = Category::create(['name' => 'Tutorial']); 48 | $post = Post::create(['title' => 'My awesome tutorial']); 49 | 50 | $category->posts()->save($post); 51 | 52 | $found = Post::first(); 53 | 54 | $this->assertNotNull($found->category); 55 | $this->assertEquals($category->id, $found->category->id); 56 | $this->assertEquals('Tutorial', $found->category->name); 57 | } 58 | 59 | public function test_belongs_to() 60 | { 61 | $category = Category::create(['name' => 'Tutorial']); 62 | $post = Post::create(['title' => 'My awesome tutorial']); 63 | 64 | $post->category()->associate($category); 65 | $post->save(); 66 | 67 | $found = Post::first(); 68 | 69 | $this->assertNotNull($found->category); 70 | $this->assertEquals($category->id, $found->category->id); 71 | $this->assertEquals('Tutorial', $found->category->name); 72 | } 73 | 74 | public function test_many_to_many() 75 | { 76 | $post = Post::create(['title' => 'My awesome tutorial']); 77 | 78 | $post->tags()->attach(Tag::create(['name' => 'PHP'])); 79 | $found = Post::first(); 80 | 81 | $this->assertCount(1, $found->tags); 82 | 83 | $post->tags()->attach(Tag::create(['name' => 'Laravel'])); 84 | $post->tags()->attach(Tag::create(['name' => 'Uuid'])); 85 | $found = Post::first(); 86 | 87 | $this->assertCount(3, $found->tags); 88 | } 89 | 90 | public function test_is_failed_when_update_id() 91 | { 92 | $post = Post::create(['title' => 'My awesome tutorial']); 93 | 94 | $original_id = $post->id; 95 | $new_id = (string)Str::uuid(); 96 | 97 | $post->id = $new_id; 98 | $post->save(); 99 | 100 | $found = Post::first(); 101 | $this->assertEquals($original_id, $found->id); 102 | 103 | $post->update(['id' => $new_id]); 104 | 105 | $found = Post::first(); 106 | $this->assertEquals($original_id, $found->id); 107 | } 108 | 109 | public function test_save_new_with_empty_id() 110 | { 111 | $post = new Post(); 112 | $post->id = ''; 113 | $post->title = 'My awesome tutorial'; 114 | $post->save(); 115 | 116 | $found = Post::first(); 117 | 118 | $this->assertNotEmpty($found->id); 119 | $this->assertNotNull($found->id); 120 | $this->assertMatchesRegularExpression(self::UUID_REGEX, $found->id); 121 | } 122 | 123 | public function test_create_with_empty_id() 124 | { 125 | Post::unguarded(function () { 126 | Post::create([ 127 | 'id' => '', 128 | 'title' => 'My awesome tutorial' 129 | ]); 130 | 131 | $found = Post::first(); 132 | 133 | $this->assertNotEmpty($found->id); 134 | $this->assertNotNull($found->id); 135 | $this->assertMatchesRegularExpression(self::UUID_REGEX, $found->id); 136 | }); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /tests/Models/Category.php: -------------------------------------------------------------------------------- 1 | hasMany(Post::class); 17 | } 18 | } -------------------------------------------------------------------------------- /tests/Models/Post.php: -------------------------------------------------------------------------------- 1 | belongsTo(Category::class); 16 | } 17 | 18 | public function tags(){ 19 | return $this->belongsToMany(Tag::class); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/Models/Tag.php: -------------------------------------------------------------------------------- 1 | belongsToMany(Post::class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | uuid('id')->primary(); 20 | $table->string('title'); 21 | $table->uuid('category_id')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | 25 | Schema::create('categories', function (Blueprint $table) { 26 | $table->uuid('id')->primary(); 27 | $table->string('name'); 28 | }); 29 | 30 | Schema::create('tags', function (Blueprint $table) { 31 | $table->uuid('id')->primary(); 32 | $table->string('name'); 33 | }); 34 | 35 | Schema::create('post_tag', function (Blueprint $table) { 36 | $table->uuid('post_id')->nullable(); 37 | $table->foreign('post_id')->references('id')->on('posts')->onDelete('cascade'); 38 | 39 | $table->uuid('tag_id')->nullable(); 40 | $table->foreign('tag_id')->references('id')->on('tags')->onDelete('cascade'); 41 | }); 42 | } 43 | 44 | /** 45 | * Define environment setup. 46 | * @param Illuminate\Foundation\Application $app 47 | * @return void 48 | */ 49 | protected function getEnvironmentSetUp($app) 50 | { 51 | // Setup default database to use sqlite :memory: 52 | $app['config']->set('database.default', 'testing'); 53 | $app['config']->set('database.connections.testing', [ 54 | 'driver' => 'sqlite', 55 | 'database' => ':memory:', 56 | 'prefix' => '', 57 | ]); 58 | } 59 | } 60 | --------------------------------------------------------------------------------