Laravel migration: fields that defined as interger wil be treated as auto increment? - laravel

Here is my code:
public function up()
{
Schema::create('sysmods', function (Blueprint $table) {
$table->increments('mod_id');
$table->string('mod_name','60');
$table->string('mod_alias_name','60');
$table->integer('mod_tb_id','6');// this will become auto increment?
$table->timestamps();
});
}
when i run the migrate command, it says:
" [PDOException]
SQLSTATE[42000]: Syntax error or access violation: 1075 Incorrect table def
inition; there can be only one auto column and it must be defined as a key"
Then as i check the sql, I found this:
(SQL: create table la_sysmods (mod_id int unsigned not null auto_increm
ent primary key, mod_name varchar(60) not null, mod_alias_name varchar(
60) not null, mod_tb_id int not null auto_increment primary key, created
_at timestamp null, updated_at timestamp null) default character set utf
8 collate utf8_unicode_ci)
The field 'mod_tb_id' which marked as integer has become increments. What's the problem? Thanks!

The second argument for the integer method is the autoIncrement variable. You need to remove it.
The Laravel method
public function integer($column, $autoIncrement = false, $unsigned = false)
{
return $this->addColumn('integer', $column, compact('autoIncrement', 'unsigned'));
}
Change this:
$table->integer('mod_tb_id','6');// this will become auto increment?
To this:
$table->integer('mod_tb_id');

You need to replace your
$table->integer('mod_tb_id','6');// this will become auto increment?
with
$table->integer('mod_tb_id')->length(6);
or
$table->integer('mod_tb_id', false, false)->length(6);
The second argument for integer() method is the flag for autoincrement and the third is for unsigned.
Hope it helps

Related

Dropping sqlite column fails when updating laravel/eloquent model using database manager migration script

