├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── composer.json ├── config └── database-dump.php ├── database ├── factories │ └── ModelFactory.php └── migrations │ └── create_database_dump_table.php.stub ├── resources ├── stubs │ └── .gitignore.stub └── views │ └── .gitkeep ├── sample-dump.json └── src ├── Commands ├── DatabaseDumpCommand.php └── FreshCommand.php ├── DatabaseDump.php ├── DatabaseDumpServiceProvider.php ├── Facades └── DatabaseDump.php └── tests └── Feature └── DatabaseDumpTest.php /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `database-dump` will be documented in this file. 4 | 5 | ## 4.1 - 2024-08-22 6 | 7 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/4...4.1 8 | 9 | ## Introduce isConsistent with database - 2024-08-02 10 | 11 | This function check the records we have in the dump file against what is in the database, this is especially useful when you drop database and seed it back, to be sure all records are back before bringing the application up 12 | 13 | ## 3.1 - 2024-07-11 14 | 15 | This version decreases downtime when generating database dump 16 | 17 | We generate a schema that contains number of rows in each table and limit the records added by that. 18 | 19 | This maintains the relationships and allows application to continue running while dump is being generated. 20 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/3.0...3.1 21 | 22 | ## Better handling of JSON delimiter - 2024-06-22 23 | 24 | The previous versions used the search for }, to get the existence of valid JSON. But since there can be nested JSON, this can throw an error, since it is not valid JSON. 25 | 26 | To solve this issue, a delimiter string is added at the end of each row, when this delimiter is found, it indicates the end of the row 27 | 28 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/2.1.1...3.0 29 | 30 | ## 2.1.1 - 2024-06-15 31 | 32 | ### What's Changed 33 | 34 | * chore(deps): bump aglipanci/laravel-pint-action from 2.3.1 to 2.4 by @dependabot in https://github.com/justinkekeocha/database-dump/pull/4 35 | 36 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/2.1...2.1.1 37 | 38 | ## 2.1 - 2024-04-15 39 | 40 | This version uses `stream_get_line`for better JSON detection. 41 | 42 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/2.0...2.1 43 | 44 | ## 2.0 - 2024-04-15 45 | 46 | This version uses a memory efficient method of streaming the records in the dump file using `fread` function and yielding the result. This entails that there is only one record in memory at any point in time. With this approach, this package can read a theoretical large size of file without exhausting memory. 47 | 48 | When the seed method is called first, it reads the whole file and generates a schema that stores the offset of the tables in the file before it starts the seeding action. This schema is created so subsequent seed calls on the same instance (obviously the same file) will just move to the file offset where the table was last found and start reading from the offset 49 | 50 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/1.5.1...2.0 51 | 52 | ## fix empty rows in tables - 2024-03-23 53 | 54 | This release fixes empty rows in tables due to character encoding 55 | 56 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/1.5...1.5.1 57 | 58 | ## add support for laravel 9 & 10 - 2024-03-22 59 | 60 | ### What's Changed 61 | 62 | * chore(deps): bump ramsey/composer-install from 2 to 3 by @dependabot in https://github.com/justinkekeocha/database-dump/pull/2 63 | 64 | Illuminate contract was update to support multiple versions 65 | 66 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/1.4.1...1.5 67 | 68 | ## fix copying of commands - 2024-02-13 69 | 70 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/1.4...1.4.1 71 | 72 | ## 1.4 - 2024-02-09 73 | 74 | Make more methods chainable 75 | 76 | **Full Changelog**: https://github.com/justinkekeocha/database-dump/compare/1.3...1.4 77 | 78 | ## 1.3 - 2024-02-09 79 | 80 | New methods were added to aid in seeding the database with dump file. 81 | 82 | ## 1.1.2 - 2023-12-16 83 | 84 | This release refactors the process of generating dump files. Instead of looping through the whole tables at once and adding to the dump file, the new release, chunks the records of each table and streams them into file. This refactored process, prevents hitting of memory limit. 85 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) justinkekeocha 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Latest Version on Packagist](https://img.shields.io/packagist/v/justinkekeocha/database-dump.svg?style=flat-square)](https://packagist.org/packages/justinkekeocha/database-dump) 2 | [![GitHub Tests Action Status](https://img.shields.io/github/actions/workflow/status/justinkekeocha/database-dump/run-tests.yml?branch=main&label=tests&style=flat-square)](https://github.com/justinkekeocha/database-dump/actions?query=workflow%3Arun-tests+branch%3Amain) 3 | [![GitHub Code Style Action Status](https://img.shields.io/github/actions/workflow/status/justinkekeocha/database-dump/fix-php-code-style-issues.yml?branch=main&label=code%20style&style=flat-square)](https://github.com/justinkekeocha/database-dump/actions?query=workflow%3A"Fix+PHP+code+style+issues"+branch%3Amain) 4 | [![Total Downloads](https://img.shields.io/packagist/dt/justinkekeocha/database-dump.svg?style=flat-square)](https://packagist.org/packages/justinkekeocha/database-dump) 5 | 6 | This package enhances the `migrate:fresh` command by creating a dump of your database, allowing you to make migration changes and then re-seed the database with the previous data. This is particularly useful for developers who need to preserve their data before running migrations. 7 | 8 | Utilizing a memory-efficient method, this package streams records from the dump file, ensuring only one record is in memory at any given time. This approach allows it to handle theoretically infinite file sizes without exhausting memory. 9 | 10 | Inspired by the export function in phpMyAdmin, this package not only enables you to restore your data but also provides the flexibility to alter the data during the seeding process. 11 | 12 | ## Contents 13 | 14 | - [Installation](#installation) 15 | - [Usage](#usage) 16 | - [Dump database data](#dump-database-data) 17 | - [Seed database with dump file](#seed-database-with-dump-file) 18 | - [Get specific dump file](#get-specific-dump-file) 19 | - [Seed table](#seed-table) 20 | - [Sample](#sample) 21 | - [Testing](#testing) 22 | - [Changelog](#changelog) 23 | - [Contributing](#contributing) 24 | - [Credits](#credits) 25 | - [License](#license) 26 | 27 | ## Installation 28 | 29 | You can install the package via composer: 30 | 31 | ```bash 32 | composer require justinkekeocha/database-dump 33 | ``` 34 | 35 | You can publish the config file with: 36 | 37 | ```bash 38 | php artisan vendor:publish --tag="database-dump-config" 39 | ``` 40 | 41 | These are the contents of the published config file: 42 | 43 | ```php 44 | return [ 45 | 46 | /* 47 | * Enable or disable the package. 48 | */ 49 | 'enable' => true, 50 | 51 | /* 52 | * Set the folder generated dumps should be save in. 53 | */ 54 | 55 | 'folder' => database_path('dumps/'), 56 | 57 | /* 58 | * Set the chunk length of data to be processed at once. 59 | */ 60 | 'chunk_length' => 5000, 61 | 62 | /* 63 | * Set the maximum stream length of data to be processed at once. 64 | * This is the maximum size a row in a table is expected to have in your database 65 | * This is set to a reasonable default of 1MB 66 | * If your database rows are larger than this, you may want to increase this value. 67 | * Read more: https://www.php.net/manual/en/function.stream-get-line.php 68 | */ 69 | 70 | 'stream_length' => (2 * 1024 * 1024), 71 | ]; 72 | ``` 73 | 74 | ## Usage 75 | 76 | ### Dump database data 77 | 78 | ```php 79 | # Dump database data before running migrations 80 | php artisan migrate:fresh 81 | 82 | # Dump database data 83 | php artisan database:dump 84 | ``` 85 | 86 | ### Seed database with dump file 87 | 88 | Load dump file from DatabaseSeeder and pass the dump tables through the `$this->call` method in the seeder class: 89 | 90 | ```php 91 | 92 | # database/seeders/DatabaseSeeder.php 93 | 94 | namespace Database\Seeders; 95 | 96 | use Illuminate\Database\Seeder; 97 | use Justinkekeocha\DatabaseDump\Facades\DatabaseDump; 98 | use Database\Seeders\UserSeeder; 99 | 100 | 101 | class DatabaseSeeder extends Seeder 102 | { 103 | /** 104 | * Seed the application's database. 105 | */ 106 | public function run(): void 107 | { 108 | 109 | $databaseDump = DatabaseDump::getLatestDump("save/2024_04_14_233109.json"); 110 | 111 | $this->command->outputComponents()->info("Using dump: $databaseDump->filePath"); 112 | 113 | 114 | $this->call([ 115 | UserSeeder::class, 116 | ], parameters: compact('databaseDump')); 117 | } 118 | } 119 | ``` 120 | 121 | The dump tables data are now available in individual seeder files and you can now seed the table with the data provided: 122 | 123 | ```php 124 | # database/seeders/UserSeeder.php 125 | 126 | namespace Database\Seeders; 127 | 128 | use App\Models\User; 129 | 130 | class UserSeeder extends Seeder 131 | { 132 | /** 133 | * Run the database seeds. 134 | */ 135 | public function run($databaseDump): void 136 | { 137 | $databaseDump->seed(User::class); 138 | 139 | //You can also use table name instead of model. 140 | 141 | $databaseDump->seed('users'); 142 | } 143 | } 144 | 145 | ``` 146 | 147 | You can manipulate the rows before seeding: 148 | 149 | ```php 150 | # database/seeders/CountrySeeder.php 151 | 152 | namespace Database\Seeders; 153 | 154 | use App\Models\Country; 155 | 156 | class CountrySeeder extends Seeder 157 | { 158 | /** 159 | * Run the database seeds. 160 | */ 161 | public function run($databaseDump): void 162 | { 163 | $databaseDump->seed(Country::class, formatRowCallback: function ($row) { 164 | //331.69 ms 165 | return [ 166 | 'id' => $row['id'], 167 | 'name' => $row['name'], 168 | 'code' => 22 169 | ]; 170 | 171 | //OR 172 | 173 | //338.95 ms 174 | $changes = [ 175 | 'code' => '22' 176 | ]; 177 | 178 | return collect($row)->only(['id', 'name'])->merge($changes)->all(); 179 | }); 180 | } 181 | } 182 | 183 | ``` 184 | 185 | ### Get specific dump file 186 | 187 | ```php 188 | 189 | use Justinkekeocha\DatabaseDump\Facades\DatabaseDump; 190 | 191 | //Get dump by position in array of directory listing 192 | //Array starts from latest dump file in specified config('database-dump.folder') 193 | DatabaseDump::getDump(1); //Get second dump in the array. 194 | 195 | //Get dump by dump file name 196 | DatabaseDump::getDump("2024_01_08_165939.json"); 197 | 198 | //Get the latest dump 199 | DatabaseDump::getLatestDump(); 200 | 201 | ``` 202 | 203 | ### Seed table 204 | 205 | ```php 206 | 207 | use Justinkekeocha\DatabaseDump\Facades\DatabaseDump; 208 | use App\Models\Country; 209 | use App\Models\Timezone; 210 | use App\Models\User; 211 | 212 | DatabaseDump::getLatestDump()->seed(User::class); 213 | 214 | //You can seed multiple tables at once. 215 | DatabaseDump::getLatestDump()->seed(Country::class) 216 | ->seed(Timezone::class) 217 | ->seed(User::class); 218 | 219 | ``` 220 | 221 | When seeding from the same dump file, it is more efficient to call the seed method on the already instantiated class. This is because when the seed method is called first, it reads the whole file and generates a schema that stores the offset of the tables in the file before it starts the seeding action. This schema is created so subsequent seed calls on the same instance (obviously the same file) will just move to the file offset where the table was last found and start reading from the offset. 222 | 223 | ```php 224 | 225 | use Justinkekeocha\DatabaseDump\Facades\DatabaseDump; 226 | use App\Models\Country; 227 | use App\Models\Timezone; 228 | use App\Models\User; 229 | 230 | //Whole file will be read 3 times 231 | DatabaseDump::getLatestDump()->seed(Country::class); 232 | DatabaseDump::getLatestDump()->seed(Timezone::class); 233 | DatabaseDump::getLatestDump()->seed(User::class); 234 | 235 | 236 | //Whole file will be read only once. 237 | DatabaseDump::getLatestDump()->seed(Country::class) 238 | ->seed(Timezone::class) 239 | ->seed(User::class); 240 | 241 | ``` 242 | 243 | ## Sample 244 | 245 | Sample dump can be found [here](../../blob/main/sample-dump.json) 246 | 247 | ## Testing 248 | 249 | ```bash 250 | composer test 251 | ``` 252 | 253 | ## Changelog 254 | 255 | Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently. 256 | 257 | ## Contributing 258 | 259 | Please see [CONTRIBUTING](CONTRIBUTING.md) for details. 260 | 261 | ## Security Vulnerabilities 262 | 263 | Please review [our security policy](../../security/policy) on how to report security vulnerabilities. 264 | 265 | ## Credits 266 | 267 | - [Kekeocha Justin Chetachukwu](https://github.com/justinkekeocha) 268 | 269 | ## License 270 | 271 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 272 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "justinkekeocha/database-dump", 3 | "description": "This package will save you from loosing database records, supposing you run the laravel migrate:fresh command without exporting a database dump", 4 | "keywords": [ 5 | "justinkekeocha", 6 | "laravel", 7 | "database-dump" 8 | ], 9 | "homepage": "https://github.com/justinkekeocha/database-dump", 10 | "license": "MIT", 11 | "authors": [ 12 | { 13 | "name": "Kekeocha Justin Chetachukwu", 14 | "email": "justinratzinger@gmail.com", 15 | "role": "Developer" 16 | } 17 | ], 18 | "require": { 19 | "php": "^8.1", 20 | "spatie/laravel-package-tools": "^1.14.0", 21 | "illuminate/contracts": "^11.0" 22 | }, 23 | "require-dev": { 24 | "laravel/pint": "^1.0", 25 | "nunomaduro/collision": "^7.8", 26 | "nunomaduro/larastan": "^2.0.1", 27 | "orchestra/testbench": "^8.8", 28 | "pestphp/pest": "^2.20", 29 | "pestphp/pest-plugin-arch": "^2.0", 30 | "pestphp/pest-plugin-laravel": "^2.0", 31 | "phpstan/extension-installer": "^1.1", 32 | "phpstan/phpstan-deprecation-rules": "^1.0", 33 | "phpstan/phpstan-phpunit": "^1.0", 34 | "spatie/laravel-ray": "^1.26" 35 | }, 36 | "autoload": { 37 | "psr-4": { 38 | "Justinkekeocha\\DatabaseDump\\": "src/", 39 | "Justinkekeocha\\DatabaseDump\\Database\\Factories\\": "database/factories/" 40 | } 41 | }, 42 | "autoload-dev": { 43 | "psr-4": { 44 | "Justinkekeocha\\DatabaseDump\\Tests\\": "tests/", 45 | "Workbench\\App\\": "workbench/app/" 46 | } 47 | }, 48 | "scripts": { 49 | "post-autoload-dump": "@composer run prepare", 50 | "clear": "@php vendor/bin/testbench package:purge-database-dump --ansi", 51 | "prepare": "@php vendor/bin/testbench package:discover --ansi", 52 | "build": [ 53 | "@composer run prepare", 54 | "@php vendor/bin/testbench workbench:build --ansi" 55 | ], 56 | "start": [ 57 | "Composer\\Config::disableProcessTimeout", 58 | "@composer run build", 59 | "@php vendor/bin/testbench serve" 60 | ], 61 | "analyse": "vendor/bin/phpstan analyse", 62 | "test": "vendor/bin/pest", 63 | "test-coverage": "vendor/bin/pest --coverage", 64 | "format": "vendor/bin/pint" 65 | }, 66 | "config": { 67 | "sort-packages": true, 68 | "allow-plugins": { 69 | "pestphp/pest-plugin": true, 70 | "phpstan/extension-installer": true 71 | } 72 | }, 73 | "extra": { 74 | "laravel": { 75 | "providers": [ 76 | "Justinkekeocha\\DatabaseDump\\DatabaseDumpServiceProvider" 77 | ], 78 | "aliases": { 79 | "DatabaseDump": "Justinkekeocha\\DatabaseDump\\Facades\\DatabaseDump" 80 | } 81 | } 82 | }, 83 | "minimum-stability": "dev", 84 | "prefer-stable": true 85 | } 86 | -------------------------------------------------------------------------------- /config/database-dump.php: -------------------------------------------------------------------------------- 1 | true, 9 | 10 | /* 11 | * Set the folder generated dumps should be save in. 12 | */ 13 | 14 | 'folder' => database_path('dumps/'), 15 | 16 | /* 17 | * Set the chunk length of data to be processed at once. 18 | * The lower this is, the more time it may take to process things. 19 | * The higher this is, the more memory it may consume 20 | * and more likely hood of hitting database placeholder limit of 65,535 placeholders. 21 | */ 22 | 'chunk_length' => 5_000, 23 | 24 | /* 25 | * Set the maximum stream length of data to be processed at once. 26 | * This is the maximum size a row in a table is expected to have in your database 27 | * This is set to a reasonable default of 1MB 28 | * If your database rows are larger than this, you may want to increase this value. 29 | * Read more: https://www.php.net/manual/en/function.stream-get-line.php 30 | */ 31 | 32 | 'stream_length' => (1 * 1024 * 1024), 33 | ]; 34 | -------------------------------------------------------------------------------- /database/factories/ModelFactory.php: -------------------------------------------------------------------------------- 1 | id(); 13 | 14 | // add fields 15 | 16 | $table->timestamps(); 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /resources/stubs/.gitignore.stub: -------------------------------------------------------------------------------- 1 | *.json -------------------------------------------------------------------------------- /resources/views/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/justinkekeocha/database-dump/cefe1dea0dc8edc6bc850125b7ecd1eadd76f513/resources/views/.gitkeep -------------------------------------------------------------------------------- /sample-dump.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"markup":"header","type":"database","name":"yotjob","comment":"Export database to JSON", "version":"2"}, 3 | {"markup":"footer","type":"database","name":"yotjob"}, 4 | 5 | {"markup":"header","type":"table","name":"approval_times"}, 6 | 7 | {"id":4,"name":"4 hours"}, 8 | {"id":12,"name":"12 hours"}, 9 | {"id":24,"name":"1 day"}, 10 | {"id":72,"name":"3 days"}, 11 | {"id":120,"name":"5 days"}, 12 | 13 | {"markup":"footer","type":"table","name":"approval_times"}, 14 | 15 | {"markup":"header","type":"table","name":"cache"}, 16 | {"markup":"footer","type":"table","name":"cache"}, 17 | 18 | {"markup":"header","type":"table","name":"cache_locks"}, 19 | {"markup":"footer","type":"table","name":"cache_locks"}, 20 | 21 | {"markup":"header","type":"table","name":"countries"}, 22 | 23 | {"id":1,"name":"Afghanistan","code":"AF","latitude":"33.00000000","longitude":"65.00000000"}, 24 | {"id":2,"name":"Aland Islands","code":"AX","latitude":"60.11666700","longitude":"19.90000000"}, 25 | {"id":3,"name":"Albania","code":"AL","latitude":"41.00000000","longitude":"20.00000000"}, 26 | {"id":4,"name":"Algeria","code":"DZ","latitude":"28.00000000","longitude":"3.00000000"}, 27 | {"id":5,"name":"American Samoa","code":"AS","latitude":"-14.33333333","longitude":"-170.00000000"}, 28 | {"id":6,"name":"Andorra","code":"AD","latitude":"42.50000000","longitude":"1.50000000"}, 29 | {"id":7,"name":"Angola","code":"AO","latitude":"-12.50000000","longitude":"18.50000000"}, 30 | {"id":8,"name":"Anguilla","code":"AI","latitude":"18.25000000","longitude":"-63.16666666"}, 31 | {"id":9,"name":"Antarctica","code":"AQ","latitude":"-74.65000000","longitude":"4.48000000"}, 32 | {"id":10,"name":"Antigua And Barbuda","code":"AG","latitude":"17.05000000","longitude":"-61.80000000"}, 33 | {"id":11,"name":"Argentina","code":"AR","latitude":"-34.00000000","longitude":"-64.00000000"}, 34 | {"id":12,"name":"Armenia","code":"AM","latitude":"40.00000000","longitude":"45.00000000"}, 35 | {"id":13,"name":"Aruba","code":"AW","latitude":"12.50000000","longitude":"-69.96666666"}, 36 | {"id":14,"name":"Australia","code":"AU","latitude":"-27.00000000","longitude":"133.00000000"}, 37 | {"id":15,"name":"Austria","code":"AT","latitude":"47.33333333","longitude":"13.33333333"}, 38 | {"id":16,"name":"Azerbaijan","code":"AZ","latitude":"40.50000000","longitude":"47.50000000"}, 39 | {"id":17,"name":"The Bahamas","code":"BS","latitude":"24.25000000","longitude":"-76.00000000"}, 40 | {"id":18,"name":"Bahrain","code":"BH","latitude":"26.00000000","longitude":"50.55000000"}, 41 | {"id":19,"name":"Bangladesh","code":"BD","latitude":"24.00000000","longitude":"90.00000000"}, 42 | {"id":20,"name":"Barbados","code":"BB","latitude":"13.16666666","longitude":"-59.53333333"}, 43 | {"id":21,"name":"Belarus","code":"BY","latitude":"53.00000000","longitude":"28.00000000"}, 44 | {"id":22,"name":"Belgium","code":"BE","latitude":"50.83333333","longitude":"4.00000000"}, 45 | {"id":23,"name":"Belize","code":"BZ","latitude":"17.25000000","longitude":"-88.75000000"}, 46 | {"id":24,"name":"Benin","code":"BJ","latitude":"9.50000000","longitude":"2.25000000"}, 47 | {"id":25,"name":"Bermuda","code":"BM","latitude":"32.33333333","longitude":"-64.75000000"}, 48 | {"id":26,"name":"Bhutan","code":"BT","latitude":"27.50000000","longitude":"90.50000000"}, 49 | {"id":27,"name":"Bolivia","code":"BO","latitude":"-17.00000000","longitude":"-65.00000000"}, 50 | {"id":28,"name":"Bosnia and Herzegovina","code":"BA","latitude":"44.00000000","longitude":"18.00000000"}, 51 | {"id":29,"name":"Botswana","code":"BW","latitude":"-22.00000000","longitude":"24.00000000"}, 52 | {"id":30,"name":"Bouvet Island","code":"BV","latitude":"-54.43333333","longitude":"3.40000000"}, 53 | {"id":31,"name":"Brazil","code":"BR","latitude":"-10.00000000","longitude":"-55.00000000"}, 54 | {"id":32,"name":"British Indian Ocean Territory","code":"IO","latitude":"-6.00000000","longitude":"71.50000000"}, 55 | {"id":33,"name":"Brunei","code":"BN","latitude":"4.50000000","longitude":"114.66666666"}, 56 | {"id":34,"name":"Bulgaria","code":"BG","latitude":"43.00000000","longitude":"25.00000000"}, 57 | {"id":35,"name":"Burkina Faso","code":"BF","latitude":"13.00000000","longitude":"-2.00000000"}, 58 | {"id":36,"name":"Burundi","code":"BI","latitude":"-3.50000000","longitude":"30.00000000"}, 59 | {"id":37,"name":"Cambodia","code":"KH","latitude":"13.00000000","longitude":"105.00000000"}, 60 | {"id":38,"name":"Cameroon","code":"CM","latitude":"6.00000000","longitude":"12.00000000"}, 61 | {"id":39,"name":"Canada","code":"CA","latitude":"60.00000000","longitude":"-95.00000000"}, 62 | {"id":40,"name":"Cape Verde","code":"CV","latitude":"16.00000000","longitude":"-24.00000000"}, 63 | {"id":41,"name":"Cayman Islands","code":"KY","latitude":"19.50000000","longitude":"-80.50000000"}, 64 | {"id":42,"name":"Central African Republic","code":"CF","latitude":"7.00000000","longitude":"21.00000000"}, 65 | {"id":43,"name":"Chad","code":"TD","latitude":"15.00000000","longitude":"19.00000000"}, 66 | {"id":44,"name":"Chile","code":"CL","latitude":"-30.00000000","longitude":"-71.00000000"}, 67 | {"id":45,"name":"China","code":"CN","latitude":"35.00000000","longitude":"105.00000000"}, 68 | {"id":46,"name":"Christmas Island","code":"CX","latitude":"-10.50000000","longitude":"105.66666666"}, 69 | {"id":47,"name":"Cocos (Keeling) Islands","code":"CC","latitude":"-12.50000000","longitude":"96.83333333"}, 70 | {"id":48,"name":"Colombia","code":"CO","latitude":"4.00000000","longitude":"-72.00000000"}, 71 | {"id":49,"name":"Comoros","code":"KM","latitude":"-12.16666666","longitude":"44.25000000"}, 72 | {"id":50,"name":"Congo","code":"CG","latitude":"-1.00000000","longitude":"15.00000000"}, 73 | {"id":51,"name":"Democratic Republic of the Congo","code":"CD","latitude":"0.00000000","longitude":"25.00000000"}, 74 | {"id":52,"name":"Cook Islands","code":"CK","latitude":"-21.23333333","longitude":"-159.76666666"}, 75 | {"id":53,"name":"Costa Rica","code":"CR","latitude":"10.00000000","longitude":"-84.00000000"}, 76 | {"id":54,"name":"Cote D'Ivoire (Ivory Coast)","code":"CI","latitude":"8.00000000","longitude":"-5.00000000"}, 77 | {"id":55,"name":"Croatia","code":"HR","latitude":"45.16666666","longitude":"15.50000000"}, 78 | {"id":56,"name":"Cuba","code":"CU","latitude":"21.50000000","longitude":"-80.00000000"}, 79 | {"id":57,"name":"Cyprus","code":"CY","latitude":"35.00000000","longitude":"33.00000000"}, 80 | {"id":58,"name":"Czech Republic","code":"CZ","latitude":"49.75000000","longitude":"15.50000000"}, 81 | {"id":59,"name":"Denmark","code":"DK","latitude":"56.00000000","longitude":"10.00000000"}, 82 | {"id":60,"name":"Djibouti","code":"DJ","latitude":"11.50000000","longitude":"43.00000000"}, 83 | {"id":61,"name":"Dominica","code":"DM","latitude":"15.41666666","longitude":"-61.33333333"}, 84 | {"id":62,"name":"Dominican Republic","code":"DO","latitude":"19.00000000","longitude":"-70.66666666"}, 85 | {"id":63,"name":"East Timor","code":"TL","latitude":"-8.83333333","longitude":"125.91666666"}, 86 | {"id":64,"name":"Ecuador","code":"EC","latitude":"-2.00000000","longitude":"-77.50000000"}, 87 | {"id":65,"name":"Egypt","code":"EG","latitude":"27.00000000","longitude":"30.00000000"}, 88 | {"id":66,"name":"El Salvador","code":"SV","latitude":"13.83333333","longitude":"-88.91666666"}, 89 | {"id":67,"name":"Equatorial Guinea","code":"GQ","latitude":"2.00000000","longitude":"10.00000000"}, 90 | {"id":68,"name":"Eritrea","code":"ER","latitude":"15.00000000","longitude":"39.00000000"}, 91 | {"id":69,"name":"Estonia","code":"EE","latitude":"59.00000000","longitude":"26.00000000"}, 92 | {"id":70,"name":"Ethiopia","code":"ET","latitude":"8.00000000","longitude":"38.00000000"}, 93 | {"id":71,"name":"Falkland Islands","code":"FK","latitude":"-51.75000000","longitude":"-59.00000000"}, 94 | {"id":72,"name":"Faroe Islands","code":"FO","latitude":"62.00000000","longitude":"-7.00000000"}, 95 | {"id":73,"name":"Fiji Islands","code":"FJ","latitude":"-18.00000000","longitude":"175.00000000"}, 96 | {"id":74,"name":"Finland","code":"FI","latitude":"64.00000000","longitude":"26.00000000"}, 97 | {"id":75,"name":"France","code":"FR","latitude":"46.00000000","longitude":"2.00000000"}, 98 | {"id":76,"name":"French Guiana","code":"GF","latitude":"4.00000000","longitude":"-53.00000000"}, 99 | {"id":77,"name":"French Polynesia","code":"PF","latitude":"-15.00000000","longitude":"-140.00000000"}, 100 | {"id":78,"name":"French Southern Territories","code":"TF","latitude":"-49.25000000","longitude":"69.16700000"}, 101 | {"id":79,"name":"Gabon","code":"GA","latitude":"-1.00000000","longitude":"11.75000000"}, 102 | {"id":80,"name":"Gambia The","code":"GM","latitude":"13.46666666","longitude":"-16.56666666"}, 103 | {"id":81,"name":"Georgia","code":"GE","latitude":"42.00000000","longitude":"43.50000000"}, 104 | {"id":82,"name":"Germany","code":"DE","latitude":"51.00000000","longitude":"9.00000000"}, 105 | {"id":83,"name":"Ghana","code":"GH","latitude":"8.00000000","longitude":"-2.00000000"}, 106 | {"id":84,"name":"Gibraltar","code":"GI","latitude":"36.13333333","longitude":"-5.35000000"}, 107 | {"id":85,"name":"Greece","code":"GR","latitude":"39.00000000","longitude":"22.00000000"}, 108 | {"id":86,"name":"Greenland","code":"GL","latitude":"72.00000000","longitude":"-40.00000000"}, 109 | {"id":87,"name":"Grenada","code":"GD","latitude":"12.11666666","longitude":"-61.66666666"}, 110 | {"id":88,"name":"Guadeloupe","code":"GP","latitude":"16.25000000","longitude":"-61.58333300"}, 111 | {"id":89,"name":"Guam","code":"GU","latitude":"13.46666666","longitude":"144.78333333"}, 112 | {"id":90,"name":"Guatemala","code":"GT","latitude":"15.50000000","longitude":"-90.25000000"}, 113 | {"id":91,"name":"Guernsey and Alderney","code":"GG","latitude":"49.46666666","longitude":"-2.58333333"}, 114 | {"id":92,"name":"Guinea","code":"GN","latitude":"11.00000000","longitude":"-10.00000000"}, 115 | {"id":93,"name":"Guinea-Bissau","code":"GW","latitude":"12.00000000","longitude":"-15.00000000"}, 116 | {"id":94,"name":"Guyana","code":"GY","latitude":"5.00000000","longitude":"-59.00000000"}, 117 | {"id":95,"name":"Haiti","code":"HT","latitude":"19.00000000","longitude":"-72.41666666"}, 118 | {"id":96,"name":"Heard Island and McDonald Islands","code":"HM","latitude":"-53.10000000","longitude":"72.51666666"}, 119 | {"id":97,"name":"Honduras","code":"HN","latitude":"15.00000000","longitude":"-86.50000000"}, 120 | {"id":98,"name":"Hong Kong S.A.R.","code":"HK","latitude":"22.25000000","longitude":"114.16666666"}, 121 | {"id":99,"name":"Hungary","code":"HU","latitude":"47.00000000","longitude":"20.00000000"}, 122 | {"id":100,"name":"Iceland","code":"IS","latitude":"65.00000000","longitude":"-18.00000000"}, 123 | {"id":101,"name":"India","code":"IN","latitude":"20.00000000","longitude":"77.00000000"}, 124 | {"id":102,"name":"Indonesia","code":"ID","latitude":"-5.00000000","longitude":"120.00000000"}, 125 | {"id":103,"name":"Iran","code":"IR","latitude":"32.00000000","longitude":"53.00000000"}, 126 | {"id":104,"name":"Iraq","code":"IQ","latitude":"33.00000000","longitude":"44.00000000"}, 127 | {"id":105,"name":"Ireland","code":"IE","latitude":"53.00000000","longitude":"-8.00000000"}, 128 | {"id":106,"name":"Israel","code":"IL","latitude":"31.50000000","longitude":"34.75000000"}, 129 | {"id":107,"name":"Italy","code":"IT","latitude":"42.83333333","longitude":"12.83333333"}, 130 | {"id":108,"name":"Jamaica","code":"JM","latitude":"18.25000000","longitude":"-77.50000000"}, 131 | {"id":109,"name":"Japan","code":"JP","latitude":"36.00000000","longitude":"138.00000000"}, 132 | {"id":110,"name":"Jersey","code":"JE","latitude":"49.25000000","longitude":"-2.16666666"}, 133 | {"id":111,"name":"Jordan","code":"JO","latitude":"31.00000000","longitude":"36.00000000"}, 134 | {"id":112,"name":"Kazakhstan","code":"KZ","latitude":"48.00000000","longitude":"68.00000000"}, 135 | {"id":113,"name":"Kenya","code":"KE","latitude":"1.00000000","longitude":"38.00000000"}, 136 | {"id":114,"name":"Kiribati","code":"KI","latitude":"1.41666666","longitude":"173.00000000"}, 137 | {"id":115,"name":"North Korea","code":"KP","latitude":"40.00000000","longitude":"127.00000000"}, 138 | {"id":116,"name":"South Korea","code":"KR","latitude":"37.00000000","longitude":"127.50000000"}, 139 | {"id":117,"name":"Kuwait","code":"KW","latitude":"29.50000000","longitude":"45.75000000"}, 140 | {"id":118,"name":"Kyrgyzstan","code":"KG","latitude":"41.00000000","longitude":"75.00000000"}, 141 | {"id":119,"name":"Laos","code":"LA","latitude":"18.00000000","longitude":"105.00000000"}, 142 | {"id":120,"name":"Latvia","code":"LV","latitude":"57.00000000","longitude":"25.00000000"}, 143 | {"id":121,"name":"Lebanon","code":"LB","latitude":"33.83333333","longitude":"35.83333333"}, 144 | {"id":122,"name":"Lesotho","code":"LS","latitude":"-29.50000000","longitude":"28.50000000"}, 145 | {"id":123,"name":"Liberia","code":"LR","latitude":"6.50000000","longitude":"-9.50000000"}, 146 | {"id":124,"name":"Libya","code":"LY","latitude":"25.00000000","longitude":"17.00000000"}, 147 | {"id":125,"name":"Liechtenstein","code":"LI","latitude":"47.26666666","longitude":"9.53333333"}, 148 | {"id":126,"name":"Lithuania","code":"LT","latitude":"56.00000000","longitude":"24.00000000"}, 149 | {"id":127,"name":"Luxembourg","code":"LU","latitude":"49.75000000","longitude":"6.16666666"}, 150 | {"id":128,"name":"Macau S.A.R.","code":"MO","latitude":"22.16666666","longitude":"113.55000000"}, 151 | {"id":129,"name":"North Macedonia","code":"MK","latitude":"41.83333333","longitude":"22.00000000"}, 152 | {"id":130,"name":"Madagascar","code":"MG","latitude":"-20.00000000","longitude":"47.00000000"}, 153 | {"id":131,"name":"Malawi","code":"MW","latitude":"-13.50000000","longitude":"34.00000000"}, 154 | {"id":132,"name":"Malaysia","code":"MY","latitude":"2.50000000","longitude":"112.50000000"}, 155 | {"id":133,"name":"Maldives","code":"MV","latitude":"3.25000000","longitude":"73.00000000"}, 156 | {"id":134,"name":"Mali","code":"ML","latitude":"17.00000000","longitude":"-4.00000000"}, 157 | {"id":135,"name":"Malta","code":"MT","latitude":"35.83333333","longitude":"14.58333333"}, 158 | {"id":136,"name":"Man (Isle of)","code":"IM","latitude":"54.25000000","longitude":"-4.50000000"}, 159 | {"id":137,"name":"Marshall Islands","code":"MH","latitude":"9.00000000","longitude":"168.00000000"}, 160 | {"id":138,"name":"Martinique","code":"MQ","latitude":"14.66666700","longitude":"-61.00000000"}, 161 | {"id":139,"name":"Mauritania","code":"MR","latitude":"20.00000000","longitude":"-12.00000000"}, 162 | {"id":140,"name":"Mauritius","code":"MU","latitude":"-20.28333333","longitude":"57.55000000"}, 163 | {"id":141,"name":"Mayotte","code":"YT","latitude":"-12.83333333","longitude":"45.16666666"}, 164 | {"id":142,"name":"Mexico","code":"MX","latitude":"23.00000000","longitude":"-102.00000000"}, 165 | {"id":143,"name":"Micronesia","code":"FM","latitude":"6.91666666","longitude":"158.25000000"}, 166 | {"id":144,"name":"Moldova","code":"MD","latitude":"47.00000000","longitude":"29.00000000"}, 167 | {"id":145,"name":"Monaco","code":"MC","latitude":"43.73333333","longitude":"7.40000000"}, 168 | {"id":146,"name":"Mongolia","code":"MN","latitude":"46.00000000","longitude":"105.00000000"}, 169 | {"id":147,"name":"Montenegro","code":"ME","latitude":"42.50000000","longitude":"19.30000000"}, 170 | {"id":148,"name":"Montserrat","code":"MS","latitude":"16.75000000","longitude":"-62.20000000"}, 171 | {"id":149,"name":"Morocco","code":"MA","latitude":"32.00000000","longitude":"-5.00000000"}, 172 | {"id":150,"name":"Mozambique","code":"MZ","latitude":"-18.25000000","longitude":"35.00000000"}, 173 | {"id":151,"name":"Myanmar","code":"MM","latitude":"22.00000000","longitude":"98.00000000"}, 174 | {"id":152,"name":"Namibia","code":"NA","latitude":"-22.00000000","longitude":"17.00000000"}, 175 | {"id":153,"name":"Nauru","code":"NR","latitude":"-0.53333333","longitude":"166.91666666"}, 176 | {"id":154,"name":"Nepal","code":"NP","latitude":"28.00000000","longitude":"84.00000000"}, 177 | {"id":155,"name":"Bonaire, Sint Eustatius and Saba","code":"BQ","latitude":"12.15000000","longitude":"-68.26666700"}, 178 | {"id":156,"name":"Netherlands","code":"NL","latitude":"52.50000000","longitude":"5.75000000"}, 179 | {"id":157,"name":"New Caledonia","code":"NC","latitude":"-21.50000000","longitude":"165.50000000"}, 180 | {"id":158,"name":"New Zealand","code":"NZ","latitude":"-41.00000000","longitude":"174.00000000"}, 181 | {"id":159,"name":"Nicaragua","code":"NI","latitude":"13.00000000","longitude":"-85.00000000"}, 182 | {"id":160,"name":"Niger","code":"NE","latitude":"16.00000000","longitude":"8.00000000"}, 183 | {"id":161,"name":"Nigeria","code":"NG","latitude":"10.00000000","longitude":"8.00000000"}, 184 | {"id":162,"name":"Niue","code":"NU","latitude":"-19.03333333","longitude":"-169.86666666"}, 185 | {"id":163,"name":"Norfolk Island","code":"NF","latitude":"-29.03333333","longitude":"167.95000000"}, 186 | {"id":164,"name":"Northern Mariana Islands","code":"MP","latitude":"15.20000000","longitude":"145.75000000"}, 187 | {"id":165,"name":"Norway","code":"NO","latitude":"62.00000000","longitude":"10.00000000"}, 188 | {"id":166,"name":"Oman","code":"OM","latitude":"21.00000000","longitude":"57.00000000"}, 189 | {"id":167,"name":"Pakistan","code":"PK","latitude":"30.00000000","longitude":"70.00000000"}, 190 | {"id":168,"name":"Palau","code":"PW","latitude":"7.50000000","longitude":"134.50000000"}, 191 | {"id":169,"name":"Palestinian Territory Occupied","code":"PS","latitude":"31.90000000","longitude":"35.20000000"}, 192 | {"id":170,"name":"Panama","code":"PA","latitude":"9.00000000","longitude":"-80.00000000"}, 193 | {"id":171,"name":"Papua new Guinea","code":"PG","latitude":"-6.00000000","longitude":"147.00000000"}, 194 | {"id":172,"name":"Paraguay","code":"PY","latitude":"-23.00000000","longitude":"-58.00000000"}, 195 | {"id":173,"name":"Peru","code":"PE","latitude":"-10.00000000","longitude":"-76.00000000"}, 196 | {"id":174,"name":"Philippines","code":"PH","latitude":"13.00000000","longitude":"122.00000000"}, 197 | {"id":175,"name":"Pitcairn Island","code":"PN","latitude":"-25.06666666","longitude":"-130.10000000"}, 198 | {"id":176,"name":"Poland","code":"PL","latitude":"52.00000000","longitude":"20.00000000"}, 199 | {"id":177,"name":"Portugal","code":"PT","latitude":"39.50000000","longitude":"-8.00000000"}, 200 | {"id":178,"name":"Puerto Rico","code":"PR","latitude":"18.25000000","longitude":"-66.50000000"}, 201 | {"id":179,"name":"Qatar","code":"QA","latitude":"25.50000000","longitude":"51.25000000"}, 202 | {"id":180,"name":"Reunion","code":"RE","latitude":"-21.15000000","longitude":"55.50000000"}, 203 | {"id":181,"name":"Romania","code":"RO","latitude":"46.00000000","longitude":"25.00000000"}, 204 | {"id":182,"name":"Russia","code":"RU","latitude":"60.00000000","longitude":"100.00000000"}, 205 | {"id":183,"name":"Rwanda","code":"RW","latitude":"-2.00000000","longitude":"30.00000000"}, 206 | {"id":184,"name":"Saint Helena","code":"SH","latitude":"-15.95000000","longitude":"-5.70000000"}, 207 | {"id":185,"name":"Saint Kitts And Nevis","code":"KN","latitude":"17.33333333","longitude":"-62.75000000"}, 208 | {"id":186,"name":"Saint Lucia","code":"LC","latitude":"13.88333333","longitude":"-60.96666666"}, 209 | {"id":187,"name":"Saint Pierre and Miquelon","code":"PM","latitude":"46.83333333","longitude":"-56.33333333"}, 210 | {"id":188,"name":"Saint Vincent And The Grenadines","code":"VC","latitude":"13.25000000","longitude":"-61.20000000"}, 211 | {"id":189,"name":"Saint-Barthelemy","code":"BL","latitude":"18.50000000","longitude":"-63.41666666"}, 212 | {"id":190,"name":"Saint-Martin (French part)","code":"MF","latitude":"18.08333333","longitude":"-63.95000000"}, 213 | {"id":191,"name":"Samoa","code":"WS","latitude":"-13.58333333","longitude":"-172.33333333"}, 214 | {"id":192,"name":"San Marino","code":"SM","latitude":"43.76666666","longitude":"12.41666666"}, 215 | {"id":193,"name":"Sao Tome and Principe","code":"ST","latitude":"1.00000000","longitude":"7.00000000"}, 216 | {"id":194,"name":"Saudi Arabia","code":"SA","latitude":"25.00000000","longitude":"45.00000000"}, 217 | {"id":195,"name":"Senegal","code":"SN","latitude":"14.00000000","longitude":"-14.00000000"}, 218 | {"id":196,"name":"Serbia","code":"RS","latitude":"44.00000000","longitude":"21.00000000"}, 219 | {"id":197,"name":"Seychelles","code":"SC","latitude":"-4.58333333","longitude":"55.66666666"}, 220 | {"id":198,"name":"Sierra Leone","code":"SL","latitude":"8.50000000","longitude":"-11.50000000"}, 221 | {"id":199,"name":"Singapore","code":"SG","latitude":"1.36666666","longitude":"103.80000000"}, 222 | {"id":200,"name":"Slovakia","code":"SK","latitude":"48.66666666","longitude":"19.50000000"}, 223 | {"id":201,"name":"Slovenia","code":"SI","latitude":"46.11666666","longitude":"14.81666666"}, 224 | {"id":202,"name":"Solomon Islands","code":"SB","latitude":"-8.00000000","longitude":"159.00000000"}, 225 | {"id":203,"name":"Somalia","code":"SO","latitude":"10.00000000","longitude":"49.00000000"}, 226 | {"id":204,"name":"South Africa","code":"ZA","latitude":"-29.00000000","longitude":"24.00000000"}, 227 | {"id":205,"name":"South Georgia","code":"GS","latitude":"-54.50000000","longitude":"-37.00000000"}, 228 | {"id":206,"name":"South Sudan","code":"SS","latitude":"7.00000000","longitude":"30.00000000"}, 229 | {"id":207,"name":"Spain","code":"ES","latitude":"40.00000000","longitude":"-4.00000000"}, 230 | {"id":208,"name":"Sri Lanka","code":"LK","latitude":"7.00000000","longitude":"81.00000000"}, 231 | {"id":209,"name":"Sudan","code":"SD","latitude":"15.00000000","longitude":"30.00000000"}, 232 | {"id":210,"name":"Suriname","code":"SR","latitude":"4.00000000","longitude":"-56.00000000"}, 233 | {"id":211,"name":"Svalbard And Jan Mayen Islands","code":"SJ","latitude":"78.00000000","longitude":"20.00000000"}, 234 | {"id":212,"name":"Swaziland","code":"SZ","latitude":"-26.50000000","longitude":"31.50000000"}, 235 | {"id":213,"name":"Sweden","code":"SE","latitude":"62.00000000","longitude":"15.00000000"}, 236 | {"id":214,"name":"Switzerland","code":"CH","latitude":"47.00000000","longitude":"8.00000000"}, 237 | {"id":215,"name":"Syria","code":"SY","latitude":"35.00000000","longitude":"38.00000000"}, 238 | {"id":216,"name":"Taiwan","code":"TW","latitude":"23.50000000","longitude":"121.00000000"}, 239 | {"id":217,"name":"Tajikistan","code":"TJ","latitude":"39.00000000","longitude":"71.00000000"}, 240 | {"id":218,"name":"Tanzania","code":"TZ","latitude":"-6.00000000","longitude":"35.00000000"}, 241 | {"id":219,"name":"Thailand","code":"TH","latitude":"15.00000000","longitude":"100.00000000"}, 242 | {"id":220,"name":"Togo","code":"TG","latitude":"8.00000000","longitude":"1.16666666"}, 243 | {"id":221,"name":"Tokelau","code":"TK","latitude":"-9.00000000","longitude":"-172.00000000"}, 244 | {"id":222,"name":"Tonga","code":"TO","latitude":"-20.00000000","longitude":"-175.00000000"}, 245 | {"id":223,"name":"Trinidad And Tobago","code":"TT","latitude":"11.00000000","longitude":"-61.00000000"}, 246 | {"id":224,"name":"Tunisia","code":"TN","latitude":"34.00000000","longitude":"9.00000000"}, 247 | {"id":225,"name":"Turkey","code":"TR","latitude":"39.00000000","longitude":"35.00000000"}, 248 | {"id":226,"name":"Turkmenistan","code":"TM","latitude":"40.00000000","longitude":"60.00000000"}, 249 | {"id":227,"name":"Turks And Caicos Islands","code":"TC","latitude":"21.75000000","longitude":"-71.58333333"}, 250 | {"id":228,"name":"Tuvalu","code":"TV","latitude":"-8.00000000","longitude":"178.00000000"}, 251 | {"id":229,"name":"Uganda","code":"UG","latitude":"1.00000000","longitude":"32.00000000"}, 252 | {"id":230,"name":"Ukraine","code":"UA","latitude":"49.00000000","longitude":"32.00000000"}, 253 | {"id":231,"name":"United Arab Emirates","code":"AE","latitude":"24.00000000","longitude":"54.00000000"}, 254 | {"id":232,"name":"United Kingdom","code":"GB","latitude":"54.00000000","longitude":"-2.00000000"}, 255 | {"id":233,"name":"United States","code":"US","latitude":"38.00000000","longitude":"-97.00000000"}, 256 | {"id":234,"name":"United States Minor Outlying Islands","code":"UM","latitude":"0.00000000","longitude":"0.00000000"}, 257 | {"id":235,"name":"Uruguay","code":"UY","latitude":"-33.00000000","longitude":"-56.00000000"}, 258 | {"id":236,"name":"Uzbekistan","code":"UZ","latitude":"41.00000000","longitude":"64.00000000"}, 259 | {"id":237,"name":"Vanuatu","code":"VU","latitude":"-16.00000000","longitude":"167.00000000"}, 260 | {"id":238,"name":"Vatican City State (Holy See)","code":"VA","latitude":"41.90000000","longitude":"12.45000000"}, 261 | {"id":239,"name":"Venezuela","code":"VE","latitude":"8.00000000","longitude":"-66.00000000"}, 262 | {"id":240,"name":"Vietnam","code":"VN","latitude":"16.16666666","longitude":"107.83333333"}, 263 | {"id":241,"name":"Virgin Islands (British)","code":"VG","latitude":"18.43138300","longitude":"-64.62305000"}, 264 | {"id":242,"name":"Virgin Islands (US)","code":"VI","latitude":"18.34000000","longitude":"-64.93000000"}, 265 | {"id":243,"name":"Wallis And Futuna Islands","code":"WF","latitude":"-13.30000000","longitude":"-176.20000000"}, 266 | {"id":244,"name":"Western Sahara","code":"EH","latitude":"24.50000000","longitude":"-13.00000000"}, 267 | {"id":245,"name":"Yemen","code":"YE","latitude":"15.00000000","longitude":"48.00000000"}, 268 | {"id":246,"name":"Zambia","code":"ZM","latitude":"-15.00000000","longitude":"30.00000000"}, 269 | {"id":247,"name":"Zimbabwe","code":"ZW","latitude":"-20.00000000","longitude":"30.00000000"}, 270 | {"id":248,"name":"Kosovo","code":"XK","latitude":"42.56129090","longitude":"20.34030350"}, 271 | {"id":249,"name":"Curaçao","code":"CW","latitude":"12.11666700","longitude":"-68.93333300"}, 272 | {"id":250,"name":"Sint Maarten (Dutch part)","code":"SX","latitude":"18.03333300","longitude":"-63.05000000"}, 273 | {"id":251,"name":"Unknown","code":"UN","latitude":"0.00000000","longitude":"0.00000000"}, 274 | 275 | {"markup":"footer","type":"table","name":"countries"}, 276 | 277 | {"markup":"header","type":"table","name":"failed_jobs"}, 278 | {"markup":"footer","type":"table","name":"failed_jobs"}, 279 | 280 | {"markup":"header","type":"table","name":"job_batches"}, 281 | {"markup":"footer","type":"table","name":"job_batches"}, 282 | 283 | {"markup":"header","type":"table","name":"jobs"}, 284 | {"markup":"footer","type":"table","name":"jobs"}, 285 | 286 | {"markup":"header","type":"table","name":"migrations"}, 287 | 288 | {"id":1,"migration":"0000_01_01_create_countries_table","batch":1}, 289 | {"id":2,"migration":"0000_01_01_create_timezones_table","batch":1}, 290 | {"id":3,"migration":"0001_01_01_000000_create_users_table","batch":1}, 291 | {"id":4,"migration":"0001_01_01_000001_create_cache_table","batch":1}, 292 | {"id":5,"migration":"0001_01_01_000002_create_jobs_table","batch":1}, 293 | {"id":6,"migration":"2024_03_20_154731_create_personal_access_tokens_table","batch":1}, 294 | {"id":7,"migration":"2024_03_22_142702_create_approval_times_table","batch":1}, 295 | {"id":8,"migration":"2024_03_22_145323_create_pulse_tables","batch":1}, 296 | {"id":9,"migration":"2024_03_22_151642_create_telescope_entries_table","batch":1}, 297 | {"id":10,"migration":"2024_04_01_105007_create_roles_table","batch":1}, 298 | {"id":11,"migration":"2024_04_01_110140_create_role_user_table","batch":1}, 299 | {"id":12,"migration":"2024_04_01_113928_create_permissions_table","batch":1}, 300 | {"id":13,"migration":"2024_04_01_122245_create_permission_role_table","batch":1}, 301 | {"id":14,"migration":"2024_04_11_162528_create_wallets_table","batch":1}, 302 | 303 | {"markup":"footer","type":"table","name":"migrations"}, 304 | 305 | {"markup":"header","type":"table","name":"password_reset_tokens"}, 306 | {"markup":"footer","type":"table","name":"password_reset_tokens"}, 307 | 308 | {"markup":"header","type":"table","name":"permission_role"}, 309 | {"markup":"footer","type":"table","name":"permission_role"}, 310 | 311 | {"markup":"header","type":"table","name":"permissions"}, 312 | {"markup":"footer","type":"table","name":"permissions"}, 313 | 314 | {"markup":"header","type":"table","name":"personal_access_tokens"}, 315 | {"markup":"footer","type":"table","name":"personal_access_tokens"}, 316 | 317 | {"markup":"header","type":"table","name":"pulse_aggregates"}, 318 | {"markup":"footer","type":"table","name":"pulse_aggregates"}, 319 | 320 | {"markup":"header","type":"table","name":"pulse_entries"}, 321 | {"markup":"footer","type":"table","name":"pulse_entries"}, 322 | 323 | {"markup":"header","type":"table","name":"pulse_values"}, 324 | {"markup":"footer","type":"table","name":"pulse_values"}, 325 | 326 | {"markup":"header","type":"table","name":"role_user"}, 327 | {"markup":"footer","type":"table","name":"role_user"}, 328 | 329 | {"markup":"header","type":"table","name":"roles"}, 330 | 331 | {"id":1,"name":"User","created_at":"2024-04-14 23:29:10","updated_at":"2024-04-14 23:29:10"}, 332 | {"id":2,"name":"Moderator","created_at":"2024-04-14 23:29:10","updated_at":"2024-04-14 23:29:10"}, 333 | {"id":3,"name":"Admin","created_at":"2024-04-14 23:29:10","updated_at":"2024-04-14 23:29:10"}, 334 | 335 | {"markup":"footer","type":"table","name":"roles"}, 336 | 337 | {"markup":"header","type":"table","name":"sessions"}, 338 | {"markup":"footer","type":"table","name":"sessions"}, 339 | 340 | {"markup":"header","type":"table","name":"telescope_entries"}, 341 | {"markup":"footer","type":"table","name":"telescope_entries"}, 342 | 343 | {"markup":"header","type":"table","name":"telescope_entries_tags"}, 344 | {"markup":"footer","type":"table","name":"telescope_entries_tags"}, 345 | 346 | {"markup":"header","type":"table","name":"telescope_monitoring"}, 347 | {"markup":"footer","type":"table","name":"telescope_monitoring"}, 348 | 349 | {"markup":"header","type":"table","name":"timezones"}, 350 | 351 | {"id":1,"offset":"UTC-11:00","zone":"Pacific\/Midway","location":"Midway Island"}, 352 | {"id":2,"offset":"UTC-11:00","zone":"Pacific\/Samoa","location":"Samoa"}, 353 | {"id":3,"offset":"UTC-10:00","zone":"Pacific\/Honolulu","location":"Hawaii"}, 354 | {"id":4,"offset":"UTC-09:00","zone":"US\/Alaska","location":"Alaska"}, 355 | {"id":5,"offset":"UTC-08:00","zone":"America\/Los_Angeles","location":"Pacific Time (US & Canada)"}, 356 | {"id":6,"offset":"UTC-08:00","zone":"America\/Tijuana","location":"Tijuana"}, 357 | {"id":7,"offset":"UTC-07:00","zone":"US\/Arizona","location":"Arizona"}, 358 | {"id":8,"offset":"UTC-07:00","zone":"America\/Chihuahua","location":"Chihuahua"}, 359 | {"id":9,"offset":"UTC-07:00","zone":"America\/Chihuahua","location":"La Paz"}, 360 | {"id":10,"offset":"UTC-07:00","zone":"America\/Mazatlan","location":"Mazatlan"}, 361 | {"id":11,"offset":"UTC-07:00","zone":"US\/Mountain","location":"Mountain Time (US & Canada)"}, 362 | {"id":12,"offset":"UTC-06:00","zone":"America\/Managua","location":"Central America"}, 363 | {"id":13,"offset":"UTC-06:00","zone":"US\/Central","location":"Central Time (US & Canada)"}, 364 | {"id":14,"offset":"UTC-06:00","zone":"America\/Mexico_City","location":"Guadalajara"}, 365 | {"id":15,"offset":"UTC-06:00","zone":"America\/Mexico_City","location":"Mexico City"}, 366 | {"id":16,"offset":"UTC-06:00","zone":"America\/Monterrey","location":"Monterrey"}, 367 | {"id":17,"offset":"UTC-06:00","zone":"Canada\/Saskatchewan","location":"Saskatchewan"}, 368 | {"id":18,"offset":"UTC-05:00","zone":"America\/Bogota","location":"Bogota"}, 369 | {"id":19,"offset":"UTC-05:00","zone":"US\/Eastern","location":"Eastern Time (US & Canada)"}, 370 | {"id":20,"offset":"UTC-05:00","zone":"US\/East-Indiana","location":"Indiana (East)"}, 371 | {"id":21,"offset":"UTC-05:00","zone":"America\/Lima","location":"Lima"}, 372 | {"id":22,"offset":"UTC-05:00","zone":"America\/Bogota","location":"Quito"}, 373 | {"id":23,"offset":"UTC-04:00","zone":"Canada\/Atlantic","location":"Atlantic Time (Canada)"}, 374 | {"id":24,"offset":"UTC-04:30","zone":"America\/Caracas","location":"Caracas"}, 375 | {"id":25,"offset":"UTC-04:00","zone":"America\/La_Paz","location":"La Paz"}, 376 | {"id":26,"offset":"UTC-04:00","zone":"America\/Santiago","location":"Santiago"}, 377 | {"id":27,"offset":"UTC-03:30","zone":"Canada\/Newfoundland","location":"Newfoundland"}, 378 | {"id":28,"offset":"UTC-03:00","zone":"America\/Sao_Paulo","location":"Brasilia"}, 379 | {"id":29,"offset":"UTC-03:00","zone":"America\/Argentina\/Buenos_Aires","location":"Buenos Aires"}, 380 | {"id":30,"offset":"UTC-03:00","zone":"America\/Argentina\/Buenos_Aires","location":"Georgetown"}, 381 | {"id":31,"offset":"UTC-03:00","zone":"America\/Godthab","location":"Greenland"}, 382 | {"id":32,"offset":"UTC-02:00","zone":"America\/Noronha","location":"Mid-Atlantic"}, 383 | {"id":33,"offset":"UTC-01:00","zone":"Atlantic\/Azores","location":"Azores"}, 384 | {"id":34,"offset":"UTC-01:00","zone":"Atlantic\/Cape_Verde","location":"Cape Verde Is."}, 385 | {"id":35,"offset":"UTC+00:00","zone":"Africa\/Casablanca","location":"Casablanca"}, 386 | {"id":36,"offset":"UTC+00:00","zone":"Europe\/London","location":"Edinburgh"}, 387 | {"id":37,"offset":"UTC+00:00","zone":"Etc\/Greenwich","location":"Greenwich Mean Time : Dublin"}, 388 | {"id":38,"offset":"UTC+00:00","zone":"Europe\/Lisbon","location":"Lisbon"}, 389 | {"id":39,"offset":"UTC+00:00","zone":"Europe\/London","location":"London"}, 390 | {"id":40,"offset":"UTC+00:00","zone":"Africa\/Monrovia","location":"Monrovia"}, 391 | {"id":41,"offset":"UTC+00:00","zone":"UTC","location":"UTC"}, 392 | {"id":42,"offset":"UTC+01:00","zone":"Europe\/Amsterdam","location":"Amsterdam"}, 393 | {"id":43,"offset":"UTC+01:00","zone":"Europe\/Belgrade","location":"Belgrade"}, 394 | {"id":44,"offset":"UTC+01:00","zone":"Europe\/Berlin","location":"Berlin"}, 395 | {"id":45,"offset":"UTC+01:00","zone":"Europe\/Berlin","location":"Bern"}, 396 | {"id":46,"offset":"UTC+01:00","zone":"Europe\/Bratislava","location":"Bratislava"}, 397 | {"id":47,"offset":"UTC+01:00","zone":"Europe\/Brussels","location":"Brussels"}, 398 | {"id":48,"offset":"UTC+01:00","zone":"Europe\/Budapest","location":"Budapest"}, 399 | {"id":49,"offset":"UTC+01:00","zone":"Europe\/Copenhagen","location":"Copenhagen"}, 400 | {"id":50,"offset":"UTC+01:00","zone":"Europe\/Ljubljana","location":"Ljubljana"}, 401 | {"id":51,"offset":"UTC+01:00","zone":"Europe\/Madrid","location":"Madrid"}, 402 | {"id":52,"offset":"UTC+01:00","zone":"Europe\/Paris","location":"Paris"}, 403 | {"id":53,"offset":"UTC+01:00","zone":"Europe\/Prague","location":"Prague"}, 404 | {"id":54,"offset":"UTC+01:00","zone":"Europe\/Rome","location":"Rome"}, 405 | {"id":55,"offset":"UTC+01:00","zone":"Europe\/Sarajevo","location":"Sarajevo"}, 406 | {"id":56,"offset":"UTC+01:00","zone":"Europe\/Skopje","location":"Skopje"}, 407 | {"id":57,"offset":"UTC+01:00","zone":"Europe\/Stockholm","location":"Stockholm"}, 408 | {"id":58,"offset":"UTC+01:00","zone":"Europe\/Vienna","location":"Vienna"}, 409 | {"id":59,"offset":"UTC+01:00","zone":"Europe\/Warsaw","location":"Warsaw"}, 410 | {"id":60,"offset":"UTC+01:00","zone":"Africa\/Lagos","location":"West Central Africa"}, 411 | {"id":61,"offset":"UTC+01:00","zone":"Europe\/Zagreb","location":"Zagreb"}, 412 | {"id":62,"offset":"UTC+02:00","zone":"Europe\/Athens","location":"Athens"}, 413 | {"id":63,"offset":"UTC+02:00","zone":"Europe\/Bucharest","location":"Bucharest"}, 414 | {"id":64,"offset":"UTC+02:00","zone":"Africa\/Cairo","location":"Cairo"}, 415 | {"id":65,"offset":"UTC+02:00","zone":"Africa\/Harare","location":"Harare"}, 416 | {"id":66,"offset":"UTC+02:00","zone":"Europe\/Helsinki","location":"Helsinki"}, 417 | {"id":67,"offset":"UTC+02:00","zone":"Europe\/Istanbul","location":"Istanbul"}, 418 | {"id":68,"offset":"UTC+02:00","zone":"Asia\/Jerusalem","location":"Jerusalem"}, 419 | {"id":69,"offset":"UTC+02:00","zone":"Europe\/Helsinki","location":"Kyiv"}, 420 | {"id":70,"offset":"UTC+02:00","zone":"Africa\/Johannesburg","location":"Pretoria"}, 421 | {"id":71,"offset":"UTC+02:00","zone":"Europe\/Riga","location":"Riga"}, 422 | {"id":72,"offset":"UTC+02:00","zone":"Europe\/Sofia","location":"Sofia"}, 423 | {"id":73,"offset":"UTC+02:00","zone":"Europe\/Tallinn","location":"Tallinn"}, 424 | {"id":74,"offset":"UTC+02:00","zone":"Europe\/Vilnius","location":"Vilnius"}, 425 | {"id":75,"offset":"UTC+03:00","zone":"Asia\/Baghdad","location":"Baghdad"}, 426 | {"id":76,"offset":"UTC+03:00","zone":"Asia\/Kuwait","location":"Kuwait"}, 427 | {"id":77,"offset":"UTC+03:00","zone":"Europe\/Minsk","location":"Minsk"}, 428 | {"id":78,"offset":"UTC+03:00","zone":"Europe\/Moscow","location":"Moscow"}, 429 | {"id":79,"offset":"UTC+03:00","zone":"Africa\/Nairobi","location":"Nairobi"}, 430 | {"id":80,"offset":"UTC+03:00","zone":"Asia\/Riyadh","location":"Riyadh"}, 431 | {"id":81,"offset":"UTC+03:00","zone":"Europe\/Moscow","location":"St. Petersburg"}, 432 | {"id":82,"offset":"UTC+03:00","zone":"Europe\/Volgograd","location":"Volgograd"}, 433 | {"id":83,"offset":"UTC+03:30","zone":"Asia\/Tehran","location":"Tehran"}, 434 | {"id":84,"offset":"UTC+04:00","zone":"Asia\/Muscat","location":"Abu Dhabi"}, 435 | {"id":85,"offset":"UTC+04:00","zone":"Asia\/Baku","location":"Baku"}, 436 | {"id":86,"offset":"UTC+04:00","zone":"Asia\/Muscat","location":"Muscat"}, 437 | {"id":87,"offset":"UTC+04:00","zone":"Asia\/Tbilisi","location":"Tbilisi"}, 438 | {"id":88,"offset":"UTC+04:00","zone":"Asia\/Yerevan","location":"Yerevan"}, 439 | {"id":89,"offset":"UTC+04:30","zone":"Asia\/Kabul","location":"Kabul"}, 440 | {"id":90,"offset":"UTC+05:00","zone":"Asia\/Yekaterinburg","location":"Ekaterinburg"}, 441 | {"id":91,"offset":"UTC+05:00","zone":"Asia\/Karachi","location":"Islamabad"}, 442 | {"id":92,"offset":"UTC+05:00","zone":"Asia\/Karachi","location":"Karachi"}, 443 | {"id":93,"offset":"UTC+05:00","zone":"Asia\/Tashkent","location":"Tashkent"}, 444 | {"id":94,"offset":"UTC+05:30","zone":"Asia\/Calcutta","location":"Chennai"}, 445 | {"id":95,"offset":"UTC+05:30","zone":"Asia\/Kolkata","location":"Kolkata"}, 446 | {"id":96,"offset":"UTC+05:30","zone":"Asia\/Calcutta","location":"Mumbai"}, 447 | {"id":97,"offset":"UTC+05:30","zone":"Asia\/Calcutta","location":"New Delhi"}, 448 | {"id":98,"offset":"UTC+05:30","zone":"Asia\/Calcutta","location":"Sri Jayawardenepura"}, 449 | {"id":99,"offset":"UTC+05:45","zone":"Asia\/Katmandu","location":"Kathmandu"}, 450 | {"id":100,"offset":"UTC+06:00","zone":"Asia\/Almaty","location":"Almaty"}, 451 | {"id":101,"offset":"UTC+06:00","zone":"Asia\/Dhaka","location":"Astana"}, 452 | {"id":102,"offset":"UTC+06:00","zone":"Asia\/Dhaka","location":"Dhaka"}, 453 | {"id":103,"offset":"UTC+06:00","zone":"Asia\/Novosibirsk","location":"Novosibirsk"}, 454 | {"id":104,"offset":"UTC+06:30","zone":"Asia\/Rangoon","location":"Rangoon"}, 455 | {"id":105,"offset":"UTC+07:00","zone":"Asia\/Bangkok","location":"Bangkok"}, 456 | {"id":106,"offset":"UTC+07:00","zone":"Asia\/Bangkok","location":"Hanoi"}, 457 | {"id":107,"offset":"UTC+07:00","zone":"Asia\/Jakarta","location":"Jakarta"}, 458 | {"id":108,"offset":"UTC+07:00","zone":"Asia\/Krasnoyarsk","location":"Krasnoyarsk"}, 459 | {"id":109,"offset":"UTC+08:00","zone":"Asia\/Hong_Kong","location":"Beijing"}, 460 | {"id":110,"offset":"UTC+08:00","zone":"Asia\/Chongqing","location":"Chongqing"}, 461 | {"id":111,"offset":"UTC+08:00","zone":"Asia\/Hong_Kong","location":"Hong Kong"}, 462 | {"id":112,"offset":"UTC+08:00","zone":"Asia\/Irkutsk","location":"Irkutsk"}, 463 | {"id":113,"offset":"UTC+08:00","zone":"Asia\/Kuala_Lumpur","location":"Kuala Lumpur"}, 464 | {"id":114,"offset":"UTC+08:00","zone":"Asia\/Manila","location":"Manila"}, 465 | {"id":115,"offset":"UTC+08:00","zone":"Australia\/Perth","location":"Perth"}, 466 | {"id":116,"offset":"UTC+08:00","zone":"Asia\/Singapore","location":"Singapore"}, 467 | {"id":117,"offset":"UTC+08:00","zone":"Asia\/Taipei","location":"Taipei"}, 468 | {"id":118,"offset":"UTC+08:00","zone":"Asia\/Ulan_Bator","location":"Ulaan Bataar"}, 469 | {"id":119,"offset":"UTC+08:00","zone":"Asia\/Urumqi","location":"Urumqi"}, 470 | {"id":120,"offset":"UTC+09:00","zone":"Asia\/Tokyo","location":"Osaka"}, 471 | {"id":121,"offset":"UTC+09:00","zone":"Asia\/Tokyo","location":"Sapporo"}, 472 | {"id":122,"offset":"UTC+09:00","zone":"Asia\/Seoul","location":"Seoul"}, 473 | {"id":123,"offset":"UTC+09:00","zone":"Asia\/Tokyo","location":"Tokyo"}, 474 | {"id":124,"offset":"UTC+09:00","zone":"Asia\/Yakutsk","location":"Yakutsk"}, 475 | {"id":125,"offset":"UTC+09:30","zone":"Australia\/Adelaide","location":"Adelaide"}, 476 | {"id":126,"offset":"UTC+09:30","zone":"Australia\/Darwin","location":"Darwin"}, 477 | {"id":127,"offset":"UTC+10:00","zone":"Australia\/Brisbane","location":"Brisbane"}, 478 | {"id":128,"offset":"UTC+10:00","zone":"Australia\/Canberra","location":"Canberra"}, 479 | {"id":129,"offset":"UTC+10:00","zone":"Pacific\/Guam","location":"Guam"}, 480 | {"id":130,"offset":"UTC+10:00","zone":"Australia\/Hobart","location":"Hobart"}, 481 | {"id":131,"offset":"UTC+10:00","zone":"Asia\/Magadan","location":"Magadan"}, 482 | {"id":132,"offset":"UTC+10:00","zone":"Australia\/Melbourne","location":"Melbourne"}, 483 | {"id":133,"offset":"UTC+10:00","zone":"Pacific\/Port_Moresby","location":"Port Moresby"}, 484 | {"id":134,"offset":"UTC+10:00","zone":"Australia\/Sydney","location":"Sydney"}, 485 | {"id":135,"offset":"UTC+10:00","zone":"Asia\/Vladivostok","location":"Vladivostok"}, 486 | {"id":136,"offset":"UTC+12:00","zone":"Pacific\/Auckland","location":"Auckland"}, 487 | {"id":137,"offset":"UTC+12:00","zone":"Pacific\/Fiji","location":"Fiji"}, 488 | {"id":138,"offset":"UTC+12:00","zone":"Pacific\/Kwajalein","location":"International Date Line West"}, 489 | {"id":139,"offset":"UTC+12:00","zone":"Asia\/Kamchatka","location":"Kamchatka"}, 490 | {"id":140,"offset":"UTC+12:00","zone":"Pacific\/Fiji","location":"Marshall Is."}, 491 | {"id":141,"offset":"UTC+12:00","zone":"Asia\/Magadan","location":"New Caledonia"}, 492 | {"id":142,"offset":"UTC+12:00","zone":"Asia\/Magadan","location":"Solomon Is."}, 493 | {"id":143,"offset":"UTC+12:00","zone":"Pacific\/Auckland","location":"Wellington"}, 494 | {"id":144,"offset":"UTC+13:00","zone":"Pacific\/Tongatapu","location":"Nuku'alofa"}, 495 | 496 | {"markup":"footer","type":"table","name":"timezones"}, 497 | 498 | {"markup":"header","type":"table","name":"users"}, 499 | {"markup":"footer","type":"table","name":"users"}, 500 | 501 | {"markup":"header","type":"table","name":"wallets"}, 502 | {"markup":"footer","type":"table","name":"wallets"} 503 | ] 504 | -------------------------------------------------------------------------------- /src/Commands/DatabaseDumpCommand.php: -------------------------------------------------------------------------------- 1 | generateSchema(); 40 | 41 | with(new TwoColumnDetail($this->getOutput()))->render( 42 | 'Database dump', 43 | 'GENERATING' 44 | ); 45 | 46 | $databaseName = $schema['database_name']; 47 | 48 | //Create file to stream records into 49 | $dumpFolder = config('database-dump.folder'); 50 | $fileName = $schema['file_name']; 51 | 52 | if (! is_dir($dumpFolder)) { 53 | mkdir($dumpFolder, 0755, true); 54 | } 55 | 56 | $filePath = "$dumpFolder$fileName"; 57 | 58 | $lineBreak = "\n"; 59 | 60 | $complexDelimiter = 'Kekeochafd77|Justinbdaaefc4e5'; 61 | $splittedDelimiter = explode('|', $complexDelimiter); 62 | $delimiter = '"'.$splittedDelimiter[0].'":"'.$splittedDelimiter[1].'"'; 63 | 64 | $databaseHeader = "[$lineBreak".json_encode([ 65 | 'markup' => 'header', 66 | 'type' => 'database', 67 | 'name' => $databaseName, 68 | 'data' => [ 69 | 'version' => 4, 70 | 'comment' => 'Export database to JSON', 71 | 'schema' => $schema, 72 | ], 73 | $splittedDelimiter[0] => $splittedDelimiter[1], 74 | ], JSON_UNESCAPED_UNICODE).",$lineBreak"; 75 | 76 | $databaseHeader .= '{"markup":"footer","type":"database","name":"'.$databaseName.'",'.$delimiter.'},'."$lineBreak$lineBreak"; 77 | 78 | file_put_contents($filePath, $databaseHeader, FILE_APPEND); 79 | 80 | $tables = $schema['tables']; 81 | 82 | foreach ($tables as $tableName => $array) { 83 | 84 | $numberOfTableRecords = $array['count']; 85 | 86 | $table = DB::table($tableName); 87 | 88 | $tableHeaderFinishing = $numberOfTableRecords > 0 ? "$lineBreak$lineBreak" : "$lineBreak"; 89 | 90 | $tableHeader = '{"markup":"header","type":"table","name":"'.$tableName.'",'.$delimiter.'},'.$tableHeaderFinishing; 91 | 92 | //Append table header 93 | file_put_contents($filePath, $tableHeader, FILE_APPEND); 94 | 95 | // Chunk and stream table records 96 | $orderByColumn = $this->getOrderByColumn($tableName); 97 | 98 | $processedRecords = 0; 99 | 100 | $table->orderBy($orderByColumn)->chunk(config('database-dump.chunk_length'), function ($records) use ($numberOfTableRecords, &$processedRecords, $lineBreak, $delimiter, $filePath) { 101 | $tableData = ''; 102 | 103 | foreach ($records as $record) { 104 | 105 | $encodedJSON = json_encode($record, JSON_UNESCAPED_UNICODE); 106 | //If malformed JSON filter the record 107 | if ($encodedJSON === false) { 108 | $filteredData = []; 109 | 110 | foreach ($record as $key => $value) { 111 | // If boolean or UTF-8 encoded, keep the value 112 | if (is_bool($value) || mb_detect_encoding($value, 'UTF-8', true)) { 113 | $filteredData[$key] = $value; 114 | } 115 | } 116 | 117 | $encodedJSON = json_encode($filteredData, JSON_UNESCAPED_UNICODE); 118 | } 119 | 120 | if ($encodedJSON) { 121 | $encodedJSON = rtrim($encodedJSON, '}').','.$delimiter.'}'; 122 | $tableData .= "$encodedJSON,$lineBreak"; 123 | } 124 | 125 | $processedRecords++; 126 | 127 | //Limit to when snapshot was taken. 128 | if ($numberOfTableRecords == $processedRecords) { 129 | break; 130 | } 131 | } 132 | 133 | file_put_contents($filePath, "$tableData", FILE_APPEND); 134 | 135 | //Limit to when snapshot was taken. 136 | //Must return false to break. 137 | if ($numberOfTableRecords == $processedRecords) { 138 | return false; 139 | } 140 | }); 141 | 142 | $tableFooterBeginning = $numberOfTableRecords > 0 ? "$lineBreak" : ''; 143 | $tableFooter = $tableFooterBeginning.'{"markup":"footer","type":"table","name":"'.$tableName.'",'.$delimiter.'}'; 144 | $addFinishing = array_key_last($tables) == $tableName ? "$tableFooter$lineBreak]" : "$tableFooter,$lineBreak$lineBreak"; 145 | 146 | file_put_contents($filePath, $addFinishing, FILE_APPEND); 147 | } 148 | 149 | $runTime = $this->runTimeForHumans($startTime); 150 | 151 | with(new TwoColumnDetail($this->getOutput()))->render( 152 | 'Database dump', 153 | "$runTime DONE" 154 | ); 155 | 156 | $this->newLine(); 157 | 158 | $this->components->info("Database dump saved to $filePath"); 159 | 160 | return self::SUCCESS; 161 | } catch (\Exception $e) { 162 | throw $e; 163 | } 164 | } 165 | 166 | private function generateSchema(): array 167 | { 168 | //This function is used to reduce downtime. 169 | //We count the number of records in each table before we start the dump. 170 | //This way, we don't need to want till we stream all records before bring application up. 171 | $this->call('down'); 172 | 173 | $databaseName = DB::connection()->getDatabaseName(); 174 | $tables = DB::select('SHOW TABLES'); 175 | 176 | $schema = [ 177 | 'file_name' => date('Y_m_d_His').'.json', 178 | 'database_name' => $databaseName, 179 | ]; 180 | 181 | foreach ($tables as $table) { 182 | $tableName = $table->{'Tables_in_'.$databaseName}; 183 | $tableCount = DB::table($tableName)->count(); 184 | $schema['tables'][$tableName] = [ 185 | 'count' => $tableCount, 186 | ]; 187 | with(new TwoColumnDetail($this->getOutput()))->render( 188 | $tableName, 189 | "$tableCount rows" 190 | ); 191 | } 192 | 193 | $this->newLine(); 194 | 195 | if (! $this->option('seed')) { 196 | //TODO: Test that application is not called up when --seed option is passed 197 | $this->call('up'); 198 | } 199 | 200 | return $schema; 201 | } 202 | 203 | /** 204 | * Get a suitable column for ordering if 'id' column is not present. 205 | */ 206 | private function getOrderByColumn(string $tableName): string 207 | { 208 | $columns = DB::getSchemaBuilder()->getColumnListing($tableName); 209 | 210 | if (in_array('id', $columns)) { 211 | $orderByColumn = 'id'; 212 | } elseif (in_array('created_at', $columns)) { 213 | $orderByColumn = 'created_at'; 214 | } else { 215 | //Pick first column 216 | $orderByColumn = reset($columns); 217 | } 218 | 219 | return $orderByColumn; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/Commands/FreshCommand.php: -------------------------------------------------------------------------------- 1 | laravel->environment('production')) { 16 | 17 | if (! config('database-dump.enable')) { 18 | 19 | $this->warn('Warning: The database-dump package is disabled. Running this command in production can lead to potential loss of data.'); 20 | 21 | if (! $this->confirm('Do you wish to continue?')) { 22 | return; 23 | } 24 | } 25 | } 26 | 27 | if (config('database-dump.enable')) { 28 | $this->call('database:dump', [ 29 | '--seed' => $this->needsSeeding(), 30 | ]); 31 | } 32 | 33 | parent::handle(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/DatabaseDump.php: -------------------------------------------------------------------------------- 1 | getDirectoryListing($dumpFolder); 54 | 55 | //check if the pointer is an integer 56 | $this->filePath = is_int($needle) 57 | ? $dumpFolder.array_reverse($dumpListings)[$needle] 58 | : "$dumpFolder$needle"; 59 | 60 | return $this; 61 | } 62 | 63 | /** 64 | * Reverse the array and get the first file in the array. 65 | */ 66 | public function getLatestDump(int|string $needle = 0): self 67 | { 68 | return $this->getDump($needle); 69 | } 70 | 71 | /** 72 | * Resolves a model or table name to its corresponding table name. 73 | */ 74 | public function resolveModelOrTableName(string $modelOrTableName): string 75 | { 76 | $resolvedTableName = (class_exists($modelOrTableName) && method_exists($modelOrTableName, 'getTable')) 77 | ? (new $modelOrTableName)->getTable() 78 | : $modelOrTableName; 79 | 80 | return $resolvedTableName; 81 | } 82 | 83 | protected function isMarkupTag(stdClass $row): bool 84 | { 85 | return isset($row->markup) && isset($row->type) && isset($row->name); 86 | } 87 | 88 | protected function isTableHeader(stdClass $row) 89 | { 90 | return $this->isMarkupTag($row) && ($row->markup == 'header' && $row->type == 'table'); 91 | } 92 | 93 | protected function isTableFooter(stdClass $row): bool 94 | { 95 | return $this->isMarkupTag($row) && ($row->markup == 'footer' && $row->type == 'table'); 96 | } 97 | 98 | protected function readFile(int $offset = 0) 99 | { 100 | $file = fopen($this->filePath, 'r'); 101 | 102 | // Ensure the file is opened 103 | if (! $file) { 104 | throw new Exception("Unable to open the file: {$this->filePath}"); 105 | } 106 | 107 | fseek($file, $offset); 108 | 109 | $streamLength = config('database-dump.stream_length'); 110 | 111 | try { 112 | 113 | $delimiter = explode('|', 'Kekeochafd77|Justinbdaaefc4e5'); 114 | 115 | while (! feof($file)) { 116 | 117 | $this->fileOffset = ftell($file); 118 | 119 | //Removing of white space is mainly in case of JSON pretty print. 120 | //The reason we don't use something like "Kekeochaee":"Justindbbceaf5" below is that pretty print can add white space in between. 121 | $line = stream_get_line($file, $streamLength, '"'.$delimiter[1].'"'); 122 | $line = trim("$line"); //remove any leading or trailing whitespace 123 | $line = rtrim("$line", '"'.$delimiter[0].'":'); 124 | $line = trim("$line"); //remove any leading or trailing whitespace 125 | 126 | if ($this->fileOffset == 0) { 127 | $line = trim("$line", '[,'); 128 | } else { 129 | $line = trim("$line", ']},'); 130 | } 131 | 132 | $line = "$line}"; 133 | 134 | if (! feof($file)) { 135 | 136 | if ($decodedJson = json_decode($line)) { 137 | yield $decodedJson; 138 | } else { 139 | throw new Exception("Unable to decode the JSON string: {$line}"); 140 | } 141 | } 142 | } 143 | } finally { 144 | // Ensure the file is closed 145 | fclose($file); 146 | } 147 | } 148 | 149 | protected function generateSchema(): void 150 | { 151 | foreach ($this->readFile() as $row) { 152 | if ($this->isTableHeader($row)) { 153 | //search for tables and note offsets 154 | $this->schema['tables'][$row->name]['file_offset'] = intval($this->fileOffset); 155 | } 156 | } 157 | } 158 | 159 | protected function ensureSafeChunkLength(int $chunkLength, string $tableName): int 160 | { 161 | $maxQueryPlaceholders = 65_535; 162 | $numberOfTableColums = count(Schema::getColumnListing($tableName)); 163 | $safeChunkLength = floor($maxQueryPlaceholders / $numberOfTableColums); 164 | 165 | if ($safeChunkLength < $chunkLength) { 166 | $chunkLength = $safeChunkLength; 167 | } 168 | 169 | return $chunkLength; 170 | } 171 | 172 | /** 173 | * Seed a table with data from a dump. 174 | */ 175 | public function seed(string $modelOrTableName, ?int $chunkLength = null, ?callable $formatRowCallback = null): self 176 | { 177 | $chunkLength = $chunkLength ?? config('database-dump.chunk_length'); 178 | 179 | $tableName = $this->resolveModelOrTableName($modelOrTableName); 180 | 181 | $chunkLength = $this->ensureSafeChunkLength($chunkLength, $tableName); 182 | 183 | /* Generate a schema of the dump and note the file offset of each table. 184 | This ensures that subsequent seed calls on the same dump file instance don't start afresh, 185 | But starts gets the already saved offset for the particular table and starts reading from there 186 | */ 187 | if (! $this->schema) { 188 | $this->generateSchema(); 189 | } 190 | 191 | if (array_key_exists($tableName, $this->schema['tables']) == false) { 192 | throw new \InvalidArgumentException("The table '{$tableName}' does not exist in the dump provided."); 193 | } 194 | 195 | $tableOffset = $this->schema['tables'][$tableName]['file_offset']; 196 | 197 | $tableData = []; 198 | 199 | foreach ($this->readFile($tableOffset) as $row) { 200 | 201 | $isTableHeader = ( 202 | $this->isTableHeader($row) && 203 | $row->name == $tableName 204 | ); 205 | 206 | $isTableFooter = ( 207 | $this->isTableFooter($row) && 208 | $row->name == $tableName 209 | ); 210 | 211 | //Check header tag 212 | if (! $isTableHeader && ! $isTableFooter) { 213 | $rowToArray = (array) $row; 214 | if (is_callable($formatRowCallback)) { 215 | $rowToArray = call_user_func($formatRowCallback, $rowToArray); 216 | } 217 | $tableData[] = $rowToArray; 218 | } 219 | 220 | if ($isTableFooter || count($tableData) == $chunkLength) { 221 | DB::table($tableName)->insert($tableData); 222 | $tableData = []; 223 | } 224 | 225 | //check footer tag 226 | if ($isTableFooter) { 227 | break; 228 | } 229 | } 230 | 231 | return $this; 232 | } 233 | 234 | public function isConsistentWithDatabase(): bool 235 | { 236 | 237 | //TODO: add test to ensure that the first row of the dump is the header that has the schema and stuffs. 238 | //Also add test for this function. 239 | foreach ($this->readFile(0) as $row) { 240 | $tables = (array) $row->data->schema->tables; 241 | foreach ($tables as $tableName => $data) { 242 | $tableCount = DB::table($tableName)->count(); 243 | if ($tableCount != $data->count) { 244 | throw new Exception("The dump has $data->count records for the table `{$tableName}`, but the database has $tableCount records in the database."); 245 | } 246 | } 247 | break; 248 | } 249 | 250 | return true; 251 | } 252 | } 253 | -------------------------------------------------------------------------------- /src/DatabaseDumpServiceProvider.php: -------------------------------------------------------------------------------- 1 | name('database-dump') 22 | ->hasConfigFile() 23 | ->hasCommand(DatabaseDumpCommand::class); 24 | } 25 | 26 | public function boot() 27 | { 28 | parent::boot(); 29 | 30 | if ($this->app->runningInConsole()) { 31 | 32 | $this->publishes([ 33 | __DIR__.'/../resources/stubs/.gitignore.stub' => config('database-dump.folder').'.gitignore', 34 | ], 'database-dump-config'); 35 | 36 | $filesystem = (new FileSystem); 37 | 38 | /** 39 | * Copy commands 40 | */ 41 | $source = __DIR__.'/../src/Commands/FreshCommand.php'; 42 | 43 | // Define target directory 44 | $targetDirectory = base_path('app/Console/Commands'); 45 | // Define target file path 46 | $target = $targetDirectory.'/FreshCommand.php'; 47 | 48 | if (! $filesystem->exists($target)) { 49 | // Create target directory if it doesn't exist 50 | $filesystem->ensureDirectoryExists($targetDirectory); 51 | 52 | // Copy the file, creating it if it doesn't exist 53 | $filesystem->put($target, $filesystem->get($source)); 54 | } 55 | 56 | /** 57 | * Copy tests 58 | */ 59 | $source = __DIR__.'/../src/tests/Feature'; 60 | 61 | $target = base_path('tests/Feature/DatabaseDump'); 62 | 63 | // Check if the directory exists before copying 64 | if (! $filesystem->isDirectory($target)) { 65 | $filesystem->copyDirectory($source, $target); 66 | } 67 | } 68 | 69 | //TODO: test that artisan command works when not running in console and when running in console 70 | //https://laracasts.com/discuss/channels/laravel/target-illuminatedatabasemigrationsmigrationrepositoryinterface-is-not-instantiable?reply=448657 71 | //https://laracasts.com/discuss/channels/laravel/target-illuminatedatabasemigrationsmigrationrepositoryinterface-is-not-instantiable-after-updating-to-laravel-11?reply=938527 72 | $this->app->singleton(FreshCommand::class, function ($app) { 73 | return new FreshCommand($app['migrator']); 74 | }); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Facades/DatabaseDump.php: -------------------------------------------------------------------------------- 1 | assertTrue(class_exists('Justinkekeocha\DatabaseDump\DatabaseDumpServiceProvider')); 7 | }); 8 | 9 | // Or check if the package is present in the composer.lock file 10 | it('ensures the package is listed in composer.lock', function () { 11 | 12 | $composerLock = json_decode(file_get_contents(base_path('composer.lock')), true); 13 | $packages = array_merge($composerLock['packages'], $composerLock['packages-dev']); 14 | 15 | // Replace 'vendor/package' with the vendor and package name you are checking for. 16 | $packageInstalled = false; 17 | foreach ($packages as $package) { 18 | if ($package['name'] == 'justinkekeocha/database-dump') { 19 | $packageInstalled = true; 20 | break; 21 | } 22 | } 23 | 24 | $this->assertTrue($packageInstalled); 25 | }); 26 | 27 | it('checks if package is enabled', function () { 28 | $this->assertTrue(config('database-dump.enable')); 29 | }); 30 | --------------------------------------------------------------------------------