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

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

Related

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"):

Populate table after running a migration

Background:
In my application, Users can own Files. It used to be that a File belongs to a single User, so I used a one-to-many relationship. Now, the requirements have changed, and my relationship needs to become many-to-many.
Previously, the data structure looked like this:
files
(
id,
user_id
...
);
and the new data structure looks like this:
files
(
id,
...
);
file_user
(
id,
file_id,
user_id,
);
Problem:
I created a migration to change my data structure, like this:
public function up()
{
Schema::create('file_user', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('file_id', 36);
$table->string('user_id', 36);
});
}
Then, I set up the relationship in the corresponding models. Now i need to convert the old data into the new data structure. That means taking all the existing Files, and creating a new record in the file_user table. The code itself is simple:
foreach(File:all() as $file) {
$file->users()->attach($file->user_id);
}
I want to run this code alongside the migration, so that after running the migration, the table will populate. However, when I try to put it directly in the migration file like this:
public function up()
{
// create table
Schema::create('file_user', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->string('file_id', 36);
$table->string('user_id', 36);
});
// convert old data
foreach(File:all() as $file) {
$file->users()->attach($file);
}
}
I get an exception:
PDOException: SQLSTATE[42P01]: Undefined table: 7 ERROR: relation "file_user" does not exist. It seems like the table has not been created yet at the time when the code is trying to run.
Question:
Is there a way that I can put the conversion code inside the migration file to make it work like expected? Or is there a better way to achieve this?
Try to create a second migration file, insert the // convert old data foreach(File::all() as $file) ... part there and migrate the two files together.

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();

How to change enum type column in laravel migration?

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

Laravel 4.2 Migrations - Alter decimal precision and scale without dropping column

I wish to increase decimal precision and scale for a decimal column.
I am aware that I can drop the column, and re-create it, but doing so will mean losing the data in the column.
Is there a way using Laravel Schema::table that I can alter the precision and scale of the column without dropping it?
e.g. something like:
Schema::table('prices', function(Blueprint $t) {
$t->buy_price->decimal(5,2);
});
this worked for me:
public function up()
{
Schema::table('prices', function(Blueprint $t) {
$t->decimal('buy_price', 5, 2)->change();
});
}
when rolling back use original precision values of 3, 1 instead
public function down()
{
Schema::table('prices', function(Blueprint $t) {
$t->decimal('buy_price', 3, 1)->change();
});
}
I avoid DB specific "raw" statements as they might fail when I change to another DBMS engine. So I let Laravel handle necessary syntax when working w/DB
Just create another migration and in the up method add following code:
public function up()
{
// Change db_name and table_name
DB::select(DB::raw('ALTER TABLE `db_name`.`table_name` CHANGE COLUMN `buy_price` `buy_price` decimal(10,2) NOT NULL;'));
}
Also in the down method just set the old value so you can roll-back:
public function down()
{
// Change db_name and table_name
DB::select(DB::raw('ALTER TABLE `db_name`.`table_name` CHANGE COLUMN `buy_price` `buy_price` decimal(5,2) NOT NULL;'));
}
Then migrate as usual from the terminal/command prompt using php artisan migrate.
Didn't work for me, using select gives me a General Error: 2053 because select expects aa array to be returned. I'm not sure if it's the version of MySQL, or a windows/linux thing.
I had to use DB:statement instead:
public function up()
{
// Change db_name and table_name
DB::statement(DB::raw('ALTER TABLE `db_name`.`table_name` CHANGE COLUMN `buy_price` `buy_price` decimal(10,2) NOT NULL;'));
}
and
public function down()
{
// Change db_name and table_name
DB::statement(DB::raw('ALTER TABLE `db_name`.`table_name` CHANGE COLUMN `buy_price` `buy_price` decimal(5,2) NOT NULL;'));
}
Hope this helps anyone who comes across this.

Resources