Laravel Artisan Command: Truncate Table and All Related Tables

rafaelogic

Rafa Rafael

Posted on June 29, 2024

Laravel Artisan Command: Truncate Table and All Related Tables

Managing database tables often involves performing operations like truncating tables, especially during development or testing phases. Truncating a table means deleting all its records while keeping its structure intact. However, when dealing with tables having foreign key relationships, truncating them can become cumbersome.

This blog post introduces a custom Laravel Artisan command that efficiently handles truncating a specified table and all its related tables. The command is useful when you need to reset the database state by clearing out all records, ensuring no foreign key constraints are violated.

The Command

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;

class TruncateTableAndAllRelationshipsTableCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'table:truncate-all {table}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Truncates the specified table and all dependent tables with foreign key references.';

    /**
     * Execute the console command.
     */

    public function handle()
    {
        $table = $this->argument('table');

        $this->info(PHP_EOL."Truncating $table and the following related tables:");

        // Disable foreign key checks
        DB::statement('SET FOREIGN_KEY_CHECKS=0;');

        // Get related tables via foreign keys
        $relatedTables = $this->getReferencingTablesFrom($table);

        if (count($relatedTables)) {
            // Truncate the related tables
            foreach ($relatedTables as $relatedTable) {
                if ($relatedTable != $table) {
                    DB::table($relatedTable)->truncate();
                    $this->info("Table {$relatedTable} truncated.");
                }
            }
        }

        // Truncate the specified table
        DB::table($table)->truncate();
        $this->info("Table {$table} truncated.");

        // Re-enable foreign key checks
        DB::statement('SET FOREIGN_KEY_CHECKS=1;');

        $this->info(PHP_EOL."Done!");
        return 0;
    }

    protected function getReferencingTablesFrom(string $table)
    {
        $referencingTables = [];

        // Get all tables in the database
        $tables = Schema::getConnection()->getDoctrineSchemaManager()->listTableNames();
        $refTable = Str::singular($table);

        foreach ($tables as $table) {
            // Check if the table has a referencing column
            if (Schema::hasColumn($table, $refTable.'_uuid')) {
                // Assume it is a foreign key referencing the specified table
                $referencingTables[] = $table;
            }
        }

        return $referencingTables;
    }
}
Enter fullscreen mode Exit fullscreen mode

Avoiding Headache

This command is useful if the dependent table is referencing only a foreign key of the specified table otherwise you need to re-strategize and tweak the codes to avoid truncating other tables that other dependents.

Great, now that we're ready to proceed, to see the backbone of the command.

Understanding the Command

The provided code defines a console command named TruncateTableAndAllRelationshipsTableCommand. This command takes a table name as an argument, finds all related tables through foreign key references, and truncates both the specified table and its related tables. Let’s break down the key components of this command.

Handling the Command Execution

public function handle()
{
    $table = $this->argument('table');

    $this->info(PHP_EOL."Truncating $table and the following related tables:");

    // Disable foreign key checks
    DB::statement('SET FOREIGN_KEY_CHECKS=0;');

    // Get related tables via foreign keys
    $relatedTables = $this->getReferencingTablesFrom($table);

    if (count($relatedTables)) {
        // Truncate the related tables
        foreach ($relatedTables as $relatedTable) {
            if ($relatedTable != $table) {
                DB::table($relatedTable)->truncate();
                $this->info("Table {$relatedTable} truncated.");
            }
        }
    }

    // Truncate the specified table
    DB::table($table)->truncate();
    $this->info("Table {$table} truncated.");

    // Re-enable foreign key checks
    DB::statement('SET FOREIGN_KEY_CHECKS=1;');

    $this->info(PHP_EOL."Done!");
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

The handle method is the entry point of the command execution. It performs the following steps:

  1. Retrieve the Table Name: Gets the table name from the command argument.
  2. Disable Foreign Key Checks: Temporarily disables foreign key checks to avoid constraint violations while truncating.
  3. Get Related Tables: Calls getReferencingTablesFrom method to find all tables referencing the specified table.
  4. Truncate Related Tables: Iterates over the related tables and truncates them.
  5. Truncate Specified Table: Truncates the specified table.
  6. Re-enable Foreign Key Checks: Re-enables foreign key checks after truncation.

Finding Related Tables

protected function getReferencingTablesFrom(string $table)
{
    $referencingTables = [];

    // Get all tables in the database
    $tables = Schema::getConnection()->getDoctrineSchemaManager()->listTableNames();
    $refTable = Str::singular($table);

    foreach ($tables as $table) {
        // Check if the table has a referencing column
        if (Schema::hasColumn($table, $refTable.'_uuid')) {
            // Assume it is a foreign key referencing the specified table
            $referencingTables[] = $table;
        }
    }

    return $referencingTables;
}
Enter fullscreen mode Exit fullscreen mode

The getReferencingTablesFrom method inspects all tables in the database to find those containing a column that likely references the specified table. It assumes that a column named {table}_uuid or you can name it {table}_id if you are not using uuid for indicating a foreign key relationship.

Example Usage

Let’s consider an example where you have the following tables:

  • users
  • posts (contains a user_uuid column referencing users)
  • comments (contains a post_uuid column referencing posts)

To truncate the users table and all related tables, you can run the following command:

php artisan table:truncate-all users
Enter fullscreen mode Exit fullscreen mode

This command will:

  1. Disable foreign key checks.
  2. Identify posts as a table related to users and comments as a table related to posts.
  3. Truncate comments, posts, and users.
  4. Re-enable foreign key checks.

Possible Scenarios

Testing and Development

During testing or development, you might need to reset your database state frequently. This command ensures all related data is cleared without violating foreign key constraints, making it easier to reset the database.

Data Migration

When performing data migration or restructuring, you may need to truncate tables and repopulate them with new data. This command helps in clearing the existing data while maintaining the integrity of foreign key relationships.

Bulk Data Deletion

In scenarios where you need to delete a large volume of data across multiple related tables, this command provides a clean and efficient way to achieve that.

Conclusion

The TruncateTableAndAllRelationshipsTableCommand is a powerful tool for managing database tables with foreign key relationships in Laravel. It simplifies the process of truncating tables and ensures data integrity by handling related tables automatically. This command is particularly useful in development, testing, and data migration scenarios. Implementing such a command can significantly streamline database management tasks, making your workflow more efficient and error-free.

💖 💪 🙅 🚩
rafaelogic
Rafa Rafael

Posted on June 29, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related