main 14648d21c1df cached
66 files
237.2 KB
57.5k tokens
444 symbols
1 requests
Download .txt
Showing preview only (256K chars total). Download the full file or copy to clipboard to get everything.
Repository: bennett-treptow/laravel-migration-generator
Branch: main
Commit: 14648d21c1df
Files: 66
Total size: 237.2 KB

Directory structure:
gitextract_qojz53lt/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   └── workflows/
│       └── php.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── UPGRADE.md
├── composer.json
├── config/
│   └── laravel-migration-generator.php
├── docs/
│   ├── _config.yml
│   ├── command.md
│   ├── config.md
│   ├── index.md
│   └── stubs.md
├── phpunit.xml
├── pint.json
├── src/
│   ├── Commands/
│   │   └── GenerateMigrationsCommand.php
│   ├── Definitions/
│   │   ├── ColumnDefinition.php
│   │   ├── IndexDefinition.php
│   │   ├── TableDefinition.php
│   │   └── ViewDefinition.php
│   ├── Formatters/
│   │   ├── TableFormatter.php
│   │   └── ViewFormatter.php
│   ├── GeneratorManagers/
│   │   ├── BaseGeneratorManager.php
│   │   ├── Interfaces/
│   │   │   └── GeneratorManagerInterface.php
│   │   └── MySQLGeneratorManager.php
│   ├── Generators/
│   │   ├── BaseTableGenerator.php
│   │   ├── BaseViewGenerator.php
│   │   ├── Concerns/
│   │   │   ├── CleansUpColumnIndices.php
│   │   │   ├── CleansUpForeignKeyIndices.php
│   │   │   ├── CleansUpMorphColumns.php
│   │   │   ├── CleansUpTimestampsColumn.php
│   │   │   ├── WritesToFile.php
│   │   │   └── WritesViewsToFile.php
│   │   ├── Interfaces/
│   │   │   ├── TableGeneratorInterface.php
│   │   │   └── ViewGeneratorInterface.php
│   │   └── MySQL/
│   │       ├── TableGenerator.php
│   │       └── ViewGenerator.php
│   ├── Helpers/
│   │   ├── ConfigResolver.php
│   │   ├── DependencyResolver.php
│   │   ├── Formatter.php
│   │   ├── ValueToString.php
│   │   └── WritableTrait.php
│   ├── LaravelMigrationGeneratorProvider.php
│   └── Tokenizers/
│       ├── BaseColumnTokenizer.php
│       ├── BaseIndexTokenizer.php
│       ├── BaseTokenizer.php
│       ├── Interfaces/
│       │   ├── ColumnTokenizerInterface.php
│       │   └── IndexTokenizerInterface.php
│       └── MySQL/
│           ├── ColumnTokenizer.php
│           └── IndexTokenizer.php
├── stubs/
│   ├── table-create.stub
│   ├── table-modify.stub
│   ├── table.stub
│   └── view.stub
└── tests/
    ├── TestCase.php
    └── Unit/
        ├── ColumnDefinitionTest.php
        ├── DependencyResolverTest.php
        ├── FormatterTest.php
        ├── GeneratorManagers/
        │   └── MySQLGeneratorManagerTest.php
        ├── Generators/
        │   ├── MySQLTableGeneratorTest.php
        │   └── MySQLViewGeneratorTest.php
        ├── Tokenizers/
        │   └── MySQL/
        │       ├── ColumnTokenizerTest.php
        │       └── IndexTokenizerTest.php
        └── ValueToStringTest.php

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: [bennett-treptow]
custom: ['https://buymeacoffee.com/btreptow']


================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: ''
assignees: ''

---

**Package Version**
What version are you running? 2.2.*, 3.*

**Database Version**
What database driver are you using? And what version is that database?

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Please include any stack traces and applicable .env / config changes you've made

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Additional context**
Add any other context about the problem here.


================================================
FILE: .github/workflows/php.yml
================================================
name: PHP Composer

on:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main

jobs:
  build:
    strategy:
      matrix:
        php: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4']
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: ${{ matrix.php }}

      - name: Cache Composer packages
        id: composer-cache
        uses: actions/cache@v4
        with:
          path: vendor
          key: "${{ runner.os }}-php-${{ matrix.php }}-${{ hashFiles('**/composer.lock') }}"
          restore-keys: |
            ${{ runner.os }}-php-${{ matrix.php }}-

      - name: Remove pint
        run: composer remove laravel/pint --dev --no-update

      - name: Install dependencies
        run: composer install --prefer-dist --no-progress

      - name: Run test suite
        run: composer run test


================================================
FILE: .gitignore
================================================
vendor/
.idea
.phpunit.result.cache
.php_cs.cache
composer.lock

================================================
FILE: CHANGELOG.md
================================================
# Version 3.1.6
### New Modifier
`useCurrentOnUpdate` has been implemented
### Bugfix
Issue #27 - `useCurrent` on `timestamps()` method fix

# Version 3.1.3
### [Timestamp:format] Removal
The [Timestamp:format] token for file names has been removed. Migration file names require that [Timestamp] be at the beginning in that specific format. Any other format would cause the migrations to not be loaded.


# Version 3.1.0
### Environment Variables
New environment variables:

| Key | Default Value | Allowed Values | Description |
| --- | ------------- | -------------- | ----------- |
| LMG_SKIP_VIEWS | false | boolean | When true, skip all views |
| LMG_SKIPPABLE_VIEWS | '' | comma delimited string | The views to be skipped |
| LMG_MYSQL_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `mysql` |
| LMG_SQLITE_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `sqlite` |
| LMG_PGSQL_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `pgsql` |
| LMG_SQLSRV_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `sqlsrv` |

# Version 3.0.0

### Run after migrations
When `LMG_RUN_AFTER_MIGRATIONS` is set to true, after running any of the `artisan migrate` commands, the `generate:migrations` command will be run using all the default options for the command. It will only run when the app environment is `local`.

### Environment Variables
New environment variables to replace config updates:

