├── .github └── workflows │ └── build.yml ├── .gitignore ├── README.md ├── composer.json ├── phpunit.xml ├── src ├── HasHashid.php ├── HashidRouting.php └── HashidScope.php └── tests ├── HasHashidTest.php ├── HashidRoutingTest.php ├── Models ├── Comment.php ├── Item.php ├── ItemWithCustomRouteKeyName.php └── Vendor.php ├── TestCase.php ├── config └── hashids.php └── database ├── factories ├── CommentFactory.php ├── ItemFactory.php └── VendorFactory.php └── migrations ├── 2019_07_10_222200_create_items_table.php ├── 2022_06_15_000000_create_comments_table.php └── 2022_06_15_000000_create_vendors_table.php /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-latest 8 | 9 | strategy: 10 | matrix: 11 | php: [7.3, 7.4, 8.0, 8.1, 8.2] 12 | illuminate: [^8.0, ^9.0, ^10.0, ^11.0, ^12.0] 13 | exclude: 14 | - php: 7.3 15 | illuminate: ^9.0 16 | - php: 7.4 17 | illuminate: ^9.0 18 | - php: 7.3 19 | illuminate: ^10.0 20 | - php: 7.4 21 | illuminate: ^10.0 22 | - php: 8.0 23 | illuminate: ^10.0 24 | - php: 7.3 25 | illuminate: ^11.0 26 | - php: 7.4 27 | illuminate: ^11.0 28 | - php: 8.0 29 | illuminate: ^11.0 30 | - php: 8.1 31 | illuminate: ^11.0 32 | - php: 7.3 33 | illuminate: ^12.0 34 | - php: 7.4 35 | illuminate: ^12.0 36 | - php: 8.0 37 | illuminate: ^12.0 38 | - php: 8.1 39 | illuminate: ^12.0 40 | 41 | 42 | name: PHP ${{ matrix.php }} & Illuminate ${{ matrix.illuminate }} 43 | 44 | steps: 45 | - uses: actions/checkout@v3 46 | - uses: shivammathur/setup-php@v2 47 | with: 48 | php-version: ${{ matrix.php }} 49 | tools: composer:v2 50 | - run: composer require "illuminate/database:${{ matrix.illuminate }}" --no-update 51 | - run: composer update --no-interaction 52 | - run: vendor/bin/phpunit 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | .idea 3 | composer.lock 4 | .phpunit.cache/ 5 | .phpunit.result.cache 6 | 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > Using hashids instead of integer ids in urls and list items can be more 2 | appealing and clever. For more information visit [hashids.org](https://hashids.org/). 3 | 4 | # Eloquent-Hashids ![Build Status](https://github.com/mtvs/eloquent-hashids/actions/workflows/build.yml/badge.svg) 5 | 6 | This adds hashids to Laravel Eloquent models by encoding/decoding them on the fly 7 | rather than persisting them in the database. So no need for another database column 8 | and also higher performance by using primary keys in queries. 9 | 10 | Features include: 11 | 12 | * Generating hashids for models 13 | * Resloving hashids to models 14 | * Ability to customize hashid settings for each model 15 | * Route binding with hashids (optional) 16 | 17 | ## Installation 18 | 19 | Install the package with Composer: 20 | 21 | ```sh 22 | 23 | $ composer require mtvs/eloquent-hashids 24 | 25 | ``` 26 | 27 | Also, publish the vendor config files to your application (necessary for the dependencies): 28 | 29 | ```sh 30 | $ php artisan vendor:publish 31 | ``` 32 | 33 | ## Setup 34 | 35 | Base features are provided by using `HasHashid` trait then route binding with 36 | hashids can be added by using `HashidRouting`. 37 | 38 | ```php 39 | 40 | use Illuminate\Database\Eloquent\Model; 41 | use Mtvs\EloquentHashids\HasHashid; 42 | use Mtvs\EloquentHashids\HashidRouting; 43 | 44 | Class Item extends Model 45 | { 46 | use HasHashid, HashidRouting; 47 | } 48 | 49 | ``` 50 | 51 | ### Custom Hashid Settings 52 | 53 | It's possible to customize hashids settings for each model by overwriting 54 | `getHashidsConnection()`. It must return the name of a connection of 55 | [`vinkla/hashids`](https://github.com/vinkla/laravel-hashids) that provides 56 | the desired settings. 57 | 58 | ## Usage 59 | 60 | ### Basics 61 | 62 | ```php 63 | 64 | // Generating the model hashid based on its key 65 | $item->hashid(); 66 | 67 | // Equivalent to the above but with the attribute style 68 | $item->hashid; 69 | 70 | // Finding a model based on the provided hashid or 71 | // returning null on failure 72 | Item::findByHashid($hashid); 73 | 74 | // Finding a model based on the provided hashid or 75 | // throwing a ModelNotFoundException on failure 76 | Item::findByHashidOrFail($hashid); 77 | 78 | // Decoding a hashid to its equivalent id 79 | $item->hashidToId($hashid); 80 | 81 | // Encoding an id to its equivalent hashid 82 | $item->idToHashid($id); 83 | 84 | // Getting the name of the hashid connection 85 | $item->getHashidsConnection(); 86 | 87 | ``` 88 | 89 | ### Add the hashid to the serialized model 90 | 91 | Set it as default: 92 | 93 | ```php 94 | 95 | use Illuminate\Database\Eloquent\Model; 96 | use Mtvs\EloquentHashids\HasHashid; 97 | 98 | class Item extends Model 99 | { 100 | use HasHashid; 101 | 102 | /** 103 | * The accessors to append to the model's array form. 104 | * 105 | * @var array 106 | */ 107 | protected $appends = ['hashid']; 108 | } 109 | 110 | ``` 111 | 112 | or specify it specificly: 113 | 114 | `return $item->append('hashid')->toJson();` 115 | 116 | 117 | ### Implicit Route Bindings 118 | 119 | If you want to resolve implicit route bindings for the model using its hahsid 120 | value you can use `HashidRouting` in the model. 121 | 122 | ```php 123 | 124 | use Illuminate\Database\Eloquent\Model; 125 | use Mtvs\EloquentHashids\HasHashid; 126 | use Mtvs\EloquentHashids\HashidRouting; 127 | 128 | class Item extends Model 129 | { 130 | use HasHashid, HashidRouting; 131 | } 132 | 133 | ``` 134 | It overwrites `getRouteKeyName()`, `getRouteKey()` and `resolveRouteBindingQuery()` 135 | to use the hashids as the route keys. 136 | 137 | It supports the Laravel's feature for customizing the key for specific routes. 138 | 139 | ```php 140 | 141 | Route::get('/items/{item:slug}', function (Item $item) { 142 | return $item; 143 | }); 144 | 145 | ``` 146 | 147 | #### Customizing The Default Route Key Name 148 | 149 | If you want to by default resolve the implicit route bindings using another 150 | field you can overwrite `getRouteKeyName()` to return the field's name to the 151 | resolving process and `getRouteKey()` to return its value in your links. 152 | 153 | ```php 154 | 155 | use Illuminate\Database\Eloquent\Model; 156 | use Mtvs\EloquentHashids\HasHashid; 157 | use Mtvs\EloquentHashids\HashidRouting; 158 | 159 | class Item extends Model 160 | { 161 | use HasHashid, HashidRouting; 162 | 163 | public function getRouteKeyName() 164 | { 165 | return 'slug'; 166 | } 167 | 168 | public function getRouteKey() 169 | { 170 | return $this->slug; 171 | } 172 | } 173 | 174 | ``` 175 | 176 | You'll still be able to specify the hashid for specific routes. 177 | 178 | ```php 179 | 180 | Route::get('/items/{item:hashid}', function (Item $item) { 181 | return $item; 182 | }); 183 | 184 | ``` 185 | 186 | #### Supporting The Other Laravel's Implicit Route Binding Features 187 | 188 | When using `HashidRouting` you'll still be able to use softdeletable and child 189 | route bindings. 190 | 191 | ```php 192 | 193 | Route::get('/items/{item}', function (Item $item) { 194 | return $item; 195 | })->withTrashed(); 196 | 197 | Route::get('/user/{user}/items/{item}', function (User $user, Item $item) { 198 | return $item; 199 | })->scopeBindings(); 200 | 201 | ``` 202 | 203 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mtvs/eloquent-hashids", 3 | "description": "On-the-fly hashids for Laravel Eloquent models.", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Mohammad Ali Tavassoli", 9 | "email": "mtvs.dev@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "illuminate/database": "^8.0|^9.0|^10.0|^11.0|^12.0", 14 | "vinkla/hashids": "^9.0|^10.0|^11.0|^12.0|^13.0" 15 | }, 16 | "require-dev": { 17 | "orchestra/testbench": "^6.0|^7.0|^8.0|^9.0|^10.0", 18 | "phpunit/phpunit": "^8.4|^9.0|^10.0|^11.0", 19 | "laravel/legacy-factories": "^1.0" 20 | }, 21 | "autoload": { 22 | "psr-4": { 23 | "Mtvs\\EloquentHashids\\": "src/" 24 | } 25 | }, 26 | "autoload-dev": { 27 | "psr-4": { 28 | "Mtvs\\EloquentHashids\\Tests\\": "tests/" 29 | } 30 | }, 31 | "scripts": { 32 | "test": "phpunit" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | tests/ 6 | tests/database/ 7 | tests/Models/ 8 | tests/config/ 9 | tests/TestCase.php 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/HasHashid.php: -------------------------------------------------------------------------------- 1 | idToHashid($this->getKey()); 25 | } 26 | 27 | /** 28 | * Decode the hashid to the id 29 | * 30 | * @param string $hashid 31 | * @return int|null 32 | */ 33 | public function hashidToId($hashid) 34 | { 35 | return @Hashids::connection($this->getHashidsConnection()) 36 | ->decode($hashid)[0]; 37 | } 38 | 39 | /** 40 | * Encode an id to its equivalent hashid 41 | * 42 | * @param string $id 43 | * @return string|null 44 | */ 45 | public function idToHashid($id) 46 | { 47 | return @Hashids::connection($this->getHashidsConnection()) 48 | ->encode($id); 49 | } 50 | 51 | public function getHashidsConnection() 52 | { 53 | return config('hashids.default'); 54 | } 55 | 56 | protected function getHashidAttribute() 57 | { 58 | return $this->hashid(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/HashidRouting.php: -------------------------------------------------------------------------------- 1 | getRouteKeyName(); 15 | 16 | if ( 17 | $field && $field !== 'hashid' && 18 | // Check for qualified columns 19 | Str::afterLast($field, '.') !== 'hashid' && 20 | // Avoid risking breaking backward compatibility by modifying 21 | // the getRouteKeyName() to return 'hashid' instead of null 22 | Str::afterLast($field, '.') !== '' 23 | ) { 24 | return parent::resolveRouteBindingQuery($query, $value, $field); 25 | } 26 | 27 | return $query->byHashid($value); 28 | } 29 | 30 | /** 31 | * @see parent 32 | */ 33 | public function getRouteKey() 34 | { 35 | return $this->hashid(); 36 | } 37 | 38 | /** 39 | * @see parent 40 | */ 41 | public function getRouteKeyName() 42 | { 43 | return null; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/HashidScope.php: -------------------------------------------------------------------------------- 1 | macro('findByHashid', function (Builder $builder, $hashid) { 19 | return $builder->byHashid($hashid)->first(); 20 | }); 21 | 22 | $builder->macro('findByHashidOrFail', function (Builder $builder, $hashid) 23 | { 24 | return $builder->byHashid($hashid)->firstOrFail(); 25 | }); 26 | 27 | $builder->macro('byHashid', function (Builder $builder, $hashid) { 28 | $model = $builder->getModel(); 29 | 30 | return $builder->where( 31 | $model->qualifyColumn($model->getKeyName()), 32 | $model->hashidToId($hashid) 33 | ); 34 | }); 35 | } 36 | } -------------------------------------------------------------------------------- /tests/HasHashidTest.php: -------------------------------------------------------------------------------- 1 | create(); 20 | 21 | $hashid = Hashids::encode($item->getKey()); 22 | 23 | $this->assertEquals($hashid, $item->hashid()); 24 | $this->assertEquals($hashid, $item->hashid); 25 | } 26 | 27 | /** 28 | * @test 29 | */ 30 | public function it_can_append_its_hashid_to_its_serialized_version() 31 | { 32 | $item = factory(Item::class)->create(); 33 | 34 | $hashid = Hashids::encode($item->getKey()); 35 | 36 | $serialized = json_decode($item->append('hashid')->toJson()); 37 | 38 | $this->assertEquals($hashid, $serialized->hashid); 39 | } 40 | 41 | /** 42 | * @test 43 | */ 44 | public function it_can_enceode_an_arbitrary_id_to_its_hashid() 45 | { 46 | $item = new Item(); 47 | 48 | $hashid = Hashids::encode(123); 49 | 50 | $this->assertEquals($hashid, $item->idToHashid(123)); 51 | } 52 | 53 | /** 54 | * @test 55 | */ 56 | public function it_can_find_models_by_hashid() 57 | { 58 | $item = factory(Item::class)->create(); 59 | 60 | $hashid = Hashids::encode($item->getKey()); 61 | 62 | $found = Item::findByHashid($hashid); 63 | 64 | $this->assertNotNull($found); 65 | $this->assertEquals($item->id, $found->id); 66 | } 67 | 68 | /** 69 | * @test 70 | */ 71 | public function it_returns_null_when_cannot_find_a_model_with_a_hashid() 72 | { 73 | $hashid = Hashids::encode(1); 74 | 75 | $found = Item::findByHashid($hashid); 76 | 77 | $this->assertNull($found); 78 | } 79 | 80 | /** 81 | * @test 82 | */ 83 | public function it_can_find_a_model_by_its_hashid_or_fail() 84 | { 85 | $item = factory(Item::class)->create(); 86 | 87 | $hashid = Hashids::encode($item->getKey()); 88 | 89 | $found = Item::findByHashidOrFail($hashid); 90 | 91 | $this->assertEquals($item->id, $found->id); 92 | 93 | $item->delete(); 94 | 95 | $this->expectException(ModelNotFoundException::class); 96 | 97 | Item::findByHashidOrFail($hashid); 98 | } 99 | 100 | /** 101 | * @test 102 | */ 103 | public function it_can_decode_a_hashid_to_the_id() 104 | { 105 | $item = factory(Item::class)->create(); 106 | 107 | $hashid = Hashids::encode($item->getKey()); 108 | 109 | $id = (new Item)->hashidToId($hashid); 110 | 111 | $this->assertEquals($item->id, $id); 112 | } 113 | 114 | /** 115 | * @test 116 | */ 117 | public function it_can_handle_invalid_hashids_properly() 118 | { 119 | $this->assertNull((new Item)->hashidToId('not a hashid')); 120 | 121 | $this->assertNull(Item::findByHashid('not a hashid')); 122 | 123 | $this->expectException(ModelNotFoundException::class); 124 | Item::findByHashidOrFail('not a hashid'); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /tests/HashidRoutingTest.php: -------------------------------------------------------------------------------- 1 | create(); 23 | 24 | $hashid = Hashids::encode($given->getKey()); 25 | 26 | Route::get('/item/{item}', function (Item $item) use ($given) { 27 | $this->assertEquals($given->id, $item->id); 28 | })->middleware(SubstituteBindings::class); 29 | 30 | $this->get("/item/$hashid"); 31 | } 32 | 33 | /** @test */ 34 | public function it_supports_custom_route_key_names() 35 | { 36 | $given = ItemWithCustomRouteKeyName::create( 37 | factory(Item::class)->raw() 38 | ); 39 | 40 | Route::get('/item/{item}', function (ItemWithCustomRouteKeyName $item) use ($given) { 41 | $this->assertEquals($given->id, $item->id); 42 | })->middleware(SubstituteBindings::class); 43 | 44 | $this->get("/item/$given->slug"); 45 | 46 | Route::get('/admin/item/{item:hashid}', function (ItemWithCustomRouteKeyName $item) use ($given) { 47 | $this->assertEquals($given->id, $item->id); 48 | })->middleware(SubstituteBindings::class); 49 | 50 | $this->get("/admin/item/$given->hashid"); 51 | 52 | } 53 | 54 | /** @test */ 55 | public function it_supports_resolving_softdeletable_route_bindings() 56 | { 57 | $given = factory(Item::class)->state('softDeleted')->create(); 58 | 59 | $hashid = Hashids::encode($given->getKey()); 60 | 61 | Route::get('/item/{item}', function (Item $item) use ($given) { 62 | $this->assertEquals($given->id, $item->id); 63 | })->withTrashed()->middleware(SubstituteBindings::class); 64 | 65 | $this->get("/item/$hashid"); 66 | } 67 | 68 | /** @test */ 69 | public function it_supports_resolving_child_route_bindings() 70 | { 71 | $item = factory(Item::class)->create(); 72 | 73 | $given = $item->comments()->save( 74 | factory(Comment::class)->make() 75 | ); 76 | 77 | Route::get( 78 | '/item/{item}/comments/{comment}', 79 | function (Item $item, Comment $comment) use ($given) { 80 | $this->assertEquals($given->id, $comment->id); 81 | })->scopeBindings()->middleware(SubstituteBindings::class); 82 | 83 | $this->get("item/{$item->hashid}/comments/{$given->hashid}"); 84 | 85 | $notRelated = factory(Comment::class)->create(); 86 | 87 | $this->expectException(ModelNotFoundException::class); 88 | 89 | $this->get("item/{$item->hashid}/comments/{$notRelated->hashid}"); 90 | } 91 | 92 | /** @test */ 93 | public function it_supports_resolving_special_child_route_bindings() 94 | { 95 | $vendor = factory(Vendor::class)->create(); 96 | 97 | $item = $vendor->items()->save( 98 | factory(Item::class)->make() 99 | ); 100 | 101 | $given = $item->comments()->save( 102 | factory(Comment::class)->make() 103 | ); 104 | 105 | Route::get( 106 | '/vendor/{vendor}/comments/{comment}', 107 | function (Vendor $vendor, Comment $comment) use ($given) { 108 | $this->assertEquals($given->id, $comment->id); 109 | })->scopeBindings()->middleware(SubstituteBindings::class); 110 | 111 | $this->get("/vendor/{$vendor->id}/comments/{$given->hashid}"); 112 | } 113 | 114 | /** 115 | * @test 116 | */ 117 | public function it_supports_specifying_a_field_in_the_route_binding() 118 | { 119 | $given = factory(Item::class)->create(['slug' => 'item-1']); 120 | 121 | Route::get('/item/{item:slug}', function (Item $item) use ($given) { 122 | $this->assertEquals($given->id, $item->id); 123 | })->middleware(SubstituteBindings::class); 124 | 125 | $this->get("/item/item-1"); 126 | } 127 | 128 | /** @test */ 129 | public function it_supports_specifying_the_hashid_in_the_route_binding() 130 | { 131 | $given = factory(Item::class)->create(); 132 | 133 | $hashid = Hashids::encode($given->getKey()); 134 | 135 | Route::get('/item/{item:hashid}', function (Item $item) use ($given) { 136 | $this->assertEquals($given->id, $item->id); 137 | })->middleware(SubstituteBindings::class); 138 | 139 | $this->get("/item/$hashid"); 140 | } 141 | 142 | /** 143 | * @test 144 | */ 145 | public function it_returns_the_hashid_of_the_model_as_its_route_key() 146 | { 147 | $item = factory(Item::class)->create(); 148 | 149 | $hashid = Hashids::encode($item->id); 150 | 151 | $this->assertEquals($hashid, $item->getRouteKey()); 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /tests/Models/Comment.php: -------------------------------------------------------------------------------- 1 | hasMany(Comment::class); 20 | } 21 | } -------------------------------------------------------------------------------- /tests/Models/ItemWithCustomRouteKeyName.php: -------------------------------------------------------------------------------- 1 | hasMany(Item::class); 14 | } 15 | 16 | public function comments() 17 | { 18 | return $this->hasManyThrough(Comment::class, Item::class); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | withoutExceptionHandling(); 15 | 16 | $this->loadMigrationsFrom(__DIR__.'/database/migrations'); 17 | 18 | $this->withFactories(__DIR__.'/database/factories'); 19 | 20 | $this->app['config']->set('hashids', require 'config/hashids.php'); 21 | } 22 | 23 | protected function getPackageProviders($app) 24 | { 25 | return [ 26 | HashidsServiceProvider::class, 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests/config/hashids.php: -------------------------------------------------------------------------------- 1 | 'main', 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Hashids Connections 30 | |-------------------------------------------------------------------------- 31 | | 32 | | Here are each of the connections setup for your application. Example 33 | | configuration has been included, but you may add as many connections as 34 | | you would like. 35 | | 36 | */ 37 | 38 | 'connections' => [ 39 | 40 | 'main' => [ 41 | 'salt' => 'your-salt-string', 42 | 'length' => 4, 43 | // 'alphabet' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' 44 | ], 45 | 46 | 'alternative' => [ 47 | 'salt' => 'your-salt-string', 48 | 'length' => 'your-length-integer', 49 | // 'alphabet' => 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890' 50 | ], 51 | 52 | ], 53 | 54 | ]; 55 | -------------------------------------------------------------------------------- /tests/database/factories/CommentFactory.php: -------------------------------------------------------------------------------- 1 | define(Comment::class, function (Generator $faker) { 8 | return [ 9 | 'body' => $faker->paragraph(), 10 | 11 | 'item_id' => function () { 12 | return factory(Item::class)->create(); 13 | } 14 | ]; 15 | }); 16 | -------------------------------------------------------------------------------- /tests/database/factories/ItemFactory.php: -------------------------------------------------------------------------------- 1 | define(Item::class, function (Generator $faker) { 8 | return [ 9 | 'slug' => $faker->unique()->word(), 10 | 'vendor_id' => function() { 11 | return factory(Vendor::class)->create(); 12 | } 13 | ]; 14 | }); 15 | 16 | $factory->state(Item::class, 'softDeleted', [ 17 | 'deleted_at' => now(), 18 | ]); -------------------------------------------------------------------------------- /tests/database/factories/VendorFactory.php: -------------------------------------------------------------------------------- 1 | define(Vendor::class, function (Generator $faker) { 7 | return [ 8 | 'name' => $faker->name, 9 | ]; 10 | }); 11 | -------------------------------------------------------------------------------- /tests/database/migrations/2019_07_10_222200_create_items_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('slug')->unique(); 19 | $table->timestamps(); 20 | $table->softDeletes(); 21 | $table->unsignedInteger('vendor_id'); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('items'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests/database/migrations/2022_06_15_000000_create_comments_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('body')->unique(); 19 | $table->timestamps(); 20 | $table->unsignedInteger('item_id'); 21 | }); 22 | } 23 | 24 | /** 25 | * Reverse the migrations. 26 | * 27 | * @return void 28 | */ 29 | public function down() 30 | { 31 | Schema::dropIfExists('comments'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /tests/database/migrations/2022_06_15_000000_create_vendors_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('vendors'); 31 | } 32 | } 33 | --------------------------------------------------------------------------------