Last Updated: January 16, 2019
·
12.81K
· nazarkinroman

Custom datatype in Laravel schema builder

Recently I faced some problem with Laravel schema builder: it does not support mysql set data type, which was necessary for one of my projects. After some searching for any information about it, I have found way to implement custom data types in schema builder. It a little bit ugly, but working well ;)

1. Extend 'Grammar' and 'Blueprint' classes

Put this code to the beginning of your migration file:

use Illuminate\Support\Fluent;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Grammars\MySqlGrammar;

/**
 * Extended version of MySqlGrammar with
 * support of 'set' data type
 */
class ExtendedMySqlGrammar extends MySqlGrammar {

    /**
     * Create the column definition for an 'set' type.
     *
     * @param  \Illuminate\Support\Fluent  $column
     * @return string
     */
    protected function typeSet(Fluent $column)
    {
        return "set('".implode("', '", $column->allowed)."')";
    }

}

/**
 * Extended version of Blueprint with
 * support of 'set' data type
 */
class ExtendedBlueprint extends Blueprint {

    /**
     * Create a new 'set' column on the table.
     *
     * @param  string  $column
     * @param  array   $allowed
     * @return \Illuminate\Support\Fluent
     */
    public function set($column, array $allowed)
    {
        return $this->addColumn('set', $column, compact('allowed'));
    }

} 

2. Replace default grammar and blueprint classes to ours

Insert this code before using Schema::create:

// register new grammar class
DB::connection()->setSchemaGrammar(new ExtendedMySqlGrammar());
$schema = DB::connection()->getSchemaBuilder();

// replace blueprint
$schema->blueprintResolver(function($table, $callback) {
    return new ExtendedBlueprint($table, $callback);
});

then replace Schema::create to $schema->create, like this:

$schema->create('example_table', function(ExtendedBlueprint $table)
{
    $table->increments('id');
    $table->text('sentence');

    // text source & author
    $table->string('author')->nullable();
    $table->string('source')->nullable();

    // technical data
    $table->set('difficulty', range(1, 10)); // use new datatype
    $table->boolean('enabled')->default(true);
});

Thats all, folks ;)

PS. Sorry for my English, I still studying it.

4 Responses
Add your response

Thanks for the tip, exactly what I needed.

over 1 year ago ·

The only issue i have found with this method thus far is that for some reason when creating tables using this method, the created tables do not honor any prefix set (in config) for the database connection, why this is i do not know.

A workaround is to fetch the prefix and then manually prepend it while creating your tables eg:

$prefix = DB::connection()->getTablePrefix();

$schema->create($prefix.'yourtable',function(ExtendedBlueprint $table) {
...
});

over 1 year ago ·

Having looked into this a little more i found an enlightening post regarding this subject here: http://forumsarchive.laravel.io/viewtopic.php?pid=34937 if you follow the method outlined in that post then you no longer need to worry about things like database prefix.

over 1 year ago ·

As the prefix seems to break in every migration after the custom grammar is set, you can use
class ExtendedMySqlGrammar extends MySqlGrammar
{
public function __construct()
{
$this->tablePrefix = DB::getTablePrefix();
}
...
I used same logic but with postgres.

over 1 year ago ·