| Key | Default Value | Allowed Values | Description |
| --- | ------------- | -------------- | ----------- |
| LMG_RUN_AFTER_MIGRATIONS | false | boolean | Whether or not the migration generator should run after migrations have completed. |
| LMG_CLEAR_OUTPUT_PATH | false | boolean | Whether or not to clear out the output path before creating new files |
| LMG_TABLE_NAMING_SCHEME | [Timestamp]_create_[TableName]_table.php | string | The string to be used to name table migration files |
| LMG_VIEW_NAMING_SCHEME | [Timestamp]_create_[ViewName]_view.php | string | The string to be used to name view migration files |
| LMG_OUTPUT_PATH | tests/database/migrations | string | The path (relative to the root of your project) to where the files will be output to |
| LMG_SKIPPABLE_TABLES | migrations | comma delimited string | The tables to be skipped |
| LMG_PREFER_UNSIGNED_PREFIX | true | boolean | When true, uses `unsigned` variant methods instead of the `->unsigned()` modifier. |
| LMG_USE_DEFINED_INDEX_NAMES | true | boolean | When true, uses index names defined by the database as the name parameter for index methods |
| LMG_USE_DEFINED_FOREIGN_KEY_INDEX_NAMES | true | boolean | When true, uses foreign key index names defined by the database as the name parameter for foreign key methods |
| LMG_USE_DEFINED_UNIQUE_KEY_INDEX_NAMES | true | boolean | When true, uses unique key index names defined by the database as the name parameter for the `unique` methods |
| LMG_USE_DEFINED_PRIMARY_KEY_INDEX_NAMES | true | boolean | When true, uses primary key index name defined by the database as the name parameter for the `primary` method |
| LMG_MYSQL_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `mysql`. |
| LMG_MYSQL_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `mysql`. |
| LMG_MYSQL_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `mysql`. |
| LMG_MYSQL_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `mysql`. |
| LMG_SQLITE_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `sqlite`. |
| LMG_SQLITE_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `sqlite`. |
| LMG_SQLITE_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `sqlite`. |
| LMG_SQLITE_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `sqlite`. |
| LMG_PGSQL_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `pgsql`. |
| LMG_PGSQL_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `pgsql`. |
| LMG_PGSQL_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `pgsql`. |
| LMG_PGSQL_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `pgsql`. |
| LMG_SQLSRV_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `sqlsrc`. |
| LMG_SQLSRV_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `sqlsrv`. |
| LMG_SQLSRV_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `sqlsrv`. |
| LMG_SQLSRV_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `sqlsrv`. |


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2021 Bennett Treptow

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# Laravel Migration Generator
![Latest Version on Packagist](https://img.shields.io/packagist/v/bennett-treptow/laravel-migration-generator.svg)

Generate migrations from existing database structures, an alternative to the schema dump provided by Laravel. A primary use case for this package would be a project that has many migrations that alter tables using `->change()` from doctrine/dbal that SQLite doesn't support and need a way to get table structures updated for SQLite to use in tests.
Another use case would be taking a project with a database and no migrations and turning that database into base migrations.

# Installation
```bash
composer require --dev bennett-treptow/laravel-migration-generator
```

```bash
php artisan vendor:publish --provider="LaravelMigrationGenerator\LaravelMigrationGeneratorProvider"
```
# Lumen Installation  
```bash  
composer require --dev bennett-treptow/laravel-migration-generator
```  
  
Copy config file from `vendor/bennett-treptow/laravel-migration-generator/config` to your Lumen config folder  
  
Register service provider in bootstrap/app.php  
```php  
$app->register(\LaravelMigrationGenerator\LaravelMigrationGeneratorProvider::class);  
```

# Usage

Whenever you have database changes or are ready to squash your database structure down to migrations, run:
```bash
php artisan generate:migrations
```

By default, the migrations will be created in `tests/database/migrations`. You can specify a different path with the `--path` option: 
```bash
php artisan generate:migrations --path=database/migrations
```

You can specify the connection to use as the database with the `--connection` option:
```bash
php artisan generate:migrations --connection=mysql2
```

You can also clear the directory with the `--empty-path` option:
```bash
php artisan generate:migrations --empty-path
```

This command can also be run by setting the `LMG_RUN_AFTER_MIGRATIONS` environment variable to `true` and running your migrations as normal. This will latch into the `MigrationsEnded` event and run this command using the default options specified via your environment variables. Note: it will only run when your app environment is set to `local`.

# Configuration

Want to customize the migration stubs? Make sure you've published the vendor assets with the artisan command to publish vendor files above.

## Environment Variables

| Key | Default Value | Allowed Values | Description |
| --- | ------------- | -------------- | ----------- |
| LMG_RUN_AFTER_MIGRATIONS | false | boolean | Whether or not the migration generator should run after migrations have completed. |
| LMG_CLEAR_OUTPUT_PATH | false | boolean | Whether or not to clear out the output path before creating new files. Same as specifying `--empty-path` on the command |
| LMG_TABLE_NAMING_SCHEME | `[Timestamp]_create_[TableName]_table.php` | string | The string to be used to name table migration files |
| LMG_VIEW_NAMING_SCHEME | `[Timestamp]_create_[ViewName]_view.php` | string | The string to be used to name view migration files |
| LMG_OUTPUT_PATH | tests/database/migrations | string | The path (relative to the root of your project) to where the files will be output to. Same as specifying `--path=` on the command |
| LMG_SKIPPABLE_TABLES | migrations | comma delimited string | The tables to be skipped |
| LMG_SKIP_VIEWS | false | boolean | When true, skip all views |
| LMG_SKIPPABLE_VIEWS | '' | comma delimited string | The views to be skipped |
| LMG_SORT_MODE | 'foreign_key' | string | The sorting mode to be used. Options: `foreign_key` |
| LMG_PREFER_UNSIGNED_PREFIX | true | boolean | When true, uses `unsigned` variant methods instead of the `->unsigned()` modifier. |
| LMG_USE_DEFINED_INDEX_NAMES | true | boolean | When true, uses index names defined by the database as the name parameter for index methods |
| LMG_USE_DEFINED_FOREIGN_KEY_INDEX_NAMES | true | boolean | When true, uses foreign key index names defined by the database as the name parameter for foreign key methods |
| LMG_USE_DEFINED_UNIQUE_KEY_INDEX_NAMES | true | boolean | When true, uses unique key index names defined by the database as the name parameter for the `unique` methods |
| LMG_USE_DEFINED_PRIMARY_KEY_INDEX_NAMES | true | boolean | When true, uses primary key index name defined by the database as the name parameter for the `primary` method |
| LMG_WITH_COMMENTS | true | boolean | When true, export comment using `->comment()` method. |
| LMG_USE_DEFINED_DATATYPE_ON_TIMESTAMP | false | boolean | When false, uses `->timestamps()` by mashing up `created_at` and `updated_at` regardless of  datatype defined by the database |
| LMG_MYSQL_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `mysql`. |
| LMG_MYSQL_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `mysql`. |
| LMG_MYSQL_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `mysql`. |
| LMG_MYSQL_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `mysql`. |
| LMG_MYSQL_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `mysql` |
| LMG_SQLITE_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `sqlite`. |
| LMG_SQLITE_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `sqlite`. |
| LMG_SQLITE_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `sqlite`. |
| LMG_SQLITE_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `sqlite`. |
| LMG_SQLITE_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `sqlite` |
| LMG_PGSQL_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `pgsql`. |
| LMG_PGSQL_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `pgsql`. |
| LMG_PGSQL_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `pgsql`. |
| LMG_PGSQL_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `pgsql`. |
| LMG_PGSQL_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `pgsql` |
| LMG_SQLSRV_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `sqlsrc`. |
| LMG_SQLSRV_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `sqlsrv`. |
| LMG_SQLSRV_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `sqlsrv`. |
| LMG_SQLSRV_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `sqlsrv`. |
| LMG_SQLSRV_SKIPPABLE_VIEWS | null | comma delimited string | The views to be skipped when driver is `sqlsrv` |

## Stubs
There is a default stub for tables and views, found in `resources/stubs/vendor/laravel-migration-generator/`.
Each database driver can be assigned a specific migration stub by creating a new stub file in `resources/stubs/vendor/laravel-migration-generator/` with a `driver`-prefix, e.g. `mysql-table.stub` for a MySQL specific table stub.

## Stub Naming
Table and view stubs can be named using the `LMG_(TABLE|VIEW)_NAMING_SCHEME` environment variables. Optionally, driver-specific naming schemes can be used as well by specifying `LMG_{driver}_TABLE_NAMING_SCHEME` environment vars using the same tokens. See below for available tokens that can be replaced.

### Table Name Stub Tokens
Table stubs have the following tokens available for the naming scheme:

| Token | Example | Description |
| ----- |-------- | ----------- |
| `[TableName]` | users | Table's name, same as what is defined in the database |
| `[TableName:Studly]` | Users | Table's name with `Str::studly()` applied to it (useful for standardizing table names if they are inconsistent) |
| `[TableName:Lowercase]` | users | Table's name with `strtolower` applied to it (useful for standardizing table names if they are inconsistent) |
| `[Timestamp]` | 2021_04_25_110000 | The standard migration timestamp format, at the time of calling the command: `Y_m_d_His` |
| `[Index]` | 0 | The key of the migration in the sorted order, for use with enforcing a sort order |
| `[IndexedEmptyTimestamp]` | 0000_00_00_000041 | The standard migration timestamp format, but filled with 0s and incremented by `[Index]` seconds |
| `[IndexedTimestamp]` | 2021_04_25_110003 | The standard migration timestamp format, at the time of calling the command: `Y_m_d_His` incremented by `[Index]` seconds |


### Table Schema Stub Tokens
Table schema stubs have the following tokens available:

| Token | Description |
| ----- | ----------- |
| `[TableName]` | Table's name, same as what is defined in the database |
| `[TableName:Studly]` | Table's name with `Str::studly()` applied to it, for use with the class name |
| `[TableUp]` | Table's `up()` function |
| `[TableDown]` | Table's `down()` function |
| `[Schema]` | The table's generated schema |


### View Name Stub Tokens
View stubs have the following tokens available for the naming scheme:

| Token | Example | Description |
| ----- |-------- | ----------- |
| `[ViewName]` | user_earnings | View's name, same as what is defined in the database |
| `[ViewName:Studly]` | UserEarnings | View's name with `Str::studly()` applied to it (useful for standardizing view names if they are inconsistent) |
| `[ViewName:Lowercase]` | user_earnings | View's name with `strtolower` applied to it (useful for standardizing view names if they are inconsistent) |
| `[Timestamp]` | 2021_04_25_110000 | The standard migration timestamp format, at the time of calling the command: `Y_m_d_His` |
| `[Index]` | 0 | The key of the migration in the sorted order, for use with enforcing a sort order |
| `[IndexedEmptyTimestamp]` | 0000_00_00_000041 | The standard migration timestamp format, but filled with 0s and incremented by `[Index]` seconds |
| `[IndexedTimestamp]` | 2021_04_25_110003 | The standard migration timestamp format, at the time of calling the command: `Y_m_d_His` incremented by `[Index]` seconds |

### View Schema Stub Tokens
View schema stubs have the following tokens available:

| Token | Description |
| ----- | ----------- |
| `[ViewName]` | View's name, same as what is defined in the database |
| `[ViewName:Studly]` | View's name with `Str::studly()` applied to it, for use with the class name |
| `[Schema]` | The view's schema |


# Example Usage

Given a database structure for a `users` table of:
```sql
CREATE TABLE `users` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `username` varchar(128) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `first_name` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `last_name` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `timezone` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'America/New_York',
  `location_id` int(10) unsigned NOT NULL,
  `deleted_at` timestamp NULL DEFAULT NULL,
  `remember_token` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `users_username_index` (`username`),
  KEY `users_first_name_index` (`first_name`),
  KEY `users_last_name_index` (`last_name`),
  KEY `users_email_index` (`email`),
  KEY `fk_users_location_id_index` (`location_id`)
  CONSTRAINT `users_location_id_foreign` FOREIGN KEY (`location_id`) REFERENCES `locations` (`id`) ON UPDATE CASCADE ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
```

A `tests/database/migrations/[TIMESTAMP]_create_users_table.php` with the following Blueprint would be created:
```php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateUsersTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('username', 128)->nullable()->index();
            $table->string('email', 255)->index();
            $table->string('password', 255);
            $table->string('first_name', 45)->nullable()->index();
            $table->string('last_name', 45)->index();
            $table->string('timezone', 45)->default('America/New_York');
            $table->unsignedInteger('location_id');
            $table->softDeletes();
            $table->string('remember_token', 255)->nullable();
            $table->timestamps();
            $table->foreign('location_id', 'users_location_id_foreign')->references('id')->on('locations')->onUpdate('cascade')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
}
```


# Currently Supported DBMS's
These DBMS's are what are currently supported for creating migrations **from**. Migrations created will, as usual, follow what [database drivers Laravel migrations allow for](https://laravel.com/docs/8.x/database#introduction)

- [x] MySQL
- [ ] Postgres
- [ ] SQLite
- [ ] SQL Server


================================================
FILE: UPGRADE.md
================================================
## Upgrade from 3.* to 4.0

### Foreign Key Sorting
New foreign key dependency sorting options, available as an env variable to potentially not sort by foreign key dependencies if not necessary.
Update your `config/laravel-migration-generator.php` to have a new `sort_mode` key:

```dotenv
'sort_mode' => env('LMG_SORT_MODE', 'foreign_key'),
```

### New Stubs
New stubs for a `create` and a `modify` version for tables.
If you want to change how a `Schema::create` or `Schema::table` is output as, create a new `table-create.stub` or `table-modify.stub` and their driver variants as well if desired.

### New Table and View Naming Tokens

Three new tokens were added for the table stubs: `[Index]`, `[IndexedEmptyTimestamp]`, and `[IndexedTimestamp]`.
For use with foreign key / sorting in general to enforce a final sorting.

`[Index]` is the numeric key (0,1,2,...) that the migration holds in the sort order.

`[IndexedEmptyTimestamp]` is the `[Index]` but prefixed with the necessary digits and underscores for the file to be recognized as a migration. `0000_00_00_000001_migration.php`

`[IndexedTimestamp]` is the current time incremented by `[Index]` seconds. So first migration would be the current time, second migration would be +1 second, third +2 seconds, etc.

### New Table Stub Tokens
Two new tokens were added for table stubs: `[TableUp]` and `[TableDown]`.
See latest `stubs/table.stub`. It is suggested to upgrade all of your stubs using the latest stubs available by `vendor:publish --force`

## Upgrade from 2.2.* to 3.0.0

`skippable_tables` is now a comma delimited string instead of an array so they are compatible with .env files.

All config options have been moved to equivalent .env variables. Please update `config/laravel-migration-generator.php` with a `vendor:publish --force`.
The new environment variables are below:

| Key | Default Value | Allowed Values | Description |
| --- | ------------- | -------------- | ----------- |
| LMG_RUN_AFTER_MIGRATIONS | false | boolean | Whether or not the migration generator should run after migrations have completed. |
| LMG_CLEAR_OUTPUT_PATH | false | boolean | Whether or not to clear out the output path before creating new files |
| LMG_TABLE_NAMING_SCHEME | [Timestamp]_create_[TableName]_table.php | string | The string to be used to name table migration files |
| LMG_VIEW_NAMING_SCHEME | [Timestamp]_create_[ViewName]_view.php | string | The string to be used to name view migration files |
| LMG_OUTPUT_PATH | tests/database/migrations | string | The path (relative to the root of your project) to where the files will be output to |
| LMG_SKIPPABLE_TABLES | migrations | comma delimited string | The tables to be skipped |
| LMG_PREFER_UNSIGNED_PREFIX | true | boolean | When true, uses `unsigned` variant methods instead of the `->unsigned()` modifier. |
| LMG_USE_DEFINED_INDEX_NAMES | true | boolean | When true, uses index names defined by the database as the name parameter for index methods |
| LMG_USE_DEFINED_FOREIGN_KEY_INDEX_NAMES | true | boolean | When true, uses foreign key index names defined by the database as the name parameter for foreign key methods |
| LMG_USE_DEFINED_UNIQUE_KEY_INDEX_NAMES | true | boolean | When true, uses unique key index names defined by the database as the name parameter for the `unique` methods |
| LMG_USE_DEFINED_PRIMARY_KEY_INDEX_NAMES | true | boolean | When true, uses primary key index name defined by the database as the name parameter for the `primary` method |
| LMG_MYSQL_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `mysql`. |
| LMG_MYSQL_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `mysql`. |
| LMG_MYSQL_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `mysql`. |
| LMG_MYSQL_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `mysql`. |
| LMG_SQLITE_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `sqlite`. |
| LMG_SQLITE_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `sqlite`. |
| LMG_SQLITE_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `sqlite`. |
| LMG_SQLITE_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `sqlite`. |
| LMG_PGSQL_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `pgsql`. |
| LMG_PGSQL_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `pgsql`. |
| LMG_PGSQL_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `pgsql`. |
| LMG_PGSQL_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `pgsql`. |
| LMG_SQLSRV_TABLE_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_TABLE_NAMING_SCHEME when the database driver is `sqlsrc`. |
| LMG_SQLSRV_VIEW_NAMING_SCHEME | null | ?boolean | When not null, this setting will override LMG_VIEW_NAMING_SCHEME when the database driver is `sqlsrv`. |
| LMG_SQLSRV_OUTPUT_PATH | null | ?boolean | When not null, this setting will override LMG_OUTPUT_PATH when the database driver is `sqlsrv`. |
| LMG_SQLSRV_SKIPPABLE_TABLES | null | ?boolean | When not null, this setting will override LMG_SKIPPABLE_TABLES when the database driver is `sqlsrv`. |


================================================
FILE: composer.json
================================================
{
    "name": "bennett-treptow/laravel-migration-generator",
    "description": "Generate migrations from existing database structures",
    "minimum-stability": "stable",
    "license": "MIT",
    "authors": [
        {
            "name": "Bennett Treptow",
            "email": "me@btreptow.com"
        }
    ],
    "require": {
        "php": "^7.4|^8.0|^8.1|^8.2|^8.3|^8.4",
        "illuminate/support": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
        "illuminate/console": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
        "illuminate/database": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
        "illuminate/config": "^6.0|^7.0|^8.0|^9.0|^10.0|^11.0|^12.0",
        "marcj/topsort": "^2.0"
    },
    "autoload": {
        "psr-4": {
            "LaravelMigrationGenerator\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Tests\\": "tests/"
        }
    },
    "require-dev": {
        "orchestra/testbench": "^6.17|^8.0|^9.0|^10.0",
        "laravel/pint": "^1.15"
    },
    "scripts": {
        "post-autoload-dump": [
            "@php ./vendor/bin/testbench package:discover --ansi"
        ],
        "test": [
            "vendor/bin/phpunit"
        ],
        "lint": "vendor/bin/pint"
    },
    "extra": {
        "laravel": {
            "providers": [
                "LaravelMigrationGenerator\\LaravelMigrationGeneratorProvider"
            ]
        }
    },
    "prefer-stable": true,
    "config": {
        "audit": {
          "block-insecure": false
        }
    }
}


================================================
FILE: config/laravel-migration-generator.php
================================================
<?php

return [
    'run_after_migrations' => env('LMG_RUN_AFTER_MIGRATIONS', false),
    'clear_output_path' => env('LMG_CLEAR_OUTPUT_PATH', false),
    //default configs
    'table_naming_scheme' => env('LMG_TABLE_NAMING_SCHEME', '[IndexedTimestamp]_create_[TableName]_table.php'),
    'view_naming_scheme' => env('LMG_VIEW_NAMING_SCHEME', '[IndexedTimestamp]_create_[ViewName]_view.php'),
    'path' => env('LMG_OUTPUT_PATH', 'tests/database/migrations'),
    'skippable_tables' => env('LMG_SKIPPABLE_TABLES', 'migrations'),
    'skip_views' => env('LMG_SKIP_VIEWS', false),
    'skippable_views' => env('LMG_SKIPPABLE_VIEWS', ''),
    'sort_mode' => env('LMG_SORT_MODE', 'foreign_key'),
    'definitions' => [
        'prefer_unsigned_prefix' => env('LMG_PREFER_UNSIGNED_PREFIX', true),
        'use_defined_index_names' => env('LMG_USE_DEFINED_INDEX_NAMES', true),
        'use_defined_foreign_key_index_names' => env('LMG_USE_DEFINED_FOREIGN_KEY_INDEX_NAMES', true),
        'use_defined_unique_key_index_names' => env('LMG_USE_DEFINED_UNIQUE_KEY_INDEX_NAMES', true),
        'use_defined_primary_key_index_names' => env('LMG_USE_DEFINED_PRIMARY_KEY_INDEX_NAMES', true),
        'with_comments' => env('LMG_WITH_COMMENTS', true),
        'use_defined_datatype_on_timestamp' => env('LMG_USE_DEFINED_DATATYPE_ON_TIMESTAMP', false),
    ],

    //now driver specific configs
    //null = use default
    'mysql' => [
        'table_naming_scheme' => env('LMG_MYSQL_TABLE_NAMING_SCHEME', null),
        'view_naming_scheme' => env('LMG_MYSQL_VIEW_NAMING_SCHEME', null),
        'path' => env('LMG_MYSQL_OUTPUT_PATH', null),
        'skippable_tables' => env('LMG_MYSQL_SKIPPABLE_TABLES', null),
        'skippable_views' => env('LMG_MYSQL_SKIPPABLE_VIEWS', null),
    ],
    'sqlite' => [
        'table_naming_scheme' => env('LMG_SQLITE_TABLE_NAMING_SCHEME', null),
        'view_naming_scheme' => env('LMG_SQLITE_VIEW_NAMING_SCHEME', null),
        'path' => env('LMG_SQLITE_OUTPUT_PATH', null),
        'skippable_tables' => env('LMG_SQLITE_SKIPPABLE_TABLES', null),
        'skippable_views' => env('LMG_SQLITE_SKIPPABLE_VIEWS', null),
    ],
    'pgsql' => [
        'table_naming_scheme' => env('LMG_PGSQL_TABLE_NAMING_SCHEME', null),
        'view_naming_scheme' => env('LMG_PGSQL_VIEW_NAMING_SCHEME', null),
        'path' => env('LMG_PGSQL_OUTPUT_PATH', null),
        'skippable_tables' => env('LMG_PGSQL_SKIPPABLE_TABLES', null),
        'skippable_views' => env('LMG_PGSQL_SKIPPABLE_VIEWS', null),
    ],
    'sqlsrv' => [
        'table_naming_scheme' => env('LMG_SQLSRV_TABLE_NAMING_SCHEME', null),
        'view_naming_scheme' => env('LMG_SQLSRV_VIEW_NAMING_SCHEME', null),
        'path' => env('LMG_SQLSRV_OUTPUT_PATH', null),
        'skippable_tables' => env('LMG_SQLSRV_SKIPPABLE_TABLES', null),
        'skippable_views' => env('LMG_SQLSRV_SKIPPABLE_VIEWS', null),
    ],
];


================================================
FILE: docs/_config.yml
================================================
remote_theme: pmarsceill/just-the-docs
compress_html:
  ignore:
    envs: all
aux_links:
  "View on GitHub":
    - "//github.com/bennett-treptow/laravel-migration-generator"


================================================
FILE: docs/command.md
================================================
---
layout: default
title: Running the Generator
nav_order: 3
---

================================================
FILE: docs/config.md
================================================
---
layout: default
title: Config
nav_order: 1
---

================================================
FILE: docs/index.md
================================================
---
layout: default
title: Laravel Migration Generator
nav_order: 0
---
# Laravel Migration Generator

Generate migrations from existing database structures, an alternative to the schema dump provided by Laravel. This package will connect to your database and introspect the schema and generate migration files with columns and indices like they would be if they had originally come from a migration.

## Quick Start
```bash
composer require --dev bennett-treptow/laravel-migration-generator
php artisan vendor:publish --provider="LaravelMigrationGenerator\LaravelMigrationGeneratorProvider"
```

Learn more about [config options](config.md) and [stubs](stubs.md).

================================================
FILE: docs/stubs.md
================================================
---
layout: default
title: Stubs
nav_order: 2
---

================================================
FILE: phpunit.xml
================================================
<phpunit>
    <testsuites>
        <testsuite name="Unit">
            <directory suffix="Test.php">./tests/Unit</directory>
        </testsuite>
    </testsuites>
</phpunit>

================================================
FILE: pint.json
================================================
{
  "preset": "laravel"
}

================================================
FILE: src/Commands/GenerateMigrationsCommand.php
================================================
<?php

namespace LaravelMigrationGenerator\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\DB;
use LaravelMigrationGenerator\GeneratorManagers\Interfaces\GeneratorManagerInterface;
use LaravelMigrationGenerator\GeneratorManagers\MySQLGeneratorManager;
use LaravelMigrationGenerator\Helpers\ConfigResolver;

class GenerateMigrationsCommand extends Command
{
    protected $signature = 'generate:migrations {--path=default : The path where migrations will be output to} {--table=* : Only generate output for specified tables} {--view=* : Only generate output for specified views} {--connection=default : Use a different database connection specified in database config} {--empty-path : Clear other files in path, eg if wanting to replace all migrations}';

    protected $description = 'Generate migrations from an existing database';

    public function getConnection()
    {
        $connection = $this->option('connection');

        if ($connection === 'default') {
            $connection = Config::get('database.default');
        }

        if (! Config::has('database.connections.'.$connection)) {
            throw new \Exception('Could not find connection `'.$connection.'` in your config.');
        }

        return $connection;
    }

    public function getPath($driver)
    {
        $basePath = $this->option('path');
        if ($basePath === 'default') {
            $basePath = ConfigResolver::path($driver);
        }

        return $basePath;
    }

    public function handle()
    {
        try {
            $connection = $this->getConnection();
        } catch (\Exception $e) {
            $this->error($e->getMessage());

            return 1;
        }

        $this->info('Using connection '.$connection);
        DB::setDefaultConnection($connection);

        $driver = Config::get('database.connections.'.$connection)['driver'];

        $manager = $this->resolveGeneratorManager($driver);
        if ($manager === false) {
            $this->error('The `'.$driver.'` driver is not supported at this time.');

            return 1;
        }

        $basePath = base_path($this->getPath($driver));

        if ($this->option('empty-path') || config('laravel-migration-generator.clear_output_path')) {
            foreach (glob($basePath.'/*.php') as $file) {
                unlink($file);
            }
        }

        $this->info('Using '.$basePath.' as the output path..');

        $tableNames = Arr::wrap($this->option('table'));

        $viewNames = Arr::wrap($this->option('view'));

        $manager->handle($basePath, $tableNames, $viewNames);
    }

    /**
     * @return false|GeneratorManagerInterface
     */
    protected function resolveGeneratorManager(string $driver)
    {
        $supported = [
            'mysql' => MySQLGeneratorManager::class,
        ];

        if (! isset($supported[$driver])) {
            return false;
        }

        return new $supported[$driver]();
    }
}


================================================
FILE: src/Definitions/ColumnDefinition.php
================================================
<?php

namespace LaravelMigrationGenerator\Definitions;

use Illuminate\Support\Str;
use LaravelMigrationGenerator\Helpers\ValueToString;
use LaravelMigrationGenerator\Helpers\WritableTrait;

/**
 * Class ColumnDefinition
 */
class ColumnDefinition
{
    use WritableTrait;

    protected string $methodName = '';

    protected array $methodParameters = [];

    protected ?string $columnName = null;

    protected bool $unsigned = false;

    protected ?bool $nullable = null;

    protected $defaultValue;

    protected ?string $comment = null;

    protected ?string $characterSet = null;

    protected ?string $collation = null;

    protected bool $autoIncrementing = false;

    protected bool $index = false;

    protected bool $primary = false;

    protected bool $unique = false;

    protected bool $useCurrent = false;

    protected bool $useCurrentOnUpdate = false;

    protected ?string $storedAs = null;

    protected ?string $virtualAs = null;

    protected bool $isUUID = false;

    /** @var IndexDefinition[] */
    protected array $indexDefinitions = [];

    public function __construct($attributes = [])
    {
        foreach ($attributes as $attribute => $value) {
            if (property_exists($this, $attribute)) {
                $this->$attribute = $value;
            }
        }
    }

    //region Getters

    public function getMethodName(): string
    {
        return $this->methodName;
    }

    public function getMethodParameters(): array
    {
        return $this->methodParameters;
    }

    public function getColumnName(): ?string
    {
        return $this->columnName;
    }

    public function isUnsigned(): bool
    {
        return $this->unsigned;
    }

    /**
     * @return ?bool
     */
    public function isNullable(): ?bool
    {
        return $this->nullable;
    }

    /**
     * @return mixed
     */
    public function getDefaultValue()
    {
        if (ValueToString::isCastedValue($this->defaultValue)) {
            return ValueToString::parseCastedValue($this->defaultValue);
        }

        return $this->defaultValue;
    }

    public function getComment(): ?string
    {
        return $this->comment;
    }

    public function getCharacterSet(): ?string
    {
        return $this->characterSet;
    }

    public function getCollation(): ?string
    {
        return $this->collation;
    }

    public function isAutoIncrementing(): bool
    {
        return $this->autoIncrementing;
    }

    public function isIndex(): bool
    {
        return $this->index;
    }

    public function isPrimary(): bool
    {
        return $this->primary;
    }

    public function isUnique(): bool
    {
        return $this->unique;
    }

    public function useCurrent(): bool
    {
        return $this->useCurrent;
    }

    public function useCurrentOnUpdate(): bool
    {
        return $this->useCurrentOnUpdate;
    }

    public function getStoredAs(): ?string
    {
        return $this->storedAs;
    }

    public function getVirtualAs(): ?string
    {
        return $this->virtualAs;
    }

    public function isUUID(): bool
    {
        return $this->isUUID;
    }

    //endregion

    //region Setters

    public function setMethodName(string $methodName): ColumnDefinition
    {
        $this->methodName = $methodName;

        return $this;
    }

    public function setMethodParameters(array $methodParameters): ColumnDefinition
    {
        $this->methodParameters = $methodParameters;

        return $this;
    }

    public function setColumnName(?string $columnName): ColumnDefinition
    {
        $this->columnName = $columnName;

        return $this;
    }

    public function setUnsigned(bool $unsigned): ColumnDefinition
    {
        $this->unsigned = $unsigned;

        return $this;
    }

    /**
     * @param  ?bool  $nullable
     */
    public function setNullable(?bool $nullable): ColumnDefinition
    {
        $this->nullable = $nullable;

        return $this;
    }

    /**
     * @param  mixed  $defaultValue
     * @return ColumnDefinition
     */
    public function setDefaultValue($defaultValue)
    {
        $this->defaultValue = $defaultValue;

        return $this;
    }

    public function setComment(?string $comment): ColumnDefinition
    {
        $this->comment = $comment;

        return $this;
    }

    /**
     * @param  string|null  $collation
     */
    public function setCharacterSet(?string $characterSet): ColumnDefinition
    {
        $this->characterSet = $characterSet;

        return $this;
    }

    public function setCollation(?string $collation): ColumnDefinition
    {
        $this->collation = $collation;

        return $this;
    }

    public function setAutoIncrementing(bool $autoIncrementing): ColumnDefinition
    {
        $this->autoIncrementing = $autoIncrementing;

        return $this;
    }

    public function setStoredAs(?string $storedAs): ColumnDefinition
    {
        $this->storedAs = $storedAs;

        return $this;
    }

    public function setVirtualAs(?string $virtualAs): ColumnDefinition
    {
        $this->virtualAs = $virtualAs;

        return $this;
    }

    public function addIndexDefinition(IndexDefinition $definition): ColumnDefinition
    {
        $this->indexDefinitions[] = $definition;

        return $this;
    }

    public function setIndex(bool $index): ColumnDefinition
    {
        $this->index = $index;

        return $this;
    }

    public function setPrimary(bool $primary): ColumnDefinition
    {
        $this->primary = $primary;

        return $this;
    }

    public function setUnique(bool $unique): ColumnDefinition
    {
        $this->unique = $unique;

        return $this;
    }

    public function setUseCurrent(bool $useCurrent): ColumnDefinition
    {
        $this->useCurrent = $useCurrent;

        return $this;
    }

    public function setUseCurrentOnUpdate(bool $useCurrentOnUpdate): ColumnDefinition
    {
        $this->useCurrentOnUpdate = $useCurrentOnUpdate;

        return $this;
    }

    public function setIsUUID(bool $isUUID): ColumnDefinition
    {
        $this->isUUID = $isUUID;

        return $this;
    }

    //endregion

    protected function isNullableMethod($methodName)
    {
        return ! in_array($methodName, ['softDeletes', 'morphs', 'nullableMorphs', 'rememberToken', 'nullableUuidMorphs']) && ! $this->isPrimaryKeyMethod($methodName);
    }

    protected function isPrimaryKeyMethod($methodName)
    {
        return in_array($methodName, ['tinyIncrements', 'mediumIncrements', 'increments', 'bigIncrements', 'id']);
    }

    protected function canBeUnsigned($methodName)
    {
        return ! in_array($methodName, ['morphs', 'nullableMorphs']) && ! $this->isPrimaryKeyMethod($methodName);
    }

    protected function guessLaravelMethod()
    {
        if ($this->primary && $this->unsigned && $this->autoIncrementing) {
            //some sort of increments field
            if ($this->methodName === 'bigInteger') {
                if ($this->columnName === 'id') {
                    return [null, 'id', []];
                } else {
                    return [$this->columnName, 'bigIncrements', []];
                }
            } elseif ($this->methodName === 'mediumInteger') {
                return [$this->columnName, 'mediumIncrements', []];
            } elseif ($this->methodName === 'integer') {
                return [$this->columnName, 'increments', []];
            } elseif ($this->methodName === 'smallInteger') {
                return [$this->columnName, 'smallIncrements', []];
            } elseif ($this->methodName === 'tinyInteger') {
                return [$this->columnName, 'tinyIncrements', []];
            }
        }

        if ($this->methodName === 'tinyInteger' && ! $this->unsigned) {
            $boolean = false;
            if (in_array($this->defaultValue, ['true', 'false', true, false, 'TRUE', 'FALSE', '1', '0', 1, 0], true)) {
                $boolean = true;
            }
            if (Str::startsWith(strtoupper($this->columnName), ['IS_', 'HAS_'])) {
                $boolean = true;
            }
            if ($boolean) {
                return [$this->columnName, 'boolean', []];
            }
        }

        if ($this->methodName === 'morphs' && $this->nullable === true) {
            return [$this->columnName, 'nullableMorphs', []];
        }

        if ($this->methodName === 'uuidMorphs' && $this->nullable === true) {
            return [$this->columnName, 'nullableUuidMorphs', []];
        }

        if ($this->methodName === 'string' && $this->columnName === 'remember_token' && $this->nullable === true) {
            return [null, 'rememberToken', []];
        }
        if ($this->isUUID() && $this->methodName !== 'uuidMorphs') {
            //only override if not already uuidMorphs
            return [$this->columnName, 'uuid', []];
        }

        if (config('laravel-migration-generator.definitions.prefer_unsigned_prefix') && $this->unsigned) {
            $availableUnsignedPrefixes = [
                'bigInteger',
                'decimal',
                'integer',
                'mediumInteger',
                'smallInteger',
                'tinyInteger',
            ];
            if (in_array($this->methodName, $availableUnsignedPrefixes)) {
                return [$this->columnName, 'unsigned'.ucfirst($this->methodName), $this->methodParameters];
            }
        }

        return [$this->columnName, $this->methodName, $this->methodParameters];
    }

    public function render(): string
    {
        [$finalColumnName, $finalMethodName, $finalMethodParameters] = $this->guessLaravelMethod();

        $initialString = '$table->'.$finalMethodName.'(';
        if ($finalColumnName !== null) {
            $initialString .= ValueToString::make($finalColumnName);
        }
        if (count($finalMethodParameters) > 0) {
            foreach ($finalMethodParameters as $param) {
                $initialString .= ', '.ValueToString::make($param);
            }
        }
        $initialString .= ')';
        if ($this->unsigned && $this->canBeUnsigned($finalMethodName) && ! Str::startsWith($finalMethodName, 'unsigned')) {
            $initialString .= '->unsigned()';
        }

        if ($this->defaultValue === 'NULL') {
            $this->defaultValue = null;
            $this->nullable = true;
        }

        if ($this->isNullableMethod($finalMethodName)) {
            if ($this->nullable === true) {
                $initialString .= '->nullable()';
            }
        }

        if ($this->defaultValue !== null) {
            $initialString .= '->default(';
            $initialString .= ValueToString::make($this->defaultValue, false);
            $initialString .= ')';
        }
        if ($this->useCurrent) {
            $initialString .= '->useCurrent()';
        }
        if ($this->useCurrentOnUpdate) {
            $initialString .= '->useCurrentOnUpdate()';
        }

        if ($this->index) {
            $indexName = '';
            if (count($this->indexDefinitions) === 1 && config('laravel-migration-generator.definitions.use_defined_index_names')) {
                $indexName = ValueToString::make($this->indexDefinitions[0]->getIndexName());
            }
            $initialString .= '->index('.$indexName.')';
        }

        if ($this->primary && ! $this->isPrimaryKeyMethod($finalMethodName)) {
            $indexName = '';
            if (count($this->indexDefinitions) === 1 && config('laravel-migration-generator.definitions.use_defined_primary_key_index_names')) {
                if ($this->indexDefinitions[0]->getIndexName() !== null) {
                    $indexName = ValueToString::make($this->indexDefinitions[0]->getIndexName());
                }
            }
            $initialString .= '->primary('.$indexName.')';
        }

        if ($this->unique) {
            $indexName = '';
            if (count($this->indexDefinitions) === 1 && config('laravel-migration-generator.definitions.use_defined_unique_key_index_names')) {
                $indexName = ValueToString::make($this->indexDefinitions[0]->getIndexName());
            }
            $initialString .= '->unique('.$indexName.')';
        }

        if ($this->storedAs !== null) {
            $initialString .= '->storedAs('.ValueToString::make($this->storedAs, false, false).')';
        }

        if ($this->virtualAs !== null) {
            $initialString .= '->virtualAs('.ValueToString::make($this->virtualAs, false, false).')';
        }

        if ($this->comment !== null && config('laravel-migration-generator.definitions.with_comments')) {
            $initialString .= '->comment('.ValueToString::make($this->comment, false, false).')';
        }

        return $initialString;
    }
}


================================================
FILE: src/Definitions/IndexDefinition.php
================================================
<?php

namespace LaravelMigrationGenerator\Definitions;

use LaravelMigrationGenerator\Helpers\ValueToString;
use LaravelMigrationGenerator\Helpers\WritableTrait;

class IndexDefinition
{
    use WritableTrait;

    public const TYPE_FOREIGN = 'foreign';

    protected string $indexType = '';

    protected ?string $indexName = null; //primary keys usually don't have a name

    protected array $indexColumns = [];

    protected array $foreignReferencedColumns = [];

    protected string $foreignReferencedTable = '';

    protected array $constraintActions = [];

    public function __construct($attributes = [])
    {
        foreach ($attributes as $attribute => $value) {
            if (property_exists($this, $attribute)) {
                $this->$attribute = $value;
            }
        }
    }

    //region Getters

    public function getIndexType(): string
    {
        return $this->indexType;
    }

    public function getIndexName(): ?string
    {
        return $this->indexName;
    }

    public function getIndexColumns(): array
    {
        return $this->indexColumns;
    }

    public function getForeignReferencedColumns(): array
    {
        return $this->foreignReferencedColumns;
    }

    public function getForeignReferencedTable(): string
    {
        return $this->foreignReferencedTable;
    }

    public function getConstraintActions(): array
    {
        return $this->constraintActions;
    }

    //endregion
    //region Setters

    public function setIndexType(string $indexType): IndexDefinition
    {
        $this->indexType = $indexType;

        return $this;
    }

    public function setIndexName(string $indexName): IndexDefinition
    {
        $this->indexName = $indexName;

        return $this;
    }

    public function setIndexColumns(array $indexColumns): IndexDefinition
    {
        $this->indexColumns = $indexColumns;

        return $this;
    }

    public function setForeignReferencedColumns(array $foreignReferencedColumns): IndexDefinition
    {
        $this->foreignReferencedColumns = $foreignReferencedColumns;

        return $this;
    }

    public function setForeignReferencedTable(string $foreignReferencedTable): IndexDefinition
    {
        $this->foreignReferencedTable = $foreignReferencedTable;

        return $this;
    }

    public function setConstraintActions(array $constraintActions): IndexDefinition
    {
        $this->constraintActions = $constraintActions;

        return $this;
    }

    //endregion

    public function isMultiColumnIndex()
    {
        return count($this->indexColumns) > 1;
    }

    /**
     * Get the escaped index name for safe inclusion in generated PHP code.
     */
    protected function getEscapedIndexName(): string
    {
        return ValueToString::escape($this->getIndexName() ?? '');
    }

    public function render(): string
    {
        if ($this->indexType === 'foreign') {
            $indexName = '';
            if (config('laravel-migration-generator.definitions.use_defined_foreign_key_index_names')) {
                $indexName = ', \''.$this->getEscapedIndexName().'\'';
            }

            $base = '$table->foreign('.ValueToString::make($this->indexColumns, true).$indexName.')->references('.ValueToString::make($this->foreignReferencedColumns, true).')->on('.ValueToString::make($this->foreignReferencedTable).')';
            foreach ($this->constraintActions as $type => $action) {
                $base .= '->on'.ucfirst($type).'('.ValueToString::make($action).')';
            }

            return $base;
        } elseif ($this->indexType === 'primary') {
            $indexName = '';
            if (config('laravel-migration-generator.definitions.use_defined_primary_key_index_names') && $this->getIndexName() !== null) {
                $indexName = ', \''.$this->getEscapedIndexName().'\'';
            }

            return '$table->primary('.ValueToString::make($this->indexColumns).$indexName.')';
        } elseif ($this->indexType === 'unique') {
            $indexName = '';
            if (config('laravel-migration-generator.definitions.use_defined_unique_key_index_names')) {
                $indexName = ', \''.$this->getEscapedIndexName().'\'';
            }

            return '$table->unique('.ValueToString::make($this->indexColumns).$indexName.')';
        } elseif ($this->indexType === 'index') {
            $indexName = '';
            if (config('laravel-migration-generator.definitions.use_defined_index_names')) {
                $indexName = ', \''.$this->getEscapedIndexName().'\'';
            }

            return '$table->index('.ValueToString::make($this->indexColumns).$indexName.')';
        } elseif ($this->indexType === 'fulltext') {
            $indexName = '';
            if (config('laravel-migration-generator.definitions.use_defined_index_names')) {
                $indexName = ', \''.$this->getEscapedIndexName().'\'';
            }

            return '$table->fullText('.ValueToString::make($this->indexColumns).$indexName.')';
        } elseif ($this->indexType === 'spatial') {
            $indexName = '';
            if (config('laravel-migration-generator.definitions.use_defined_index_names')) {
                $indexName = ', \''.$this->getEscapedIndexName().'\'';
            }

            return '$table->spatialIndex('.ValueToString::make($this->indexColumns).$indexName.')';
        }

        return '';
    }
}


================================================
FILE: src/Definitions/TableDefinition.php
================================================
<?php

namespace LaravelMigrationGenerator\Definitions;

use LaravelMigrationGenerator\Formatters\TableFormatter;

class TableDefinition
{
    protected string $tableName = '';

    protected string $driver = '';

    /** @var array<ColumnDefinition> */
    protected array $columnDefinitions = [];

    protected array $indexDefinitions = [];

    public function __construct($attributes = [])
    {
        foreach ($attributes as $attribute => $value) {
            if (property_exists($this, $attribute)) {
                $this->$attribute = $value;
            }
        }
    }

    public function getDriver(): string
    {
        return $this->driver;
    }

    public function getPresentableTableName(): string
    {
        if (count($this->getColumnDefinitions()) === 0) {
            if (count($definitions = $this->getIndexDefinitions()) > 0) {
                $first = collect($definitions)->first();

                //a fk only table from dependency resolution
                return $this->getTableName().'_'.$first->getIndexName();
            }
        }

        return $this->getTableName();
    }

    public function getTableName(): string
    {
        return $this->tableName;
    }

    public function setTableName(string $tableName)
    {
        $this->tableName = $tableName;

        return $this;
    }

    public function getColumnDefinitions(): array
    {
        return $this->columnDefinitions;
    }

    public function setColumnDefinitions(array $columnDefinitions)
    {
        $this->columnDefinitions = $columnDefinitions;

        return $this;
    }

    public function addColumnDefinition(ColumnDefinition $definition)
    {
        $this->columnDefinitions[] = $definition;

        return $this;
    }

    /**
     * @return array<IndexDefinition>
     */
    public function getIndexDefinitions(): array
    {
        return $this->indexDefinitions;
    }

    /** @return array<IndexDefinition> */
    public function getForeignKeyDefinitions(): array
    {
        return collect($this->getIndexDefinitions())->filter(function ($indexDefinition) {
            return $indexDefinition->getIndexType() == IndexDefinition::TYPE_FOREIGN;
        })->toArray();
    }

    public function setIndexDefinitions(array $indexDefinitions)
    {
        $this->indexDefinitions = $indexDefinitions;

        return $this;
    }

    public function addIndexDefinition(IndexDefinition $definition)
    {
        $this->indexDefinitions[] = $definition;

        return $this;
    }

    public function removeIndexDefinition(IndexDefinition $definition)
    {
        foreach ($this->indexDefinitions as $key => $indexDefinition) {
            if ($definition->getIndexName() == $indexDefinition->getIndexName()) {
                unset($this->indexDefinitions[$key]);

                break;
            }
        }

        return $this;
    }

    public function getPrimaryKey(): array
    {
        return collect($this->getColumnDefinitions())
            ->filter(function (ColumnDefinition $columnDefinition) {
                return $columnDefinition->isPrimary();
            })->toArray();
    }

    public function formatter(): TableFormatter
    {
        return new TableFormatter($this);
    }
}


================================================
FILE: src/Definitions/ViewDefinition.php
================================================
<?php

namespace LaravelMigrationGenerator\Definitions;

use LaravelMigrationGenerator\Formatters\ViewFormatter;

class ViewDefinition
{
    protected string $driver = '';

    protected string $viewName = '';

    protected ?string $schema = null;

    public function __construct($attributes = [])
    {
        foreach ($attributes as $attribute => $value) {
            if (property_exists($this, $attribute)) {
                $this->$attribute = $value;
            }
        }
    }

    public function getDriver(): string
    {
        return $this->driver;
    }

    public function setDriver(string $driver): ViewDefinition
    {
        $this->driver = $driver;

        return $this;
    }

    public function getSchema(): string
    {
        return $this->schema;
    }

    public function setSchema(string $schema): ViewDefinition
    {
        $this->schema = $schema;

        return $this;
    }

    public function getViewName(): string
    {
        return $this->viewName;
    }

    public function setViewName(string $viewName): ViewDefinition
    {
        $this->viewName = $viewName;

        return $this;
    }

    public function formatter(): ViewFormatter
    {
        return new ViewFormatter($this);
    }
}


================================================
FILE: src/Formatters/TableFormatter.php
================================================
<?php

namespace LaravelMigrationGenerator\Formatters;

use Illuminate\Support\Str;
use LaravelMigrationGenerator\Definitions\TableDefinition;
use LaravelMigrationGenerator\Helpers\ConfigResolver;
use LaravelMigrationGenerator\Helpers\Formatter;

class TableFormatter
{
    private TableDefinition $tableDefinition;

    public function __construct(TableDefinition $tableDefinition)
    {
        $this->tableDefinition = $tableDefinition;
    }

    public function render($tabCharacter = '    ')
    {
        $tableName = $this->tableDefinition->getPresentableTableName();

        $schema = $this->getSchema($tabCharacter);
        $stub = file_get_contents($this->getStubPath());
        if (strpos($stub, '[TableUp]') !== false) {
            //uses new syntax
            $stub = Formatter::replace($tabCharacter, '[TableUp]', $this->stubTableUp($tabCharacter), $stub);
            $stub = Formatter::replace($tabCharacter, '[TableDown]', $this->stubTableDown($tabCharacter), $stub);
        }

        $stub = str_replace('[TableName:Studly]', Str::studly($tableName), $stub);
        $stub = str_replace('[TableName]', $tableName, $stub);
        $stub = Formatter::replace($tabCharacter, '[Schema]', $schema, $stub);

        return $stub;
    }

    public function getStubFileName($index = 0): string
    {
        $driver = $this->tableDefinition->getDriver();
        $baseStubFileName = ConfigResolver::tableNamingScheme($driver);
        foreach ($this->stubNameVariables($index) as $variable => $replacement) {
            if (preg_match("/\[".$variable."\]/i", $baseStubFileName) === 1) {
                $baseStubFileName = preg_replace("/\[".$variable."\]/i", $replacement, $baseStubFileName);
            }
        }

        return $baseStubFileName;
    }

    public function getStubPath(): string
    {
        $driver = $this->tableDefinition->getDriver();

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/'.$driver.'-table.stub'))) {
            return $overridden;
        }

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/table.stub'))) {
            return $overridden;
        }

        return __DIR__.'/../../stubs/table.stub';
    }

    public function getStubCreatePath(): string
    {
        $driver = $this->tableDefinition->getDriver();

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/'.$driver.'-table-create.stub'))) {
            return $overridden;
        }

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/table-create.stub'))) {
            return $overridden;
        }

        return __DIR__.'/../../stubs/table-create.stub';
    }

    public function getStubModifyPath(): string
    {
        $driver = $this->tableDefinition->getDriver();

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/'.$driver.'-table-modify.stub'))) {
            return $overridden;
        }

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/table-modify.stub'))) {
            return $overridden;
        }

        return __DIR__.'/../../stubs/table-modify.stub';
    }

    public function stubNameVariables($index): array
    {
        $tableName = $this->tableDefinition->getPresentableTableName();

        return [
            'TableName:Studly' => Str::studly($tableName),
            'TableName:Lowercase' => strtolower($tableName),
            'TableName' => $tableName,
            'Timestamp' => app('laravel-migration-generator:time')->format('Y_m_d_His'),
            'Index' => (string) $index,
            'IndexedEmptyTimestamp' => '0000_00_00_'.str_pad((string) $index, 6, '0', STR_PAD_LEFT),
            'IndexedTimestamp' => app('laravel-migration-generator:time')->clone()->addSeconds($index)->format('Y_m_d_His'),
        ];
    }

    public function getSchema($tab = ''): string
    {
        $formatter = new Formatter($tab);
        collect($this->tableDefinition->getColumnDefinitions())
            ->filter(fn ($col) => $col->isWritable())
            ->each(function ($column) use ($formatter) {
                $formatter->line($column->render().';');
            });

        $indices = collect($this->tableDefinition->getIndexDefinitions())
            ->filter(fn ($index) => $index->isWritable());

        if ($indices->count() > 0) {
            if (count($this->tableDefinition->getColumnDefinitions()) > 0) {
                $formatter->line('');
            }
            $indices->each(function ($index) use ($formatter) {
                $formatter->line($index->render().';');
            });
        }

        return $formatter->render();
    }

    public function stubTableUp($tab = '', $variables = null): string
    {
        if ($variables === null) {
            $variables = $this->getStubVariables($tab);
        }
        if (count($this->tableDefinition->getColumnDefinitions()) === 0) {
            $tableModifyStub = file_get_contents($this->getStubModifyPath());
            foreach ($variables as $var => $replacement) {
                $tableModifyStub = Formatter::replace($tab, '['.$var.']', $replacement, $tableModifyStub);
            }

            return $tableModifyStub;
        }

        $tableUpStub = file_get_contents($this->getStubCreatePath());
        foreach ($variables as $var => $replacement) {
            $tableUpStub = Formatter::replace($tab, '['.$var.']', $replacement, $tableUpStub);
        }

        return $tableUpStub;
    }

    public function stubTableDown($tab = ''): string
    {
        if (count($this->tableDefinition->getColumnDefinitions()) === 0) {
            $schema = 'Schema::table(\''.$this->tableDefinition->getTableName().'\', function(Blueprint $table){'."\n";
            foreach ($this->tableDefinition->getForeignKeyDefinitions() as $indexDefinition) {
                $schema .= $tab.'$table->dropForeign(\''.$indexDefinition->getIndexName().'\');'."\n";
            }

            return $schema.'});';
        }

        return 'Schema::dropIfExists(\''.$this->tableDefinition->getTableName().'\');';
    }

    protected function getStubVariables($tab = '')
    {
        $tableName = $this->tableDefinition->getTableName();

        return [
            'TableName:Studly' => Str::studly($tableName),
            'TableName:Lowercase' => strtolower($tableName),
            'TableName' => $tableName,
            'Schema' => $this->getSchema($tab),
        ];
    }

    public function write(string $basePath, $index = 0, string $tabCharacter = '    '): string
    {
        $stub = $this->render($tabCharacter);

        $fileName = $this->getStubFileName($index);
        file_put_contents($final = $basePath.'/'.$fileName, $stub);

        return $final;
    }
}


