Skip to content

Instantly share code, notes, and snippets.

@Razoxane
Created August 8, 2017 01:35
Show Gist options
  • Select an option

  • Save Razoxane/3bc74900b4eb5c983eb0927fa13b95f5 to your computer and use it in GitHub Desktop.

Select an option

Save Razoxane/3bc74900b4eb5c983eb0927fa13b95f5 to your computer and use it in GitHub Desktop.
Laravel - Create Index If Not Exists / Drop Index If Exists
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class LaravelConditionalIndexMigration extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('tablename', function (Blueprint $table) {
$sm = Schema::getConnection()->getDoctrineSchemaManager();
$doctrineTable = $sm->listTableDetails('tablename');
if (! $doctrineTable->hasIndex('singlecolumnindexname')) {
$table->index('column1', 'singlecolumnindexname');
}
if (! $doctrineTable->hasIndex('multicolumnindexname')) {
$table->index(['column2', 'column3'], 'multicolumnindexname');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('tablename', function (Blueprint $table) {
$sm = Schema::getConnection()->getDoctrineSchemaManager();
$doctrineTable = $sm->listTableDetails('tablename');
if ($doctrineTable->hasIndex('singlecolumnindexname')) {
$table->dropIndex('singlecolumnindexname');
}
if ($doctrineTable->hasIndex('multicolumnindexname')) {
$table->dropIndex('multicolumnindexname');
}
});
}
}
@am05mhz

am05mhz commented Jul 13, 2019

Copy link
Copy Markdown

this needs to be included in official release

@vjotchere

Copy link
Copy Markdown

Nice! had to adjust
$doctrineTable->hasIndex('singlecolumnindexname')
to
$doctrineTable->hasColumn('singlecolumnindexname')
to get this to work for me

@tyhvb

tyhvb commented Sep 13, 2021

Copy link
Copy Markdown

Great! thanks

@skylerkatz

Copy link
Copy Markdown

Thanks so much for this!

@extralam

extralam commented Feb 7, 2022

Copy link
Copy Markdown

I create a wrap function for that now

`
protected function createIndexName($prefix , $table , $type, array $columns)
{
$index = strtolower($prefix.$table.''.implode('', $columns).'_'.$type);

    return str_replace(['-', '.'], '_', $index);
}

`

`
public function createIndex(Blueprint &$table , array|string $indexColumns , $indexName = null){

    $sm = Schema::getConnection()->getDoctrineSchemaManager();
    $doctrineTable = $sm->listTableDetails($table->getTable());

    $indexColumns = (array) $indexColumns;

    $indexName = $indexName ?: $this->createIndexName( "", $table->getTable(),'index', $indexColumns);

    if (! $doctrineTable->hasIndex($indexName)) {
        $table->index($indexColumns, $indexName);
    }

}

`

@wallrandal

Copy link
Copy Markdown

this helped me a lot, thank you!

@SaintPeter

Copy link
Copy Markdown

It appears that the listTableDetails function is deprecated. There wasn't an obvious replacement from the documentation

I solved the problem this way:

$sm = Schema::getConnection()->getDoctrineSchemaManager();
$index_list = $sm->listTableIndexes('tablename');

if(in_array('indexname', $index_list)) {
    $table->dropIndex('indexname');
}

@Cryborg

Cryborg commented Feb 27, 2023

Copy link
Copy Markdown

It appears that the listTableDetails function is deprecated. There wasn't an obvious replacement from the documentation

I solved the problem this way:

$sm = Schema::getConnection()->getDoctrineSchemaManager();
$index_list = $sm->listTableIndexes('tablename');

if(in_array('indexname', $index_list)) {
    $table->dropIndex('indexname');
}

I don't know in which version it appeared, but now there is a replacement : introspectTable()

@Benjaminhu

Copy link
Copy Markdown

I'm glad I found this, because that's exactly what I was looking for! Thanks for the comments!
I made a portable/reuseable solution for it (app/Domain/Migration/MigrationUtil.php):

<?php

declare(strict_types=1);

namespace App\Domain\Migration;

use Doctrine\DBAL\Exception;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class MigrationUtil
{
    /**
     * INFO: https://gist.github.com/Razoxane/3bc74900b4eb5c983eb0927fa13b95f5
     *
     * @throws Exception
     */
    public static function hasIndex(string $tableName, string $indexName): bool
    {
        $doctrineSchemaManager = Schema::getConnection()->getDoctrineSchemaManager();

        return $doctrineSchemaManager->introspectTable($tableName)->hasIndex($indexName);
    }
}

usage:

<?php

declare(strict_types=1);

use App\Domain\Migration\MigrationUtil;
use Illuminate\Database\Migrations\Migration;

return new class() extends Migration {
    public function up(): void
    {
        Schema::table('users', static function ($table) {
            if (MigrationUtil::hasIndex('users', 'unique_email')) {
                $table->dropUnique('unique_email');
            }
            if (!MigrationUtil::hasIndex('users', 'users_email_unique')) {
                $table->unique('email');
            }
        });
    }
};

@mbuyco

mbuyco commented Sep 4, 2024

Copy link
Copy Markdown

Thanks! Great stuff 🙇‍♂️ 💯

@daniellesniak

Copy link
Copy Markdown

In Laravel 11 Symfony's DBAL has been replaced by Laravel's native classes so above solutions does not work anymore. Here's the working dropIndexIfNotExists for Laravel 11.

<?php

namespace App\Traits;

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

trait MigrationIndex
{
    public function dropIndexIfExist(string $tableName, string $indexName): void
    {
        $indexes = Schema::getIndexes($tableName);

        foreach ($indexes as $index) {
            if ($index['name'] === $indexName) {
                Schema::table($tableName, static function (Blueprint $table) use ($indexName) {
                    $table->dropIndex($indexName);
                });
            }
        }
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment