Laravel integer v biginteger in foreign keys - laravel

I'm trying to assign big integers in my columns but it won't allow me to define the relationship unless I change it to standard integer. No biggy, but would be interested to know why this throws up migration errors for me.
MIGRATION (working)
public function up()
{
//create the table
Schema::create(self::TBL_eportfoliouservalues, function($table){
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('user')->unsigned()->index();
$table->integer('document')->unsigned()->index();
$table->integer('section')->unsigned()->index();
$table->integer('element')->unsigned()->index();
$table->integer('parent');
$table->text('value');
// foreign indexes
$table->foreign('user')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('document')->references('id')->on('eportfoliodocuments')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('section')->references('id')->on('eportfoliosections')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('element')->references('id')->on('eportfolioformelements')->onDelete('cascade')->onUpdate('cascade');
});
}
MIGRATION (not working)
public function up()
{
//create the table
Schema::create(self::TBL_eportfoliouservalues, function($table){
$table->engine = 'InnoDB';
$table->increments('id');
$table->bigInteger('user')->unsigned()->index();
$table->bigInteger('document')->unsigned()->index();
$table->bigInteger('section')->unsigned()->index();
$table->bigInteger('element')->unsigned()->index();
$table->bigInteger('parent');
$table->text('value');
// foreign indexes
$table->foreign('user')->references('id')->on('users')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('document')->references('id')->on('eportfoliodocuments')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('section')->references('id')->on('eportfoliosections')->onDelete('cascade')->onUpdate('cascade');
$table->foreign('element')->references('id')->on('eportfolioformelements')->onDelete('cascade')->onUpdate('cascade');
});
}
ERROR
[Exception]
SQLSTATE[HY000]: General error: 1005 Can't create table 'drillers.#sql-1a4_2c2' (errno: 150) (SQL:
alter table `eportfoliouservalues` add constraint eportfoliouservalues_user_foreign foreign key (
`user`) references `users` (`id`) on delete cascade on update cascade) (Bindings: array (
))

How are the IDs defined on the other tables? If you use $table->increments('id') then that will create columns of the type integer, and as the foreign key field has to match the primary key field on the other table, you won't be able to create a relationship.
The way to get around this would be to use $table->bigIncrements('id') on the other tables.

