Multiple primary keys with one auto increment laravel - composite-primary-key

I have to migrate this database with Laravel to MySQL, but it seems that I can't use multiple primary key for autoincrements elements.
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->bigIncrements('user_id');
$table->string('name');
$table->string('surname');
$table->string('email')->unique();
$table->string('tel');
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('collections', function (Blueprint $table) {
$table->unsignedBigInteger('user_id');
$table->foreign('user_id')->references('user_id')->on('users');
$table->bigIncrements('collection_id');
$table->primary(['user_id', 'collection_id']);
$table->string('title');
$table->string('img_url');
$table->timestamps();
});
Schema::table('collections', function (Blueprint $table) {
$table->dropPrimary('collections_collection_id_fk_primary');
});
Schema::create('contents', function (Blueprint $table) {
$table->unsignedBigInteger('collection_id');
$table->foreign('collection_id')->references('collection_id')->on('collections');
$table->bigIncrements('content_id');
$table->primary(['content_id', 'collection_id']);
$table->string('title');
$table->string('author');
$table->string('publisher');
$table->string('pages');
$table->string('google_id');
$table->string('img_url');
$table->timestamps();
});
Schema::table('collections', function (Blueprint $table){
$table->dropPrimary('contents_content_id_fk_primary');
});
}
I've tried to use dropPrimary but it doesn't execute the query, because it stops at the error: " Illuminate\Database\QueryException : SQLSTATE[42000]: Syntax error or access violation: 1068 Multiple primary key defined (SQL: alter table collections add primary key collections_user_id_collection_id_primary(user_id, collection_id))"
Can you help me?

TLDR:
The following column definition already automatically creates a primary key:
$table->bigIncrements('collection_id');
Therefore your explicit call to $table->primary() tries to create a second primary key.
Long answer
I've encountered a similar problem. I had a composite primary key on two columns which caused problems when I started to queue jobs because the framework serialized only the $primaryKey of the model (you can't define two columns as $primaryKey in models. So I added an auto increment primary column to the migration:
$this->bigIncrements('id')->primary();
This caused the error message which you are getting. After removing the primary() call it worked and I noticed that the primary key got automatically set.
Instead I am now using a composite unique key:
$table->unique(['column_a', 'column_b']);
I hope this helps in your case.

Related

Column does not exist Laravel Factory

I have a table
public function up()
{
Schema::create('parties', function (Blueprint $table) {
$table->id();
$table->integer('place_id');
$table->tinyInteger('status')->default(0);
$table->dateTime('utc_date');
$table->dateTime('local_date');
$table->timestamps();
});
Schema::table('parties', function (Blueprint $table) {
$table->index('place_id');
$table->foreign('place_id')
->references('id')
->on('places')
->onDelete('restrict');
});
}
and model factory
<?php
/** #var \Illuminate\Database\Eloquent\Factory $factory */
use App\Models\Party;
use Faker\Generator as Faker;
$factory->define(Party::class, function (Faker $faker) {
$date = $faker->dateTimeBetween(now()->subDays(3), now());
return [
'place_id' => $faker->numberBetween(1, 9),
'status' => $faker->numberBetween(0, 3),
'utc_date' => $date,
'local_date' => \Carbon\Carbon::make($date)->addHours(3),
];
});
If i call the method create in HomeController it works correctly
factory(Party::class, 10)->create();
but when i call it in my tests I got an error
SQLSTATE[42703]: Undefined column: 7 ERROR: column "utc_date" of
relation "parties" does not exist LINE 1: insert into "parties"
("place_id", "status", "utc_date", "lo...
^ (SQL: insert into "parties" ("place_id", "status", "utc_date", "local_date",
"updated_at", "created_at") values (5, 3, 2020-08-04 00:34:52,
2020-08-04 03:34:52, 2020-08-04 11:42:18, 2020-08-04 11:42:18)
returning "id")
<?php
namespace Tests\Feature\API;
use App\User;
use Tests\TestCase;
use Laravel\Sanctum\Sanctum;
use App\Models\{Party, Place};
use Illuminate\Foundation\Testing\RefreshDatabase;
class PartiesControllerTest extends TestCase
{
use RefreshDatabase;
private User $user;
protected function setUp(): void
{
parent::setUp();
$this->user = factory(User::class)->create();
}
public function test_get_parties()
{
factory(Party::class)->create([
'place_id' => factory(Place::class)->create()->id,
]);
dd(Party::all());
Sanctum::actingAs($this->user);
}
}
I am using second database for testing. I connected to this db and found that this table has not been refreshed. I don't know why because I used RefreshDatabase trait. I added this field manually for solving this problem.
Try to run php artisan migrate:fresh. If you use another database for testing and using another .env file then try to run php artisan migrate:fresh --env=testing.
For example: you can use file env.testing and APP_ENV=testing inside this file.
Maybe you added this field later and did not refresh DB. Also, check if the field exists in visual DB.
If it didn't help try to run composer dump-autoload and retry.
please make sure the fillable array in model has this key,
and try using carbon instead of faker with date
(Carbon::now())->subDays(3);

foreign keys and databases (laravel 6 from scratch episdoe 30)

Hi I need help with my learning I do the following code I that I am writing from laracast for some reason dont oupout foreign key value in my database Please help:
public function up()
{
Schema::create('articles', function (Blueprint $table) {
$table->bigIncrements('id');
$table->unsignedBigInteger('user_id');
$table->string('title');
$table->text('excerpt');
$table->text('body');
$table->timestamps();
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
});
}
Thank you for your help
Thanks done abit more digging and got suggestion on laracast website so if some one stuck try to do the following changes:
in your laravel /config/database.php file change the following:
'engine' => null,
//Change the null to InnoDB as bellow:
'engine' => InnoDB,

Can't create table in laravel "General error: 1 table has more than one primary key"

When using the following command php artisan migrate it returns the following error:
General error: 1 table - has more than one primary key
I have tried forcing primary key using
$table->primary('id');
Note: the ID being referenced in the other table is also of data type bigIncrements
Schema::create('stuff', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('class');
$table->timestamps();
$table->bigIncrements('owner');
$table->float('price');
$table->bigIncrements('teacher');
$table->foreign('owner')->references('id')->on('othertable');
$table->foreign('teacher')->references('id')->on('othertable');
});
}
You should make owner and teacher bigInteger instead
Schema::create('stuff', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('class');
$table->timestamps();
$table->bigInteger('owner')->unsigned();
$table->float('price');
$table->bigInteger('teacher')->unsigned();
$table->foreign('owner')->references('id')->on('othertable');
$table->foreign('teacher')->references('id')->on('othertable');
});
}
you use bigIncrements instead of integer
bigIncrements create primary key
if owners and teachers tables has bigIncrements id column so replace migration codes with:
Schema::create('stuff', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string('class');
$table->timestamps();
$table->unsignedBigInteger('owner');
$table->float('price');
$table->unsignedBigInteger('teacher');
$table->foreign('owner')->references('id')->on('othertable');
$table->foreign('teacher')->references('id')->on('othertable');
});
}
Obviously bigIncrements datatype in Laravel is going to make that field primary key. In this case, you are having three primary keys which are Id, Owner and Teacher Fields.
To remove the last two from being primary keys add the following code in your migration file.
Schema::table('stuff', function (Blueprint $table) {
$table->dropPrimary('owner');
$table->dropPrimary('teacher');
});
Also, If these two fields (owner, teacher) are reference fields, I guess you don't need to give them a datatype of bigIncrements. Changing them to unsignedBigInteger can be a better choice.

