├── LICENSE.md ├── README.md ├── composer.json ├── examples └── CountryExample.php └── src └── Sushi.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Caleb Porzio 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 | # Sushi 🍣 2 | Eloquent's missing "array" driver. 3 | 4 | Sometimes you want to use Eloquent, but without dealing with a database. 5 | 6 | ## This Package Is Sponsorware 💰💰💰 7 | Originally, this package was only available to my sponsors on GitHub Sponsors until I reached 75 sponsors. 8 | 9 | Now that we've reached the goal, the package is fully open source. 10 | 11 | Enjoy, and thanks for the support! ❤️ 12 | 13 | Learn more about **Sponsorware** at [github.com/sponsorware/docs](https://github.com/sponsorware/docs) 💰. 14 | 15 | 16 | ## Requirements 17 | 18 | The [`pdo-sqlite` PHP extension](https://www.php.net/manual/en/ref.pdo-sqlite.php) must be installed on your system to use this package. 19 | 20 | ## Install 21 | ``` 22 | composer require calebporzio/sushi 23 | ``` 24 | 25 | ## Use 26 | 27 | Using this package consists of two steps: 28 | 1. Add the `Sushi` trait to a model. 29 | 2. Add a `$rows` property to the model. 30 | 31 | That's it. 32 | 33 | ```php 34 | class State extends Model 35 | { 36 | use \Sushi\Sushi; 37 | 38 | protected $rows = [ 39 | [ 40 | 'abbr' => 'NY', 41 | 'name' => 'New York', 42 | ], 43 | [ 44 | 'abbr' => 'CA', 45 | 'name' => 'California', 46 | ], 47 | ]; 48 | } 49 | ``` 50 | 51 | Now, you can use this model anywhere you like, and it will behave as if you created a table with the rows you provided. 52 | ```php 53 | $stateName = State::whereAbbr('NY')->first()->name; 54 | ``` 55 | 56 | This is really useful for "Fixture" data, like states, countries, zip codes, user_roles, sites_settings, etc... 57 | 58 | ### Relationships 59 | Let's say you created a `Role` model, based on an array using Sushi, that looked like this: 60 | ```php 61 | class Role extends Model 62 | { 63 | use \Sushi\Sushi; 64 | 65 | protected $rows = [ 66 | ['id' => 1, 'label' => 'admin'], 67 | ['id' => 2, 'label' => 'manager'], 68 | ['id' => 3, 'label' => 'user'], 69 | ]; 70 | } 71 | ``` 72 | 73 | You can add a relationship to another standard model, just like you normally would: 74 | ```php 75 | class User extends Model 76 | { 77 | ... 78 | 79 | public function role() 80 | { 81 | return $this->belongsTo(Role::class); 82 | } 83 | } 84 | ``` 85 | 86 | Assuming the `users` table has a `role_id` column, you can do things like this: 87 | ```php 88 | // Grab a User. 89 | $user = User::first(); 90 | // Grab a Role. 91 | $role = Role::whereLabel('admin')->first(); 92 | 93 | // Associate them. 94 | $user->role()->associate($role); 95 | 96 | // Access like normal. 97 | $user->role; 98 | 99 | // Eager load. 100 | $user->load('role'); 101 | User::with('role')->first(); 102 | ``` 103 | 104 | > Note: There is one caveat when dealing with Sushi model relationships. The `whereHas` method will NOT work. This is because the two models are spread across two separate databases. 105 | 106 | ### Using database-checking validation rules 107 | You can even use Laravel's `exists:table,column` database checking request validation rule. 108 | 109 | ```php 110 | $data = request()->validate([ 111 | 'state' => ['required', 'exists:App\Models\State,abbr'], 112 | ]); 113 | ``` 114 | 115 | > Note: Be aware that you must use the fully-qualified namespace of the model instead of a table name. This ensures that Laravel will correctly resolve the model's connection. 116 | 117 | ### Custom Schema 118 | If Sushi's schema auto-detection system doesn't meet your specific requirements for the supplied row data, you can customize them with the `$schema` property or the `getSchema()` method. 119 | 120 | ```php 121 | class Products extends Model 122 | { 123 | use \Sushi\Sushi; 124 | 125 | protected $rows = [ 126 | ['name' => 'Lawn Mower', 'price' => '226.99'], 127 | ['name' => 'Leaf Blower', 'price' => '134.99'], 128 | ['name' => 'Rake', 'price' => '9.99'], 129 | ]; 130 | 131 | protected $schema = [ 132 | 'price' => 'float', 133 | ]; 134 | } 135 | ``` 136 | 137 | ## Advanced Usage 138 | 139 | When you need more flexibility, you can implement the `afterMigrate(BluePrint $table)` method, allowing you to customize the table after it has been created. This might be useful for adding indexes to certain columns. 140 | 141 | ```php 142 | class Products extends Model 143 | { 144 | use \Sushi\Sushi; 145 | 146 | protected $rows = [ 147 | ['name' => 'Lawn Mower', 'price' => '226.99'], 148 | ['name' => 'Leaf Blower', 'price' => '134.99'], 149 | ['name' => 'Rake', 'price' => '9.99'], 150 | ]; 151 | 152 | protected function afterMigrate(Blueprint $table) 153 | { 154 | $table->index('name'); 155 | } 156 | } 157 | ``` 158 | 159 | ## How It Works 160 | Under the hood, this package creates and caches a SQLite database JUST for this model. It creates a table and populates the rows. If, for whatever reason, it can't cache a .sqlite file, it will default to using an in-memory sqlite database. 161 | 162 | ## Using ->getRows() 163 | You can optionally opt out of using the `protected $rows` property, and directly implement your own `getRows()` method. 164 | 165 | This will allow you to determine the rows for the model at runtime. You can even generate the model's rows from an external source like a third-party API. 166 | 167 | 168 | ```php 169 | class Role extends Model 170 | { 171 | use \Sushi\Sushi; 172 | 173 | public function getRows() 174 | { 175 | return [ 176 | ['id' => 1, 'label' => 'admin'], 177 | ['id' => 2, 'label' => 'manager'], 178 | ['id' => 3, 'label' => 'user'], 179 | ]; 180 | } 181 | } 182 | ``` 183 | 184 | ### Caching ->getRows() 185 | 186 | If you choose to use your own ->getRows() method, the rows will NOT be cached between requests by default. 187 | 188 | You can force Sushi to cache your dataset with the following method: `sushiShouldCache()`. 189 | 190 | Let's look at a configuration where `->getRows()` datasets would be cached as an example: 191 | 192 | ```php 193 | class Role extends Model 194 | { 195 | use \Sushi\Sushi; 196 | 197 | public function getRows() 198 | { 199 | return [ 200 | ['id' => 1, 'label' => 'admin'], 201 | ['id' => 2, 'label' => 'manager'], 202 | ['id' => 3, 'label' => 'user'], 203 | ]; 204 | } 205 | 206 | protected function sushiShouldCache() 207 | { 208 | return true; 209 | } 210 | } 211 | ``` 212 | 213 | By default, Sushi looks at the "last modified" timestamp of your model PHP file and compares it with its internal `.sqlite` cache file. If the model file has been changed more recently than the `.sqlite` cache file, then Sushi will destroy and rebuild the `.sqlite` cache. 214 | Additionally, you can configure an external file for Sushi to reference when determining if the cache is up to date or needs to be refreshed. 215 | 216 | If, for example, you are using Sushi to provide an Eloquent model for an external data source file like an `.csv` file, you can use `sushiCacheReferencePath` to force Sushi to reference the `.csv` file when determining if the cache is stale. 217 | 218 | For example: 219 | 220 | ```php 221 | class Role extends Model 222 | { 223 | use \Sushi\Sushi; 224 | 225 | public function getRows() 226 | { 227 | return CSV::fromFile(__DIR__.'/roles.csv')->toArray(); 228 | } 229 | 230 | protected function sushiShouldCache() 231 | { 232 | return true; 233 | } 234 | 235 | protected function sushiCacheReferencePath() 236 | { 237 | return __DIR__.'/roles.csv'; 238 | } 239 | } 240 | ``` 241 | 242 | Now, Sushi will only "bust" its internal cache if `roles.csv` changes, rather than looking at the `Role.php` model. 243 | 244 | ### Handling Empty Datasets 245 | Sushi reads the first row in your dataset to work out the scheme of the SQLite table. If you are using `getRows()` and this returns an empty array (e.g an API returns nothing back) then Sushi would throw an error. 246 | 247 | If you would like Sushi to work even if the dataset is empty, you can define your schema in the optional `protected $schema` array. 248 | 249 | > Note: If you choose to use your own ->getRows() method, the rows will NOT be cached between requests. 250 | 251 | ```php 252 | class Currency extends Model 253 | { 254 | use \Sushi\Sushi; 255 | 256 | protected $schema = [ 257 | 'id' => 'integer', 258 | 'name' => 'string', 259 | 'symbol' => 'string', 260 | 'precision' => 'float' 261 | ]; 262 | 263 | public function getRows() 264 | { 265 | return []; 266 | } 267 | } 268 | ``` 269 | 270 | ### Handling String-based Primary Keys 271 | Sushi requires you to add two properties to your model, if it uses a string-based primary key - `$incrementing` and `$keyType`: 272 | 273 | ```php 274 | class Role extends Model 275 | { 276 | use \Sushi\Sushi; 277 | 278 | public $incrementing = false; 279 | 280 | protected $keyType = 'string'; 281 | 282 | protected $rows = [ 283 | ['id' => 'admin', 'label' => 'Admin'], 284 | ['id' => 'manager', 'label' => 'Manager'], 285 | ['id' => 'user', 'label' => 'User'], 286 | ]; 287 | } 288 | ``` 289 | 290 | ### Troubleshoot 291 | 292 | **ERROR:** `SQLSTATE[HY000]: General error: 1 too many SQL variables` 293 | 294 | By default Sushi uses chunks of `100` to insert your data in the SQLite database. In some scenarios this might hit some SQLite limits. 295 | You can configure the chunk size in the model: `public $sushiInsertChunkSize = 50;` 296 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "calebporzio/sushi", 3 | "description": "Eloquent's missing \"array\" driver.", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Caleb Porzio", 8 | "email": "calebporzio@gmail.com" 9 | } 10 | ], 11 | "require": { 12 | "php": "^7.1.3|^8.0", 13 | "ext-pdo_sqlite": "*", 14 | "ext-sqlite3": "*", 15 | "illuminate/database": "^5.8 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0", 16 | "illuminate/support": "^5.8 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0" 17 | }, 18 | "require-dev": { 19 | "doctrine/dbal": "^2.9 || ^3.1.4", 20 | "orchestra/testbench": "3.8.* || 3.9.* || ^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0 || ^10.0", 21 | "phpunit/phpunit": "^7.5 || ^8.4 || ^9.0 || ^10.0 || ^11.0" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "Sushi\\": "src/" 26 | } 27 | }, 28 | "autoload-dev": { 29 | "psr-4": { 30 | "Tests\\": "tests/" 31 | } 32 | }, 33 | "config": { 34 | "sort-packages": true 35 | }, 36 | "minimum-stability": "dev", 37 | "prefer-stable": true 38 | } 39 | -------------------------------------------------------------------------------- /examples/CountryExample.php: -------------------------------------------------------------------------------- 1 | 'AF', 'code3' => 'AFG', 'name' => 'Afghanistan', 'number' => '004'], 13 | ['code' => 'AL', 'code3' => 'ALB', 'name' => 'Albania', 'number' => '008'], 14 | ['code' => 'AX', 'code3' => 'ALA', 'name' => 'Åland Islands', 'number' => '248'], 15 | ['code' => 'DZ', 'code3' => 'DZA', 'name' => 'Algeria', 'number' => '012'], 16 | ['code' => 'AS', 'code3' => 'ASM', 'name' => 'American Samoa', 'number' => '016'], 17 | ['code' => 'AD', 'code3' => 'AND', 'name' => 'Andorra', 'number' => '020'], 18 | ['code' => 'AO', 'code3' => 'AGO', 'name' => 'Angola', 'number' => '024'], 19 | ['code' => 'AI', 'code3' => 'AIA', 'name' => 'Anguilla', 'number' => '660'], 20 | ['code' => 'AQ', 'code3' => 'ATA', 'name' => 'Antarctica', 'number' => '010'], 21 | ['code' => 'AG', 'code3' => 'ATG', 'name' => 'Antigua and Barbuda', 'number' => '028'], 22 | ['code' => 'AR', 'code3' => 'ARG', 'name' => 'Argentina', 'number' => '032'], 23 | ['code' => 'AM', 'code3' => 'ARM', 'name' => 'Armenia', 'number' => '051'], 24 | ['code' => 'AW', 'code3' => 'ABW', 'name' => 'Aruba', 'number' => '533'], 25 | ['code' => 'AU', 'code3' => 'AUS', 'name' => 'Australia', 'number' => '036'], 26 | ['code' => 'AT', 'code3' => 'AUT', 'name' => 'Austria', 'number' => '040'], 27 | ['code' => 'AZ', 'code3' => 'AZE', 'name' => 'Azerbaijan', 'number' => '031'], 28 | ['code' => 'BS', 'code3' => 'BHS', 'name' => 'Bahamas (the)', 'number' => '044'], 29 | ['code' => 'BH', 'code3' => 'BHR', 'name' => 'Bahrain', 'number' => '048'], 30 | ['code' => 'BD', 'code3' => 'BGD', 'name' => 'Bangladesh', 'number' => '050'], 31 | ['code' => 'BB', 'code3' => 'BRB', 'name' => 'Barbados', 'number' => '052'], 32 | ['code' => 'BY', 'code3' => 'BLR', 'name' => 'Belarus', 'number' => '112'], 33 | ['code' => 'BE', 'code3' => 'BEL', 'name' => 'Belgium', 'number' => '056'], 34 | ['code' => 'BZ', 'code3' => 'BLZ', 'name' => 'Belize', 'number' => '084'], 35 | ['code' => 'BJ', 'code3' => 'BEN', 'name' => 'Benin', 'number' => '204'], 36 | ['code' => 'BM', 'code3' => 'BMU', 'name' => 'Bermuda', 'number' => '060'], 37 | ['code' => 'BT', 'code3' => 'BTN', 'name' => 'Bhutan', 'number' => '064'], 38 | ['code' => 'BO', 'code3' => 'BOL', 'name' => 'Bolivia (Plurinational State of)', 'number' => '068'], 39 | ['code' => 'BQ', 'code3' => 'BES', 'name' => 'Bonaire, Sint Eustatius and Saba', 'number' => '535'], 40 | ['code' => 'BA', 'code3' => 'BIH', 'name' => 'Bosnia and Herzegovina', 'number' => '070'], 41 | ['code' => 'BW', 'code3' => 'BWA', 'name' => 'Botswana', 'number' => '072'], 42 | ['code' => 'BV', 'code3' => 'BVT', 'name' => 'Bouvet Island', 'number' => '074'], 43 | ['code' => 'BR', 'code3' => 'BRA', 'name' => 'Brazil', 'number' => '076'], 44 | ['code' => 'IO', 'code3' => 'IOT', 'name' => 'British Indian Ocean Territory (the)', 'number' => '086'], 45 | ['code' => 'BN', 'code3' => 'BRN', 'name' => 'Brunei Darussalam', 'number' => '096'], 46 | ['code' => 'BG', 'code3' => 'BGR', 'name' => 'Bulgaria', 'number' => '100'], 47 | ['code' => 'BF', 'code3' => 'BFA', 'name' => 'Burkina Faso', 'number' => '854'], 48 | ['code' => 'BI', 'code3' => 'BDI', 'name' => 'Burundi', 'number' => '108'], 49 | ['code' => 'CV', 'code3' => 'CPV', 'name' => 'Cabo Verde', 'number' => '132'], 50 | ['code' => 'KH', 'code3' => 'KHM', 'name' => 'Cambodia', 'number' => '116'], 51 | ['code' => 'CM', 'code3' => 'CMR', 'name' => 'Cameroon', 'number' => '120'], 52 | ['code' => 'CA', 'code3' => 'CAN', 'name' => 'Canada', 'number' => '124'], 53 | ['code' => 'KY', 'code3' => 'CYM', 'name' => 'Cayman Islands (the)', 'number' => '136'], 54 | ['code' => 'CF', 'code3' => 'CAF', 'name' => 'Central African Republic (the)', 'number' => '140'], 55 | ['code' => 'TD', 'code3' => 'TCD', 'name' => 'Chad', 'number' => '148'], 56 | ['code' => 'CL', 'code3' => 'CHL', 'name' => 'Chile', 'number' => '152'], 57 | ['code' => 'CN', 'code3' => 'CHN', 'name' => 'China', 'number' => '156'], 58 | ['code' => 'CX', 'code3' => 'CXR', 'name' => 'Christmas Island', 'number' => '162'], 59 | ['code' => 'CC', 'code3' => 'CCK', 'name' => 'Cocos (Keeling) Islands (the)', 'number' => '166'], 60 | ['code' => 'CO', 'code3' => 'COL', 'name' => 'Colombia', 'number' => '170'], 61 | ['code' => 'KM', 'code3' => 'COM', 'name' => 'Comoros (the)', 'number' => '174'], 62 | ['code' => 'CD', 'code3' => 'COD', 'name' => 'Congo (the Democratic Republic of the)', 'number' => '180'], 63 | ['code' => 'CG', 'code3' => 'COG', 'name' => 'Congo (the)', 'number' => '178'], 64 | ['code' => 'CK', 'code3' => 'COK', 'name' => 'Cook Islands (the)', 'number' => '184'], 65 | ['code' => 'CR', 'code3' => 'CRI', 'name' => 'Costa Rica', 'number' => '188'], 66 | ['code' => 'HR', 'code3' => 'HRV', 'name' => 'Croatia', 'number' => '191'], 67 | ['code' => 'CU', 'code3' => 'CUB', 'name' => 'Cuba', 'number' => '192'], 68 | ['code' => 'CW', 'code3' => 'CUW', 'name' => 'Curaçao', 'number' => '531'], 69 | ['code' => 'CY', 'code3' => 'CYP', 'name' => 'Cyprus', 'number' => '196'], 70 | ['code' => 'CZ', 'code3' => 'CZE', 'name' => 'Czechia', 'number' => '203'], 71 | ['code' => 'CI', 'code3' => 'CIV', 'name' => 'Côte d\'Ivoire', 'number' => '384'], 72 | ['code' => 'DK', 'code3' => 'DNK', 'name' => 'Denmark', 'number' => '208'], 73 | ['code' => 'DJ', 'code3' => 'DJI', 'name' => 'Djibouti', 'number' => '262'], 74 | ['code' => 'DM', 'code3' => 'DMA', 'name' => 'Dominica', 'number' => '212'], 75 | ['code' => 'DO', 'code3' => 'DOM', 'name' => 'Dominican Republic (the)', 'number' => '214'], 76 | ['code' => 'EC', 'code3' => 'ECU', 'name' => 'Ecuador', 'number' => '218'], 77 | ['code' => 'EG', 'code3' => 'EGY', 'name' => 'Egypt', 'number' => '818'], 78 | ['code' => 'SV', 'code3' => 'SLV', 'name' => 'El Salvador', 'number' => '222'], 79 | ['code' => 'GQ', 'code3' => 'GNQ', 'name' => 'Equatorial Guinea', 'number' => '226'], 80 | ['code' => 'ER', 'code3' => 'ERI', 'name' => 'Eritrea', 'number' => '232'], 81 | ['code' => 'EE', 'code3' => 'EST', 'name' => 'Estonia', 'number' => '233'], 82 | ['code' => 'SZ', 'code3' => 'SWZ', 'name' => 'Eswatini', 'number' => '748'], 83 | ['code' => 'ET', 'code3' => 'ETH', 'name' => 'Ethiopia', 'number' => '231'], 84 | ['code' => 'FK', 'code3' => 'FLK', 'name' => 'Falkland Islands (the) [Malvinas]', 'number' => '238'], 85 | ['code' => 'FO', 'code3' => 'FRO', 'name' => 'Faroe Islands (the)', 'number' => '234'], 86 | ['code' => 'FJ', 'code3' => 'FJI', 'name' => 'Fiji', 'number' => '242'], 87 | ['code' => 'FI', 'code3' => 'FIN', 'name' => 'Finland', 'number' => '246'], 88 | ['code' => 'FR', 'code3' => 'FRA', 'name' => 'France', 'number' => '250'], 89 | ['code' => 'GF', 'code3' => 'GUF', 'name' => 'French Guiana', 'number' => '254'], 90 | ['code' => 'PF', 'code3' => 'PYF', 'name' => 'French Polynesia', 'number' => '258'], 91 | ['code' => 'TF', 'code3' => 'ATF', 'name' => 'French Southern Territories (the)', 'number' => '260'], 92 | ['code' => 'GA', 'code3' => 'GAB', 'name' => 'Gabon', 'number' => '266'], 93 | ['code' => 'GM', 'code3' => 'GMB', 'name' => 'Gambia (the)', 'number' => '270'], 94 | ['code' => 'GE', 'code3' => 'GEO', 'name' => 'Georgia', 'number' => '268'], 95 | ['code' => 'DE', 'code3' => 'DEU', 'name' => 'Germany', 'number' => '276'], 96 | ['code' => 'GH', 'code3' => 'GHA', 'name' => 'Ghana', 'number' => '288'], 97 | ['code' => 'GI', 'code3' => 'GIB', 'name' => 'Gibraltar', 'number' => '292'], 98 | ['code' => 'GR', 'code3' => 'GRC', 'name' => 'Greece', 'number' => '300'], 99 | ['code' => 'GL', 'code3' => 'GRL', 'name' => 'Greenland', 'number' => '304'], 100 | ['code' => 'GD', 'code3' => 'GRD', 'name' => 'Grenada', 'number' => '308'], 101 | ['code' => 'GP', 'code3' => 'GLP', 'name' => 'Guadeloupe', 'number' => '312'], 102 | ['code' => 'GU', 'code3' => 'GUM', 'name' => 'Guam', 'number' => '316'], 103 | ['code' => 'GT', 'code3' => 'GTM', 'name' => 'Guatemala', 'number' => '320'], 104 | ['code' => 'GG', 'code3' => 'GGY', 'name' => 'Guernsey', 'number' => '831'], 105 | ['code' => 'GN', 'code3' => 'GIN', 'name' => 'Guinea', 'number' => '324'], 106 | ['code' => 'GW', 'code3' => 'GNB', 'name' => 'Guinea-Bissau', 'number' => '624'], 107 | ['code' => 'GY', 'code3' => 'GUY', 'name' => 'Guyana', 'number' => '328'], 108 | ['code' => 'HT', 'code3' => 'HTI', 'name' => 'Haiti', 'number' => '332'], 109 | ['code' => 'HM', 'code3' => 'HMD', 'name' => 'Heard Island and McDonald Islands', 'number' => '334'], 110 | ['code' => 'VA', 'code3' => 'VAT', 'name' => 'Holy See (the)', 'number' => '336'], 111 | ['code' => 'HN', 'code3' => 'HND', 'name' => 'Honduras', 'number' => '340'], 112 | ['code' => 'HK', 'code3' => 'HKG', 'name' => 'Hong Kong', 'number' => '344'], 113 | ['code' => 'HU', 'code3' => 'HUN', 'name' => 'Hungary', 'number' => '348'], 114 | ['code' => 'IS', 'code3' => 'ISL', 'name' => 'Iceland', 'number' => '352'], 115 | ['code' => 'IN', 'code3' => 'IND', 'name' => 'India', 'number' => '356'], 116 | ['code' => 'ID', 'code3' => 'IDN', 'name' => 'Indonesia', 'number' => '360'], 117 | ['code' => 'IR', 'code3' => 'IRN', 'name' => 'Iran (Islamic Republic of)', 'number' => '364'], 118 | ['code' => 'IQ', 'code3' => 'IRQ', 'name' => 'Iraq', 'number' => '368'], 119 | ['code' => 'IE', 'code3' => 'IRL', 'name' => 'Ireland', 'number' => '372'], 120 | ['code' => 'IM', 'code3' => 'IMN', 'name' => 'Isle of Man', 'number' => '833'], 121 | ['code' => 'IL', 'code3' => 'ISR', 'name' => 'Israel', 'number' => '376'], 122 | ['code' => 'IT', 'code3' => 'ITA', 'name' => 'Italy', 'number' => '380'], 123 | ['code' => 'JM', 'code3' => 'JAM', 'name' => 'Jamaica', 'number' => '388'], 124 | ['code' => 'JP', 'code3' => 'JPN', 'name' => 'Japan', 'number' => '392'], 125 | ['code' => 'JE', 'code3' => 'JEY', 'name' => 'Jersey', 'number' => '832'], 126 | ['code' => 'JO', 'code3' => 'JOR', 'name' => 'Jordan', 'number' => '400'], 127 | ['code' => 'KZ', 'code3' => 'KAZ', 'name' => 'Kazakhstan', 'number' => '398'], 128 | ['code' => 'KE', 'code3' => 'KEN', 'name' => 'Kenya', 'number' => '404'], 129 | ['code' => 'KI', 'code3' => 'KIR', 'name' => 'Kiribati', 'number' => '296'], 130 | ['code' => 'KP', 'code3' => 'PRK', 'name' => 'Korea (the Democratic People\'s Republic of)', 'number' => '408'], 131 | ['code' => 'KR', 'code3' => 'KOR', 'name' => 'Korea (the Republic of)', 'number' => '410'], 132 | ['code' => 'KW', 'code3' => 'KWT', 'name' => 'Kuwait', 'number' => '414'], 133 | ['code' => 'KG', 'code3' => 'KGZ', 'name' => 'Kyrgyzstan', 'number' => '417'], 134 | ['code' => 'LA', 'code3' => 'LAO', 'name' => 'Lao People\'s Democratic Republic (the)', 'number' => '418'], 135 | ['code' => 'LV', 'code3' => 'LVA', 'name' => 'Latvia', 'number' => '428'], 136 | ['code' => 'LB', 'code3' => 'LBN', 'name' => 'Lebanon', 'number' => '422'], 137 | ['code' => 'LS', 'code3' => 'LSO', 'name' => 'Lesotho', 'number' => '426'], 138 | ['code' => 'LR', 'code3' => 'LBR', 'name' => 'Liberia', 'number' => '430'], 139 | ['code' => 'LY', 'code3' => 'LBY', 'name' => 'Libya', 'number' => '434'], 140 | ['code' => 'LI', 'code3' => 'LIE', 'name' => 'Liechtenstein', 'number' => '438'], 141 | ['code' => 'LT', 'code3' => 'LTU', 'name' => 'Lithuania', 'number' => '440'], 142 | ['code' => 'LU', 'code3' => 'LUX', 'name' => 'Luxembourg', 'number' => '442'], 143 | ['code' => 'MO', 'code3' => 'MAC', 'name' => 'Macao', 'number' => '446'], 144 | ['code' => 'MG', 'code3' => 'MDG', 'name' => 'Madagascar', 'number' => '450'], 145 | ['code' => 'MW', 'code3' => 'MWI', 'name' => 'Malawi', 'number' => '454'], 146 | ['code' => 'MY', 'code3' => 'MYS', 'name' => 'Malaysia', 'number' => '458'], 147 | ['code' => 'MV', 'code3' => 'MDV', 'name' => 'Maldives', 'number' => '462'], 148 | ['code' => 'ML', 'code3' => 'MLI', 'name' => 'Mali', 'number' => '466'], 149 | ['code' => 'MT', 'code3' => 'MLT', 'name' => 'Malta', 'number' => '470'], 150 | ['code' => 'MH', 'code3' => 'MHL', 'name' => 'Marshall Islands (the)', 'number' => '584'], 151 | ['code' => 'MQ', 'code3' => 'MTQ', 'name' => 'Martinique', 'number' => '474'], 152 | ['code' => 'MR', 'code3' => 'MRT', 'name' => 'Mauritania', 'number' => '478'], 153 | ['code' => 'MU', 'code3' => 'MUS', 'name' => 'Mauritius', 'number' => '480'], 154 | ['code' => 'YT', 'code3' => 'MYT', 'name' => 'Mayotte', 'number' => '175'], 155 | ['code' => 'MX', 'code3' => 'MEX', 'name' => 'Mexico', 'number' => '484'], 156 | ['code' => 'FM', 'code3' => 'FSM', 'name' => 'Micronesia (Federated States of)', 'number' => '583'], 157 | ['code' => 'MD', 'code3' => 'MDA', 'name' => 'Moldova (the Republic of)', 'number' => '498'], 158 | ['code' => 'MC', 'code3' => 'MCO', 'name' => 'Monaco', 'number' => '492'], 159 | ['code' => 'MN', 'code3' => 'MNG', 'name' => 'Mongolia', 'number' => '496'], 160 | ['code' => 'ME', 'code3' => 'MNE', 'name' => 'Montenegro', 'number' => '499'], 161 | ['code' => 'MS', 'code3' => 'MSR', 'name' => 'Montserrat', 'number' => '500'], 162 | ['code' => 'MA', 'code3' => 'MAR', 'name' => 'Morocco', 'number' => '504'], 163 | ['code' => 'MZ', 'code3' => 'MOZ', 'name' => 'Mozambique', 'number' => '508'], 164 | ['code' => 'MM', 'code3' => 'MMR', 'name' => 'Myanmar', 'number' => '104'], 165 | ['code' => 'NA', 'code3' => 'NAM', 'name' => 'Namibia', 'number' => '516'], 166 | ['code' => 'NR', 'code3' => 'NRU', 'name' => 'Nauru', 'number' => '520'], 167 | ['code' => 'NP', 'code3' => 'NPL', 'name' => 'Nepal', 'number' => '524'], 168 | ['code' => 'NL', 'code3' => 'NLD', 'name' => 'Netherlands (the)', 'number' => '528'], 169 | ['code' => 'NC', 'code3' => 'NCL', 'name' => 'New Caledonia', 'number' => '540'], 170 | ['code' => 'NZ', 'code3' => 'NZL', 'name' => 'New Zealand', 'number' => '554'], 171 | ['code' => 'NI', 'code3' => 'NIC', 'name' => 'Nicaragua', 'number' => '558'], 172 | ['code' => 'NE', 'code3' => 'NER', 'name' => 'Niger (the)', 'number' => '562'], 173 | ['code' => 'NG', 'code3' => 'NGA', 'name' => 'Nigeria', 'number' => '566'], 174 | ['code' => 'NU', 'code3' => 'NIU', 'name' => 'Niue', 'number' => '570'], 175 | ['code' => 'NF', 'code3' => 'NFK', 'name' => 'Norfolk Island', 'number' => '574'], 176 | ['code' => 'MP', 'code3' => 'MNP', 'name' => 'Northern Mariana Islands (the)', 'number' => '580'], 177 | ['code' => 'NO', 'code3' => 'NOR', 'name' => 'Norway', 'number' => '578'], 178 | ['code' => 'OM', 'code3' => 'OMN', 'name' => 'Oman', 'number' => '512'], 179 | ['code' => 'PK', 'code3' => 'PAK', 'name' => 'Pakistan', 'number' => '586'], 180 | ['code' => 'PW', 'code3' => 'PLW', 'name' => 'Palau', 'number' => '585'], 181 | ['code' => 'PS', 'code3' => 'PSE', 'name' => 'Palestine, State of', 'number' => '275'], 182 | ['code' => 'PA', 'code3' => 'PAN', 'name' => 'Panama', 'number' => '591'], 183 | ['code' => 'PG', 'code3' => 'PNG', 'name' => 'Papua New Guinea', 'number' => '598'], 184 | ['code' => 'PY', 'code3' => 'PRY', 'name' => 'Paraguay', 'number' => '600'], 185 | ['code' => 'PE', 'code3' => 'PER', 'name' => 'Peru', 'number' => '604'], 186 | ['code' => 'PH', 'code3' => 'PHL', 'name' => 'Philippines (the)', 'number' => '608'], 187 | ['code' => 'PN', 'code3' => 'PCN', 'name' => 'Pitcairn', 'number' => '612'], 188 | ['code' => 'PL', 'code3' => 'POL', 'name' => 'Poland', 'number' => '616'], 189 | ['code' => 'PT', 'code3' => 'PRT', 'name' => 'Portugal', 'number' => '620'], 190 | ['code' => 'PR', 'code3' => 'PRI', 'name' => 'Puerto Rico', 'number' => '630'], 191 | ['code' => 'QA', 'code3' => 'QAT', 'name' => 'Qatar', 'number' => '634'], 192 | ['code' => 'MK', 'code3' => 'MKD', 'name' => 'Republic of North Macedonia', 'number' => '807'], 193 | ['code' => 'RO', 'code3' => 'ROU', 'name' => 'Romania', 'number' => '642'], 194 | ['code' => 'RU', 'code3' => 'RUS', 'name' => 'Russian Federation (the)', 'number' => '643'], 195 | ['code' => 'RW', 'code3' => 'RWA', 'name' => 'Rwanda', 'number' => '646'], 196 | ['code' => 'RE', 'code3' => 'REU', 'name' => 'Réunion', 'number' => '638'], 197 | ['code' => 'BL', 'code3' => 'BLM', 'name' => 'Saint Barthélemy', 'number' => '652'], 198 | ['code' => 'SH', 'code3' => 'SHN', 'name' => 'Saint Helena, Ascension and Tristan da Cunha', 'number' => '654'], 199 | ['code' => 'KN', 'code3' => 'KNA', 'name' => 'Saint Kitts and Nevis', 'number' => '659'], 200 | ['code' => 'LC', 'code3' => 'LCA', 'name' => 'Saint Lucia', 'number' => '662'], 201 | ['code' => 'MF', 'code3' => 'MAF', 'name' => 'Saint Martin (French part)', 'number' => '663'], 202 | ['code' => 'PM', 'code3' => 'SPM', 'name' => 'Saint Pierre and Miquelon', 'number' => '666'], 203 | ['code' => 'VC', 'code3' => 'VCT', 'name' => 'Saint Vincent and the Grenadines', 'number' => '670'], 204 | ['code' => 'WS', 'code3' => 'WSM', 'name' => 'Samoa', 'number' => '882'], 205 | ['code' => 'SM', 'code3' => 'SMR', 'name' => 'San Marino', 'number' => '674'], 206 | ['code' => 'ST', 'code3' => 'STP', 'name' => 'Sao Tome and Principe', 'number' => '678'], 207 | ['code' => 'SA', 'code3' => 'SAU', 'name' => 'Saudi Arabia', 'number' => '682'], 208 | ['code' => 'SN', 'code3' => 'SEN', 'name' => 'Senegal', 'number' => '686'], 209 | ['code' => 'RS', 'code3' => 'SRB', 'name' => 'Serbia', 'number' => '688'], 210 | ['code' => 'SC', 'code3' => 'SYC', 'name' => 'Seychelles', 'number' => '690'], 211 | ['code' => 'SL', 'code3' => 'SLE', 'name' => 'Sierra Leone', 'number' => '694'], 212 | ['code' => 'SG', 'code3' => 'SGP', 'name' => 'Singapore', 'number' => '702'], 213 | ['code' => 'SX', 'code3' => 'SXM', 'name' => 'Sint Maarten (Dutch part)', 'number' => '534'], 214 | ['code' => 'SK', 'code3' => 'SVK', 'name' => 'Slovakia', 'number' => '703'], 215 | ['code' => 'SI', 'code3' => 'SVN', 'name' => 'Slovenia', 'number' => '705'], 216 | ['code' => 'SB', 'code3' => 'SLB', 'name' => 'Solomon Islands', 'number' => '090'], 217 | ['code' => 'SO', 'code3' => 'SOM', 'name' => 'Somalia', 'number' => '706'], 218 | ['code' => 'ZA', 'code3' => 'ZAF', 'name' => 'South Africa', 'number' => '710'], 219 | ['code' => 'GS', 'code3' => 'SGS', 'name' => 'South Georgia and the South Sandwich Islands', 'number' => '239'], 220 | ['code' => 'SS', 'code3' => 'SSD', 'name' => 'South Sudan', 'number' => '728'], 221 | ['code' => 'ES', 'code3' => 'ESP', 'name' => 'Spain', 'number' => '724'], 222 | ['code' => 'LK', 'code3' => 'LKA', 'name' => 'Sri Lanka', 'number' => '144'], 223 | ['code' => 'SD', 'code3' => 'SDN', 'name' => 'Sudan (the)', 'number' => '729'], 224 | ['code' => 'SR', 'code3' => 'SUR', 'name' => 'Suriname', 'number' => '740'], 225 | ['code' => 'SJ', 'code3' => 'SJM', 'name' => 'Svalbard and Jan Mayen', 'number' => '744'], 226 | ['code' => 'SE', 'code3' => 'SWE', 'name' => 'Sweden', 'number' => '752'], 227 | ['code' => 'CH', 'code3' => 'CHE', 'name' => 'Switzerland', 'number' => '756'], 228 | ['code' => 'SY', 'code3' => 'SYR', 'name' => 'Syrian Arab Republic', 'number' => '760'], 229 | ['code' => 'TW', 'code3' => 'TWN', 'name' => 'Taiwan', 'number' => '158'], 230 | ['code' => 'TJ', 'code3' => 'TJK', 'name' => 'Tajikistan', 'number' => '762'], 231 | ['code' => 'TZ', 'code3' => 'TZA', 'name' => 'Tanzania, United Republic of', 'number' => '834'], 232 | ['code' => 'TH', 'code3' => 'THA', 'name' => 'Thailand', 'number' => '764'], 233 | ['code' => 'TL', 'code3' => 'TLS', 'name' => 'Timor-Leste', 'number' => '626'], 234 | ['code' => 'TG', 'code3' => 'TGO', 'name' => 'Togo', 'number' => '768'], 235 | ['code' => 'TK', 'code3' => 'TKL', 'name' => 'Tokelau', 'number' => '772'], 236 | ['code' => 'TO', 'code3' => 'TON', 'name' => 'Tonga', 'number' => '776'], 237 | ['code' => 'TT', 'code3' => 'TTO', 'name' => 'Trinidad and Tobago', 'number' => '780'], 238 | ['code' => 'TN', 'code3' => 'TUN', 'name' => 'Tunisia', 'number' => '788'], 239 | ['code' => 'TR', 'code3' => 'TUR', 'name' => 'Turkey', 'number' => '792'], 240 | ['code' => 'TM', 'code3' => 'TKM', 'name' => 'Turkmenistan', 'number' => '795'], 241 | ['code' => 'TC', 'code3' => 'TCA', 'name' => 'Turks and Caicos Islands (the)', 'number' => '796'], 242 | ['code' => 'TV', 'code3' => 'TUV', 'name' => 'Tuvalu', 'number' => '798'], 243 | ['code' => 'UG', 'code3' => 'UGA', 'name' => 'Uganda', 'number' => '800'], 244 | ['code' => 'UA', 'code3' => 'UKR', 'name' => 'Ukraine', 'number' => '804'], 245 | ['code' => 'AE', 'code3' => 'ARE', 'name' => 'United Arab Emirates (the)', 'number' => '784'], 246 | ['code' => 'GB', 'code3' => 'GBR', 'name' => 'United Kingdom of Great Britain and Northern Ireland (the)', 'number' => '826'], 247 | ['code' => 'UM', 'code3' => 'UMI', 'name' => 'United States Minor Outlying Islands (the)', 'number' => '581'], 248 | ['code' => 'US', 'code3' => 'USA', 'name' => 'United States of America (the)', 'number' => '840'], 249 | ['code' => 'UY', 'code3' => 'URY', 'name' => 'Uruguay', 'number' => '858'], 250 | ['code' => 'UZ', 'code3' => 'UZB', 'name' => 'Uzbekistan', 'number' => '860'], 251 | ['code' => 'VU', 'code3' => 'VUT', 'name' => 'Vanuatu', 'number' => '548'], 252 | ['code' => 'VE', 'code3' => 'VEN', 'name' => 'Venezuela (Bolivarian Republic of)', 'number' => '862'], 253 | ['code' => 'VN', 'code3' => 'VNM', 'name' => 'Viet Nam', 'number' => '704'], 254 | ['code' => 'VG', 'code3' => 'VGB', 'name' => 'Virgin Islands (British)', 'number' => '092'], 255 | ['code' => 'VI', 'code3' => 'VIR', 'name' => 'Virgin Islands (U.S.)', 'number' => '850'], 256 | ['code' => 'WF', 'code3' => 'WLF', 'name' => 'Wallis and Futuna', 'number' => '876'], 257 | ['code' => 'EH', 'code3' => 'ESH', 'name' => 'Western Sahara', 'number' => '732'], 258 | ['code' => 'YE', 'code3' => 'YEM', 'name' => 'Yemen', 'number' => '887'], 259 | ['code' => 'ZM', 'code3' => 'ZMB', 'name' => 'Zambia', 'number' => '894'], 260 | ['code' => 'ZW', 'code3' => 'ZWE', 'name' => 'Zimbabwe', 'number' => '716'], 261 | ]; 262 | } 263 | -------------------------------------------------------------------------------- /src/Sushi.php: -------------------------------------------------------------------------------- 1 | rows; 18 | } 19 | 20 | public function getSchema() 21 | { 22 | return $this->schema ?? []; 23 | } 24 | 25 | protected function sushiCacheReferencePath() 26 | { 27 | return (new \ReflectionClass(static::class))->getFileName(); 28 | } 29 | 30 | protected function sushiShouldCache() 31 | { 32 | return property_exists(static::class, 'rows'); 33 | } 34 | 35 | public static function resolveConnection($connection = null) 36 | { 37 | return static::$sushiConnection; 38 | } 39 | 40 | protected function sushiCachePath() 41 | { 42 | return implode(DIRECTORY_SEPARATOR, [ 43 | $this->sushiCacheDirectory(), 44 | $this->sushiCacheFileName(), 45 | ]); 46 | } 47 | 48 | protected function sushiCacheFileName() 49 | { 50 | return config('sushi.cache-prefix', 'sushi').'-'.Str::kebab(str_replace('\\', '', static::class)).'.sqlite'; 51 | } 52 | 53 | protected function sushiCacheDirectory() 54 | { 55 | return realpath(config('sushi.cache-path', storage_path('framework/cache'))); 56 | } 57 | 58 | public static function bootSushi() 59 | { 60 | $instance = (new static); 61 | 62 | $cachePath = $instance->sushiCachePath(); 63 | $dataPath = $instance->sushiCacheReferencePath(); 64 | 65 | $states = [ 66 | 'cache-file-found-and-up-to-date' => function () use ($cachePath) { 67 | static::setSqliteConnection($cachePath); 68 | }, 69 | 'cache-file-not-found-or-stale' => function () use ($cachePath, $dataPath, $instance) { 70 | static::cacheFileNotFoundOrStale($cachePath, $dataPath, $instance); 71 | }, 72 | 'no-caching-capabilities' => function () use ($instance) { 73 | static::setSqliteConnection(':memory:'); 74 | 75 | $instance->migrate(); 76 | }, 77 | ]; 78 | 79 | switch (true) { 80 | case ! $instance->sushiShouldCache(): 81 | $states['no-caching-capabilities'](); 82 | break; 83 | 84 | case file_exists($cachePath) && filemtime($dataPath) <= filemtime($cachePath): 85 | $states['cache-file-found-and-up-to-date'](); 86 | break; 87 | 88 | case file_exists($instance->sushiCacheDirectory()) && is_writable($instance->sushiCacheDirectory()): 89 | $states['cache-file-not-found-or-stale'](); 90 | break; 91 | 92 | default: 93 | $states['no-caching-capabilities'](); 94 | break; 95 | } 96 | } 97 | 98 | protected static function cacheFileNotFoundOrStale($cachePath, $dataPath, $instance) 99 | { 100 | file_put_contents($cachePath, ''); 101 | 102 | static::setSqliteConnection($cachePath); 103 | 104 | $instance->migrate(); 105 | 106 | touch($cachePath, filemtime($dataPath)); 107 | } 108 | 109 | protected function newRelatedInstance($class) 110 | { 111 | return tap(new $class, function ($instance) { 112 | if (!$instance->getConnectionName()) { 113 | $instance->setConnection($this->getConnectionResolver()->getDefaultConnection()); 114 | } 115 | }); 116 | } 117 | 118 | protected static function setSqliteConnection($database) 119 | { 120 | $config = [ 121 | 'driver' => 'sqlite', 122 | 'database' => $database, 123 | ]; 124 | 125 | static::$sushiConnection = app(ConnectionFactory::class)->make($config); 126 | 127 | app('config')->set('database.connections.'.static::class, $config); 128 | } 129 | 130 | public function migrate() 131 | { 132 | $rows = $this->getRows(); 133 | $tableName = $this->getTable(); 134 | 135 | if (count($rows)) { 136 | $this->createTable($tableName, $rows[0]); 137 | } else { 138 | $this->createTableWithNoData($tableName); 139 | } 140 | 141 | foreach (array_chunk($rows, $this->getSushiInsertChunkSize()) ?? [] as $inserts) { 142 | if (! empty($inserts)) { 143 | static::insert($inserts); 144 | } 145 | } 146 | } 147 | 148 | public function createTable(string $tableName, $firstRow) 149 | { 150 | $this->createTableSafely($tableName, function ($table) use ($firstRow) { 151 | // Add the "id" column if it doesn't already exist in the rows. 152 | if ($this->incrementing && ! array_key_exists($this->primaryKey, $firstRow)) { 153 | $table->increments($this->primaryKey); 154 | } 155 | 156 | foreach ($firstRow as $column => $value) { 157 | switch (true) { 158 | case is_int($value): 159 | $type = 'integer'; 160 | break; 161 | case is_numeric($value): 162 | $type = 'float'; 163 | break; 164 | case is_string($value): 165 | $type = 'string'; 166 | break; 167 | case is_object($value) && $value instanceof \DateTime: 168 | $type = 'dateTime'; 169 | break; 170 | default: 171 | $type = 'string'; 172 | } 173 | 174 | if ($column === $this->primaryKey && $type == 'integer') { 175 | $table->increments($this->primaryKey); 176 | continue; 177 | } 178 | 179 | $schema = $this->getSchema(); 180 | 181 | $type = $schema[$column] ?? $type; 182 | 183 | $table->{$type}($column)->nullable(); 184 | } 185 | 186 | if ($this->usesTimestamps() && (! in_array('updated_at', array_keys($firstRow)) || ! in_array('created_at', array_keys($firstRow)))) { 187 | $table->timestamps(); 188 | } 189 | 190 | $this->afterMigrate($table); 191 | }); 192 | } 193 | 194 | protected function afterMigrate(BluePrint $table) 195 | { 196 | // 197 | } 198 | 199 | public function createTableWithNoData(string $tableName) 200 | { 201 | $this->createTableSafely($tableName, function ($table) { 202 | $schema = $this->getSchema(); 203 | 204 | if ($this->incrementing && ! in_array($this->primaryKey, array_keys($schema))) { 205 | $table->increments($this->primaryKey); 206 | } 207 | 208 | foreach ($schema as $name => $type) { 209 | if ($name === $this->primaryKey && $type == 'integer') { 210 | $table->increments($this->primaryKey); 211 | continue; 212 | } 213 | 214 | $table->{$type}($name)->nullable(); 215 | } 216 | 217 | if ($this->usesTimestamps() && (! in_array('updated_at', array_keys($schema)) || ! in_array('created_at', array_keys($schema)))) { 218 | $table->timestamps(); 219 | } 220 | }); 221 | } 222 | 223 | protected function createTableSafely(string $tableName, Closure $callback) 224 | { 225 | /** @var \Illuminate\Database\Schema\SQLiteBuilder $schemaBuilder */ 226 | $schemaBuilder = static::resolveConnection()->getSchemaBuilder(); 227 | 228 | try { 229 | $schemaBuilder->create($tableName, $callback); 230 | } catch (QueryException $e) { 231 | if (Str::contains($e->getMessage(), [ 232 | 'already exists (SQL: create table', 233 | sprintf('table "%s" already exists', $tableName), 234 | ])) { 235 | // This error can happen in rare circumstances due to a race condition. 236 | // Concurrent requests may both see the necessary preconditions for 237 | // the table creation, but only one can actually succeed. 238 | return; 239 | } 240 | 241 | throw $e; 242 | } 243 | } 244 | 245 | public function usesTimestamps() 246 | { 247 | // Override the Laravel default value of $timestamps = true; Unless otherwise set. 248 | return (new \ReflectionClass($this))->getProperty('timestamps')->class === static::class 249 | ? parent::usesTimestamps() 250 | : false; 251 | } 252 | 253 | public function getSushiInsertChunkSize() { 254 | return $this->sushiInsertChunkSize ?? 100; 255 | } 256 | 257 | public function getConnectionName() 258 | { 259 | return static::class; 260 | } 261 | } 262 | --------------------------------------------------------------------------------