================================================
FILE: src/Formatters/ViewFormatter.php
================================================
<?php

namespace LaravelMigrationGenerator\Formatters;

use Illuminate\Support\Str;
use LaravelMigrationGenerator\Definitions\ViewDefinition;
use LaravelMigrationGenerator\Helpers\ConfigResolver;
use LaravelMigrationGenerator\Helpers\Formatter;

class ViewFormatter
{
    private ViewDefinition $definition;

    public function __construct(ViewDefinition $definition)
    {
        $this->definition = $definition;
    }

    public function stubNameVariables($index = 0)
    {
        return [
            'ViewName:Studly' => Str::studly($viewName = $this->definition->getViewName()),
            'ViewName:Lowercase' => strtolower($viewName),
            'ViewName' => $viewName,
            'Timestamp' => app('laravel-migration-generator:time')->format('Y_m_d_His'),
            'Index' => '0000_00_00_'.str_pad((string) $index, 6, '0', STR_PAD_LEFT),
            'IndexedEmptyTimestamp' => '0000_00_00_'.str_pad((string) $index, 6, '0', STR_PAD_LEFT),
            'IndexedTimestamp' => app('laravel-migration-generator:time')->clone()->addSeconds($index)->format('Y_m_d_His'),
        ];
    }

    protected function getStubFileName($index = 0)
    {
        $driver = $this->definition->getDriver();

        $baseStubFileName = ConfigResolver::viewNamingScheme($driver);
        foreach ($this->stubNameVariables($index) as $variable => $replacement) {
            if (preg_match("/\[".$variable."\]/i", $baseStubFileName) === 1) {
                $baseStubFileName = preg_replace("/\[".$variable."\]/i", $replacement, $baseStubFileName);
            }
        }

        return $baseStubFileName;
    }

    protected function getStubPath()
    {
        $driver = $this->definition->getDriver();

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/'.$driver.'-view.stub'))) {
            return $overridden;
        }

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/view.stub'))) {
            return $overridden;
        }

        return __DIR__.'/../../stubs/view.stub';
    }

    public function render($tabCharacter = '    ')
    {
        $schema = $this->definition->getSchema();
        $stub = file_get_contents($this->getStubPath());
        $variables = [
            '[ViewName:Studly]' => Str::studly($viewName = $this->definition->getViewName()),
            '[ViewName]' => $viewName,
            '[Schema]' => $schema,
        ];
        foreach ($variables as $key => $value) {
            $stub = Formatter::replace($tabCharacter, $key, $value, $stub);
        }

        return $stub;
    }

    public function write(string $basePath, $index = 0, string $tabCharacter = '    '): string
    {
        $stub = $this->render($tabCharacter);

        $fileName = $this->getStubFileName($index);
        file_put_contents($final = $basePath.'/'.$fileName, $stub);

        return $final;
    }
}


================================================
FILE: src/GeneratorManagers/BaseGeneratorManager.php
================================================
<?php

namespace LaravelMigrationGenerator\GeneratorManagers;

use LaravelMigrationGenerator\Definitions\TableDefinition;
use LaravelMigrationGenerator\Definitions\ViewDefinition;
use LaravelMigrationGenerator\GeneratorManagers\Interfaces\GeneratorManagerInterface;
use LaravelMigrationGenerator\Helpers\ConfigResolver;
use LaravelMigrationGenerator\Helpers\DependencyResolver;

abstract class BaseGeneratorManager implements GeneratorManagerInterface
{
    protected array $tableDefinitions = [];

    protected array $viewDefinitions = [];

    abstract public function init();

    public function createMissingDirectory($basePath)
    {
        if (! is_dir($basePath)) {
            mkdir($basePath, 0777, true);
        }
    }

    /**
     * @return array<TableDefinition>
     */
    public function getTableDefinitions(): array
    {
        return $this->tableDefinitions;
    }

    /**
     * @return array<ViewDefinition>
     */
    public function getViewDefinitions(): array
    {
        return $this->viewDefinitions;
    }

    public function addTableDefinition(TableDefinition $tableDefinition): BaseGeneratorManager
    {
        $this->tableDefinitions[] = $tableDefinition;

        return $this;
    }

    public function addViewDefinition(ViewDefinition $definition): BaseGeneratorManager
    {
        $this->viewDefinitions[] = $definition;

        return $this;
    }

    public function handle(string $basePath, array $tableNames = [], array $viewNames = [])
    {
        $this->init();

        $tableDefinitions = collect($this->getTableDefinitions());
        $viewDefinitions = collect($this->getViewDefinitions());

        $this->createMissingDirectory($basePath);

        if (count($tableNames) > 0) {
            $tableDefinitions = $tableDefinitions->filter(function ($tableDefinition) use ($tableNames) {
                return in_array($tableDefinition->getTableName(), $tableNames);
            });
        }
        if (count($viewNames) > 0) {
            $viewDefinitions = $viewDefinitions->filter(function ($viewGenerator) use ($viewNames) {
                return in_array($viewGenerator->getViewName(), $viewNames);
            });
        }

        $tableDefinitions = $tableDefinitions->filter(function ($tableDefinition) {
            return ! $this->skipTable($tableDefinition->getTableName());
        });

        $viewDefinitions = $viewDefinitions->filter(function ($viewDefinition) {
            return ! $this->skipView($viewDefinition->getViewName());
        });

        $sorted = $this->sortTables($tableDefinitions->toArray());

        $this->writeTableMigrations($sorted, $basePath);

        $this->writeViewMigrations($viewDefinitions->toArray(), $basePath, count($sorted));
    }

    /**
     * @param  array<TableDefinition>  $tableDefinitions
     * @return array<TableDefinition>
     */
    public function sortTables(array $tableDefinitions): array
    {
        if (count($tableDefinitions) <= 1) {
            return $tableDefinitions;
        }

        if (config('laravel-migration-generator.sort_mode') == 'foreign_key') {
            return (new DependencyResolver($tableDefinitions))->getDependencyOrder();
        }

        return $tableDefinitions;
    }

    /**
     * @param  array<TableDefinition>  $tableDefinitions
     */
    public function writeTableMigrations(array $tableDefinitions, $basePath)
    {
        foreach ($tableDefinitions as $key => $tableDefinition) {
            $tableDefinition->formatter()->write($basePath, $key);
        }
    }

    /**
     * @param  array<ViewDefinition>  $viewDefinitions
     */
    public function writeViewMigrations(array $viewDefinitions, $basePath, $tableCount = 0)
    {
        foreach ($viewDefinitions as $key => $view) {
            $view->formatter()->write($basePath, $tableCount + $key);
        }
    }

    /**
     * @return array<string>
     */
    public function skippableTables(): array
    {
        return ConfigResolver::skippableTables(static::driver());
    }

    public function skipTable($table): bool
    {
        return in_array($table, $this->skippableTables());
    }

    /**
     * @return array<string>
     */
    public function skippableViews(): array
    {
        return ConfigResolver::skippableViews(static::driver());
    }

    public function skipView($view): bool
    {
        $skipViews = config('laravel-migration-generator.skip_views');
        if ($skipViews) {
            return true;
        }

        return in_array($view, $this->skippableViews());
    }
}


================================================
FILE: src/GeneratorManagers/Interfaces/GeneratorManagerInterface.php
================================================
<?php

namespace LaravelMigrationGenerator\GeneratorManagers\Interfaces;

use LaravelMigrationGenerator\Definitions\TableDefinition;
use LaravelMigrationGenerator\Definitions\ViewDefinition;

interface GeneratorManagerInterface
{
    public static function driver(): string;

    public function handle(string $basePath, array $tableNames = [], array $viewNames = []);

    public function addTableDefinition(TableDefinition $definition);

    public function addViewDefinition(ViewDefinition $definition);

    public function getTableDefinitions(): array;

    public function getViewDefinitions(): array;
}


================================================
FILE: src/GeneratorManagers/MySQLGeneratorManager.php
================================================
<?php

namespace LaravelMigrationGenerator\GeneratorManagers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use LaravelMigrationGenerator\Definitions\TableDefinition;
use LaravelMigrationGenerator\GeneratorManagers\Interfaces\GeneratorManagerInterface;
use LaravelMigrationGenerator\Generators\MySQL\TableGenerator;
use LaravelMigrationGenerator\Generators\MySQL\ViewGenerator;

class MySQLGeneratorManager extends BaseGeneratorManager implements GeneratorManagerInterface
{
    public static function driver(): string
    {
        return 'mysql';
    }

    public function init()
    {
        $tables = DB::select('SHOW FULL TABLES');

        foreach ($tables as $rowNumber => $table) {
            $tableData = (array) $table;
            $table = $tableData[array_key_first($tableData)];
            $tableType = $tableData['Table_type'];
            if ($tableType === 'BASE TABLE') {
                $this->addTableDefinition(TableGenerator::init($table)->definition());
            } elseif ($tableType === 'VIEW') {
                $this->addViewDefinition(ViewGenerator::init($table)->definition());
            }
        }
    }

    public function addTableDefinition(TableDefinition $tableDefinition): BaseGeneratorManager
    {
        $prefix = config('database.connections.'.DB::getDefaultConnection().'.prefix', '');
        if (! empty($prefix) && Str::startsWith($tableDefinition->getTableName(), $prefix)) {
            $tableDefinition->setTableName(Str::replaceFirst($prefix, '', $tableDefinition->getTableName()));
        }

        return parent::addTableDefinition($tableDefinition);
    }
}


================================================
FILE: src/Generators/BaseTableGenerator.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators;

use LaravelMigrationGenerator\Definitions\TableDefinition;
use LaravelMigrationGenerator\Generators\Concerns\CleansUpColumnIndices;
use LaravelMigrationGenerator\Generators\Concerns\CleansUpForeignKeyIndices;
use LaravelMigrationGenerator\Generators\Concerns\CleansUpMorphColumns;
use LaravelMigrationGenerator\Generators\Concerns\CleansUpTimestampsColumn;
use LaravelMigrationGenerator\Generators\Interfaces\TableGeneratorInterface;

abstract class BaseTableGenerator implements TableGeneratorInterface
{
    use CleansUpColumnIndices;
    use CleansUpForeignKeyIndices;
    use CleansUpMorphColumns;
    use CleansUpTimestampsColumn;

    protected array $rows = [];

    protected TableDefinition $definition;

    public function __construct(string $tableName, array $rows = [])
    {
        $this->definition = new TableDefinition([
            'driver' => static::driver(),
            'tableName' => $tableName,
        ]);
        $this->rows = $rows;
    }

    public function definition(): TableDefinition
    {
        return $this->definition;
    }

    abstract public function resolveStructure();

    abstract public function parse();

    public static function init(string $tableName, array $rows = [])
    {
        $instance = (new static($tableName, $rows));

        if ($instance->shouldResolveStructure()) {
            $instance->resolveStructure();
        }

        $instance->parse();
        $instance->cleanUp();

        return $instance;
    }

    public function shouldResolveStructure(): bool
    {
        return count($this->rows) === 0;
    }

    public function cleanUp(): void
    {
        $this->cleanUpForeignKeyIndices();

        $this->cleanUpMorphColumns();

        if (! config('laravel-migration-generator.definitions.use_defined_datatype_on_timestamp')) {
            $this->cleanUpTimestampsColumn();
        }

        $this->cleanUpColumnsWithIndices();
    }
}


================================================
FILE: src/Generators/BaseViewGenerator.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators;

use LaravelMigrationGenerator\Definitions\ViewDefinition;
use LaravelMigrationGenerator\Generators\Interfaces\ViewGeneratorInterface;

abstract class BaseViewGenerator implements ViewGeneratorInterface
{
    protected ViewDefinition $definition;

    public function __construct(string $viewName, ?string $schema = null)
    {
        $this->definition = new ViewDefinition([
            'driver' => static::driver(),
            'viewName' => $viewName,
            'schema' => $schema,
        ]);
    }

    public function definition(): ViewDefinition
    {
        return $this->definition;
    }

    public static function init(string $viewName, ?string $schema = null)
    {
        $obj = new static($viewName, $schema);
        if ($schema === null) {
            $obj->resolveSchema();
        }
        $obj->parse();

        return $obj;
    }
}


================================================
FILE: src/Generators/Concerns/CleansUpColumnIndices.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Concerns;

use LaravelMigrationGenerator\Generators\BaseTableGenerator;

/**
 * Trait CleansUpColumnIndices
 *
 * @mixin BaseTableGenerator
 */
trait CleansUpColumnIndices
{
    protected function cleanUpColumnsWithIndices(): void
    {
        foreach ($this->definition()->getIndexDefinitions() as &$index) {
            /** @var \LaravelMigrationGenerator\Definitions\IndexDefinition $index */
            if (! $index->isWritable()) {
                continue;
            }
            $columns = $index->getIndexColumns();

            foreach ($columns as $indexColumn) {
                foreach ($this->definition()->getColumnDefinitions() as $column) {
                    if ($column->getColumnName() === $indexColumn) {
                        $indexType = $index->getIndexType();
                        $isMultiColumnIndex = $index->isMultiColumnIndex();

                        if ($indexType === 'primary' && ! $isMultiColumnIndex) {
                            $column->setPrimary(true)->addIndexDefinition($index);
                            $index->markAsWritable(false);
                        } elseif ($indexType === 'index' && ! $isMultiColumnIndex) {
                            $isForeignKeyIndex = false;
                            foreach ($this->definition()->getIndexDefinitions() as $innerIndex) {
                                $innerIndexColumns = $innerIndex->getIndexColumns();
                                if ($innerIndex->getIndexType() === 'foreign' && ! $innerIndex->isMultiColumnIndex() && ! empty($innerIndexColumns) && $innerIndexColumns[0] == $column->getColumnName()) {
                                    $isForeignKeyIndex = true;

                                    break;
                                }
                            }
                            if ($isForeignKeyIndex === false) {
                                $column->setIndex(true)->addIndexDefinition($index);
                            }
                            $index->markAsWritable(false);
                        } elseif ($indexType === 'unique' && ! $isMultiColumnIndex) {
                            $column->setUnique(true)->addIndexDefinition($index);
                            $index->markAsWritable(false);
                        }
                    }
                }
            }
        }
    }
}


================================================
FILE: src/Generators/Concerns/CleansUpForeignKeyIndices.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Concerns;

use LaravelMigrationGenerator\Generators\BaseTableGenerator;

/**
 * Trait CleansUpForeignKeyIndices
 *
 * @mixin BaseTableGenerator
 */
trait CleansUpForeignKeyIndices
{
    protected function cleanUpForeignKeyIndices(): void
    {
        $indexDefinitions = $this->definition()->getIndexDefinitions();
        foreach ($indexDefinitions as $index) {
            /** @var \LaravelMigrationGenerator\Definitions\IndexDefinition $index */
            if ($index->getIndexType() === 'index') {
                //look for corresponding foreign key for this index
                $columns = $index->getIndexColumns();
                $indexName = $index->getIndexName();

                foreach ($indexDefinitions as $innerIndex) {
                    /** @var \LaravelMigrationGenerator\Definitions\IndexDefinition $innerIndex */
                    if ($innerIndex->getIndexName() !== $indexName) {
                        if ($innerIndex->getIndexType() === 'foreign') {
                            $cols = $innerIndex->getIndexColumns();
                            if (count(array_intersect($columns, $cols)) === count($columns)) {
                                //has same columns
                                $index->markAsWritable(false);

                                break;
                            }
                        }
                    }
                }
            }
        }
    }
}


================================================
FILE: src/Generators/Concerns/CleansUpMorphColumns.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Concerns;

use Illuminate\Support\Str;
use LaravelMigrationGenerator\Definitions\ColumnDefinition;
use LaravelMigrationGenerator\Generators\BaseTableGenerator;

/**
 * Trait CleansUpMorphColumns
 *
 * @mixin BaseTableGenerator
 */
trait CleansUpMorphColumns
{
    protected function cleanUpMorphColumns(): void
    {
        $morphColumns = [];

        foreach ($this->definition()->getColumnDefinitions() as &$column) {
            if (Str::endsWith($columnName = $column->getColumnName(), ['_id', '_type'])) {
                $pieces = explode('_', $columnName);
                $type = array_pop($pieces); //pop off id or type
                $morphColumn = implode('_', $pieces);
                $morphColumns[$morphColumn][$type] = $column;
            }
        }

        foreach ($morphColumns as $columnName => $fields) {
            if (count($fields) === 2) {
                /** @var ColumnDefinition $idField */
                $idField = $fields['id'];
                /** @var ColumnDefinition $typeField */
                $typeField = $fields['type'];

                if (! ($idField->isUUID() || Str::contains($idField->getMethodName(), 'integer'))) {
                    //should only be a uuid field or integer
                    continue;
                }
                if ($typeField->getMethodName() != 'string') {
                    //should only be a string field
                    continue;
                }

                if ($idField->isUUID()) {
                    //UUID morph
                    $idField
                        ->setMethodName('uuidMorphs')
                        ->setMethodParameters([])
                        ->setColumnName($columnName);
                } else {
                    //regular morph
                    $idField
                        ->setMethodName('morphs')
                        ->setColumnName($columnName);
                }
                $typeField->markAsWritable(false);

                foreach ($this->definition->getIndexDefinitions() as $index) {
                    $columns = $index->getIndexColumns();
                    $morphColumns = [$columnName.'_id', $columnName.'_type'];

                    if (count($columns) == count($morphColumns) && array_diff($columns, $morphColumns) === array_diff($morphColumns, $columns)) {
                        $index->markAsWritable(false);

                        break;
                    }
                }
            }
        }
    }
}


================================================
FILE: src/Generators/Concerns/CleansUpTimestampsColumn.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Concerns;

use LaravelMigrationGenerator\Generators\BaseTableGenerator;

/**
 * Trait CleansUpTimestampsColumn
 *
 * @mixin BaseTableGenerator
 */
trait CleansUpTimestampsColumn
{
    protected function cleanUpTimestampsColumn(): void
    {
        $timestampColumns = [];
        foreach ($this->definition()->getColumnDefinitions() as &$column) {
            $columnName = $column->getColumnName();
            if ($columnName === 'created_at') {
                $timestampColumns['created_at'] = $column;
            } elseif ($columnName === 'updated_at') {
                $timestampColumns['updated_at'] = $column;
            }
            if (count($timestampColumns) === 2) {
                foreach ($timestampColumns as $timestampColumn) {
                    if ($timestampColumn->useCurrent() || $timestampColumn->useCurrentOnUpdate()) {
                        //don't convert to a `timestamps()` method if useCurrent is used

                        return;
                    }
                }
                $timestampColumns['created_at']
                    ->setColumnName(null)
                    ->setMethodName('timestamps')
                    ->setNullable(false);
                $timestampColumns['updated_at']->markAsWritable(false);

                break;
            }
        }
    }
}


================================================
FILE: src/Generators/Concerns/WritesToFile.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Concerns;

trait WritesToFile
{
    public function write(string $basePath, $index = 0, string $tabCharacter = '    '): void
    {
        if (method_exists($this, 'isWritable') && ! $this->isWritable()) {
            return;
        }

        $stub = $this->generateStub($tabCharacter);

        $fileName = $this->getStubFileName($index);
        file_put_contents($basePath.'/'.$fileName, $stub);
    }
}


================================================
FILE: src/Generators/Concerns/WritesViewsToFile.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Concerns;

use Illuminate\Support\Str;
use LaravelMigrationGenerator\Helpers\ConfigResolver;

trait WritesViewsToFile
{
    use WritesToFile;

    public function stubNameVariables()
    {
        return [
            'ViewName:Studly' => Str::studly($this->viewName),
            'ViewName:Lowercase' => strtolower($this->viewName),
            'ViewName' => $this->viewName,
            'Timestamp' => app('laravel-migration-generator:time')->format('Y_m_d_His'),
        ];
    }