How to migrate from string column to foreign key pointing to row in other table with same string value in Laravel?

I am using Laravel 5.6 and need some help migrating a column from a populated table preserving the content logic. There is a table pages with a column named icon that accepts string values.
Ex:
Schema::create('pages', function (Blueprint $table) {
$table->increments('id');
...
$table->string('icon')->nullable();
}
The pages table is populated and the icon column, being nullable, is not always used.
A new icons table was created to store all the usable icon classes.
Ex:
Schema::create('icons', function (Blueprint $table) {
$table->increments('id');
$table->string('name');
});
How can i migrate the icon column from the pages table to be a foreign key that points to the icons table row that has the same value in the name column or null if not populated?
I'd suggest a polymorphic many-to-many approach here so that icons are reusable and don't require a bunch of pivot tables, should you want icons on something other than a page.
Schema::create('icons', function(Blueprint $table) {
$table->increments('id');
$table->string('name');
});
Schema::create('iconables', function(Blueprint $table) {
$table->integer('icon_id');
$table->integer('iconables_id');
$table->integer('iconables_type');
});
Now you just need to determine if the pages have an existing Icon. If they do, then hold reference to them so you can insert them:
$pagesWithIcons = Page::whereNotNull('icon')->get();
At this point you need to define the polymorphic relations in your models:
// icon
class Icon extends Model
{
public function pages()
{
return $this->morphedByMany(Page::class, 'iconable');
}
}
// page
class Page extends Model
{
public function pages()
{
return $this->morphToMany(Icon::class, 'iconable');
}
}
Now you just need to create the icons (back in our migration), and then attach them if they exist:
$pagesWithIcons->each(function(Page $page) {
$icon = Icon::firstOrNew([
'name' => $page->icon
});
$icon->pages()->attach($page);
});
The above is creating an Icon if it doesn't exist, or querying for it if it does. Then it's attaching the page to that icon. As polymorphic many-to-many relationships just use belongsToMany() methods under the hood, you have all of the available operations at your leisure if this doesn't suite your needs.
Finally, drop your icons column from pages, you don't need it.
Schema::table('pages', function(Blueprint $table) {
$table->dropColumn('icon');
});
And if you need to backfill support for only an individual icon (as the many-to-many will now return an array relationship), you may add the following to your page model:
public function icon()
{
return $this->icons()->first();
}
Apologies if typos, I did this on my phone so there may be some mistakes.

Create tables and fill them with my own data after migration - Laravel

I have a few tables in a database that need specific data. I know I can always save the SQL command and execute them but I wonder if Laravel has some sort of specific command.
You can use seeders for test data, like #Alexey says. If you need the data to persist in all environments (e.g. local and production), you can insert the data after creating the table, with the Query Builder.
e.g.
<?php
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* #return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
DB::table('posts')->insert([
'title' => 'Hello, world!',
'body' => 'This post will be created after migrating.',
]);
}
Laravel uses seeder classes to fill tables with data after migrations:
You need to create and register seeders and then run this command to run migration and seed the data:
php artisan migrate --seed