Search by

ramadan / custom-fresh

mmramadan496

A Laravel package to specify the tables that you do not want to drop while refreshing the database.

v2.2.0 2026-10-08 14:15 UTC

README

Custom Fresh

Latest Version Total Downloads PHP Laravel License

Custom Fresh offers fine-grained control over migrations within your Laravel project, enabling you to select which tables will not be dropped when refreshing the database.

Tip

Always consider upgrading the package to the latest version, which is the most stable release.

Installation

Install the package by using Composer:

composer require ramadan/custom-fresh

(Optional) publish the config file:

php artisan vendor:publish --tag=custom-fresh-config

Usage

After installing the package, you will see a new fresh:custom command.

Note

Since v1.2.0, the package scans your migration files more accurately, including nested folders, custom --path locations, and package migration paths registered through Laravel.

Refreshing migrations

You can exclude specific tables while refreshing the database inside your project:

php artisan fresh:custom users,foo

The same can be expressed with the --keep option (which can be combined with the positional argument):

php artisan fresh:custom --keep=users,personal_access_tokens

When nothing is passed and the config is empty, an interactive picker lists the discovered tables.

If a kept table is created in the same migration file as other tables (Laravel's default users / password_reset_tokens / sessions file), those sibling tables are preserved too.

Important

Do not forget always to use the -h of the command to check out all supported options.

Glob patterns

Anything containing *, ?, or […] is expanded with fnmatch against the database tables, so you can preserve whole groups at once:

php artisan fresh:custom "users,oauth_*,telescope_*"

Drop only some tables

You can invert the default and drop just a few tables, while everything else is preserved:

php artisan fresh:custom --drop=posts,comments

The --drop option can be combined with --keep or --preset. Explicit --drop always wins.

Keep tables without migrations

You can preserve tables that have no migration file (common for Laravel 11+ sessions, cache, or jobs):

php artisan fresh:custom --keep=users --keep-raw=sessions,cache

The same list can be set in keep_without_migrations inside the config.

Except

You can temporarily drop a table that is otherwise always kept:

php artisan fresh:custom --except=users

Presets

You can group tables in the config and apply them by name:

php artisan fresh:custom --preset=auth
'presets' => [
    'auth' => ['users', 'password_reset_tokens', 'sessions', 'personal_access_tokens'],
],

Pending alters

By default, new alter migrations that touch a kept table still run, so add_phone_to_users_table is applied even when users is preserved.

Use --freeze-schema to mark every migration for kept tables as already run:

php artisan fresh:custom users --freeze-schema

Related tables

Keeping a child table also preserves the parent tables it references, and keeping a parent also preserves its children:

php artisan fresh:custom posts
php artisan fresh:custom users

posts keeps users, and users keeps posts. The command tells you which related tables were added.

Pass --drop-referenced to refresh child tables of a kept parent. Parent tables that a kept child still references are never dropped, on any driver, so foreign keys stay intact:

php artisan fresh:custom users --drop-referenced

That keeps users and drops posts (then migrate recreates posts with its foreign key). php artisan fresh:custom posts --drop-referenced still keeps users, because dropping the parent would strip the constraint from posts.

Multiple connections

Pass --database= to target a non-default connection. The connection is also forwarded to the migrate command:

php artisan fresh:custom users --database=tenant

Per-connection overrides can be set under the connections key in the config.

Dry run

Use --explain to preview exactly what would happen without dropping a single table:

php artisan fresh:custom users --explain

It prints the resolved connection, the tables that would be preserved, the tables that would be dropped, the migration rows that would be re-inserted, and any pending alters that will still run.

The same plan can be printed as JSON:

php artisan fresh:custom users --explain --json

List tables

Use --list to inspect the tables the scanner sees and the migration files that touch them:

php artisan fresh:custom --list

The same can be expressed as JSON with --json:

php artisan fresh:custom --list --json

Seeding

Use --seed to re-run DatabaseSeeder after migrate. If you preserved tables with unique columns, that often inserts duplicates.

Use --seed-fresh to seed only the dropped tables, through the table_seeders map in the config:

php artisan fresh:custom --keep=users --seed-fresh
'table_seeders' => [
    'posts' => Database\Seeders\PostSeeder::class,
],

Views and types

Pass --drop-views and --drop-types to match Laravel's migrate:fresh when leftover views or PostgreSQL types would break the next run:

php artisan fresh:custom users --drop-views --drop-types

The --drop-types option is supported on PostgreSQL only.

Replace migrate:fresh

Set replace_migrate_fresh to true in the config. Then php artisan migrate:fresh delegates to fresh:custom whenever always_keep, patterns, or keep_without_migrations is set. Otherwise Laravel's original command still runs.

Configuration

Publishing the config (see above) gives you config/custom-fresh.php:

return [
    'always_keep' => ['users', 'personal_access_tokens'],
    'patterns'    => ['oauth_*', 'telescope_*'],
    'keep_without_migrations' => ['sessions', 'cache'],
    'presets' => [
        'auth' => ['users', 'password_reset_tokens', 'sessions', 'personal_access_tokens'],
    ],
    'table_seeders' => [
        'posts' => Database\Seeders\PostSeeder::class,
    ],
    'connections' => [
        'tenant' => [
            'always_keep' => ['tenant_settings'],
        ],
    ],
    'confirm_in' => ['production', 'staging'],
    'replace_migrate_fresh' => false,
];
  • always_keep — tables that are preserved on every run, even if you don't list them on the command line.
  • patterns — glob patterns expanded against the database on every run.
  • keep_without_migrations — tables preserved even when they have no migration file.
  • presets — named groups applied with --preset=.
  • table_seeders — dropped-table seeders used by --seed-fresh.
  • connections — extra lists merged when --database= matches the key.
  • confirm_in — environments where the command must ask for confirmation. Use --force to bypass.
  • replace_migrate_fresh — let migrate:fresh honor the lists above.

Events

Three events are dispatched during a run, perfect for backups, audit logs, or Slack notifications:

  • Ramadan\CustomFresh\Events\RefreshingDatabase — fired before any destructive work, with the resolved preserve list and migration rows.
  • Ramadan\CustomFresh\Events\TablesDropped — fired right after the drop step, with both the preserved and dropped tables.
  • Ramadan\CustomFresh\Events\DatabaseRefreshed — fired after the underlying migrate finishes successfully.

Credits

Support me

License

The MIT License (MIT).