How to change enum type column in laravel migration? - laravel

I am using Laravel 5.1 and I have a table called packages with this structure:
id int(11)
weight decimal(10,2)
weight_unit enum('Kg.', 'Gm.')
I would like to change the weight_unit enum to:
weight_unit enum('Grams','Kgs.','Pounds')
For this I create the following migration:
public function up()
{
Schema::table('packages', function ($table) {
$table->enum('weight_unit', array('Grams','Kgs.','Pounds'))->nullable()->change();
});
}
But when I run the migration I receive an error:
Unknown database type enum requested, Doctrine\DBAL\Platforms\MySqlPlatform
may not support it.
How can I change this enum?

Use the DB::statement method:
DB::statement("ALTER TABLE packages MODIFY COLUMN weight_unit ENUM('Grams', 'Kgs', 'Pounds')");

This worked for me when adding a new enum value to the modified enum column.
Add the following to the up() method:
DB::statement("ALTER TABLE packages MODIFY weight_unit ENUM('Grams', 'Kgs', 'Pounds', 'new value') NOT NULL");
Then in the down() method you can revert the change that was made:
DB::statement("ALTER TABLE packages MODIFY weight_unit ENUM('Grams', 'Kgs', 'Pounds') NOT NULL");
Note: before the enum value is removed it needs to be changed to another enum value that will be retained.

$table->enum('level', ['easy', 'hard']);

You can add custom constructor to migration and explain to Doctrine that enum should be treated like string.
public function __construct(\Doctrine\DBAL\Migrations\Version $version)
{
parent::__construct($version);
$this->platform->registerDoctrineTypeMapping('enum', 'string');
}

In case you dont want to lose your data and update it with the new values I came up with this solution:
// Include old and new enum values
DB::statement("ALTER TABLE packages MODIFY COLUMN weight_unit ENUM('Kg.', 'Gm.', 'Grams', 'Kgs', 'Pounds')");
// Replace Kg. with Kgs
Packages::where('weight_unit', 'Kg.')->update(['weight_unit' => 'Kgs']);
// Replace Gm. with Grams
Packages::where('weight_unit', 'Gm.')->update(['weight_unit' => 'Grams']);
// Delete old values
DB::statement("ALTER TABLE packages MODIFY COLUMN weight_unit ENUM('Grams', 'Kgs', 'Pounds')");
This way you can replace your old values with the new ones.

add this before change() call :
DB::getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');

I think that is fixed on Laravel 10 by adding support for native column modifying.
https://github.com/laravel/framework/pull/45487
So from Laravel 10 you can do this:
Schema::table('jobs', function (Blueprint $table) {
$table->enum('type', ['contract', 'permanent', 'partial'])->change();
});
I tried the same migration on fresh Laravel 9.55.0 and 10.0.2 application:
laravel-9.52.0.jpg
laravel-10.0.2.jpg

I am able to solve this by removing and adding constraints. This has made sure that my existing data is also is intact.
DB::statement("ALTER TABLE purchases DROP CONSTRAINT purchases_ref_check");
$types = ['single', 'monthly', 'biannual', 'amount', 'other'];
$result = join( ', ', array_map(function( $value ){ return sprintf("'%s'::character varying", $value); }, $types) );
DB::statement("ALTER TABLE purchases add CONSTRAINT purchases_ref_check CHECK (ref::text = ANY (ARRAY[$result]::text[]))");

with default value. add this in up():
\DB::statement("ALTER TABLE `patient_appointments` CHANGE `status` `status` ENUM('pending','wait','approved', 'consulted') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'pending';");

Related

How to set a value in a null JSON column with Laravel's Eloquent?

I'm using Laravel 7.
I want to update a JSON column using Laravel's Eloquent. The problem is that if the value of the column is null the column won't be updated.
This is how the code looks like:
Model::update(['jsonColumnName->jsonColumnKey' => 'value']);
This is the SQL that it would generate:
UPDATE model
SET jsonColumnName = JSON_SET(jsonColumnName, '$.jsonColumnKey', 'value');
According to the documentation of JSON_SET, it will take the first parameter as the JSON document that it will modify. In this case, that value would be null since jsonColumnName is currently null. Because of that it returns null since it has nothing to modify and it ends doing nothing.
If I manually set the value of the column to {} and run the showed code, it works. But I guess that you are not expected to do that (right?).
You should make new alter table migration and change json column to have default value {}.
First you need to check if there is already installed dbal with
composer require doctrine/dbal
Then make new migration with code in up() method:
Schema::table('table_name', function (Blueprint $table) {
$table->json('column_name')->nullable()->default(null)->change();
});
Don't forget to backup database before work on it.
With NULL value you can also check if that field is empty.
Another way, on framework level is to set logic about this issue into model's observer.
For example:
public function saving(EntityModel $entityModel)
{
if (is_null($entityModel->json_column)) {
$entityModel->json_column = '{}';
}
}

Updating the data of an existing column and copying data with migrations