    protected function getStubFileName()
    {
        $driver = static::driver();

        $baseStubFileName = ConfigResolver::viewNamingScheme($driver);
        foreach ($this->stubNameVariables() as $variable => $replacement) {
            if (preg_match("/\[".$variable."\]/i", $baseStubFileName) === 1) {
                $baseStubFileName = preg_replace("/\[".$variable."\]/i", $replacement, $baseStubFileName);
            }
        }

        return $baseStubFileName;
    }

    protected function getStubPath()
    {
        $driver = static::driver();

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/'.$driver.'-view.stub'))) {
            return $overridden;
        }

        if (file_exists($overridden = resource_path('stubs/vendor/laravel-migration-generator/view.stub'))) {
            return $overridden;
        }

        return __DIR__.'/../../../stubs/view.stub';
    }

    protected function generateStub($tabCharacter = '    ')
    {
        $tab = str_repeat($tabCharacter, 3);

        $schema = $this->getSchema();
        $stub = file_get_contents($this->getStubPath());
        $stub = str_replace('[ViewName:Studly]', Str::studly($this->viewName), $stub);
        $stub = str_replace('[ViewName]', $this->viewName, $stub);
        $stub = str_replace('[Schema]', $tab.$schema, $stub);

        return $stub;
    }
}


================================================
FILE: src/Generators/Interfaces/TableGeneratorInterface.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Interfaces;

use LaravelMigrationGenerator\Definitions\TableDefinition;

interface TableGeneratorInterface
{
    public static function driver(): string;

    public function shouldResolveStructure(): bool;

    public function resolveStructure();

    public function parse();

    public function cleanUp();

    public function definition(): TableDefinition;
}


================================================
FILE: src/Generators/Interfaces/ViewGeneratorInterface.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\Interfaces;

interface ViewGeneratorInterface
{
    public static function driver(): string;

    public function parse();

    public function resolveSchema();
}


================================================
FILE: src/Generators/MySQL/TableGenerator.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\MySQL;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
use LaravelMigrationGenerator\Generators\BaseTableGenerator;
use LaravelMigrationGenerator\Tokenizers\MySQL\ColumnTokenizer;
use LaravelMigrationGenerator\Tokenizers\MySQL\IndexTokenizer;

/**
 * Class TableGenerator
 */
class TableGenerator extends BaseTableGenerator
{
    public static function driver(): string
    {
        return 'mysql';
    }

    public function resolveStructure()
    {
        $structure = DB::select('SHOW CREATE TABLE `'.$this->definition()->getTableName().'`');
        $structure = $structure[0];
        $structure = (array) $structure;
        if (isset($structure['Create Table'])) {
            $lines = explode("\n", $structure['Create Table']);

            array_shift($lines); //get rid of first line
            array_pop($lines); //get rid of last line

            $lines = array_map(fn ($item) => trim($item), $lines);
            $this->rows = $lines;
        } else {
            $this->rows = [];
        }
    }

    protected function isColumnLine($line)
    {
        return ! Str::startsWith($line, ['KEY', 'PRIMARY', 'UNIQUE', 'FULLTEXT', 'CONSTRAINT']);
    }

    public function parse()
    {
        foreach ($this->rows as $line) {
            if ($this->isColumnLine($line)) {
                $tokenizer = ColumnTokenizer::parse($line);
                $this->definition()->addColumnDefinition($tokenizer->definition());
            } else {
                $tokenizer = IndexTokenizer::parse($line);
                $this->definition()->addIndexDefinition($tokenizer->definition());
            }
        }
    }
}


================================================
FILE: src/Generators/MySQL/ViewGenerator.php
================================================
<?php

namespace LaravelMigrationGenerator\Generators\MySQL;

use Illuminate\Support\Facades\DB;
use LaravelMigrationGenerator\Generators\BaseViewGenerator;
use LaravelMigrationGenerator\Generators\Interfaces\ViewGeneratorInterface;

class ViewGenerator extends BaseViewGenerator implements ViewGeneratorInterface
{
    public static function driver(): string
    {
        return 'mysql';
    }

    public function resolveSchema()
    {
        $structure = DB::select('SHOW CREATE VIEW `'.$this->definition()->getViewName().'`');
        $structure = $structure[0];
        $structure = (array) $structure;
        if (isset($structure['Create View'])) {
            $this->definition()->setSchema($structure['Create View']);
        }
    }

    public function parse()
    {
        $schema = $this->definition()->getSchema();
        if (preg_match('/CREATE(.*?)VIEW/', $schema, $matches)) {
            $schema = str_replace($matches[1], ' ', $schema);
        }

        if (preg_match_all('/isnull\((.+?)\)/', $schema, $matches)) {
            foreach ($matches[0] as $key => $match) {
                $schema = str_replace($match, $matches[1][$key].' IS NULL', $schema);
            }
        }
        if (preg_match('/collate utf8mb4_unicode_ci/', $schema)) {
            $schema = str_replace('collate utf8mb4_unicode_ci', '', $schema);
        }
        $this->definition()->setSchema($schema);
    }
}


================================================
FILE: src/Helpers/ConfigResolver.php
================================================
<?php

namespace LaravelMigrationGenerator\Helpers;

class ConfigResolver
{
    protected static function resolver(string $configKey, string $driver)
    {
        return ($override = config('laravel-migration-generator.'.$driver.'.'.$configKey)) !== null ?
            $override : config('laravel-migration-generator.'.$configKey);
    }

    public static function tableNamingScheme(string $driver)
    {
        return static::resolver('table_naming_scheme', $driver);
    }

    public static function viewNamingScheme(string $driver)
    {
        return static::resolver('view_naming_scheme', $driver);
    }

    public static function path(string $driver)
    {
        return static::resolver('path', $driver);
    }

    public static function skippableTables(string $driver)
    {
        return array_map('trim', explode(',', static::resolver('skippable_tables', $driver)));
    }

    public static function skippableViews(string $driver)
    {
        return array_map('trim', explode(',', static::resolver('skippable_views', $driver)));
    }
}


================================================
FILE: src/Helpers/DependencyResolver.php
================================================
<?php

namespace LaravelMigrationGenerator\Helpers;

use LaravelMigrationGenerator\Definitions\IndexDefinition;
use LaravelMigrationGenerator\Definitions\TableDefinition;
use MJS\TopSort\Implementations\FixedArraySort;

class DependencyResolver
{
    /** @var array<TableDefinition> */
    protected array $tableDefinitions = [];

    /** @var array<TableDefinition> */
    protected array $sorted = [];

    public function __construct(array $tableDefinitions)
    {
        $this->tableDefinitions = $tableDefinitions;

        $this->build();
    }

    protected function build()
    {
        /** @var TableDefinition[] $keyedDefinitions */
        $keyedDefinitions = collect($this->tableDefinitions)
            ->keyBy(function (TableDefinition $tableDefinition) {
                return $tableDefinition->getTableName();
            });
        $dependencies = [];
        foreach ($this->tableDefinitions as $tableDefinition) {
            $dependencies[$tableDefinition->getTableName()] = [];
        }
        foreach ($this->tableDefinitions as $tableDefinition) {
            foreach ($tableDefinition->getForeignKeyDefinitions() as $indexDefinition) {
                $referencedTable = $indexDefinition->getForeignReferencedTable();
                if (empty($referencedTable)) {
                    continue;
                }
                if ($referencedTable === $tableDefinition->getTableName()) {
                    continue;
                }
                if (! in_array($referencedTable, $dependencies[$tableDefinition->getTableName()])) {
                    $dependencies[$tableDefinition->getTableName()][] = $referencedTable;
                }
            }
        }

        $sorter = new FixedArraySort();
        $circulars = [];
        $sorter->setCircularInterceptor(function ($nodes) use (&$circulars) {
            $circulars[] = [$nodes[count($nodes) - 2], $nodes[count($nodes) - 1]];
        });
        foreach ($dependencies as $table => $dependencyArray) {
            $sorter->add($table, $dependencyArray);
        }
        $sorted = $sorter->sort();
        $definitions = collect($sorted)->map(function ($item) use ($keyedDefinitions) {
            return $keyedDefinitions[$item];
        })->toArray();

        foreach ($circulars as $groups) {
            [$start, $end] = $groups;
            $startDefinition = $keyedDefinitions[$start];
            $indicesForStart = collect($startDefinition->getForeignKeyDefinitions())
                ->filter(function (IndexDefinition $index) use ($end) {
                    return $index->getForeignReferencedTable() == $end;
                });
            foreach ($indicesForStart as $index) {
                $startDefinition->removeIndexDefinition($index);
            }
            if (! in_array($start, $sorted)) {
                $definitions[] = $startDefinition;
            }

            $endDefinition = $keyedDefinitions[$end];

            $indicesForEnd = collect($endDefinition->getForeignKeyDefinitions())
                ->filter(function (IndexDefinition $index) use ($start) {
                    return $index->getForeignReferencedTable() == $start;
                });
            foreach ($indicesForEnd as $index) {
                $endDefinition->removeIndexDefinition($index);
            }
            if (! in_array($end, $sorted)) {
                $definitions[] = $endDefinition;
            }

            $definitions[] = new TableDefinition([
                'tableName' => $startDefinition->getTableName(),
                'driver' => $startDefinition->getDriver(),
                'columnDefinitions' => [],
                'indexDefinitions' => $indicesForStart->toArray(),
            ]);

            $definitions[] = new TableDefinition([
                'tableName' => $endDefinition->getTableName(),
                'driver' => $endDefinition->getDriver(),
                'columnDefinitions' => [],
                'indexDefinitions' => $indicesForEnd->toArray(),
            ]);
        }
        $this->sorted = $definitions;
    }

    /**
     * @return TableDefinition[]
     */
    public function getDependencyOrder(): array
    {
        return $this->sorted;
    }
}


================================================
FILE: src/Helpers/Formatter.php
================================================
<?php

namespace LaravelMigrationGenerator\Helpers;

class Formatter
{
    private array $lines = [];

    private string $tabCharacter;

    public function __construct(string $tabCharacter = '    ')
    {
        $this->tabCharacter = $tabCharacter;
    }

    public function line(string $data, $indentTimes = 0)
    {
        $this->lines[] = str_repeat($this->tabCharacter, $indentTimes).$data;

        return function ($data) use ($indentTimes) {
            return $this->line($data, $indentTimes + 1);
        };
    }

    public function render($extraIndent = 0)
    {
        $lines = $this->lines;
        if ($extraIndent > 0) {
            $lines = collect($lines)->map(function ($item, $index) use ($extraIndent) {
                if ($index === 0) {
                    return $item;
                }

                return str_repeat($this->tabCharacter, $extraIndent).$item;
            })->toArray();
        }

        return implode("\n", $lines);
    }

    public function replaceOnLine($toReplace, $body)
    {
        if (preg_match('/^(\s+)?'.preg_quote($toReplace).'/m', $body, $matches) !== false) {
            $gap = $matches[1] ?? '';
            $numSpaces = strlen($this->tabCharacter);
            if ($numSpaces === 0) {
                $startingTabIndent = 0;
            } else {
                $startingTabIndent = (int) (strlen($gap) / $numSpaces);
            }

            return preg_replace('/'.preg_quote($toReplace).'/', $this->render($startingTabIndent), $body);
        }

        return $body;
    }

    public static function replace($tabCharacter, $toReplace, $replacement, $body)
    {
        $formatter = new static($tabCharacter);
        foreach (explode("\n", $replacement) as $line) {
            $formatter->line($line);
        }

        return $formatter->replaceOnLine($toReplace, $body);
    }
}


================================================
FILE: src/Helpers/ValueToString.php
================================================
<?php

namespace LaravelMigrationGenerator\Helpers;

use Illuminate\Support\Str;

class ValueToString
{
    public static function castFloat($value)
    {
        return 'float$:'.$value;
    }

    public static function castBinary($value)
    {
        return 'binary$:'.$value;
    }

    public static function isCastedValue($value)
    {
        return Str::startsWith($value, ['float$:', 'binary$:']);
    }

    public static function parseCastedValue($value)
    {
        if (Str::startsWith($value, 'float$:')) {
            return str_replace('float$:', '', $value);
        }
        if (Str::startsWith($value, 'binary$:')) {
            return 'b\''.str_replace('binary$:', '', $value).'\'';
        }

        return $value;
    }

    /**
     * Escape a string value for safe inclusion in generated PHP code.
     * Prevents PHP injection through crafted index/column names.
     *
     * @param  string  $value  The value to escape
     * @param  bool  $singleQuote  Whether the string will be wrapped in single quotes (true) or double quotes (false)
     */
    public static function escape(string $value, bool $singleQuote = true): string
    {
        // Escape backslashes first
        $escaped = str_replace('\\', '\\\\', $value);

        // Then escape the quote character being used as delimiter
        if ($singleQuote) {
            return str_replace('\'', '\\\'', $escaped);
        } else {
            return str_replace('"', '\\"', $escaped);
        }
    }

    public static function make($value, $singleOutArray = false, $singleQuote = true)
    {
        $quote = $singleQuote ? '\'' : '"';
        if ($value === null) {
            return 'null';
        } elseif (is_array($value)) {
            if ($singleOutArray && count($value) === 1) {
                return $quote.static::escape($value[0], $singleQuote).$quote;
            }

            return '['.collect($value)->map(fn ($item) => $quote.static::escape($item, $singleQuote).$quote)->implode(', ').']';
        } elseif (is_int($value) || is_float($value)) {
            return $value;
        }

        if (static::isCastedValue($value)) {
            return static::parseCastedValue($value);
        }

        if (Str::startsWith($value, $quote) && Str::endsWith($value, $quote)) {
            return $value;
        }

        return $quote.static::escape($value, $singleQuote).$quote;
    }
}


================================================
FILE: src/Helpers/WritableTrait.php
================================================
<?php

namespace LaravelMigrationGenerator\Helpers;

trait WritableTrait
{
    public bool $writable = true;

    public function markAsWritable(bool $writable = true)
    {
        $this->writable = $writable;

        return $this;
    }

    public function isWritable()
    {
        return $this->writable;
    }
}


================================================
FILE: src/LaravelMigrationGeneratorProvider.php
================================================
<?php

namespace LaravelMigrationGenerator;

use Illuminate\Database\Events\MigrationsEnded;
use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
use LaravelMigrationGenerator\Commands\GenerateMigrationsCommand;

class LaravelMigrationGeneratorProvider extends ServiceProvider
{
    public function boot()
    {
        $this->mergeConfigFrom(
            __DIR__.'/../config/laravel-migration-generator.php',
            'laravel-migration-generator'
        );

        $this->publishes([
            __DIR__.'/../stubs' => resource_path('stubs/vendor/laravel-migration-generator'),
            __DIR__.'/../config/laravel-migration-generator.php' => config_path('laravel-migration-generator.php'),
        ]);

        if ($this->app->runningInConsole()) {
            $this->app->instance('laravel-migration-generator:time', now());
            $this->commands([
                GenerateMigrationsCommand::class,
            ]);
        }
        if (config('laravel-migration-generator.run_after_migrations') && config('app.env') === 'local') {
            Event::listen(MigrationsEnded::class, function () {
                Artisan::call('generate:migrations');
            });
        }
    }
}


================================================
FILE: src/Tokenizers/BaseColumnTokenizer.php
================================================
<?php

namespace LaravelMigrationGenerator\Tokenizers;

use LaravelMigrationGenerator\Definitions\ColumnDefinition;
use LaravelMigrationGenerator\Tokenizers\Interfaces\ColumnTokenizerInterface;

abstract class BaseColumnTokenizer extends BaseTokenizer implements ColumnTokenizerInterface
{
    protected ColumnDefinition $definition;

    public function __construct(string $value)
    {
        $this->definition = new ColumnDefinition();
        parent::__construct($value);
    }

    public function definition(): ColumnDefinition
    {
        return $this->definition;
    }
}


================================================
FILE: src/Tokenizers/BaseIndexTokenizer.php
================================================
<?php

namespace LaravelMigrationGenerator\Tokenizers;

use LaravelMigrationGenerator\Definitions\IndexDefinition;
use LaravelMigrationGenerator\Tokenizers\Interfaces\IndexTokenizerInterface;

abstract class BaseIndexTokenizer extends BaseTokenizer implements IndexTokenizerInterface
{
    protected IndexDefinition $definition;

    public function __construct(string $value)
    {
        $this->definition = new IndexDefinition();
        parent::__construct($value);
    }

    public function definition(): IndexDefinition
    {
        return $this->definition;
    }
}


================================================
FILE: src/Tokenizers/BaseTokenizer.php
================================================
<?php

namespace LaravelMigrationGenerator\Tokenizers;

abstract class BaseTokenizer
{
    protected $tokens = [];

    private string $value;

    private const SPACE_REPLACER = '&!@';

    private const SINGLE_QUOTE_REPLACER = '!*@';

    private const EMPTY_STRING_REPLACER = '$$EMPTY_STRING';

    public function __construct(string $value)
    {
        $this->value = $value;
        $prune = false;
        $pruneSingleQuotes = false;

        if (preg_match("/(DEFAULT|COMMENT) ''/", $value, $matches)) {
            $value = str_replace($matches[1].' \'\'', $matches[1].' '.self::EMPTY_STRING_REPLACER, $value);
        }

        //first get rid of any single quoted stuff with '' around it
        if (preg_match_all('/\'\'(.+?)\'\'/', $value, $matches)) {
            foreach ($matches[0] as $key => $singleQuoted) {
                $toReplace = $singleQuoted;
                $value = str_replace($toReplace, self::SINGLE_QUOTE_REPLACER.$matches[1][$key].self::SINGLE_QUOTE_REPLACER, $value);
                $pruneSingleQuotes = true;
            }
        }

        if (preg_match_all("/'(.*?)'/", $value, $matches)) {
            foreach ($matches[0] as $quoteWithSpace) {
                //we've got an enum or set that has spaces in the text
                //so we'll convert to a different character so it doesn't get pruned
                $toReplace = $quoteWithSpace;
                $value = str_replace($toReplace, str_replace(' ', self::SPACE_REPLACER, $toReplace), $value);
                $prune = true;
            }
        }
        $value = str_replace(self::EMPTY_STRING_REPLACER, '\'\'', $value);
        $this->tokens = array_map(function ($item) {
            return trim($item, ', ');
        }, str_getcsv($value, ' ', "'"));

        if ($prune) {
            $this->tokens = array_map(function ($item) {
                return str_replace(self::SPACE_REPLACER, ' ', $item);
            }, $this->tokens);
        }
        if ($pruneSingleQuotes) {
            $this->tokens = array_map(function ($item) {
                return str_replace(self::SINGLE_QUOTE_REPLACER, '\'', $item);
            }, $this->tokens);
        }
    }

    public static function make(string $line)
    {
        return new static($line);
    }

    /**
     * @return static
     */
    public static function parse(string $line)
    {
        return (new static($line))->tokenize();
    }

    protected function parseColumn($value)
    {
        return trim($value, '` ');
    }

    protected function columnsToArray($string)
    {
        $string = trim($string, '()');

        return array_map(fn ($item) => $this->parseColumn($item), explode(',', $string));
    }

    protected function consume()
    {
        return array_shift($this->tokens);
    }

    protected function putBack($value)
    {
        array_unshift($this->tokens, $value);
    }
}


================================================
FILE: src/Tokenizers/Interfaces/ColumnTokenizerInterface.php
================================================
<?php

namespace LaravelMigrationGenerator\Tokenizers\Interfaces;

use LaravelMigrationGenerator\Definitions\ColumnDefinition;

interface ColumnTokenizerInterface
{
    public function tokenize(): self;

    public function definition(): ColumnDefinition;
}


================================================
FILE: src/Tokenizers/Interfaces/IndexTokenizerInterface.php
================================================
<?php

namespace LaravelMigrationGenerator\Tokenizers\Interfaces;

use LaravelMigrationGenerator\Definitions\IndexDefinition;

interface IndexTokenizerInterface
{
    public function tokenize(): self;

    public function definition(): IndexDefinition;
}


================================================
FILE: src/Tokenizers/MySQL/ColumnTokenizer.php
================================================
<?php

namespace LaravelMigrationGenerator\Tokenizers\MySQL;

use Illuminate\Database\Schema\Builder;
use Illuminate\Support\Str;
use LaravelMigrationGenerator\Helpers\ValueToString;
use LaravelMigrationGenerator\Tokenizers\BaseColumnTokenizer;

class ColumnTokenizer extends BaseColumnTokenizer
{
    protected $columnDataType;

    /**
     * MySQL provides a ZEROFILL property for ints which is not an ANSI compliant modifier
     *
     * @var bool
     */
    protected $zeroFill = false;

    public function tokenize(): self
    {
        $this->consumeColumnName();
        $this->consumeColumnType();
        if ($this->isNumberType()) {
            $this->consumeUnsigned();
            $this->consumeZeroFill();
        }
        if ($this->isTextType()) {
            //possibly has a character set
            $this->consumeCharacterSet();

            //has collation data most likely
            $this->consumeCollation();
        }

        $this->consumeNullable();

        $this->consumeDefaultValue();
        if ($this->isNumberType()) {
            $this->consumeAutoIncrement();
            $this->consumeKeyConstraints();
        }

        $this->consumeGenerated();

        if ($this->columnDataType == 'timestamp' || $this->columnDataType == 'datetime') {
            $this->consumeTimestamp();
        }

        $this->consumeComment();

        return $this;
    }

    //region Consumers

    protected function consumeColumnName()
    {
        $this->definition->setColumnName($this->parseColumn($this->consume()));
    }

    protected function consumeZeroFill()
    {
        $nextPiece = $this->consume();

        if (strtoupper($nextPiece) === 'ZEROFILL') {
            $this->zeroFill = true;
        } else {
            $this->putBack($nextPiece);
        }
    }

    protected function consumeColumnType()
    {
        $originalColumnType = $columnType = $this->consume();
        $hasConstraints = Str::contains($columnType, '(');

        if ($hasConstraints) {
            $columnType = explode('(', $columnType)[0];
        }

        $this->columnDataType = strtolower($columnType);

        $this->resolveColumnMethod();
        if ($hasConstraints) {
            preg_match("/\((.+?)\)/", $originalColumnType, $constraintMatches);
            $matches = explode(',', $constraintMatches[1]);
            $this->resolveColumnConstraints($matches);
        }
    }

    private function consumeAutoIncrement()
    {
        $piece = $this->consume();
        if (strtoupper($piece) === 'AUTO_INCREMENT') {
            $this->definition->setAutoIncrementing(true);
        } else {
            $this->putBack($piece);
        }
    }

    protected function consumeNullable()
    {
        $piece = $this->consume();
        if (strtoupper($piece) === 'NOT') {
            $this->consume(); //next is NULL
            $this->definition->setNullable(false);
        } elseif (strtoupper($piece) === 'NULL') {
            $this->definition->setNullable(true);
        } else {
            //something else
            $this->putBack($piece);
        }

        if (Str::contains($this->columnDataType, 'text')) {
            //text column types are explicitly nullable unless set to NOT NULL
            if ($this->definition->isNullable() === null) {
                $this->definition->setNullable(true);
            }
        }
    }

    protected function consumeDefaultValue()
    {
        $piece = $this->consume();
        if (strtoupper($piece) === 'DEFAULT') {
            $this->definition->setDefaultValue($this->consume());

            if (strtoupper($this->definition->getDefaultValue()) === 'NULL') {
                $this->definition
                    ->setNullable(true)
                    ->setDefaultValue(null);
            } elseif (strtoupper($this->definition->getDefaultValue()) === 'CURRENT_TIMESTAMP') {
                $this->definition
                    ->setDefaultValue(null)
                    ->setUseCurrent(true);
            } elseif (preg_match("/b'([01]+)'/i", $this->definition->getDefaultValue(), $matches)) {
                // Binary digit, so let's convert to PHP's version
                $this->definition->setDefaultValue(ValueToString::castBinary($matches[1]));
            }
            if ($this->definition->getDefaultValue() !== null) {
                if ($this->isNumberType()) {
                    if (Str::contains(strtoupper($this->columnDataType), 'INT')) {
                        $this->definition->setDefaultValue((int) $this->definition->getDefaultValue());
                    } else {
                        //floats get converted to strings improperly, gotta do a string cast
                        $this->definition->setDefaultValue(ValueToString::castFloat($this->definition->getDefaultValue()));
                    }
                } else {
                    if (! $this->isBinaryType()) {
                        $this->definition->setDefaultValue((string) $this->definition->getDefaultValue());
                    }
                }
            }
        } else {
            $this->putBack($piece);
        }
    }

    protected function consumeComment()
    {
        $piece = $this->consume();
        if (strtoupper($piece) === 'COMMENT') {
            // next piece is the comment content
            $this->definition->setComment($this->consume());
        } else {
            $this->putBack($piece);
        }
    }

    protected function consumeCharacterSet()
    {
        $piece = $this->consume();

        if (strtoupper($piece) === 'CHARACTER') {
            $this->consume(); // SET, throw it away

            $this->definition->setCharacterSet($this->consume());
        } else {
            //something else
            $this->putBack($piece);
        }
    }

    protected function consumeCollation()
    {
        $piece = $this->consume();
        if (strtoupper($piece) === 'COLLATE') {
            //next piece is the collation type
            $this->definition->setCollation($this->consume());
        } else {
            $this->putBack($piece);
        }
    }

    private function consumeUnsigned()
    {
        $piece = $this->consume();
        if (strtoupper($piece) === 'UNSIGNED') {
            $this->definition->setUnsigned(true);
        } else {
            $this->putBack($piece);
        }
    }

    private function consumeKeyConstraints()
    {
        $nextPiece = $this->consume();
        if (strtoupper($nextPiece) === 'PRIMARY') {
            $this->definition->setPrimary(true);

            $next = $this->consume();
            if (strtoupper($next) !== 'KEY') {
                $this->putBack($next);
            }
        } elseif (strtoupper($nextPiece) === 'UNIQUE') {
            $this->definition->setUnique(true);

            $next = $this->consume();
            if (strtoupper($next) !== 'KEY') {
                $this->putBack($next);
            }
        } else {
            $this->putBack($nextPiece);
        }
    }