If primary key in tableOne migration (by default it is ) is BigInteger
So the foreign key in tableTwo migration should be bigInteger with unsigned() function.
Codes for example.
Migration for tableOne
public function up()
{
Schema::create('tableOne', function (Blueprint $table) {
$table->bigIncrements('id');
}
Migration for tableTwo
public function up()
{
Schema::create('tableTwo', function (Blueprint $table) {
$table->bigIncrements('id');
$table->bigInteger('fk_id')->unsigned()->nullable() ;
$table->bigInteger('fk_id')->default(20)->change();
$table->foreign('fk_id')->references('id')->on('tableOne');
}
Hope this helps someone.

This is late to the party but I thought should be shared somewhere.
This was a perfectly valid upgrade that was not so well documented. The type needs to match for ids and foreign keys.
This script outputs the migration you need.
Use at your own risk having BACKED UP YOUR DATABASE. Make a migration and add the following to up(). Leave down() empty so you can rollback and tweak as needed. It will not actually do anything but will output what needs to be done. You can then paste the output into your migration file once you are happy with what you see.
If you uncomment the echos you can see what was looked at and determined needs changing.
$tableName = $table->Tables_in_yourdatabasename; //eg Tables_in_myproject
Modify the above line in the code below
$tables = \DB::select('SHOW TABLES');
// this just to get the tabs right in sublime;
$i = 0;
foreach($tables as $table) {
// ### Change yourdatabasename below ###
$tableName = $table->Tables_in_yourdatabasename;
// echo "#######################\n";
// echo '########## '.$tableName."##########\n\n";
$columns = \DB::select('show columns from ' . $tableName);
foreach ($columns as $value) {
if((substr($value->Field, -2) === 'id') && (substr($value->Type,0,3) === "int" || substr($value->Type,0,6) === "bigint")) {
// echo "$value->Field : type is '$value->Type'\n" ;
if(substr($value->Type,0,6) === "bigint") {
// echo "No Change needed\n";
}
if(substr($value->Type,0,3) === "int") {
// echo "To Update $value->Field to 'BIGINT':\n\n";
if($i === 0) {
echo 'Schema::table(\''.$tableName.'\', function (Blueprint $table) {'."\n";
$i++; //only matters for the first row. For the tabs.
}else{
echo "\t\t".'Schema::table(\''.$tableName.'\', function (Blueprint $table) {'."\n";
}
// maintain the auto increment
if($value->Field === 'id') {
echo "\t\t".' $table->bigIncrements(\'id\')->change();'."\n\t\t";
}else{
echo "\t\t".' $table->bigInteger(\''.$value->Field.'\')->unsigned()->change();'."\n\t\t";
}
echo '});'."\n";
}
}
}
// echo "\n\n";
}
Be careful and examine the output carefully as you may not want to change all the tables. I would skip the migrations table for example as I doubt you will reference it.
In case I didn't say it. BACK UP YOUR DATABASE FIRST. Good Luck

Related

update content of stored column in laravel controller

I have a table with 3 columns:
firstname
lastname
fullname
in migration:
Schema::create('owners', function (Blueprint $table) {
$table->id();
$table->string('firstname',20);
$table->string('lastname', 20);
$table->string('fullname')->storedAs('CONCAT(firstname,lastname)');
$table->timestamps();
});
the problem is that i want to change the concatenation order in the controller i tried to use db statement but it doesn't work
-in the controller:
$owners= Owner::findOrFail($id);
$owners->update([
'firstname'=>$request['firstname'],
'lastname' =>$request['lastname'],
]);
DB::statement('UPDATE owners SET fullname AS CONCAT(lastname,firstname) STORED WHERE ID=1 ');
I don't want to just use a simple concatenation because the user can change the firstname or the lastname and the order that's why I used storedAs()
any ideas please?
The storedAs method in the migration creates a generated column in mysql. The value is automatically generated from the column values of the firstname and the lastname. There's no way you can change this via an UPDATE statement. You'd have to use an ALTER TABLE statement, which would be horrifically bad practice.
If I were you, I'd keep full name display as a model method so you could access it by using $owner->fullNameFirstLast() or $owner->fullNameLastFirst()
What you should do is create a new migration in order to change the column, the code would be something like this:
Schema::table('owners', function (Blueprint $table) {
$table->string('fullname')->storedAs('CONCAT(lastname,firstname)');
});
This way the column will be changed on a database level, and no need for the controller query you have added
Simply try this
1- update your migration to
Schema::create('owners', function (Blueprint $table) {
$table->id();
$table->string('firstname',20);
$table->string('lastname', 20);
$table->string('fullname', 56);
$table->timestamps();
});
2- in your controller
$owners= Owner::findOrFail($id);
$first_name = $request->firstname ?? $owners->firstname;
$last_name = $request->lastname ?? $owners->lastname;
$full_name = $first_name.' '.$last_name;
$owners->update([
'firstname'=>$first_name,
'lastname' =>$last_name,
'fullname' =>$full_name,
]);
You can also write it this way
DB::statement(DB::raw("UPDATE owners SET firstname = '".$first_name."', lastname = '".$last_name."', fullname = '".$full_name."' WHERE id = $id"));
And the same way for your Create function as well

Migration: Column already exists error when creating new tables

I did a search on possible resolutions to this issue and I couldn't find anything when creating a new table in Laravel 7. But, I am getting a Column already exists error when running php artisan migrate while creating new tables. The column that is in the error is contract_type_id.
Here is a snip of the migration file:
public function up()
{
Schema::create('contract_data', function (Blueprint $table) {
$table->id();
$table->integer('contract_type_id');
$table->integer('customer_id');
$table->date('start_date');
$table->date('end_date');
$table->integer('product1_id');
$table->integer('product2_id');
$table->integer('product3_id');
$table->integer('product4_id');
$table->integer('product5_id');
$table->integer('product1_gallons');
$table->integer('product2_gallons');
$table->integer('product3_gallons');
$table->integer('product4_gallons');
$table->integer('product5_gallons');
$table->decimal('product1_price', 9,6 );
$table->decimal('product2_price', 9,6 );
$table->decimal('product3_price', 9,6 );
$table->decimal('product4_price', 9,6 );
$table->decimal('product5_price', 9,6 );
$table->integer('fuel_contract_number');
$table->timestamps();
});
Schema::table('contract_data', function(Blueprint $table) {
$table->foreignId('contract_type_id')->constrained('contract_type');
$table->foreignId('customer_id')->constrained('customer_details');
$table->foreignId('product1_id')->constrained('product_details');
$table->foreignId('product2_id')->constrained('product_details');
$table->foreignId('product3_id')->constrained('product_details');
$table->foreignId('product4_id')->constrained('product_details');
$table->foreignId('product5_id')->constrained('product_details');
$table->foreignId('fuel_contract_number')->constrained('fuel_contracts');
});
}
The foreignId method is an alias for unsignedBigInteger while the constrained method will use convention to determine the table and column name being referenced
So you are making contract_type_id field twice.
$table->foreignId('contract_type_id')->constrained('contract_type'); alias of $table->unsignedBigInteger('contract_type_id'); & $table->foreign('contract_type_id')->references('id')->on('contract_type);
So in your second migration it would be :
$table->foreign('contract_type_id')->references('id')->on('contract_type);
Instead of :
$table->foreignId('contract_type_id')->constrained('contract_type');
Also you need to use Unsigned Big Integer, as :
$table->unsignedBigInteger('contract_type_id');
Not,
$table->integer('contract_type_id');
Because Laravel 5.8 Added unsignedBigInteger as defaults for foreign key, also for Laravel 6 and 7

Laravel Model::find($id) doesn't filter results

I'm trying to edit a column of datatable to show informations in my index view table.
This is my controller's method that I call throught Ajax in the view:
public function getField(){
// Current User
$user_id = \Auth::user()->id;
$users = User::find($user_id)->users();
return Datatables::of($users )
->editColumn('id', 'Edit')
->editColumn('fields_id', function($users) {
$mystring = '';
$fields_id_array = json_decode($users->fields_id);
foreach($fields_id_array as $f_id){
/* this works */
// $mystring .= Fields::where('id', '=', $f_id) -> pluck('field_name');
/* this doesn't work */
$mystring .= Fields::find($f_id) -> pluck('field_name');
}
return $mystring;
})
->rawColumns(['id'])
->make(true);
}
The field 'fields_id' in users table is a JSON field like this: ['1','2','5'] and these refers to the id's of another table: 'Fields'.
I have already read this questions but I think my db table is it ok because increments are primary-key equivalent in Laravel migrations.
This is my migration for the table 'fields':
public function up()
{
Schema::defaultStringLength(191);
Schema::create('fields', function (Blueprint $table) {
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->foreign('user_id')->references('id')->on('users');
$table->string('code')->unique();
$table->string('param1');
$table->string('param3')->nullable();
$table->timestamps();
});
}
Thanks
a wild guess here, based on your last comment...
try this inside you loop
$mystring .= Fields::find(intval($f_id))->pluck('field_name');
I've added the intval() function arround your $f_id since you said you said they are in this format ['1','2','5']. that is an array of strings (that should be cast to integers if you plan to use them with find() function.

Eloquent using wrong key for some tables

Using: Laravel 5.5
I Constructing addresses using some address elements (like: district, area, zip etc) as dropdowns & also some user inputs.
I have 5 address element & one of their schema is:
Schema::create('address_districts', function (Blueprint $table) {
$table->increments('id');
$table->integer('admin_id')->unsigned();
$table->string('name');
$table->timestamps();
$table->foreign('admin_id')->references('id')->on('admins');
});
This is for Districts, & I have another 3 exactly same like this called, address_thanas, address_areas, address_building_names & address_zips;
The only exception for the last one is that has code instead of name on other tables:
Schema::create('address_zips', function (Blueprint $table) {
$table->increments('id');
$table->integer('admin_id')->unsigned();
$table->string('code'); // Look other table has name here........
$table->timestamps();
$table->foreign('admin_id')->references('id')->on('admins');
});
I store constructed addresses on the table called addresses
Schema::create('addresses', function (Blueprint $table) {
$table->increments('id');
$table->integer('district_id')->unsigned();
$table->integer('thana_id')->unsigned();
$table->integer('area_id')->unsigned();
$table->integer('zip_id')->unsigned();
$table->integer('building_name_id')->nullable()->unsigned();
$table->string('building');
$table->integer('floor');
$table->string('apt')->nullable();
$table->text('comment')->nullable();
$table->timestamps();
$table->foreign('district_id')->references('id')->on('address_districts');
$table->foreign('thana_id')->references('id')->on('address_thanas');
$table->foreign('area_id')->references('id')->on('address_areas');
$table->foreign('zip_id')->references('id')->on('address_zips');
$table->foreign('building_name_id')->references('id')->on('address_building_names');
});
In Address Model I've defined relationships like:
public function district() {
return $this->belongsTo(AddressDistrict::class, 'district_id');
}
public function thana() {
return $this->belongsTo(AddressThana::class, 'thana_id');
}
public function area() {
return $this->belongsTo(AddressArea::class, 'area_id');
}
public function building_name() {
return $this->belongsTo(AddressBuildingName::class, 'building_name_id');
}
public function zip() {
return $this->belongsTo(AddressZip::class, 'zip_id', 'id');
}
Then when I try to create a new address using Address::create($data)
I get error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'name' in 'where clause' (SQL: select count(*) as aggregate from `address_zips` where `name` = 2)
Here we can see that it is comparing the key name instead of id
I noticed that this is not reporting that the data cannot be inserted or something like that, it fails to count related model & for that aborts insertion of data
Why is that?
But the strange thing is I can retrieve data by (inserted a row manually into db for testing to see if that can retrieve data)
$addresses = Address::orderByDesc('created_at')->get();
//loop as $address
$address->district->name
$address->zip->code
...
& this works perfect
When I am creating a new record I need that query look like:
select count(*) as aggregate from `address_zips` where `id` = 2
Any help will be highly appreciated.
Thanks for reading this long question.
The problem is in your $data array. Probably you copied a form with an input name, you need to rename it to code in order to save the values automatically.
Alternatively, you can set the input manually
$address = new Address();
$address->code = request('name');
$address->save();

How to make combination of two columns as a unique key?

I have this table :
public function up()
{
Schema::create('edition', function (Blueprint $table) {
$table->increments('id');
$table->integer('volume');
$table->text('cover')->nullable();
$table->enum('number', ['1', '2']);
$table->timestamps();
});
Schema::table('journal', function(Blueprint $table) {
$table->foreign('id_edition')->references('id')->on('edition')->onDelete('cascade')->onUpdate('cascade');
});
}
I wanted to make the combination of column Volume and Number as a unique key. For example, there is a data "Volume 1, Number 1" and when user insert the same combination it will throw an error message that the data already exist. Is there a way I can do this in Laravel ?
Just include the following snippet inside the up() method
$table->unique(['volume','number']);
By having this constraint set, if you insert 'Volume 1 , Number 1` once it'll be fine. However, if you try to do the same again, it'll throw an error.
Conditions:
You are using some good DBMS, such as MySQL and not SQLITE
You successfully migrated this change into the database.
You can also use uniquewith-validator package that allows you to validate a unique combination of fields like this
'field1' => 'required|unique_with:table_name,field2'
The above code will check if the combination of field1 and field2 is unique in the table with the given table_name.

Resources