I'm having an issue with attempting to run tests in my Laravel application. I have a table structure as follows:
These SQL Queries have been generated by an export script using TablePlus, as I figured it was the easiest way to share the table structure.
After Migration (NOW)
CREATE TABLE `business_system_role_location_type` (
`business_system_role_id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
`location_type_id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`business_system_role_id`,`location_type_id`),
KEY `business_system_role_loc_type_fk` (`location_type_id`),
CONSTRAINT `business_system_role_loc_type_fk` FOREIGN KEY (`location_type_id`) REFERENCES `location_types` (`id`),
CONSTRAINT `business_system_role_loc_type_role_id_fk` FOREIGN KEY (`business_system_role_id`) REFERENCES `business_system_roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
Before Migration
CREATE TABLE `business_system_role_location_type` (
`business_system_role_id` char(36) COLLATE utf8mb4_unicode_ci NOT NULL,
`location_type` varchar(30) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`business_system_role_id`,`location_type`),
CONSTRAINT `business_system_role_loc_type_role_id_fk` FOREIGN KEY (`business_system_role_id`) REFERENCES `business_system_roles` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
So as you can see, I have ran a migration and removed the location_type field and replaced it with a foreign key to a new location_types table. Both the business_system_role_id and location_type are set to UNIQUE PRIMARY KEY.
This all appears to work fine with MySQL but as soon as I attempt to run any of my tests (using SQLite) it stops working and complains:
Illuminate\Database\QueryException : SQLSTATE[23000]: Integrity constraint violation: 19 NOT NULL constraint failed: business_system_role_location_type.location_type (SQL: insert into "business_system_role_location_type" ("business_system_role_id", "location_type_id") values (bb2051c2-1b5c-498d-bbcf-6dd9e20c4803, 38215722-bcba-4cac-8c83-fe867d8d8e65))
Question
Why am i getting a NOT NULL constraint for business_system_role_location_type.location_type when that column no longer exists? I have tried setting location_type to nullable->(true) before the migration, on the assumption it might update some SQLite setting, but this did not work.
I tried adjusting my code so that it performed $model->location_type = 'something' before $model->save(), and that worked... Even though the column does not exist. All references to it have been removed. I don't want to have to live with a workaround here and would like to get to the bottom of the reason for this error.
The Model looks as follows:
class BusinessSystemRoleLocationType extends Model
{
protected $table = 'business_system_role_location_type';
protected $fillable = [
'business_system_role_id',
'location_type_id',
];
public $incrementing = false;
public $timestamps = false;
public function businessSystemRole(): BelongsTo
{
return $this->belongsTo(
BusinessSystemRole::class,
'business_system_role_id',
'id'
);
}
}
Any help here would be greatly appreciated. :)
Edit - Migration
Here is the portion of the migration which deals with this table:
// Add location_type_id field to table
Schema::table('business_system_role_location_type', function (Blueprint $table) {
// Must be nullable until it is populated with data
$table->uuid('location_type_id')
->nullable()
->after('business_system_role_id');
});
// Assign location_type_id value to all entries
BusinessSystemRoleLocationType::all()->each(function ($businessSystemRoleLocationType) {
$locationTypeDataSetIndex = \array_search(
$businessSystemRoleLocationType->location_type,
\array_column($this->locationTypeDataSet, 'existsAs'),
true
);
if ($locationTypeDataSetIndex !== false) {
$newLocationTypeData = $this->locationTypeDataSet[$locationTypeDataSetIndex];
$newLocationType = LocationType::whereSlug($newLocationTypeData['slug'])->get()->first();
} else {
$newLocationType = LocationType::all()->first();
}
$businessSystemRoleLocationType->location_type_id = $newLocationType->id;
$businessSystemRoleLocationType->save();
});
// Adjust primary index and add foreign keys, and drop location_type field from table
Schema::table('business_system_role_location_type', function (Blueprint $table) {
$table->dropForeign('business_system_role_loc_type_role_id_fk');
$table->dropPrimary(['business_system_role_id', 'location_type']);
});
Schema::table('business_system_role_location_type', function (Blueprint $table) {
// ATTEMPT TO SET FIELD TO NULLABLE BEFORE REMOVING IT, MAYBE THIS WILL FIX THE NOT NULL CONSTRAINT ERROR?
$table->string('location_type')->nullable()->change();
});
Schema::table('business_system_role_location_type', function (Blueprint $table) {
$table->foreign('location_type_id', 'business_system_role_loc_type_fk')
->references('id')
->on('location_types');
$table->foreign('business_system_role_id', 'business_system_role_loc_type_role_id_fk')
->references('id')
->on('business_system_roles')
->onDelete('cascade');
// Now set not nullable UUID (Doctrine (change()) does not support UUID type)
$table->string('location_type_id', 36)->change();
$table->primary(['business_system_role_id', 'location_type_id'], 'business_system_role_loc_type_pk');
$table->dropColumn('location_type');
});
Edit 2 - Solution
I am editing here to provide my solution - though I will leave this open as others will most likely provide a better answer.
From what I can understand in order to remove constraints in an SQLite database it is recommended to delete the table and recreate it. (See here: https://stackoverflow.com/a/4007086/9675332). It appears that SQLite saves these constraints somewhere and doesn't actually remove them just because you remove the column. I really did not want to go down this route though, so here is what i did:
Solution 1: I modified my migration to set the field to have a default value before removing it, and this did pass the test (though it then subsequently failed on the UNIQUE constraint so it's not a working solution in my case, but may well work for others!)
Solution 2: Probably what I should have done to begin with actually. I simply renamed the column from location_type to location_type_id and manually set it to char(36). This appears to have updated everything in the background along with it and it now passes the tests.

How to change datatype float to string in laravel migration

I am trying to change datatype of one of my field from float to string but i am facing an issue
SQLSTATE[42000]: Syntax error or access violation: 1064 You have
an error in your SQL syntax; check the manual that
corresponds to your MariaDB server version for the right syntax to use
near '' at line 1 (SQL: ALTER TABLE patient
MODIFY height varchar)
my migration:
public function up()
{
DB::statement('ALTER TABLE patient MODIFY height varchar');
}
How i can achieve my target:
Your help need here
public function up()
{
Schema::table('patient', function (Blueprint $table) {
$table->string('height')->change();
});
}
You can use laravel's change method :
public function up()
{
Schema::table('patient', function ($table) {
$table->string('height')->change();
});
}
Also your SQL syntax should be :
ALTER TABLE patient MODIFY height varchar(255);
Update :
As per the laravel's documentation :
Only the following column types can be "changed": bigInteger, binary, boolean, date, dateTime, dateTimeTz, decimal, integer, json, longText, mediumText, smallInteger, string, text, time, unsignedBigInteger, unsignedInteger and unsignedSmallInteger.
So Laravel's change will not work directly as you have a float column which laravel internally makes as double(8,2). Please update your raw SQL syntax using what I have given and try again.

Foreign key constraint fails on nullable field

I have a table organizations. This table has a primary id (int 10, unsigned, AUTO_INCREMENT).
In the table organizations, I also have a foreign key to the iself: main_organization_id. This has the following attributes: (int 10, unsigned, nullable, empty:TRUE, default:NULL).
Whenever I create a new organization:
$organization = Organization::create($request->all());
Without a main_organization_id in my request, it fails with the following error:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or
update a child row: a foreign key constraint fails
(myDB.organizations, CONSTRAINT
organizations_main_organization_id_foreign FOREIGN KEY
(main_organization_id) REFERENCES organizations (id)) (SQL:
insert into organizations (main_organization_id) values
())
But why does this fail? The field is nullable, does that mean I have to implicitly set main_organization_id to null in the request?
My $fillable:
protected $fillable = [
'main_organization_id',
];
My migration:
Schema::table('organizations', function (Blueprint $table) {
$table->integer('main_organization_id')->unsigned()->nullable();
$table->foreign('main_organization_id')->references('id')->on('organizations');
});**strong text**
I want to prevent code like this: $request['main_organization_id'] = null; before creating my new row. Is this possible?
Thank you for reading.
Yes, you should specify the field value while creating an Organization, you should do it like this:
$organization = Organization::create(array_merge([
'main_organization_id' => null,
], request()->all()));
Hope this helps!

Laravel migration fails multiple primary keys

I am trying to create a Migration in Laravel but it fails saying I got multiple primary keys.
public function up()
{
Schema::create('spins', function (Blueprint $table) {
$table->integer('rid', true, true);
$table->bigInteger('pid');
$table->integer('result');
$table->integer('bet');
$table->timestamps();
$table->primary(array('rid', 'pid'));
});
}
The error:
SQLSTATE[42000]: Syntax error or access violation: 1068 Multipleprimary key defined
(SQL: alter table `spins` add primary key `spins_rid_pid_primary` (`rid`, `pid`))
The autoincrement of rid is the problem (second parameter in the line below).
$table->integer('rid', true, true);
If you are using InnoDB as MySQL engine it doesn't allow composite primary keys with an auto increment.
But if you change to the MyISAM engine it would be possible to do so.
Add $table->engine = 'MyISAM'; to your Migration.
Declare the rid field as a normal integer column
Laravel doesn't provide a method to change existing columns so you need to run a raw SQL query: DB::statement('ALTER TABLE spins MODIFY rid INTEGER NOT NULL AUTO_INCREMENT');
public function up()
{
Schema::create('spins', function (Blueprint $table) {
$table->engine = 'MyISAM';
$table->integer('rid')->unsigned();
$table->bigInteger('pid');
$table->integer('result');
$table->integer('bet');
$table->timestamps();
$table->primary(array('rid', 'pid'));
DB::statement('ALTER TABLE spins MODIFY rid INTEGER NOT NULL AUTO_INCREMENT');
});
}
Your primary key makes no sense.
You are adding a composite primary key to an auto incrementing column and another column. The auto incrementing column will already always be unique so you should just have only that be your primary key.
If you need pid to be unique, set rid to your primary key and add a unique key on pid.
Schema::create('spins', function (Blueprint $table) {
$table->increments('rid');
$table->bigInteger('pid');
$table->integer('result');
$table->integer('bet');
$table->timestamps();
$table->unique('pid');
});
If for some reason you do need your primary key to include rid and pid, this seems to work for me.
CREATE TABLE `spins` (
`rid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`pid` BIGINT(20) NOT NULL,
`result` INT(11) NOT NULL,
`bet` INT(11) NOT NULL,
`created_at` TIMESTAMP NOT NULL,
`updated_at` TIMESTAMP NOT NULL,
PRIMARY KEY (`rid`, `pid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
You can't have multiple primary keys on a single table. You can have a composite primary key, which is a primary key made from two or more columns. Apparently Blueprint does not support creating composite keys, so you'll have to use the query builder if you want to use composite keys.
Otherwise you can just choose pid or rid as your primary key.

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

Resources