    private function consumeGenerated()
    {
        $canContinue = false;
        $nextPiece = $this->consume();
        if (strtoupper($nextPiece) === 'GENERATED') {
            $piece = $this->consume();
            if (strtoupper($piece) === 'ALWAYS') {
                $this->consume(); // AS
                $canContinue = true;
            } else {
                $this->putBack($piece);
            }
        } elseif (strtoupper($nextPiece) === 'AS') {
            $canContinue = true;
        }

        if (! $canContinue) {
            $this->putBack($nextPiece);

            return;
        }

        $expressionPieces = [];
        $parenthesisCounter = 0;
        while ($pieceOfExpression = $this->consume()) {
            $numOpeningParenthesis = substr_count($pieceOfExpression, '(');
            $numClosingParenthesis = substr_count($pieceOfExpression, ')');
            $parenthesisCounter += $numOpeningParenthesis - $numClosingParenthesis;

            $expressionPieces[] = $pieceOfExpression;

            if ($parenthesisCounter === 0) {
                break;
            }
        }
        $expression = implode(' ', $expressionPieces);
        if (Str::startsWith($expression, '((') && Str::endsWith($expression, '))')) {
            $expression = substr($expression, 1, strlen($expression) - 2);
        }

        $finalPiece = $this->consume();
        if ($finalPiece !== null && strtoupper($finalPiece) === 'STORED') {
            $this->definition->setStoredAs($expression)->setNullable(false);
        } else {
            $this->definition->setVirtualAs($expression)->setNullable(false);
        }
    }

    private function consumeTimestamp()
    {
        $nextPiece = $this->consume();
        if (strtoupper($nextPiece) === 'ON') {
            $next = $this->consume();
            if (strtoupper($next) === 'UPDATE') {
                $next = $this->consume();
                if (strtoupper($next) === 'CURRENT_TIMESTAMP') {
                    $this->definition->setUseCurrentOnUpdate(true);
                } else {
                    $this->putBack($next);
                }
            } else {
                $this->putBack($next);
            }
        } else {
            $this->putBack($nextPiece);
        }
    }

    //endregion

    //region Resolvers
    private function resolveColumnMethod()
    {
        $mapped = [
            'int' => 'integer',
            'tinyint' => 'tinyInteger',
            'smallint' => 'smallInteger',
            'mediumint' => 'mediumInteger',
            'bigint' => 'bigInteger',
            'varchar' => 'string',
            'tinytext' => 'string',  //tinytext is not a valid Blueprint method currently
            'mediumtext' => 'mediumText',
            'longtext' => 'longText',
            'blob' => 'binary',
            'datetime' => 'dateTime',
            'geometrycollection' => 'geometryCollection',
            'linestring' => 'lineString',
            'multilinestring' => 'multiLineString',
            'multipolygon' => 'multiPolygon',
            'multipoint' => 'multiPoint',
        ];
        if (isset($mapped[$this->columnDataType])) {
            $this->definition->setMethodName($mapped[$this->columnDataType]);
        } else {
            //do some custom resolution
            $this->definition->setMethodName($this->columnDataType);
        }
    }

    private function resolveColumnConstraints(array $constraints)
    {
        if ($this->columnDataType === 'char' && count($constraints) === 1 && $constraints[0] == 36) {
            //uuid for mysql
            $this->definition->setIsUUID(true);

            return;
        }
        if ($this->isArrayType()) {
            $this->definition->setMethodParameters([array_map(fn ($item) => trim($item, '\''), $constraints)]);
        } else {
            if (Str::contains(strtoupper($this->columnDataType), 'INT')) {
                $this->definition->setMethodParameters([]); //laravel does not like display field widths
            } else {
                if ($this->definition->getMethodName() === 'string') {
                    if (count($constraints) === 1) {
                        //has a width set
                        if ($constraints[0] == Builder::$defaultStringLength) {
                            $this->definition->setMethodParameters([]);

                            return;
                        }
                    }
                }
                $this->definition->setMethodParameters(array_map(fn ($item) => (int) $item, $constraints));
            }
        }
    }

    //endregion

    protected function isTextType()
    {
        return Str::contains($this->columnDataType, ['char', 'text', 'set', 'enum']);
    }

    protected function isNumberType()
    {
        return Str::contains($this->columnDataType, ['int', 'decimal', 'float', 'double']);
    }

    protected function isArrayType()
    {
        return Str::contains($this->columnDataType, ['enum', 'set']);
    }

    protected function isBinaryType()
    {
        return Str::contains($this->columnDataType, ['bit']);
    }

    /**
     * @return mixed
     */
    public function getColumnDataType()
    {
        return $this->columnDataType;
    }
}


================================================
FILE: src/Tokenizers/MySQL/IndexTokenizer.php
================================================
<?php

namespace LaravelMigrationGenerator\Tokenizers\MySQL;

use LaravelMigrationGenerator\Tokenizers\BaseIndexTokenizer;

class IndexTokenizer extends BaseIndexTokenizer
{
    public function tokenize(): self
    {
        $this->consumeIndexType();
        if ($this->definition->getIndexType() !== 'primary') {
            $this->consumeIndexName();
        }

        if ($this->definition->getIndexType() === 'foreign') {
            $this->consumeForeignKey();
        } else {
            $this->consumeIndexColumns();
        }

        return $this;
    }

    private function consumeIndexType()
    {
        $piece = $this->consume();
        $upper = strtoupper($piece);
        if (in_array($upper, ['PRIMARY', 'UNIQUE', 'FULLTEXT', 'SPATIAL'])) {
            $this->definition->setIndexType(strtolower($piece));
            $this->consume(); //just the word KEY
        } elseif ($upper === 'KEY') {
            $this->definition->setIndexType('index');
        } elseif ($upper === 'CONSTRAINT') {
            $this->definition->setIndexType('foreign');
        }
    }

    private function consumeIndexName()
    {
        $piece = $this->consume();
        $this->definition->setIndexName($this->parseColumn($piece));
    }

    private function consumeIndexColumns()
    {
        $piece = $this->consume();
        $columns = $this->columnsToArray($piece);

        $this->definition->setIndexColumns($columns);
    }

    private function consumeForeignKey()
    {
        $piece = $this->consume();
        if (strtoupper($piece) === 'FOREIGN') {
            $this->consume(); //KEY

            $columns = [];
            $token = $this->consume();

            while (! is_null($token)) {
                $columns = array_merge($columns, $this->columnsToArray($token));
                $token = $this->consume();
                if (strtoupper($token) === 'REFERENCES') {
                    $this->putBack($token);

                    break;
                }
            }
            $this->definition->setIndexColumns($columns);

            $this->consume(); //REFERENCES

            $referencedTable = $this->parseColumn($this->consume());
            $this->definition->setForeignReferencedTable($referencedTable);

            $referencedColumns = [];
            $token = $this->consume();
            while (! is_null($token)) {
                $referencedColumns = array_merge($referencedColumns, $this->columnsToArray($token));
                $token = $this->consume();
                if (strtoupper($token) === 'ON') {
                    $this->putBack($token);

                    break;
                }
            }

            $this->definition->setForeignReferencedColumns($referencedColumns);

            $this->consumeConstraintActions();
        } else {
            $this->putBack($piece);
        }
    }

    private function consumeConstraintActions()
    {
        while ($token = $this->consume()) {
            if (strtoupper($token) === 'ON') {
                $actionType = strtolower($this->consume()); //UPDATE
                $actionMethod = strtolower($this->consume()); //CASCADE | NO ACTION | SET NULL | SET DEFAULT
                if ($actionMethod === 'no') {
                    $this->consume(); //consume ACTION
                    $actionMethod = 'restrict';
                } elseif ($actionMethod === 'set') {
                    $actionMethod = 'set '.$this->consume(); //consume NULL or DEFAULT
                }
                $currentActions = $this->definition->getConstraintActions();
                $currentActions[$actionType] = $actionMethod;
                $this->definition->setConstraintActions($currentActions);
            } else {
                $this->putBack($token);

                break;
            }
        }
    }
}


================================================
FILE: stubs/table-create.stub
================================================
Schema::create('[TableName]', function (Blueprint $table) {
    [Schema]
});

================================================
FILE: stubs/table-modify.stub
================================================
Schema::table('[TableName]', function (Blueprint $table) {
    [Schema]
});

================================================
FILE: stubs/table.stub
================================================
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class Create[TableName:Studly]Table extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        [TableUp]
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        [TableDown]
    }
}


================================================
FILE: stubs/view.stub
================================================
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class Create[ViewName:Studly]View extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        DB::statement($this->dropView());
        DB::statement($this->createView());
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        DB::statement($this->dropView());
    }

    private function createView()
    {
        return <<<SQL
            [Schema]
        SQL;
    }

    private function dropView()
    {
        return <<<SQL
            DROP VIEW IF EXISTS `[ViewName]`;
        SQL;
    }
}


================================================
FILE: tests/TestCase.php
================================================
<?php

namespace Tests;

use LaravelMigrationGenerator\LaravelMigrationGeneratorProvider;

class TestCase extends \Orchestra\Testbench\TestCase
{
    protected function defineEnvironment($app)
    {
        // Setup default database to use sqlite :memory:
        $app['config']->set('database.default', 'testbench');
        $app['config']->set('database.connections.testbench', [
            'driver' => 'sqlite',
            'database' => ':memory:',
            'prefix' => '',
        ]);
    }

    protected function getPackageProviders($app)
    {
        return [
            LaravelMigrationGeneratorProvider::class,
        ];
    }
}


================================================
FILE: tests/Unit/ColumnDefinitionTest.php
================================================
<?php

namespace Tests\Unit;

use LaravelMigrationGenerator\Definitions\ColumnDefinition;
use LaravelMigrationGenerator\Definitions\IndexDefinition;
use Tests\TestCase;

class ColumnDefinitionTest extends TestCase
{
    public function test_it_can_add_index_definitions()
    {
        $columnDefinition = (new ColumnDefinition())->setIndex(true)->setColumnName('testing')->setMethodName('string');
        $indexDefinition = (new IndexDefinition())->setIndexName('test')->setIndexType('index');
        $columnDefinition->addIndexDefinition($indexDefinition);

        $this->assertEquals('$table->string(\'testing\')->index(\'test\')', $columnDefinition->render());
    }

    public function test_it_prunes_empty_primary_key_index()
    {
        $columnDefinition = (new ColumnDefinition())
            ->setPrimary(true)
            ->setColumnName('testing')
            ->setUnsigned(true)
            ->setMethodName('integer');
        $indexDefinition = (new IndexDefinition())
            ->setIndexType('primary');
        $columnDefinition->addIndexDefinition($indexDefinition);

        $this->assertEquals('$table->unsignedInteger(\'testing\')->primary()', $columnDefinition->render());
    }
}


================================================
FILE: tests/Unit/DependencyResolverTest.php
================================================
<?php

namespace Tests\Unit;

use LaravelMigrationGenerator\Definitions\ColumnDefinition;
use LaravelMigrationGenerator\Definitions\IndexDefinition;
use LaravelMigrationGenerator\Definitions\TableDefinition;
use LaravelMigrationGenerator\Helpers\DependencyResolver;
use Tests\TestCase;

class DependencyResolverTest extends TestCase
{
    public function test_it_can_find_simple_dependencies()
    {
        $tableDefinition = new TableDefinition([
            'tableName' => 'tests',
            'columnDefinitions' => [
                (new ColumnDefinition())->setColumnName('id')->setMethodName('id')->setAutoIncrementing(true)->setPrimary(true),
                (new ColumnDefinition())->setColumnName('name')->setMethodName('string')->setNullable(false),
            ],
            'indexDefinitions' => [],
        ]);

        $foreignTableDefinition = new TableDefinition([
            'tableName' => 'test_items',
            'columnDefinitions' => [
                (new ColumnDefinition())->setColumnName('id')->setMethodName('id')->setAutoIncrementing(true)->setPrimary(true),
                (new ColumnDefinition())->setColumnName('test_id')->setMethodName('bigInteger')->setNullable(false)->setUnsigned(true),
            ],
            'indexDefinitions' => [
                (new IndexDefinition())->setIndexName('fk_test_id')->setIndexType('foreign')->setForeignReferencedColumns(['id'])->setForeignReferencedTable('tests'),
            ],
        ]);

        $resolver = new DependencyResolver([$tableDefinition, $foreignTableDefinition]);
        $order = $resolver->getDependencyOrder();
        $this->assertCount(2, $order);
        $this->assertEquals('tests', $order[0]->getTableName());
        $this->assertEquals('test_items', $order[1]->getTableName());
    }

    public function test_it_finds_cyclical_dependencies()
    {
        $tableDefinition = new TableDefinition([
            'tableName' => 'tests',
            'driver' => 'mysql',
            'columnDefinitions' => [
                (new ColumnDefinition())->setColumnName('id')->setMethodName('id')->setAutoIncrementing(true)->setPrimary(true),
                (new ColumnDefinition())->setColumnName('test_item_id')->setMethodName('bigInteger')->setNullable(false)->setUnsigned(true),
            ],
            'indexDefinitions' => [
                (new IndexDefinition())->setIndexName('fk_test_item_id')->setIndexColumns(['test_item_id'])->setIndexType('foreign')->setForeignReferencedColumns(['id'])->setForeignReferencedTable('test_items'),
            ],
        ]);

        $foreignTableDefinition = new TableDefinition([
            'tableName' => 'test_items',
            'driver' => 'mysql',
            'columnDefinitions' => [
                (new ColumnDefinition())->setColumnName('id')->setMethodName('id')->setAutoIncrementing(true)->setPrimary(true),
                (new ColumnDefinition())->setColumnName('test_id')->setMethodName('bigInteger')->setNullable(false)->setUnsigned(true),
            ],
            'indexDefinitions' => [
                (new IndexDefinition())->setIndexName('fk_test_id')->setIndexColumns(['test_id'])->setIndexType('foreign')->setForeignReferencedColumns(['id'])->setForeignReferencedTable('tests'),
            ],
        ]);

        $resolver = new DependencyResolver([$tableDefinition, $foreignTableDefinition]);

        $order = $resolver->getDependencyOrder();
        $this->assertCount(4, $order);
        $this->assertEquals('$table->foreign(\'test_id\', \'fk_test_id\')->references(\'id\')->on(\'tests\');', $order[2]->formatter()->getSchema());
        $this->assertEquals('$table->foreign(\'test_item_id\', \'fk_test_item_id\')->references(\'id\')->on(\'test_items\');', $order[3]->formatter()->getSchema());
    }

    public function test_it_finds_self_referential_dependencies()
    {
        $tableDefinition = new TableDefinition([
            'tableName' => 'tests',
            'driver' => 'mysql',
            'columnDefinitions' => [
                (new ColumnDefinition())->setColumnName('id')->setMethodName('id')->setAutoIncrementing(true)->setPrimary(true),
                (new ColumnDefinition())->setColumnName('parent_id')->setMethodName('integer')->setUnsigned(true)->setNullable(false),
            ],
            'indexDefinitions' => [
                (new IndexDefinition())->setIndexName('fk_parent_id')->setIndexColumns(['parent_id'])->setIndexType('foreign')->setForeignReferencedColumns(['id'])->setForeignReferencedTable('tests'),
            ],
        ]);

        $resolver = new DependencyResolver([$tableDefinition]);

        $order = $resolver->getDependencyOrder();
        $this->assertCount(1, $order);
        $this->assertCount(2, $order[0]->getColumnDefinitions());
        $this->assertCount(1, $order[0]->getIndexDefinitions());

    }
}


================================================
FILE: tests/Unit/FormatterTest.php
================================================
<?php

namespace Tests\Unit;

use LaravelMigrationGenerator\Helpers\Formatter;
use Tests\TestCase;

class FormatterTest extends TestCase
{
    public function test_can_format_single_line()
    {
        $formatter = new Formatter();
        $formatter->line('Test');
        $this->assertEquals('Test', $formatter->render());
    }

    public function test_can_chain()
    {
        $formatter = new Formatter();
        $line = $formatter->line('$this->call(function(){');
        $line('$this->die();');
        $formatter->line('});');
        $this->assertEquals(<<<'STR'
        $this->call(function(){
            $this->die();
        });
        STR, $formatter->render());
    }

    public function test_can_get_current_line_indent_level()
    {
        $formatter = new Formatter();
        $formatter->line('Line');
        $formatter->line('Line 2');

        $body = <<<'STR'
    [Test]
STR;

        $replaced = $formatter->replaceOnLine('[Test]', $body);
        $shouldEqual = <<<'STR'
    Line
    Line 2
STR;
        $this->assertEquals($shouldEqual, $replaced);
    }

    public function test_can_replace_on_no_indent()
    {
        $replaced = Formatter::replace('    ', '[TEST]', 'Test', '[TEST]');
        $this->assertEquals('Test', $replaced);
    }
}


================================================
FILE: tests/Unit/GeneratorManagers/MySQLGeneratorManagerTest.php
================================================
<?php

namespace Tests\Unit\GeneratorManagers;

use Illuminate\Support\Facades\DB;
use LaravelMigrationGenerator\Definitions\ColumnDefinition;
use LaravelMigrationGenerator\Definitions\IndexDefinition;
use LaravelMigrationGenerator\Definitions\TableDefinition;
use LaravelMigrationGenerator\GeneratorManagers\MySQLGeneratorManager;
use Mockery\MockInterface;
use Tests\TestCase;

class MySQLGeneratorManagerTest extends TestCase
{
    protected function getManagerMock(array $tableDefinitions)
    {
        return $this->partialMock(MySQLGeneratorManager::class, function (MockInterface $mock) use ($tableDefinitions) {
            $mock->shouldReceive('init', 'createMissingDirectory', 'writeTableMigrations', 'writeViewMigrations');
            $mock->shouldReceive('createMissingDirectory');

            $mock->shouldReceive('getTableDefinitions')->andReturn($tableDefinitions);
        });
    }

    public function test_can_sort_tables()
    {
        /** @var MySQLGeneratorManager $mocked */
        $mocked = $this->getManagerMock([
            new TableDefinition([
                'tableName' => 'tests',
                'driver' => 'mysql',
                'columnDefinitions' => [
                    (new ColumnDefinition())->setColumnName('id')->setMethodName('id')->setAutoIncrementing(true)->setPrimary(true),
                    (new ColumnDefinition())->setColumnName('test_item_id')->setMethodName('bigInteger')->setNullable(false)->setUnsigned(true),
                ],
                'indexDefinitions' => [
                    (new IndexDefinition())->setIndexName('fk_test_item_id')->setIndexColumns(['test_item_id'])->setIndexType('foreign')->setForeignReferencedColumns(['id'])->setForeignReferencedTable('test_items'),
                ],
            ]),
            new TableDefinition([
                'tableName' => 'test_items',
                'driver' => 'mysql',
                'columnDefinitions' => [
                    (new ColumnDefinition())->setColumnName('id')->setMethodName('id')->setAutoIncrementing(true)->setPrimary(true),
                    (new ColumnDefinition())->setColumnName('test_id')->setMethodName('bigInteger')->setNullable(false)->setUnsigned(true),
                ],
                'indexDefinitions' => [
                    (new IndexDefinition())->setIndexName('fk_test_id')->setIndexColumns(['test_id'])->setIndexType('foreign')->setForeignReferencedColumns(['id'])->setForeignReferencedTable('tests'),
                ],
            ]),
        ]);
        $sorted = $mocked->sortTables($mocked->getTableDefinitions());
        $this->assertCount(4, $sorted);
        $this->assertStringContainsString('$table->dropForeign', $sorted[3]->formatter()->stubTableDown());
    }

    public function test_can_remove_database_prefix()
    {
        $connection = DB::getDefaultConnection();
        config()->set('database.connections.'.$connection.'.prefix', 'wp_');

        $mocked = $this->partialMock(MySQLGeneratorManager::class, function (MockInterface $mock) {
            $mock->shouldReceive('init');
        });

        $definition = (new TableDefinition())->setTableName('wp_posts');
        $mocked->addTableDefinition($definition);
        $this->assertEquals('posts', $definition->getTableName());

        $definition = (new TableDefinition())->setTableName('posts');
        $mocked->addTableDefinition($definition);
        $this->assertEquals('posts', $definition->getTableName());

        config()->set('database.connections.'.$connection.'.prefix', '');

        $definition = (new TableDefinition())->setTableName('wp_posts');
        $mocked->addTableDefinition($definition);
        $this->assertEquals('wp_posts', $definition->getTableName());

        $definition = (new TableDefinition())->setTableName('posts');
        $mocked->addTableDefinition($definition);
        $this->assertEquals('posts', $definition->getTableName());
    }
}


================================================
FILE: tests/Unit/Generators/MySQLTableGeneratorTest.php
================================================
<?php

namespace Tests\Unit\Generators;

use Illuminate\Support\Facades\Config;
use LaravelMigrationGenerator\Generators\MySQL\TableGenerator;
use Tests\TestCase;

class MySQLTableGeneratorTest extends TestCase
{
    public function tearDown(): void
    {
        parent::tearDown();

        $path = __DIR__.'/../../migrations';
        $this->cleanUpMigrations($path);
    }

    private function assertSchemaHas($str, $schema)
    {
        $this->assertStringContainsString($str, $schema);
    }

