Skip to content

Instantly share code, notes, and snippets.

@Razoxane
Created August 8, 2017 01:35
Show Gist options
  • Save Razoxane/3bc74900b4eb5c983eb0927fa13b95f5 to your computer and use it in GitHub Desktop.
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');
}
});
}
}
@beNjiox
Copy link

beNjiox commented Nov 4, 2018

Great.

You can even make it a trait if you need to do that a lot

<?php

namespace Migrations;

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

trait MigrationIndex
{
    public function _dropIndexIfExist($tableName, $indexName)
    {
        Schema::table($tableName, function (Blueprint $table) use ($tableName, $indexName) {
            $sm = Schema::getConnection()->getDoctrineSchemaManager();
            $doctrineTable = $sm->listTableDetails($tableName);

            if ($doctrineTable->hasIndex($indexName)) {
                $table->dropIndex($indexName);
            }
        });
    }

}

Then in your migration files...

use Migrations\MigrationIndex;

class RemoveProductIndex extends Migration
{
    use MigrationIndex;

    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
          $this->_dropIndexIfExist('products', 'category_id');
    }

@jdmacdiarmid
Copy link

Awesome! Just what I've been searching for. Thank you very much

@am05mhz
Copy link

am05mhz commented Jul 13, 2019

this needs to be included in official release

@vjotchere
Copy link

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

@tyhvb
Copy link

tyhvb commented Sep 13, 2021

Great! thanks

@skylerkatz
Copy link

Thanks so much for this!

@extralam
Copy link

extralam commented Feb 7, 2022

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

this helped me a lot, thank you!

@SaintPeter
Copy link

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
Copy link

Cryborg commented Feb 27, 2023

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

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');
            }
        });
    }
};

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