Is it possible to add a new column, copy some data to this new column and update the data in another column with a Laravel migration?
I have a table something like this;
id
item
price
1
ItemOne
100.00
2
ItemTwo
200.00
Now what I need to do is,
add a new column, old_price to this table
copy the values in the price column to newly added old_price column
multiply the values in the price column with 5 and update the same column
The new table should look something like this;
id
item
price
old_price
1
ItemOne
500.00
100.00
2
ItemTwo
1000.00
200.00
Is it possible to to achieve this with a migration or a seed or something?
This change needs to be done on an application that is already in production so dropping and re-creating the tables is not an option for me here.
Also the old_price column is created just to keep a reference of the current values of the price column. It will not be updated after this and probably will be removed in an later update if everything is going right with the new prices. So I don't need to use any model events to update the column afterwards.
Any help you can give me on this is really appreciated. Thanks.
Create a new migration.
Version 1. create automatically by command:
php artisan make:migration add_price_old_to_products_table
Version 2. create manually something like this:
2021_08_18_163618_add_price_old_to_products_table.php
Manage the content by following the 3 steps in the code:
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class AddPriceOldToProductsTable extends Migration
{
public function up()
{
// 1. creating a new column
Schema::table('products', function (Blueprint $table) {
// this is just an example, you can change this
// NOTE: make sure that the type is the same as "price" column
// for avoiding type conflicts
$table->decimal('price_old', 10, 2)->nullable();
});
// 2. copying the existing column values into new one
DB::statement("UPDATE products SET price_old = price");
// 3. update the old/existing column
// CHANGE YOUR "price" COLUMN HERE...
}
public function down()
{
Schema::table('products', function (Blueprint $table) {
$table->dropColumn('price_old');
});
}
}
Run that migration for creating a new column:
php artisan migrate

Change the datatype in the column with data in laravel migration

This is the migration? i have to change the string data column into integer data column with existing data
public function up()
{
Schema::table('SYS_tenants' ,function (Blueprint $table){
$table->integer('tena_type')->unsigned()->nullable()->change();
$table->foreign('tena_type')->references('id')->on('account_types');
});
}
As per laravel Documentation you can create a new migration and do it like this:
Schema::table('SYS_tenants', function (Blueprint $table) {
$table->integer('tena_type')->unsigned()->nullable()->change();
});
Before modifying a column, be sure to add the doctrine/dbal dependency
to your composer.json file.
composer require doctrine/dbal
Reference: Laravel -> Database: Migrations-> Modifying Columns
You can use change method on the field which you want to change the type after setting the new field type.
public function up() {
Schema::table('SYS_tenants' ,function (Blueprint $table){
$table->string('tena_type')->change();
});
}
I supposed the migration which create the table has already call all requirement you need like unique, nullable and so on. You can call change method, by the way there isn't restriction about any modification you want to perform like add other mysql index on that field.
Do not forget to add doctrine/dbal in composer.json file
Migrations#Modifying Columns
Looks like what you have should work:
Schema::table('SYS_tenants' ,function (Blueprint $table){
$table->integer('tena_type')->unsigned()->nullable()->change();
});
Depending on your database you may need to cast the values to the new type: (for mysql: https://www.mysqltutorial.org/mysql-cast/)
I already use this Laravel Migration
$table->integer('tena_type')->unsigned()->nullable()->change();
But it doesn't work because, the data already in the table. In that case it can't change the datatype.I use this DB statement to change the datatype with data.it's working properly.
DB::statement("alter table SYS_tenants modify tena_type integer not null"):

Dynamic dropping of table column in laravel

I am trying to add or delete a column in table2 when a record is inserted in table1(groups-id, title, rateperkg). The name of the new table2-column name will be same as title field inserted.
I was able to add the column successfully but could perform deleting. It shows undefined value but I defined it. Please help me.
Below have screenshot:
Table2 - pricings(id, product_id)
Table1 - groups(id, title)
The groups title should get inserted as new column name in pricings table and deleting.
In your first screenshot you use the use in the callback/function, you also need to add this to the latest screenshot. Take a look at how PHP scopes work, http://php.net/manual/en/language.variables.scope.php.
You can use this with this code:
Schema::table('pricings', function (Blueprint $table) use ($subgroup) {
$table->dropcolumn($subgroup);
});
This will set the scope for $subgroup in the callback/function you defined.
See http://php.net/manual/en/functions.anonymous.php for this example: #3
// Inherit $message
$example = function () use ($message) {
var_dump($message);
};
$example();

Laravel Schema Builder Update Default Value

Trying to setup a migration that will make my already existing "active" field have a default value of "1".
I see in the docs I can use something like:
$table->integer('active')->default(1);
But I tried this in my migration with no success, I guess because the field already exists. Is there a way to correctly manage existing fields using the schema builder?
My current migration:
public function up()
{
Schema::table('scores', function($table){
$table->integer('active')->default(1);
});
}
Edit:
From what I've read so far, this can't be done with the query builder. But when I try to run a raw query:
DB::query("ALTER TABLE `scores` CHANGE COLUMN `active` `active` int(11) NOT NULL DEFAULT '1';");
I'm getting a "method 'query' does not exist error", so I'm guessing this method name was changed I just can't find what it was changed to
Looks like DB::query() was changed to DB::statement()
This did the trick:
DB::statement("ALTER TABLE `scores` CHANGE COLUMN `active` `active` int(11) NOT NULL DEFAULT '1';");

Resources