    public function test_runs_correctly()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY',
            '`user_id` int(9) unsigned NOT NULL',
            '`note` varchar(255) NOT NULL',
            'KEY `fk_user_id_idx` (`user_id`)',
            'CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->increments(\'id\');', $schema);
        $this->assertSchemaHas('$table->unsignedInteger(\'user_id\');', $schema);
        $this->assertSchemaHas('$table->string(\'note\');', $schema);
        $this->assertSchemaHas('$table->foreign(\'user_id\', \'fk_user_id\')->references(\'id\')->on(\'users\')->onDelete(\'cascade\')->onUpdate(\'cascade\');', $schema);
    }

    public function test_self_referential_foreign_key()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY',
            '`parent_id` int(9) unsigned NOT NULL',
            'KEY `fk_parent_id_idx` (`parent_id`)',
            'CONSTRAINT `fk_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `tables` (`id`) ON DELETE CASCADE ON UPDATE CASCADE',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->increments(\'id\');', $schema);
        $this->assertSchemaHas('$table->unsignedInteger(\'parent_id\');', $schema);
        $this->assertSchemaHas('$table->foreign(\'parent_id\', \'fk_parent_id\')->references(\'id\')->on(\'tables\')->onDelete(\'cascade\')->onUpdate(\'cascade\');', $schema);
    }

    private function cleanUpMigrations($path)
    {
        if (is_dir($path)) {
            foreach (glob($path.'/*.php') as $file) {
                unlink($file);
            }
            rmdir($path);
        }
    }

    public function test_writes()
    {
        Config::set('laravel-migration-generator.table_naming_scheme', '0000_00_00_000000_create_[TableName]_table.php');
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY',
            '`user_id` int(9) unsigned NOT NULL',
            '`note` varchar(255) NOT NULL',
            'KEY `fk_user_id_idx` (`user_id`)',
            'CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE',
        ]);

        $path = __DIR__.'/../../migrations';

        if (! is_dir($path)) {
            mkdir($path, 0777, true);
        }

        $generator->definition()->formatter()->write($path);

        $this->assertFileExists($path.'/0000_00_00_000000_create_table_table.php');
    }

    public function test_cleans_up_regular_morphs()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY',
            '`user_id` int(9) unsigned NOT NULL',
            '`user_type` varchar(255) NOT NULL',
            '`note` varchar(255) NOT NULL',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->morphs(\'user\');', $schema);
    }

    public function test_doesnt_clean_up_morph_looking_columns()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY',
            '`user_id` varchar(255) NOT NULL',
            '`user_type` varchar(255) NOT NULL',
            '`note` varchar(255) NOT NULL',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertStringNotContainsString('$table->morphs(\'user\');', $schema);
    }

    public function test_cleans_up_uuid_morphs()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY',
            '`user_id` char(36) NOT NULL',
            '`user_type` varchar(255) NOT NULL',
            '`note` varchar(255) NOT NULL',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->uuidMorphs(\'user\');', $schema);
    }

    public function test_cleans_up_uuid_morphs_nullable()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY',
            '`user_id` char(36) DEFAULT NULL',
            '`user_type` varchar(255) DEFAULT NULL',
            '`note` varchar(255) NOT NULL',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->nullableUuidMorphs(\'user\');', $schema);
    }

    public function test_doesnt_clean_non_auto_inc_id_to_laravel_method()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL',
            'PRIMARY KEY `id`',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->unsignedInteger(\'id\')->primary();', $schema);
    }

    public function test_does_clean_auto_inc_int_to_laravel_method()
    {
        $generator = TableGenerator::init('table', [
            '`id` int(9) unsigned NOT NULL AUTO_INCREMENT',
            'PRIMARY KEY `id`',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->increments(\'id\');', $schema);
    }

    public function test_does_clean_auto_inc_big_int_to_laravel_method()
    {
        $generator = TableGenerator::init('table', [
            '`id` bigint(12) unsigned NOT NULL AUTO_INCREMENT',
            'PRIMARY KEY `id`',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->id();', $schema);
    }

    public function test_doesnt_clean_timestamps_with_use_current()
    {
        $generator = TableGenerator::init('table', [
            'id int auto_increment primary key',
            'created_at timestamp not null default CURRENT_TIMESTAMP',
            'updated_at timestamp null on update CURRENT_TIMESTAMP',
        ]);
        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->timestamp(\'created_at\')->useCurrent()', $schema);
        $this->assertSchemaHas('$table->timestamp(\'updated_at\')->nullable()->useCurrentOnUpdate()', $schema);
    }

    public function test_doesnt_clean_timestamps_with_use_current_on_update()
    {
        $generator = TableGenerator::init('table', [
            'id int auto_increment primary key',
            'created_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
            'updated_at timestamp null on update CURRENT_TIMESTAMP',
        ]);
        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->timestamp(\'created_at\')->useCurrent()->useCurrentOnUpdate()', $schema);
        $this->assertSchemaHas('$table->timestamp(\'updated_at\')->nullable()->useCurrentOnUpdate()', $schema);
    }

    public function test_doesnt_clean_timestamps_with_use_defined_datatype_on_timestamp_configuration()
    {
        config()->set('laravel-migration-generator.definitions.use_defined_datatype_on_timestamp', true);
        $generator = TableGenerator::init('table', [
            'id int auto_increment primary key',
            'created_at datetime NOT NULL',
            'updated_at datetime NOT NULL',
        ]);
        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->dateTime(\'created_at\')', $schema);
        $this->assertSchemaHas('$table->dateTime(\'updated_at\')', $schema);
    }

    public function test_removes_index_from_column_if_fk()
    {
        $generator = TableGenerator::init('test', [
            '`import_id` bigint(20) unsigned DEFAULT NULL',
            '`import_service_id` bigint(20) unsigned DEFAULT NULL',
            'KEY `fk_import_id` (`import_id`)',
            'KEY `fk_import_service_id` (`import_service_id`)',
            'CONSTRAINT `fk_import_id` FOREIGN KEY (`import_id`) REFERENCES `imports` (`id`)',
            'CONSTRAINT `fk_import_service_id` FOREIGN KEY (`import_service_id`) REFERENCES `import_services` (`id`)',
        ]);

        $schema = $generator->definition()->formatter()->getSchema();
        $this->assertSchemaHas('$table->unsignedBigInteger(\'import_id\')->nullable();', $schema);
        $this->assertSchemaHas('$table->unsignedBigInteger(\'import_service_id\')->nullable();', $schema);
        $this->assertSchemaHas('$table->foreign(\'import_id\', \'fk_import_id\')->references(\'id\')->on(\'imports\');', $schema);
        $this->assertSchemaHas('$table->foreign(\'import_service_id\', \'fk_import_service_id\')->references(\'id\')->on(\'import_services\');', $schema);
    }
}


================================================
FILE: tests/Unit/Generators/MySQLViewGeneratorTest.php
================================================
<?php

namespace Tests\Unit\Generators;

use LaravelMigrationGenerator\Generators\MySQL\ViewGenerator;
use Tests\TestCase;

class MySQLViewGeneratorTest extends TestCase
{
    public function tearDown(): void
    {
        parent::tearDown();

        $path = __DIR__.'/../../migrations';
        $this->cleanUpMigrations($path);
    }

    private function cleanUpMigrations($path)
    {
        if (is_dir($path)) {
            foreach (glob($path.'/*.php') as $file) {
                unlink($file);
            }
            rmdir($path);
        }
    }

    public function test_generates()
    {
        $generator = ViewGenerator::init('viewName', 'CREATE ALGORITHM=UNDEFINED DEFINER=`homestead`@`%` SQL SECURITY DEFINER VIEW `view_client_config` AS select `cfg`.`client_id` AS `client_id`,(case when (`cfg`.`client_type_can_edit` = 1) then 1 when (isnull(`cfg`.`client_type_can_edit`) and (`cfg`.`default_can_edit` = 1)) then 1 else 0 end) AS `can_edit` from `table` `cfg`');

        $this->assertStringStartsWith('CREATE VIEW `view_client_config` AS', $generator->definition()->getSchema());
    }

    public function test_writes()
    {
        $generator = ViewGenerator::init('viewName', 'CREATE ALGORITHM=UNDEFINED DEFINER=`homestead`@`%` SQL SECURITY DEFINER VIEW `view_client_config` AS select `cfg`.`client_id` AS `client_id`,(case when (`cfg`.`client_type_can_edit` = 1) then 1 when (isnull(`cfg`.`client_type_can_edit`) and (`cfg`.`default_can_edit` = 1)) then 1 else 0 end) AS `can_edit` from `table` `cfg`');
        $path = __DIR__.'/../../migrations';

        if (! is_dir($path)) {
            mkdir($path, 0777, true);
        }
        $written = $generator->definition()->formatter()->write($path);
        $this->assertFileExists($written);
    }
}


================================================
FILE: tests/Unit/Tokenizers/MySQL/ColumnTokenizerTest.php
================================================
<?php

namespace Tests\Unit\Tokenizers\MySQL;

use LaravelMigrationGenerator\Tokenizers\MySQL\ColumnTokenizer;
use Tests\TestCase;

class ColumnTokenizerTest extends TestCase
{
    //region VARCHAR
    public function test_it_tokenizes_a_not_null_varchar_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`email` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('email', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertEquals('$table->string(\'email\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_varchar_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`favorite_color` varchar(200) COLLATE utf8mb4_unicode_ci');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('favorite_color', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertEquals(200, $columnDefinition->getMethodParameters()[0]);
        $this->assertNull($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertEquals('$table->string(\'favorite_color\', 200)', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_varchar_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`favorite_color` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT \'orange\'');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('favorite_color', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertEquals('orange', $columnDefinition->getDefaultValue());
        $this->assertEquals('$table->string(\'favorite_color\')->default(\'orange\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_varchar_default_value_null_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`favorite_color` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('favorite_color', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertEquals('$table->string(\'favorite_color\')->nullable()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_with_setting()
    {
        $columnTokenizer = ColumnTokenizer::parse('`favorite_color` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT \'favorite color\'');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('favorite_color', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertEquals('favorite color', $columnDefinition->getComment());
        $this->assertEquals('$table->string(\'favorite_color\')->nullable()->comment("favorite color")', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_without_setting()
    {
        config()->set('laravel-migration-generator.definitions.with_comments', false);
        $columnTokenizer = ColumnTokenizer::parse('`favorite_color` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT \'favorite color\'');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('favorite_color', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertEquals('favorite color', $columnDefinition->getComment());
        $this->assertEquals('$table->string(\'favorite_color\')->nullable()', $columnDefinition->render());

        config()->set('laravel-migration-generator.definitions.with_comments', true);
    }

    public function test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_apostrophe()
    {
        $columnTokenizer = ColumnTokenizer::parse("`favorite_color` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'favorite color is ''green''");
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('favorite_color', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertEquals('favorite color is \'green\'', $columnDefinition->getComment());
        $this->assertEquals('$table->string(\'favorite_color\')->nullable()->comment("favorite color is \'green\'")', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_quotation()
    {
        $columnTokenizer = ColumnTokenizer::parse("`favorite_color` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'favorite color is \"green\"");
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('favorite_color', $columnDefinition->getColumnName());
        $this->assertEquals('varchar', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertEquals('favorite color is "green"', $columnDefinition->getComment());
        $this->assertEquals('$table->string(\'favorite_color\')->nullable()->comment("favorite color is \"green\"")', $columnDefinition->render());
    }

    public function test_it_tokenizes_varchar_with_default_and_comment()
    {
        $columnTokenizer = ColumnTokenizer::parse("`testing` varchar(255) DEFAULT 'this is ''it''' COMMENT 'this is the \"comment\"'");
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('this is \'it\'', $columnDefinition->getDefaultValue());
        $this->assertEquals('this is the "comment"', $columnDefinition->getComment());
    }

    public function test_it_tokenizes_varchar_with_default_empty_string_and_comment()
    {
        $columnTokenizer = ColumnTokenizer::parse("`testing` varchar(255) DEFAULT '' COMMENT 'this is the \"comment\"'");
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('', $columnDefinition->getDefaultValue());
        $this->assertEquals('this is the "comment"', $columnDefinition->getComment());
    }

    public function test_it_tokenizes_varchar_with_default_empty_string_and_comment_with_apostrophe()
    {
        $columnTokenizer = ColumnTokenizer::parse("`testing` varchar(255) DEFAULT '' COMMENT 'this is the \"comment\" ''inside single quote''");
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('', $columnDefinition->getDefaultValue());
        $this->assertEquals('this is the "comment" \'inside single quote\'', $columnDefinition->getComment());
    }

    public function test_it_tokenizes_varchar_with_boolean_literal_default()
    {
        $columnTokenizer = ColumnTokenizer::parse("`testing` bit(2) DEFAULT b'10'");
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('bit', $columnDefinition->getMethodName());
        $this->assertEquals('b\'10\'', $columnDefinition->getDefaultValue());
        $this->assertEquals("\$table->bit('testing', 2)->default(b'10')", $columnDefinition->render());
    }

    public function test_it_tokenizes_char_column_with_character_and_collation()
    {
        $columnTokenizer = ColumnTokenizer::parse('`country` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT \'US\'');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('country', $columnDefinition->getColumnName());
        $this->assertEquals('char', $columnTokenizer->getColumnDataType());
        $this->assertEquals('char', $columnDefinition->getMethodName());
        $this->assertCount(1, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->isNullable());
        $this->assertEquals('utf8mb4_unicode_ci', $columnDefinition->getCollation());
        $this->assertEquals('utf8mb4', $columnDefinition->getCharacterSet());
        $this->assertEquals('$table->char(\'country\', 2)->default(\'US\')', $columnDefinition->render());
    }

    //endregion

    //region TEXT & Variants
    public function test_it_tokenizes_a_not_null_tinytext_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` tinytext NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('tinytext', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->string(\'notes\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_tinytext_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` tinytext');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('tinytext', $columnTokenizer->getColumnDataType());
        $this->assertEquals('string', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->string(\'notes\')->nullable()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_text_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` text NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('text', $columnTokenizer->getColumnDataType());
        $this->assertEquals('text', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->text(\'notes\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_text_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` text');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('text', $columnTokenizer->getColumnDataType());
        $this->assertEquals('text', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->text(\'notes\')->nullable()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_mediumtext_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` mediumtext NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('mediumtext', $columnTokenizer->getColumnDataType());
        $this->assertEquals('mediumText', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->mediumText(\'notes\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_mediumtext_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` mediumtext');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('mediumtext', $columnTokenizer->getColumnDataType());
        $this->assertEquals('mediumText', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->mediumText(\'notes\')->nullable()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_longtext_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` longtext NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('longtext', $columnTokenizer->getColumnDataType());
        $this->assertEquals('longText', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->longText(\'notes\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_longtext_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`notes` longtext');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('notes', $columnDefinition->getColumnName());
        $this->assertEquals('longtext', $columnTokenizer->getColumnDataType());
        $this->assertEquals('longText', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->longText(\'notes\')->nullable()', $columnDefinition->render());
    }

    //endregion

    //region INT & Variants
    public function test_it_tokenizes_a_not_null_smallint_without_param_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`cats` smallint NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('cats', $columnDefinition->getColumnName());
        $this->assertEquals('smallint', $columnTokenizer->getColumnDataType());
        $this->assertEquals('smallInteger', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->smallInteger(\'cats\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_smallint_with_param_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`cats` smallint(6) NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('cats', $columnDefinition->getColumnName());
        $this->assertEquals('smallint', $columnTokenizer->getColumnDataType());
        $this->assertEquals('smallInteger', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->smallInteger(\'cats\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_unsigned_smallint_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`cats` smallint(6) unsigned NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('cats', $columnDefinition->getColumnName());
        $this->assertEquals('smallint', $columnTokenizer->getColumnDataType());
        $this->assertEquals('smallInteger', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertTrue($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->unsignedSmallInteger(\'cats\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_nullable_big_int_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`template_id` bigint(20) unsigned DEFAULT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('template_id', $columnDefinition->getColumnName());
        $this->assertEquals('bigint', $columnTokenizer->getColumnDataType());
        $this->assertEquals('bigInteger', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertTrue($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertEquals('$table->unsignedBigInteger(\'template_id\')->nullable()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_primary_auto_inc_int_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`id` int(9) unsigned NOT NULL AUTO_INCREMENT');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('id', $columnDefinition->getColumnName());
        $this->assertEquals('int', $columnTokenizer->getColumnDataType());
        $this->assertEquals('integer', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isPrimary());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertTrue($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->unsignedInteger(\'id\')', $columnDefinition->render());
    }

    public function test_definition_config()
    {
        config()->set('laravel-migration-generator.definitions.prefer_unsigned_prefix', false);
        $columnTokenizer = ColumnTokenizer::parse('`column` int(9) unsigned NOT NULL');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('$table->integer(\'column\')->unsigned()', $columnDefinition->render());
        config()->set('laravel-migration-generator.definitions.prefer_unsigned_prefix', true);
    }

    //endregion

    //region FLOAT
    public function test_it_tokenizes_float_without_params_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float NOT NULL');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_float_with_params_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float(4,2) NOT NULL');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(4, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\', 4, 2)', $columnDefinition->render());
    }

    public function test_it_tokenizes_float_null_without_params_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_float_null_with_params_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float(4,2)');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(4, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertNull($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\', 4, 2)', $columnDefinition->render());
    }

    public function test_it_tokenizes_float_without_params_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float NOT NULL DEFAULT 1.00');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertEquals(1.0, $columnDefinition->getDefaultValue());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\')->default(1.00)', $columnDefinition->render());
    }

    public function test_it_tokenizes_float_with_params_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float(4,2) NOT NULL DEFAULT 1.00');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(4, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertEquals(1.00, $columnDefinition->getDefaultValue());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\', 4, 2)->default(1.00)', $columnDefinition->render());
    }

    public function test_it_tokenizes_float_null_without_params_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float DEFAULT 1.0');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertEquals(1.0, $columnDefinition->getDefaultValue());
        $this->assertNull($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\')->default(1.0)', $columnDefinition->render());
    }

    public function test_it_tokenizes_float_null_with_params_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`parameter` float(4,2) DEFAULT 1.00');
        $columnDefinition = $columnTokenizer->definition();
        $this->assertEquals('parameter', $columnDefinition->getColumnName());
        $this->assertEquals('float', $columnTokenizer->getColumnDataType());
        $this->assertEquals('float', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(4, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertEquals(1.00, $columnDefinition->getDefaultValue());
        $this->assertNull($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->float(\'parameter\', 4, 2)->default(1.00)', $columnDefinition->render());
    }

    //endregion

    //region DECIMAL
    public function test_it_tokenizes_a_not_null_decimal_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` decimal(9,2) NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('decimal', $columnTokenizer->getColumnDataType());
        $this->assertEquals('decimal', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->decimal(\'amount\', 9, 2)', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_unsigned_decimal_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` decimal(9,2) unsigned NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('decimal', $columnTokenizer->getColumnDataType());
        $this->assertEquals('decimal', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertTrue($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->unsignedDecimal(\'amount\', 9, 2)', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_decimal_with_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` decimal(9,2) NOT NULL DEFAULT 1.00');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('decimal', $columnTokenizer->getColumnDataType());
        $this->assertEquals('decimal', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals(1.0, $columnDefinition->getDefaultValue());
        $this->assertEquals('$table->decimal(\'amount\', 9, 2)->default(1.00)', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_unsigned_decimal_with_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` decimal(9,2) unsigned NOT NULL DEFAULT 1.00');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('decimal', $columnTokenizer->getColumnDataType());
        $this->assertEquals('decimal', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertEquals(1.0, $columnDefinition->getDefaultValue());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertTrue($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->unsignedDecimal(\'amount\', 9, 2)->default(1.00)', $columnDefinition->render());
    }

    //endregion

    //region DOUBLE
    public function test_it_tokenizes_a_not_null_double_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` double(9,2) NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('double', $columnTokenizer->getColumnDataType());
        $this->assertEquals('double', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->double(\'amount\', 9, 2)', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_unsigned_double_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` double(9,2) unsigned NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('double', $columnTokenizer->getColumnDataType());
        $this->assertEquals('double', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertTrue($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->double(\'amount\', 9, 2)->unsigned()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_double_with_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` double(9,2) NOT NULL DEFAULT 1.00');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('double', $columnTokenizer->getColumnDataType());
        $this->assertEquals('double', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals(1.00, $columnDefinition->getDefaultValue());
        $this->assertEquals('$table->double(\'amount\', 9, 2)->default(1.00)', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_unsigned_double_with_default_value_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`amount` double(9,2) unsigned NOT NULL DEFAULT 1.00');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('amount', $columnDefinition->getColumnName());
        $this->assertEquals('double', $columnTokenizer->getColumnDataType());
        $this->assertEquals('double', $columnDefinition->getMethodName());
        $this->assertCount(2, $columnDefinition->getMethodParameters());
        $this->assertEquals(9, $columnDefinition->getMethodParameters()[0]);
        $this->assertEquals(2, $columnDefinition->getMethodParameters()[1]);
        $this->assertEquals(1.00, $columnDefinition->getDefaultValue());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertTrue($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->double(\'amount\', 9, 2)->unsigned()->default(1.00)', $columnDefinition->render());
    }

    //endregion

    //region DATETIME
    public function test_it_tokenizes_a_not_null_datetime_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`sent_at` datetime NOT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('sent_at', $columnDefinition->getColumnName());
        $this->assertEquals('datetime', $columnTokenizer->getColumnDataType());
        $this->assertEquals('dateTime', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertFalse($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->dateTime(\'sent_at\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_not_null_datetime_default_now_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`sent_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('sent_at', $columnDefinition->getColumnName());
        $this->assertEquals('datetime', $columnTokenizer->getColumnDataType());
        $this->assertEquals('dateTime', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertFalse($columnDefinition->isNullable());
        $this->assertFalse($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->dateTime(\'sent_at\')->useCurrent()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_datetime_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`sent_at` datetime');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('sent_at', $columnDefinition->getColumnName());
        $this->assertEquals('datetime', $columnTokenizer->getColumnDataType());
        $this->assertEquals('dateTime', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertNull($columnDefinition->isNullable());
        $this->assertFalse($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->dateTime(\'sent_at\')', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_default_value_datetime_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`sent_at` datetime DEFAULT NULL');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('sent_at', $columnDefinition->getColumnName());
        $this->assertEquals('datetime', $columnTokenizer->getColumnDataType());
        $this->assertEquals('dateTime', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertTrue($columnDefinition->isNullable());
        $this->assertFalse($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->dateTime(\'sent_at\')->nullable()', $columnDefinition->render());
    }

    public function test_it_tokenizes_a_null_default_value_now_datetime_column()
    {
        $columnTokenizer = ColumnTokenizer::parse('`sent_at` datetime DEFAULT CURRENT_TIMESTAMP');
        $columnDefinition = $columnTokenizer->definition();

        $this->assertEquals('sent_at', $columnDefinition->getColumnName());
        $this->assertEquals('datetime', $columnTokenizer->getColumnDataType());
        $this->assertEquals('dateTime', $columnDefinition->getMethodName());
        $this->assertCount(0, $columnDefinition->getMethodParameters());
        $this->assertNull($columnDefinition->getDefaultValue());
        $this->assertNull($columnDefinition->isNullable());
        $this->assertFalse($columnDefinition->isUnsigned());
        $this->assertNull($columnDefinition->getCollation());
        $this->assertEquals('$table->dateTime(\'sent_at\')->useCurrent()', $columnDefinition->render());
    }

    public function t
Download .txt
gitextract_qojz53lt/

├── .github/
│   ├── FUNDING.yml
│   ├── ISSUE_TEMPLATE/
│   │   └── bug_report.md
│   └── workflows/
│       └── php.yml
├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── UPGRADE.md
├── composer.json
├── config/
│   └── laravel-migration-generator.php
├── docs/
│   ├── _config.yml
│   ├── command.md
│   ├── config.md
│   ├── index.md
│   └── stubs.md
├── phpunit.xml
├── pint.json
├── src/
│   ├── Commands/
│   │   └── GenerateMigrationsCommand.php
│   ├── Definitions/
│   │   ├── ColumnDefinition.php
│   │   ├── IndexDefinition.php
│   │   ├── TableDefinition.php
│   │   └── ViewDefinition.php
│   ├── Formatters/
│   │   ├── TableFormatter.php
│   │   └── ViewFormatter.php
│   ├── GeneratorManagers/
│   │   ├── BaseGeneratorManager.php
│   │   ├── Interfaces/
│   │   │   └── GeneratorManagerInterface.php
│   │   └── MySQLGeneratorManager.php
│   ├── Generators/
│   │   ├── BaseTableGenerator.php
│   │   ├── BaseViewGenerator.php
│   │   ├── Concerns/
│   │   │   ├── CleansUpColumnIndices.php
│   │   │   ├── CleansUpForeignKeyIndices.php
│   │   │   ├── CleansUpMorphColumns.php
│   │   │   ├── CleansUpTimestampsColumn.php
│   │   │   ├── WritesToFile.php
│   │   │   └── WritesViewsToFile.php
│   │   ├── Interfaces/
│   │   │   ├── TableGeneratorInterface.php
│   │   │   └── ViewGeneratorInterface.php
│   │   └── MySQL/
│   │       ├── TableGenerator.php
│   │       └── ViewGenerator.php
│   ├── Helpers/
│   │   ├── ConfigResolver.php
│   │   ├── DependencyResolver.php
│   │   ├── Formatter.php
│   │   ├── ValueToString.php
│   │   └── WritableTrait.php
│   ├── LaravelMigrationGeneratorProvider.php
│   └── Tokenizers/
│       ├── BaseColumnTokenizer.php
│       ├── BaseIndexTokenizer.php
│       ├── BaseTokenizer.php
│       ├── Interfaces/
│       │   ├── ColumnTokenizerInterface.php
│       │   └── IndexTokenizerInterface.php
│       └── MySQL/
│           ├── ColumnTokenizer.php
│           └── IndexTokenizer.php
├── stubs/
│   ├── table-create.stub
│   ├── table-modify.stub
│   ├── table.stub
│   └── view.stub
└── tests/
    ├── TestCase.php
    └── Unit/
        ├── ColumnDefinitionTest.php
        ├── DependencyResolverTest.php
        ├── FormatterTest.php
        ├── GeneratorManagers/
        │   └── MySQLGeneratorManagerTest.php
        ├── Generators/
        │   ├── MySQLTableGeneratorTest.php
        │   └── MySQLViewGeneratorTest.php
        ├── Tokenizers/
        │   └── MySQL/
        │       ├── ColumnTokenizerTest.php
        │       └── IndexTokenizerTest.php
        └── ValueToStringTest.php
Download .txt
SYMBOL INDEX (444 symbols across 45 files)

FILE: src/Commands/GenerateMigrationsCommand.php
  class GenerateMigrationsCommand (line 13) | class GenerateMigrationsCommand extends Command
    method getConnection (line 19) | public function getConnection()
    method getPath (line 34) | public function getPath($driver)
    method handle (line 44) | public function handle()
    method resolveGeneratorManager (line 86) | protected function resolveGeneratorManager(string $driver)

FILE: src/Definitions/ColumnDefinition.php
  class ColumnDefinition (line 12) | class ColumnDefinition
    method __construct (line 55) | public function __construct($attributes = [])
    method getMethodName (line 66) | public function getMethodName(): string
    method getMethodParameters (line 71) | public function getMethodParameters(): array
    method getColumnName (line 76) | public function getColumnName(): ?string
    method isUnsigned (line 81) | public function isUnsigned(): bool
    method isNullable (line 89) | public function isNullable(): ?bool
    method getDefaultValue (line 97) | public function getDefaultValue()
    method getComment (line 106) | public function getComment(): ?string
    method getCharacterSet (line 111) | public function getCharacterSet(): ?string
    method getCollation (line 116) | public function getCollation(): ?string
    method isAutoIncrementing (line 121) | public function isAutoIncrementing(): bool
    method isIndex (line 126) | public function isIndex(): bool
    method isPrimary (line 131) | public function isPrimary(): bool
    method isUnique (line 136) | public function isUnique(): bool
    method useCurrent (line 141) | public function useCurrent(): bool
    method useCurrentOnUpdate (line 146) | public function useCurrentOnUpdate(): bool
    method getStoredAs (line 151) | public function getStoredAs(): ?string
    method getVirtualAs (line 156) | public function getVirtualAs(): ?string
    method isUUID (line 161) | public function isUUID(): bool
    method setMethodName (line 170) | public function setMethodName(string $methodName): ColumnDefinition
    method setMethodParameters (line 177) | public function setMethodParameters(array $methodParameters): ColumnDe...
    method setColumnName (line 184) | public function setColumnName(?string $columnName): ColumnDefinition
    method setUnsigned (line 191) | public function setUnsigned(bool $unsigned): ColumnDefinition
    method setNullable (line 201) | public function setNullable(?bool $nullable): ColumnDefinition
    method setDefaultValue (line 212) | public function setDefaultValue($defaultValue)
    method setComment (line 219) | public function setComment(?string $comment): ColumnDefinition
    method setCharacterSet (line 229) | public function setCharacterSet(?string $characterSet): ColumnDefinition
    method setCollation (line 236) | public function setCollation(?string $collation): ColumnDefinition
    method setAutoIncrementing (line 243) | public function setAutoIncrementing(bool $autoIncrementing): ColumnDef...
    method setStoredAs (line 250) | public function setStoredAs(?string $storedAs): ColumnDefinition
    method setVirtualAs (line 257) | public function setVirtualAs(?string $virtualAs): ColumnDefinition
    method addIndexDefinition (line 264) | public function addIndexDefinition(IndexDefinition $definition): Colum...
    method setIndex (line 271) | public function setIndex(bool $index): ColumnDefinition
    method setPrimary (line 278) | public function setPrimary(bool $primary): ColumnDefinition
    method setUnique (line 285) | public function setUnique(bool $unique): ColumnDefinition
    method setUseCurrent (line 292) | public function setUseCurrent(bool $useCurrent): ColumnDefinition
    method setUseCurrentOnUpdate (line 299) | public function setUseCurrentOnUpdate(bool $useCurrentOnUpdate): Colum...
    method setIsUUID (line 306) | public function setIsUUID(bool $isUUID): ColumnDefinition
    method isNullableMethod (line 315) | protected function isNullableMethod($methodName)
    method isPrimaryKeyMethod (line 320) | protected function isPrimaryKeyMethod($methodName)
    method canBeUnsigned (line 325) | protected function canBeUnsigned($methodName)
    method guessLaravelMethod (line 330) | protected function guessLaravelMethod()
    method render (line 397) | public function render(): string

FILE: src/Definitions/IndexDefinition.php
  class IndexDefinition (line 8) | class IndexDefinition
    method __construct (line 26) | public function __construct($attributes = [])
    method getIndexType (line 37) | public function getIndexType(): string
    method getIndexName (line 42) | public function getIndexName(): ?string
    method getIndexColumns (line 47) | public function getIndexColumns(): array
    method getForeignReferencedColumns (line 52) | public function getForeignReferencedColumns(): array
    method getForeignReferencedTable (line 57) | public function getForeignReferencedTable(): string
    method getConstraintActions (line 62) | public function getConstraintActions(): array
    method setIndexType (line 70) | public function setIndexType(string $indexType): IndexDefinition
    method setIndexName (line 77) | public function setIndexName(string $indexName): IndexDefinition
    method setIndexColumns (line 84) | public function setIndexColumns(array $indexColumns): IndexDefinition
    method setForeignReferencedColumns (line 91) | public function setForeignReferencedColumns(array $foreignReferencedCo...
    method setForeignReferencedTable (line 98) | public function setForeignReferencedTable(string $foreignReferencedTab...
    method setConstraintActions (line 105) | public function setConstraintActions(array $constraintActions): IndexD...
    method isMultiColumnIndex (line 114) | public function isMultiColumnIndex()
    method getEscapedIndexName (line 122) | protected function getEscapedIndexName(): string
    method render (line 127) | public function render(): string

FILE: src/Definitions/TableDefinition.php
  class TableDefinition (line 7) | class TableDefinition
    method __construct (line 18) | public function __construct($attributes = [])
    method getDriver (line 27) | public function getDriver(): string
    method getPresentableTableName (line 32) | public function getPresentableTableName(): string
    method getTableName (line 46) | public function getTableName(): string
    method setTableName (line 51) | public function setTableName(string $tableName)
    method getColumnDefinitions (line 58) | public function getColumnDefinitions(): array
    method setColumnDefinitions (line 63) | public function setColumnDefinitions(array $columnDefinitions)
    method addColumnDefinition (line 70) | public function addColumnDefinition(ColumnDefinition $definition)
    method getIndexDefinitions (line 80) | public function getIndexDefinitions(): array
    method getForeignKeyDefinitions (line 86) | public function getForeignKeyDefinitions(): array
    method setIndexDefinitions (line 93) | public function setIndexDefinitions(array $indexDefinitions)
    method addIndexDefinition (line 100) | public function addIndexDefinition(IndexDefinition $definition)
    method removeIndexDefinition (line 107) | public function removeIndexDefinition(IndexDefinition $definition)
    method getPrimaryKey (line 120) | public function getPrimaryKey(): array
    method formatter (line 128) | public function formatter(): TableFormatter

FILE: src/Definitions/ViewDefinition.php
  class ViewDefinition (line 7) | class ViewDefinition
    method __construct (line 15) | public function __construct($attributes = [])
    method getDriver (line 24) | public function getDriver(): string
    method setDriver (line 29) | public function setDriver(string $driver): ViewDefinition
    method getSchema (line 36) | public function getSchema(): string
    method setSchema (line 41) | public function setSchema(string $schema): ViewDefinition
    method getViewName (line 48) | public function getViewName(): string
    method setViewName (line 53) | public function setViewName(string $viewName): ViewDefinition
    method formatter (line 60) | public function formatter(): ViewFormatter

FILE: src/Formatters/TableFormatter.php
  class TableFormatter (line 10) | class TableFormatter
    method __construct (line 14) | public function __construct(TableDefinition $tableDefinition)
    method render (line 19) | public function render($tabCharacter = '    ')
    method getStubFileName (line 38) | public function getStubFileName($index = 0): string
    method getStubPath (line 51) | public function getStubPath(): string
    method getStubCreatePath (line 66) | public function getStubCreatePath(): string
    method getStubModifyPath (line 81) | public function getStubModifyPath(): string
    method stubNameVariables (line 96) | public function stubNameVariables($index): array
    method getSchema (line 111) | public function getSchema($tab = ''): string
    method stubTableUp (line 135) | public function stubTableUp($tab = '', $variables = null): string
    method stubTableDown (line 157) | public function stubTableDown($tab = ''): string
    method getStubVariables (line 171) | protected function getStubVariables($tab = '')
    method write (line 183) | public function write(string $basePath, $index = 0, string $tabCharact...

FILE: src/Formatters/ViewFormatter.php
  class ViewFormatter (line 10) | class ViewFormatter
    method __construct (line 14) | public function __construct(ViewDefinition $definition)
    method stubNameVariables (line 19) | public function stubNameVariables($index = 0)
    method getStubFileName (line 32) | protected function getStubFileName($index = 0)
    method getStubPath (line 46) | protected function getStubPath()
    method render (line 61) | public function render($tabCharacter = '    ')
    method write (line 77) | public function write(string $basePath, $index = 0, string $tabCharact...

FILE: src/GeneratorManagers/BaseGeneratorManager.php
  class BaseGeneratorManager (line 11) | abstract class BaseGeneratorManager implements GeneratorManagerInterface
    method init (line 17) | abstract public function init();
    method createMissingDirectory (line 19) | public function createMissingDirectory($basePath)
    method getTableDefinitions (line 29) | public function getTableDefinitions(): array
    method getViewDefinitions (line 37) | public function getViewDefinitions(): array
    method addTableDefinition (line 42) | public function addTableDefinition(TableDefinition $tableDefinition): ...
    method addViewDefinition (line 49) | public function addViewDefinition(ViewDefinition $definition): BaseGen...
    method handle (line 56) | public function handle(string $basePath, array $tableNames = [], array...
    method sortTables (line 95) | public function sortTables(array $tableDefinitions): array
    method writeTableMigrations (line 111) | public function writeTableMigrations(array $tableDefinitions, $basePath)
    method writeViewMigrations (line 121) | public function writeViewMigrations(array $viewDefinitions, $basePath,...
    method skippableTables (line 131) | public function skippableTables(): array
    method skipTable (line 136) | public function skipTable($table): bool
    method skippableViews (line 144) | public function skippableViews(): array
    method skipView (line 149) | public function skipView($view): bool

FILE: src/GeneratorManagers/Interfaces/GeneratorManagerInterface.php
  type GeneratorManagerInterface (line 8) | interface GeneratorManagerInterface
    method driver (line 10) | public static function driver(): string;
    method handle (line 12) | public function handle(string $basePath, array $tableNames = [], array...
    method addTableDefinition (line 14) | public function addTableDefinition(TableDefinition $definition);
    method addViewDefinition (line 16) | public function addViewDefinition(ViewDefinition $definition);
    method getTableDefinitions (line 18) | public function getTableDefinitions(): array;
    method getViewDefinitions (line 20) | public function getViewDefinitions(): array;

FILE: src/GeneratorManagers/MySQLGeneratorManager.php
  class MySQLGeneratorManager (line 12) | class MySQLGeneratorManager extends BaseGeneratorManager implements Gene...
    method driver (line 14) | public static function driver(): string
    method init (line 19) | public function init()
    method addTableDefinition (line 35) | public function addTableDefinition(TableDefinition $tableDefinition): ...

FILE: src/Generators/BaseTableGenerator.php
  class BaseTableGenerator (line 12) | abstract class BaseTableGenerator implements TableGeneratorInterface
    method __construct (line 23) | public function __construct(string $tableName, array $rows = [])
    method definition (line 32) | public function definition(): TableDefinition
    method resolveStructure (line 37) | abstract public function resolveStructure();
    method parse (line 39) | abstract public function parse();
    method init (line 41) | public static function init(string $tableName, array $rows = [])
    method shouldResolveStructure (line 55) | public function shouldResolveStructure(): bool
    method cleanUp (line 60) | public function cleanUp(): void

FILE: src/Generators/BaseViewGenerator.php
  class BaseViewGenerator (line 8) | abstract class BaseViewGenerator implements ViewGeneratorInterface
    method __construct (line 12) | public function __construct(string $viewName, ?string $schema = null)
    method definition (line 21) | public function definition(): ViewDefinition
    method init (line 26) | public static function init(string $viewName, ?string $schema = null)

FILE: src/Generators/Concerns/CleansUpColumnIndices.php
  type CleansUpColumnIndices (line 12) | trait CleansUpColumnIndices
    method cleanUpColumnsWithIndices (line 14) | protected function cleanUpColumnsWithIndices(): void

FILE: src/Generators/Concerns/CleansUpForeignKeyIndices.php
  type CleansUpForeignKeyIndices (line 12) | trait CleansUpForeignKeyIndices
    method cleanUpForeignKeyIndices (line 14) | protected function cleanUpForeignKeyIndices(): void

FILE: src/Generators/Concerns/CleansUpMorphColumns.php
  type CleansUpMorphColumns (line 14) | trait CleansUpMorphColumns
    method cleanUpMorphColumns (line 16) | protected function cleanUpMorphColumns(): void

FILE: src/Generators/Concerns/CleansUpTimestampsColumn.php
  type CleansUpTimestampsColumn (line 12) | trait CleansUpTimestampsColumn
    method cleanUpTimestampsColumn (line 14) | protected function cleanUpTimestampsColumn(): void

FILE: src/Generators/Concerns/WritesToFile.php
  type WritesToFile (line 5) | trait WritesToFile
    method write (line 7) | public function write(string $basePath, $index = 0, string $tabCharact...

FILE: src/Generators/Concerns/WritesViewsToFile.php
  type WritesViewsToFile (line 8) | trait WritesViewsToFile
    method stubNameVariables (line 12) | public function stubNameVariables()
    method getStubFileName (line 22) | protected function getStubFileName()
    method getStubPath (line 36) | protected function getStubPath()
    method generateStub (line 51) | protected function generateStub($tabCharacter = '    ')

FILE: src/Generators/Interfaces/TableGeneratorInterface.php
  type TableGeneratorInterface (line 7) | interface TableGeneratorInterface
    method driver (line 9) | public static function driver(): string;
    method shouldResolveStructure (line 11) | public function shouldResolveStructure(): bool;
    method resolveStructure (line 13) | public function resolveStructure();
    method parse (line 15) | public function parse();
    method cleanUp (line 17) | public function cleanUp();
    method definition (line 19) | public function definition(): TableDefinition;

FILE: src/Generators/Interfaces/ViewGeneratorInterface.php
  type ViewGeneratorInterface (line 5) | interface ViewGeneratorInterface
    method driver (line 7) | public static function driver(): string;
    method parse (line 9) | public function parse();
    method resolveSchema (line 11) | public function resolveSchema();

FILE: src/Generators/MySQL/TableGenerator.php
  class TableGenerator (line 14) | class TableGenerator extends BaseTableGenerator
    method driver (line 16) | public static function driver(): string
    method resolveStructure (line 21) | public function resolveStructure()
    method isColumnLine (line 39) | protected function isColumnLine($line)
    method parse (line 44) | public function parse()

FILE: src/Generators/MySQL/ViewGenerator.php
  class ViewGenerator (line 9) | class ViewGenerator extends BaseViewGenerator implements ViewGeneratorIn...
    method driver (line 11) | public static function driver(): string
    method resolveSchema (line 16) | public function resolveSchema()
    method parse (line 26) | public function parse()

FILE: src/Helpers/ConfigResolver.php
  class ConfigResolver (line 5) | class ConfigResolver
    method resolver (line 7) | protected static function resolver(string $configKey, string $driver)
    method tableNamingScheme (line 13) | public static function tableNamingScheme(string $driver)
    method viewNamingScheme (line 18) | public static function viewNamingScheme(string $driver)
    method path (line 23) | public static function path(string $driver)
    method skippableTables (line 28) | public static function skippableTables(string $driver)
    method skippableViews (line 33) | public static function skippableViews(string $driver)

FILE: src/Helpers/DependencyResolver.php
  class DependencyResolver (line 9) | class DependencyResolver
    method __construct (line 17) | public function __construct(array $tableDefinitions)
    method build (line 24) | protected function build()
    method getDependencyOrder (line 110) | public function getDependencyOrder(): array

FILE: src/Helpers/Formatter.php
  class Formatter (line 5) | class Formatter
    method __construct (line 11) | public function __construct(string $tabCharacter = '    ')
    method line (line 16) | public function line(string $data, $indentTimes = 0)
    method render (line 25) | public function render($extraIndent = 0)
    method replaceOnLine (line 41) | public function replaceOnLine($toReplace, $body)
    method replace (line 58) | public static function replace($tabCharacter, $toReplace, $replacement...

FILE: src/Helpers/ValueToString.php
  class ValueToString (line 7) | class ValueToString
    method castFloat (line 9) | public static function castFloat($value)
    method castBinary (line 14) | public static function castBinary($value)
    method isCastedValue (line 19) | public static function isCastedValue($value)
    method parseCastedValue (line 24) | public static function parseCastedValue($value)
    method escape (line 43) | public static function escape(string $value, bool $singleQuote = true)...
    method make (line 56) | public static function make($value, $singleOutArray = false, $singleQu...

FILE: src/Helpers/WritableTrait.php
  type WritableTrait (line 5) | trait WritableTrait
    method markAsWritable (line 9) | public function markAsWritable(bool $writable = true)
    method isWritable (line 16) | public function isWritable()

FILE: src/LaravelMigrationGeneratorProvider.php
  class LaravelMigrationGeneratorProvider (line 11) | class LaravelMigrationGeneratorProvider extends ServiceProvider
    method boot (line 13) | public function boot()

FILE: src/Tokenizers/BaseColumnTokenizer.php
  class BaseColumnTokenizer (line 8) | abstract class BaseColumnTokenizer extends BaseTokenizer implements Colu...
    method __construct (line 12) | public function __construct(string $value)
    method definition (line 18) | public function definition(): ColumnDefinition

FILE: src/Tokenizers/BaseIndexTokenizer.php
  class BaseIndexTokenizer (line 8) | abstract class BaseIndexTokenizer extends BaseTokenizer implements Index...
    method __construct (line 12) | public function __construct(string $value)
    method definition (line 18) | public function definition(): IndexDefinition

FILE: src/Tokenizers/BaseTokenizer.php
  class BaseTokenizer (line 5) | abstract class BaseTokenizer
    method __construct (line 17) | public function __construct(string $value)
    method make (line 62) | public static function make(string $line)
    method parse (line 70) | public static function parse(string $line)
    method parseColumn (line 75) | protected function parseColumn($value)
    method columnsToArray (line 80) | protected function columnsToArray($string)
    method consume (line 87) | protected function consume()
    method putBack (line 92) | protected function putBack($value)

FILE: src/Tokenizers/Interfaces/ColumnTokenizerInterface.php
  type ColumnTokenizerInterface (line 7) | interface ColumnTokenizerInterface
    method tokenize (line 9) | public function tokenize(): self;
    method definition (line 11) | public function definition(): ColumnDefinition;

FILE: src/Tokenizers/Interfaces/IndexTokenizerInterface.php
  type IndexTokenizerInterface (line 7) | interface IndexTokenizerInterface
    method tokenize (line 9) | public function tokenize(): self;
    method definition (line 11) | public function definition(): IndexDefinition;

FILE: src/Tokenizers/MySQL/ColumnTokenizer.php
  class ColumnTokenizer (line 10) | class ColumnTokenizer extends BaseColumnTokenizer
    method tokenize (line 21) | public function tokenize(): self
    method consumeColumnName (line 58) | protected function consumeColumnName()
    method consumeZeroFill (line 63) | protected function consumeZeroFill()
    method consumeColumnType (line 74) | protected function consumeColumnType()
    method consumeAutoIncrement (line 93) | private function consumeAutoIncrement()
    method consumeNullable (line 103) | protected function consumeNullable()
    method consumeDefaultValue (line 124) | protected function consumeDefaultValue()
    method consumeComment (line 161) | protected function consumeComment()
    method consumeCharacterSet (line 172) | protected function consumeCharacterSet()
    method consumeCollation (line 186) | protected function consumeCollation()
    method consumeUnsigned (line 197) | private function consumeUnsigned()
    method consumeKeyConstraints (line 207) | private function consumeKeyConstraints()
    method consumeGenerated (line 229) | private function consumeGenerated()
    method consumeTimestamp (line 277) | private function consumeTimestamp()
    method resolveColumnMethod (line 300) | private function resolveColumnMethod()
    method resolveColumnConstraints (line 328) | private function resolveColumnConstraints(array $constraints)
    method isTextType (line 359) | protected function isTextType()
    method isNumberType (line 364) | protected function isNumberType()
    method isArrayType (line 369) | protected function isArrayType()
    method isBinaryType (line 374) | protected function isBinaryType()
    method getColumnDataType (line 382) | public function getColumnDataType()

FILE: src/Tokenizers/MySQL/IndexTokenizer.php
  class IndexTokenizer (line 7) | class IndexTokenizer extends BaseIndexTokenizer
    method tokenize (line 9) | public function tokenize(): self
    method consumeIndexType (line 25) | private function consumeIndexType()
    method consumeIndexName (line 39) | private function consumeIndexName()
    method consumeIndexColumns (line 45) | private function consumeIndexColumns()
    method consumeForeignKey (line 53) | private function consumeForeignKey()
    method consumeConstraintActions (line 98) | private function consumeConstraintActions()

FILE: tests/TestCase.php
  class TestCase (line 7) | class TestCase extends \Orchestra\Testbench\TestCase
    method defineEnvironment (line 9) | protected function defineEnvironment($app)
    method getPackageProviders (line 20) | protected function getPackageProviders($app)

FILE: tests/Unit/ColumnDefinitionTest.php
  class ColumnDefinitionTest (line 9) | class ColumnDefinitionTest extends TestCase
    method test_it_can_add_index_definitions (line 11) | public function test_it_can_add_index_definitions()
    method test_it_prunes_empty_primary_key_index (line 20) | public function test_it_prunes_empty_primary_key_index()

FILE: tests/Unit/DependencyResolverTest.php
  class DependencyResolverTest (line 11) | class DependencyResolverTest extends TestCase
    method test_it_can_find_simple_dependencies (line 13) | public function test_it_can_find_simple_dependencies()
    method test_it_finds_cyclical_dependencies (line 42) | public function test_it_finds_cyclical_dependencies()
    method test_it_finds_self_referential_dependencies (line 76) | public function test_it_finds_self_referential_dependencies()

FILE: tests/Unit/FormatterTest.php
  class FormatterTest (line 8) | class FormatterTest extends TestCase
    method test_can_format_single_line (line 10) | public function test_can_format_single_line()
    method test_can_chain (line 17) | public function test_can_chain()
    method test_can_get_current_line_indent_level (line 30) | public function test_can_get_current_line_indent_level()
    method test_can_replace_on_no_indent (line 48) | public function test_can_replace_on_no_indent()

FILE: tests/Unit/GeneratorManagers/MySQLGeneratorManagerTest.php
  class MySQLGeneratorManagerTest (line 13) | class MySQLGeneratorManagerTest extends TestCase
    method getManagerMock (line 15) | protected function getManagerMock(array $tableDefinitions)
    method test_can_sort_tables (line 25) | public function test_can_sort_tables()
    method test_can_remove_database_prefix (line 57) | public function test_can_remove_database_prefix()

FILE: tests/Unit/Generators/MySQLTableGeneratorTest.php
  class MySQLTableGeneratorTest (line 9) | class MySQLTableGeneratorTest extends TestCase
    method tearDown (line 11) | public function tearDown(): void
    method assertSchemaHas (line 19) | private function assertSchemaHas($str, $schema)
    method test_runs_correctly (line 24) | public function test_runs_correctly()
    method test_self_referential_foreign_key (line 41) | public function test_self_referential_foreign_key()
    method cleanUpMigrations (line 56) | private function cleanUpMigrations($path)
    method test_writes (line 66) | public function test_writes()
    method test_cleans_up_regular_morphs (line 88) | public function test_cleans_up_regular_morphs()
    method test_doesnt_clean_up_morph_looking_columns (line 101) | public function test_doesnt_clean_up_morph_looking_columns()
    method test_cleans_up_uuid_morphs (line 114) | public function test_cleans_up_uuid_morphs()
    method test_cleans_up_uuid_morphs_nullable (line 127) | public function test_cleans_up_uuid_morphs_nullable()
    method test_doesnt_clean_non_auto_inc_id_to_laravel_method (line 140) | public function test_doesnt_clean_non_auto_inc_id_to_laravel_method()
    method test_does_clean_auto_inc_int_to_laravel_method (line 151) | public function test_does_clean_auto_inc_int_to_laravel_method()
    method test_does_clean_auto_inc_big_int_to_laravel_method (line 162) | public function test_does_clean_auto_inc_big_int_to_laravel_method()
    method test_doesnt_clean_timestamps_with_use_current (line 173) | public function test_doesnt_clean_timestamps_with_use_current()
    method test_doesnt_clean_timestamps_with_use_current_on_update (line 185) | public function test_doesnt_clean_timestamps_with_use_current_on_update()
    method test_doesnt_clean_timestamps_with_use_defined_datatype_on_timestamp_configuration (line 197) | public function test_doesnt_clean_timestamps_with_use_defined_datatype...
    method test_removes_index_from_column_if_fk (line 210) | public function test_removes_index_from_column_if_fk()

FILE: tests/Unit/Generators/MySQLViewGeneratorTest.php
  class MySQLViewGeneratorTest (line 8) | class MySQLViewGeneratorTest extends TestCase
    method tearDown (line 10) | public function tearDown(): void
    method cleanUpMigrations (line 18) | private function cleanUpMigrations($path)
    method test_generates (line 28) | public function test_generates()
    method test_writes (line 35) | public function test_writes()

FILE: tests/Unit/Tokenizers/MySQL/ColumnTokenizerTest.php
  class ColumnTokenizerTest (line 8) | class ColumnTokenizerTest extends TestCase
    method test_it_tokenizes_a_not_null_varchar_column (line 11) | public function test_it_tokenizes_a_not_null_varchar_column()
    method test_it_tokenizes_a_null_varchar_column (line 25) | public function test_it_tokenizes_a_null_varchar_column()
    method test_it_tokenizes_a_null_varchar_default_value_column (line 39) | public function test_it_tokenizes_a_null_varchar_default_value_column()
    method test_it_tokenizes_a_null_varchar_default_value_null_column (line 54) | public function test_it_tokenizes_a_null_varchar_default_value_null_co...
    method test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_with_setting (line 69) | public function test_it_tokenizes_a_null_varchar_default_value_null_co...
    method test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_without_setting (line 85) | public function test_it_tokenizes_a_null_varchar_default_value_null_co...
    method test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_apostrophe (line 104) | public function test_it_tokenizes_a_null_varchar_default_value_null_co...
    method test_it_tokenizes_a_null_varchar_default_value_null_column_with_comment_quotation (line 120) | public function test_it_tokenizes_a_null_varchar_default_value_null_co...
    method test_it_tokenizes_varchar_with_default_and_comment (line 136) | public function test_it_tokenizes_varchar_with_default_and_comment()
    method test_it_tokenizes_varchar_with_default_empty_string_and_comment (line 144) | public function test_it_tokenizes_varchar_with_default_empty_string_an...
    method test_it_tokenizes_varchar_with_default_empty_string_and_comment_with_apostrophe (line 152) | public function test_it_tokenizes_varchar_with_default_empty_string_an...
    method test_it_tokenizes_varchar_with_boolean_literal_default (line 160) | public function test_it_tokenizes_varchar_with_boolean_literal_default()
    method test_it_tokenizes_char_column_with_character_and_collation (line 169) | public function test_it_tokenizes_char_column_with_character_and_colla...
    method test_it_tokenizes_a_not_null_tinytext_column (line 187) | public function test_it_tokenizes_a_not_null_tinytext_column()
    method test_it_tokenizes_a_null_tinytext_column (line 201) | public function test_it_tokenizes_a_null_tinytext_column()
    method test_it_tokenizes_a_not_null_text_column (line 215) | public function test_it_tokenizes_a_not_null_text_column()
    method test_it_tokenizes_a_null_text_column (line 229) | public function test_it_tokenizes_a_null_text_column()
    method test_it_tokenizes_a_not_null_mediumtext_column (line 243) | public function test_it_tokenizes_a_not_null_mediumtext_column()
    method test_it_tokenizes_a_null_mediumtext_column (line 257) | public function test_it_tokenizes_a_null_mediumtext_column()
    method test_it_tokenizes_a_not_null_longtext_column (line 271) | public function test_it_tokenizes_a_not_null_longtext_column()
    method test_it_tokenizes_a_null_longtext_column (line 285) | public function test_it_tokenizes_a_null_longtext_column()
    method test_it_tokenizes_a_not_null_smallint_without_param_column (line 302) | public function test_it_tokenizes_a_not_null_smallint_without_param_co...
    method test_it_tokenizes_a_not_null_smallint_with_param_column (line 316) | public function test_it_tokenizes_a_not_null_smallint_with_param_column()
    method test_it_tokenizes_a_not_null_unsigned_smallint_column (line 330) | public function test_it_tokenizes_a_not_null_unsigned_smallint_column()
    method test_it_tokenizes_a_nullable_big_int_column (line 345) | public function test_it_tokenizes_a_nullable_big_int_column()
    method test_it_tokenizes_a_primary_auto_inc_int_column (line 361) | public function test_it_tokenizes_a_primary_auto_inc_int_column()
    method test_definition_config (line 377) | public function test_definition_config()
    method test_it_tokenizes_float_without_params_column (line 389) | public function test_it_tokenizes_float_without_params_column()
    method test_it_tokenizes_float_with_params_column (line 402) | public function test_it_tokenizes_float_with_params_column()
    method test_it_tokenizes_float_null_without_params_column (line 417) | public function test_it_tokenizes_float_null_without_params_column()
    method test_it_tokenizes_float_null_with_params_column (line 430) | public function test_it_tokenizes_float_null_with_params_column()
    method test_it_tokenizes_float_without_params_default_value_column (line 445) | public function test_it_tokenizes_float_without_params_default_value_c...
    method test_it_tokenizes_float_with_params_default_value_column (line 459) | public function test_it_tokenizes_float_with_params_default_value_colu...
    method test_it_tokenizes_float_null_without_params_default_value_column (line 475) | public function test_it_tokenizes_float_null_without_params_default_va...
    method test_it_tokenizes_float_null_with_params_default_value_column (line 489) | public function test_it_tokenizes_float_null_with_params_default_value...
    method test_it_tokenizes_a_not_null_decimal_column (line 508) | public function test_it_tokenizes_a_not_null_decimal_column()
    method test_it_tokenizes_a_not_null_unsigned_decimal_column (line 524) | public function test_it_tokenizes_a_not_null_unsigned_decimal_column()
    method test_it_tokenizes_a_not_null_decimal_with_default_value_column (line 541) | public function test_it_tokenizes_a_not_null_decimal_with_default_valu...
    method test_it_tokenizes_a_not_null_unsigned_decimal_with_default_value_column (line 558) | public function test_it_tokenizes_a_not_null_unsigned_decimal_with_def...
    method test_it_tokenizes_a_not_null_double_column (line 579) | public function test_it_tokenizes_a_not_null_double_column()
    method test_it_tokenizes_a_not_null_unsigned_double_column (line 595) | public function test_it_tokenizes_a_not_null_unsigned_double_column()
    method test_it_tokenizes_a_not_null_double_with_default_value_column (line 612) | public function test_it_tokenizes_a_not_null_double_with_default_value...
    method test_it_tokenizes_a_not_null_unsigned_double_with_default_value_column (line 629) | public function test_it_tokenizes_a_not_null_unsigned_double_with_defa...
    method test_it_tokenizes_a_not_null_datetime_column (line 650) | public function test_it_tokenizes_a_not_null_datetime_column()
    method test_it_tokenizes_a_not_null_datetime_default_now_column (line 666) | public function test_it_tokenizes_a_not_null_datetime_default_now_colu...
    method test_it_tokenizes_a_null_datetime_column (line 682) | public function test_it_tokenizes_a_null_datetime_column()
    method test_it_tokenizes_a_null_default_value_datetime_column (line 698) | public function test_it_tokenizes_a_null_default_value_datetime_column()
    method test_it_tokenizes_a_null_default_value_now_datetime_column (line 714) | public function test_it_tokenizes_a_null_default_value_now_datetime_co...
    method test_it_tokenizes_a_null_default_value_now_and_on_update_datetime_column (line 730) | public function test_it_tokenizes_a_null_default_value_now_and_on_upda...
    method test_it_tokenizes_a_not_null_timestamp_column (line 749) | public function test_it_tokenizes_a_not_null_timestamp_column()
    method test_it_tokenizes_a_null_timestamp_column (line 765) | public function test_it_tokenizes_a_null_timestamp_column()
    method test_it_tokenizes_a_not_null_use_current_timestamp_timestamp_column (line 781) | public function test_it_tokenizes_a_not_null_use_current_timestamp_tim...
    method test_it_tokenizes_a_not_null_default_value_timestamp_column (line 797) | public function test_it_tokenizes_a_not_null_default_value_timestamp_c...
    method test_it_tokenizes_a_null_default_value_timestamp_column (line 813) | public function test_it_tokenizes_a_null_default_value_timestamp_column()
    method test_it_tokenizes_enum_column (line 832) | public function test_it_tokenizes_enum_column()
    method test_it_tokenizes_enum_column_with_upper_case_values (line 847) | public function test_it_tokenizes_enum_column_with_upper_case_values()
    method test_it_tokenizes_not_null_enum_column (line 862) | public function test_it_tokenizes_not_null_enum_column()
    method test_it_tokenizes_enum_with_default_value_column (line 877) | public function test_it_tokenizes_enum_with_default_value_column()
    method test_it_tokenizes_not_null_enum_with_default_value_column (line 893) | public function test_it_tokenizes_not_null_enum_with_default_value_col...
    method test_it_tokenizes_enum_with_spaces (line 909) | public function test_it_tokenizes_enum_with_spaces()
    method test_it_tokenizes_enum_with_special_characters (line 917) | public function test_it_tokenizes_enum_with_special_characters()
    method test_it_tokenizes_enum_with_empty_string (line 925) | public function test_it_tokenizes_enum_with_empty_string()
    method test_it_tokenizes_point_column (line 936) | public function test_it_tokenizes_point_column()
    method test_it_tokenizes_multipoint_column (line 947) | public function test_it_tokenizes_multipoint_column()
    method test_it_tokenizes_polygon_column (line 961) | public function test_it_tokenizes_polygon_column()
    method test_it_tokenizes_multipolygon_column (line 972) | public function test_it_tokenizes_multipolygon_column()
    method test_it_tokenizes_geometry_column (line 986) | public function test_it_tokenizes_geometry_column()
    method test_it_tokenizes_geometry_collection_column (line 996) | public function test_it_tokenizes_geometry_collection_column()
    method test_it_tokenizes_set_column (line 1009) | public function test_it_tokenizes_set_column()
    method test_it_tokenizes_uuid_column (line 1026) | public function test_it_tokenizes_uuid_column()
    method test_it_tokenizes_date_column (line 1043) | public function test_it_tokenizes_date_column()
    method test_it_tokenizes_year_column (line 1057) | public function test_it_tokenizes_year_column()
    method test_it_tokenizes_time_column (line 1071) | public function test_it_tokenizes_time_column()
    method test_it_tokenizes_linestring_column (line 1088) | public function test_it_tokenizes_linestring_column()
    method test_it_tokenizes_multilinestring_column (line 1102) | public function test_it_tokenizes_multilinestring_column()
    method test_it_tokenizes_generated_as_column (line 1118) | public function test_it_tokenizes_generated_as_column()
    method test_it_tokenizes_generated_as_column_example (line 1131) | public function test_it_tokenizes_generated_as_column_example()
    method test_it_tokenizes_virtual_as_column (line 1144) | public function test_it_tokenizes_virtual_as_column()

FILE: tests/Unit/Tokenizers/MySQL/IndexTokenizerTest.php
  class IndexTokenizerTest (line 8) | class IndexTokenizerTest extends TestCase
    method test_it_tokenizes_simple_index (line 11) | public function test_it_tokenizes_simple_index()
    method test_it_doesnt_use_index_name (line 22) | public function test_it_doesnt_use_index_name()
    method test_it_tokenizes_simple_primary_key (line 38) | public function test_it_tokenizes_simple_primary_key()
    method test_it_tokenizes_two_column_primary_key (line 49) | public function test_it_tokenizes_two_column_primary_key()
    method test_it_tokenizes_simple_unique_key (line 65) | public function test_it_tokenizes_simple_unique_key()
    method test_it_doesnt_use_unique_key_index_name (line 76) | public function test_it_doesnt_use_unique_key_index_name()
    method test_it_tokenizes_two_column_unique_key (line 89) | public function test_it_tokenizes_two_column_unique_key()
    method test_it_tokenizes_two_column_unique_key_and_doesnt_use_index_name (line 102) | public function test_it_tokenizes_two_column_unique_key_and_doesnt_use...
    method test_it_tokenizes_foreign_key (line 120) | public function test_it_tokenizes_foreign_key()
    method test_it_tokenizes_foreign_key_doesnt_use_index_name (line 135) | public function test_it_tokenizes_foreign_key_doesnt_use_index_name()
    method test_it_tokenizes_foreign_key_with_update (line 152) | public function test_it_tokenizes_foreign_key_with_update()
    method test_it_tokenizes_foreign_key_with_delete (line 167) | public function test_it_tokenizes_foreign_key_with_delete()
    method test_it_tokenizes_foreign_key_with_update_and_delete (line 182) | public function test_it_tokenizes_foreign_key_with_update_and_delete()
    method test_it_tokenizes_foreign_key_with_multiple_columns (line 197) | public function test_it_tokenizes_foreign_key_with_multiple_columns()
    method test_it_tokenizes_foreign_key_with_update_restrict (line 211) | public function test_it_tokenizes_foreign_key_with_update_restrict()
    method test_it_tokenizes_foreign_key_with_update_set_null (line 226) | public function test_it_tokenizes_foreign_key_with_update_set_null()
    method test_it_tokenizes_foreign_key_with_update_set_default (line 241) | public function test_it_tokenizes_foreign_key_with_update_set_default()
    method test_it_tokenizes_simple_fulltext_index (line 259) | public function test_it_tokenizes_simple_fulltext_index()
    method test_it_tokenizes_fulltext_index_without_using_index_name (line 272) | public function test_it_tokenizes_fulltext_index_without_using_index_n...
    method test_it_tokenizes_multi_column_fulltext_index (line 285) | public function test_it_tokenizes_multi_column_fulltext_index()
    method test_it_tokenizes_multi_column_fulltext_index_without_using_index_name (line 298) | public function test_it_tokenizes_multi_column_fulltext_index_without_...
    method test_it_tokenizes_simple_spatial_index (line 315) | public function test_it_tokenizes_simple_spatial_index()
    method test_it_tokenizes_spatial_index_without_using_index_name (line 328) | public function test_it_tokenizes_spatial_index_without_using_index_na...
    method test_it_tokenizes_multi_column_spatial_index (line 341) | public function test_it_tokenizes_multi_column_spatial_index()
    method test_it_tokenizes_multi_column_spatial_index_without_using_index_name (line 354) | public function test_it_tokenizes_multi_column_spatial_index_without_u...
    method test_it_escapes_single_quotes_in_index_names (line 371) | public function test_it_escapes_single_quotes_in_index_names()
    method test_it_escapes_single_quotes_in_fulltext_index_names (line 381) | public function test_it_escapes_single_quotes_in_fulltext_index_names()
    method test_it_escapes_single_quotes_in_spatial_index_names (line 391) | public function test_it_escapes_single_quotes_in_spatial_index_names()
    method test_it_escapes_single_quotes_in_column_names (line 401) | public function test_it_escapes_single_quotes_in_column_names()
    method test_it_escapes_backslashes_in_index_names (line 411) | public function test_it_escapes_backslashes_in_index_names()
    method test_it_escapes_backslashes_and_quotes_in_unique_index_names (line 421) | public function test_it_escapes_backslashes_and_quotes_in_unique_index...
    method test_it_escapes_single_quotes_in_foreign_key_index_names (line 431) | public function test_it_escapes_single_quotes_in_foreign_key_index_nam...

FILE: tests/Unit/ValueToStringTest.php
  class ValueToStringTest (line 8) | class ValueToStringTest extends TestCase
    method test_it_returns_null_for_null_value (line 11) | public function test_it_returns_null_for_null_value()
    method test_it_returns_integer_as_is (line 16) | public function test_it_returns_integer_as_is()
    method test_it_returns_float_as_is (line 21) | public function test_it_returns_float_as_is()
    method test_it_quotes_string_value (line 26) | public function test_it_quotes_string_value()
    method test_it_uses_double_quotes_when_specified (line 31) | public function test_it_uses_double_quotes_when_specified()
    method test_it_returns_array_as_bracketed_list (line 36) | public function test_it_returns_array_as_bracketed_list()
    method test_it_singles_out_array_when_option_is_true (line 41) | public function test_it_singles_out_array_when_option_is_true()
    method test_it_does_not_single_out_multi_element_array (line 46) | public function test_it_does_not_single_out_multi_element_array()
    method test_escape_escapes_single_quotes_by_default (line 54) | public function test_escape_escapes_single_quotes_by_default()
    method test_escape_escapes_double_quotes_when_specified (line 59) | public function test_escape_escapes_double_quotes_when_specified()
    method test_escape_escapes_backslashes (line 64) | public function test_escape_escapes_backslashes()
    method test_escape_escapes_both_backslashes_and_single_quotes (line 69) | public function test_escape_escapes_both_backslashes_and_single_quotes()
    method test_escape_escapes_both_backslashes_and_double_quotes (line 74) | public function test_escape_escapes_both_backslashes_and_double_quotes()
    method test_escape_handles_empty_string (line 79) | public function test_escape_handles_empty_string()
    method test_make_escapes_single_quotes_in_string (line 87) | public function test_make_escapes_single_quotes_in_string()
    method test_make_escapes_single_quotes_in_array (line 99) | public function test_make_escapes_single_quotes_in_array()
    method test_make_escapes_single_quotes_in_singled_out_array (line 107) | public function test_make_escapes_single_quotes_in_singled_out_array()
    method test_make_escapes_backslashes_followed_by_quotes (line 115) | public function test_make_escapes_backslashes_followed_by_quotes()
    method test_make_escapes_complex_injection_attempt (line 127) | public function test_make_escapes_complex_injection_attempt()
    method test_make_escapes_multiple_quotes_in_array (line 136) | public function test_make_escapes_multiple_quotes_in_array()
    method test_cast_float_creates_casted_value (line 147) | public function test_cast_float_creates_casted_value()
    method test_cast_binary_creates_casted_value (line 153) | public function test_cast_binary_creates_casted_value()
    method test_is_casted_value_detects_float (line 159) | public function test_is_casted_value_detects_float()
    method test_is_casted_value_detects_binary (line 164) | public function test_is_casted_value_detects_binary()
    method test_is_casted_value_returns_false_for_regular_string (line 169) | public function test_is_casted_value_returns_false_for_regular_string()
    method test_make_handles_casted_float (line 174) | public function test_make_handles_casted_float()
    method test_make_handles_casted_binary (line 182) | public function test_make_handles_casted_binary()
Condensed preview — 66 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (255K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 119,
    "preview": "# These are supported funding model platforms\n\ngithub: [bennett-treptow]\ncustom: ['https://buymeacoffee.com/btreptow']\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 671,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: \"[BUG]\"\nlabels: ''\nassignees: ''\n\n---\n\n**Package V"
  },
  {
    "path": ".github/workflows/php.yml",
    "chars": 942,
    "preview": "name: PHP Composer\n\non:\n  push:\n    branches:\n      - main\n  pull_request:\n    branches:\n      - main\n\njobs:\n  build:\n  "
  },
  {
    "path": ".gitignore",
    "chars": 63,
    "preview": "vendor/\n.idea\n.phpunit.result.cache\n.php_cs.cache\ncomposer.lock"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 5632,
    "preview": "# Version 3.1.6\n### New Modifier\n`useCurrentOnUpdate` has been implemented\n### Bugfix\nIssue #27 - `useCurrent` on `times"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2021 Bennett Treptow\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 14151,
    "preview": "# Laravel Migration Generator\n![Latest Version on Packagist](https://img.shields.io/packagist/v/bennett-treptow/laravel-"
  },
  {
    "path": "UPGRADE.md",
    "chars": 5934,
    "preview": "## Upgrade from 3.* to 4.0\n\n### Foreign Key Sorting\nNew foreign key dependency sorting options, available as an env vari"
  },
  {
    "path": "composer.json",
    "chars": 1530,
    "preview": "{\n    \"name\": \"bennett-treptow/laravel-migration-generator\",\n    \"description\": \"Generate migrations from existing datab"
  },
  {
    "path": "config/laravel-migration-generator.php",
    "chars": 2898,
    "preview": "<?php\n\nreturn [\n    'run_after_migrations' => env('LMG_RUN_AFTER_MIGRATIONS', false),\n    'clear_output_path' => env('LM"
  },
  {
    "path": "docs/_config.yml",
    "chars": 174,
    "preview": "remote_theme: pmarsceill/just-the-docs\ncompress_html:\n  ignore:\n    envs: all\naux_links:\n  \"View on GitHub\":\n    - \"//gi"
  },
  {
    "path": "docs/command.md",
    "chars": 65,
    "preview": "---\nlayout: default\ntitle: Running the Generator\nnav_order: 3\n---"
  },
  {
    "path": "docs/config.md",
    "chars": 50,
    "preview": "---\nlayout: default\ntitle: Config\nnav_order: 1\n---"
  },
  {
    "path": "docs/index.md",
    "chars": 664,
    "preview": "---\nlayout: default\ntitle: Laravel Migration Generator\nnav_order: 0\n---\n# Laravel Migration Generator\n\nGenerate migratio"
  },
  {
    "path": "docs/stubs.md",
    "chars": 49,
    "preview": "---\nlayout: default\ntitle: Stubs\nnav_order: 2\n---"
  },
  {
    "path": "phpunit.xml",
    "chars": 174,
    "preview": "<phpunit>\n    <testsuites>\n        <testsuite name=\"Unit\">\n            <directory suffix=\"Test.php\">./tests/Unit</direct"
  },
  {
    "path": "pint.json",
    "chars": 25,
    "preview": "{\n  \"preset\": \"laravel\"\n}"
  },
  {
    "path": "src/Commands/GenerateMigrationsCommand.php",
    "chars": 3043,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Commands;\n\nuse Illuminate\\Console\\Command;\nuse Illuminate\\Support\\Arr;\nuse Il"
  },
  {
    "path": "src/Definitions/ColumnDefinition.php",
    "chars": 12868,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Definitions;\n\nuse Illuminate\\Support\\Str;\nuse LaravelMigrationGenerator\\Helpe"
  },
  {
    "path": "src/Definitions/IndexDefinition.php",
    "chars": 5454,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Definitions;\n\nuse LaravelMigrationGenerator\\Helpers\\ValueToString;\nuse Larave"
  },
  {
    "path": "src/Definitions/TableDefinition.php",
    "chars": 3258,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Definitions;\n\nuse LaravelMigrationGenerator\\Formatters\\TableFormatter;\n\nclass"
  },
  {
    "path": "src/Definitions/ViewDefinition.php",
    "chars": 1247,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Definitions;\n\nuse LaravelMigrationGenerator\\Formatters\\ViewFormatter;\n\nclass "
  },
  {
    "path": "src/Formatters/TableFormatter.php",
    "chars": 6889,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Formatters;\n\nuse Illuminate\\Support\\Str;\nuse LaravelMigrationGenerator\\Defini"
  },
  {
    "path": "src/Formatters/ViewFormatter.php",
    "chars": 2914,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Formatters;\n\nuse Illuminate\\Support\\Str;\nuse LaravelMigrationGenerator\\Defini"
  },
  {
    "path": "src/GeneratorManagers/BaseGeneratorManager.php",
    "chars": 4563,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\GeneratorManagers;\n\nuse LaravelMigrationGenerator\\Definitions\\TableDefinition"
  },
  {
    "path": "src/GeneratorManagers/Interfaces/GeneratorManagerInterface.php",
    "chars": 610,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\GeneratorManagers\\Interfaces;\n\nuse LaravelMigrationGenerator\\Definitions\\Tabl"
  },
  {
    "path": "src/GeneratorManagers/MySQLGeneratorManager.php",
    "chars": 1638,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\GeneratorManagers;\n\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support"
  },
  {
    "path": "src/Generators/BaseTableGenerator.php",
    "chars": 1976,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators;\n\nuse LaravelMigrationGenerator\\Definitions\\TableDefinition;\nuse L"
  },
  {
    "path": "src/Generators/BaseViewGenerator.php",
    "chars": 916,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators;\n\nuse LaravelMigrationGenerator\\Definitions\\ViewDefinition;\nuse La"
  },
  {
    "path": "src/Generators/Concerns/CleansUpColumnIndices.php",
    "chars": 2408,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Concerns;\n\nuse LaravelMigrationGenerator\\Generators\\BaseTableGener"
  },
  {
    "path": "src/Generators/Concerns/CleansUpForeignKeyIndices.php",
    "chars": 1482,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Concerns;\n\nuse LaravelMigrationGenerator\\Generators\\BaseTableGener"
  },
  {
    "path": "src/Generators/Concerns/CleansUpMorphColumns.php",
    "chars": 2546,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Concerns;\n\nuse Illuminate\\Support\\Str;\nuse LaravelMigrationGenerat"
  },
  {
    "path": "src/Generators/Concerns/CleansUpTimestampsColumn.php",
    "chars": 1377,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Concerns;\n\nuse LaravelMigrationGenerator\\Generators\\BaseTableGener"
  },
  {
    "path": "src/Generators/Concerns/WritesToFile.php",
    "chars": 463,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Concerns;\n\ntrait WritesToFile\n{\n    public function write(string $"
  },
  {
    "path": "src/Generators/Concerns/WritesViewsToFile.php",
    "chars": 1932,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Concerns;\n\nuse Illuminate\\Support\\Str;\nuse LaravelMigrationGenerat"
  },
  {
    "path": "src/Generators/Interfaces/TableGeneratorInterface.php",
    "chars": 418,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Interfaces;\n\nuse LaravelMigrationGenerator\\Definitions\\TableDefini"
  },
  {
    "path": "src/Generators/Interfaces/ViewGeneratorInterface.php",
    "chars": 217,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\Interfaces;\n\ninterface ViewGeneratorInterface\n{\n    public static "
  },
  {
    "path": "src/Generators/MySQL/TableGenerator.php",
    "chars": 1699,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\MySQL;\n\nuse Illuminate\\Support\\Facades\\DB;\nuse Illuminate\\Support\\"
  },
  {
    "path": "src/Generators/MySQL/ViewGenerator.php",
    "chars": 1417,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Generators\\MySQL;\n\nuse Illuminate\\Support\\Facades\\DB;\nuse LaravelMigrationGen"
  },
  {
    "path": "src/Helpers/ConfigResolver.php",
    "chars": 1060,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Helpers;\n\nclass ConfigResolver\n{\n    protected static function resolver(strin"
  },
  {
    "path": "src/Helpers/DependencyResolver.php",
    "chars": 4221,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Helpers;\n\nuse LaravelMigrationGenerator\\Definitions\\IndexDefinition;\nuse Lara"
  },
  {
    "path": "src/Helpers/Formatter.php",
    "chars": 1865,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Helpers;\n\nclass Formatter\n{\n    private array $lines = [];\n\n    private strin"
  },
  {
    "path": "src/Helpers/ValueToString.php",
    "chars": 2404,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Helpers;\n\nuse Illuminate\\Support\\Str;\n\nclass ValueToString\n{\n    public stati"
  },
  {
    "path": "src/Helpers/WritableTrait.php",
    "chars": 320,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Helpers;\n\ntrait WritableTrait\n{\n    public bool $writable = true;\n\n    public"
  },
  {
    "path": "src/LaravelMigrationGeneratorProvider.php",
    "chars": 1267,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator;\n\nuse Illuminate\\Database\\Events\\MigrationsEnded;\nuse Illuminate\\Support\\Faca"
  },
  {
    "path": "src/Tokenizers/BaseColumnTokenizer.php",
    "chars": 583,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Tokenizers;\n\nuse LaravelMigrationGenerator\\Definitions\\ColumnDefinition;\nuse "
  },
  {
    "path": "src/Tokenizers/BaseIndexTokenizer.php",
    "chars": 576,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Tokenizers;\n\nuse LaravelMigrationGenerator\\Definitions\\IndexDefinition;\nuse L"
  },
  {
    "path": "src/Tokenizers/BaseTokenizer.php",
    "chars": 2881,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Tokenizers;\n\nabstract class BaseTokenizer\n{\n    protected $tokens = [];\n\n    "
  },
  {
    "path": "src/Tokenizers/Interfaces/ColumnTokenizerInterface.php",
    "chars": 258,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Tokenizers\\Interfaces;\n\nuse LaravelMigrationGenerator\\Definitions\\ColumnDefin"
  },
  {
    "path": "src/Tokenizers/Interfaces/IndexTokenizerInterface.php",
    "chars": 255,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Tokenizers\\Interfaces;\n\nuse LaravelMigrationGenerator\\Definitions\\IndexDefini"
  },
  {
    "path": "src/Tokenizers/MySQL/ColumnTokenizer.php",
    "chars": 12265,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Tokenizers\\MySQL;\n\nuse Illuminate\\Database\\Schema\\Builder;\nuse Illuminate\\Sup"
  },
  {
    "path": "src/Tokenizers/MySQL/IndexTokenizer.php",
    "chars": 3825,
    "preview": "<?php\n\nnamespace LaravelMigrationGenerator\\Tokenizers\\MySQL;\n\nuse LaravelMigrationGenerator\\Tokenizers\\BaseIndexTokenize"
  },
  {
    "path": "stubs/table-create.stub",
    "chars": 76,
    "preview": "Schema::create('[TableName]', function (Blueprint $table) {\n    [Schema]\n});"
  },
  {
    "path": "stubs/table-modify.stub",
    "chars": 75,
    "preview": "Schema::table('[TableName]', function (Blueprint $table) {\n    [Schema]\n});"
  },
  {
    "path": "stubs/table.stub",
    "chars": 452,
    "preview": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Fa"
  },
  {
    "path": "stubs/view.stub",
    "chars": 769,
    "preview": "<?php\n\nuse Illuminate\\Database\\Migrations\\Migration;\nuse Illuminate\\Database\\Schema\\Blueprint;\nuse Illuminate\\Support\\Fa"
  },
  {
    "path": "tests/TestCase.php",
    "chars": 646,
    "preview": "<?php\n\nnamespace Tests;\n\nuse LaravelMigrationGenerator\\LaravelMigrationGeneratorProvider;\n\nclass TestCase extends \\Orche"
  },
  {
    "path": "tests/Unit/ColumnDefinitionTest.php",
    "chars": 1210,
    "preview": "<?php\n\nnamespace Tests\\Unit;\n\nuse LaravelMigrationGenerator\\Definitions\\ColumnDefinition;\nuse LaravelMigrationGenerator\\"
  },
  {
    "path": "tests/Unit/DependencyResolverTest.php",
    "chars": 4846,
    "preview": "<?php\n\nnamespace Tests\\Unit;\n\nuse LaravelMigrationGenerator\\Definitions\\ColumnDefinition;\nuse LaravelMigrationGenerator\\"
  },
  {
    "path": "tests/Unit/FormatterTest.php",
    "chars": 1280,
    "preview": "<?php\n\nnamespace Tests\\Unit;\n\nuse LaravelMigrationGenerator\\Helpers\\Formatter;\nuse Tests\\TestCase;\n\nclass FormatterTest "
  },
  {
    "path": "tests/Unit/GeneratorManagers/MySQLGeneratorManagerTest.php",
    "chars": 3933,
    "preview": "<?php\n\nnamespace Tests\\Unit\\GeneratorManagers;\n\nuse Illuminate\\Support\\Facades\\DB;\nuse LaravelMigrationGenerator\\Definit"
  },
  {
    "path": "tests/Unit/Generators/MySQLTableGeneratorTest.php",
    "chars": 9546,
    "preview": "<?php\n\nnamespace Tests\\Unit\\Generators;\n\nuse Illuminate\\Support\\Facades\\Config;\nuse LaravelMigrationGenerator\\Generators"
  },
  {
    "path": "tests/Unit/Generators/MySQLViewGeneratorTest.php",
    "chars": 1780,
    "preview": "<?php\n\nnamespace Tests\\Unit\\Generators;\n\nuse LaravelMigrationGenerator\\Generators\\MySQL\\ViewGenerator;\nuse Tests\\TestCas"
  },
  {
    "path": "tests/Unit/Tokenizers/MySQL/ColumnTokenizerTest.php",
    "chars": 63664,
    "preview": "<?php\n\nnamespace Tests\\Unit\\Tokenizers\\MySQL;\n\nuse LaravelMigrationGenerator\\Tokenizers\\MySQL\\ColumnTokenizer;\nuse Tests"
  },
  {
    "path": "tests/Unit/Tokenizers/MySQL/IndexTokenizerTest.php",
    "chars": 23590,
    "preview": "<?php\n\nnamespace Tests\\Tokenizers\\MySQL;\n\nuse LaravelMigrationGenerator\\Tokenizers\\MySQL\\IndexTokenizer;\nuse Tests\\TestC"
  },
  {
    "path": "tests/Unit/ValueToStringTest.php",
    "chars": 5709,
    "preview": "<?php\n\nnamespace Tests\\Unit;\n\nuse LaravelMigrationGenerator\\Helpers\\ValueToString;\nuse Tests\\TestCase;\n\nclass ValueToStr"
  }
]

About this extraction

This page contains the full source code of the bennett-treptow/laravel-migration-generator GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 66 files (237.2 KB), approximately 57.5k tokens, and a symbol index with 444 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!