1

I have a Plan model and a User model, the User has one plan, and the plan belongs to many Users;

When I run php artisan migrate:fresh I get this error:

** SQLSTATE[HY000]: General error: 1005 Can't create table service6_servicelandv1.0.users (errno: 150 "Foreign key constraint is incorrectly formed") (SQL: alter table users add constraint users_plan_id_foreign foreign key (plan_id) references plans (id) on delete set null) **

here are the migrations:

User migrations

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string("avatar_name")->default("default-male.jpg");
            $table->string('username')->unique();
            $table->string("email")->unique();
            $table->string('password');
            $table->string("role")->default('Regular')->nullable();
            $table->string("address")->nullable();
            $table->bigInteger("reputation")->default(0);
            $table->string("phone_number")->nullable();
            $table->float("wallet", 10, 2)->default(0);
            $table->unsignedBigInteger("plan_id")->nullable();
            $table->unsignedBigInteger("option_id")->nullable();
            $table->unsignedBigInteger("category_id")->nullable();//fav_category
            
            $table->rememberToken();
            $table->timestamp('email_verified_at')->nullable();
            $table->timestamp("created_at")->useCurrent();
            $table->timestamp("updated_at")->useCurrent();

            $table->foreign("plan_id")->references("id")->on("plans")->onDelete('set null');
            $table->foreign("option_id")->references("id")->on("options")->onDelete('set null');
            $table->foreign("category_id")->references("id")->on("categories")->onDelete('set null');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('users');
    }
};

And for the Plan migrations:

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('plans', function (Blueprint $table) {
            $table->id("id");
            $table->string("name");
            $table->integer("ads_number");
            $table->decimal('amount_credit', 9, 3, true);
            $table->decimal('price', 9, 3, true);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('plans');
    }
};

User Model:

<?php

namespace App\Models;

use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
// use Laravel\Sanctum\HasApiTokens; // comment this
use Laravel\Passport\HasApiTokens; // include this

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        "avatar_name",
        'username',
        'email',
        'password',
        "address",
        "role", 
        "reputation", 
        "wallet",
        "phone_number",
        "plan_id",
        "option_id",
        "category_id",
        'confirmation_password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
    /**
     * Get the image url.
     *
     * @param  string  $value
     * @return string
     */
    public function getAvatarNameAttribute($value){
        return asset('storage/avatars/' . $value);
    }

    public function role(){
        return $this->hasOne(Role::class);
    }

    public function category(){
        return $this->hasOne(Category::class);
    }

    public function plan(){
        return $this->hasOne(Plan::class);
    }

    public function option(){
        return $this->hasOne(Option::class);
    }

    public function postStars(){
        return $this->hasManyThrough(PostStar::class, Post::class);
    }
}

Plan Model:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Plan extends Model
{
    use HasFactory;

    protected $fillable=[
        "name",
        "ads_number",
        "amount_credit",
        "price"
    ];

    public function user(){
        return $this->belongsTo(User::class);
    }
}

Please I am really stuck for like two hours now and I don't understand what's going on??! what's wrong with that set null??

  • have you checked if the order of migration is correct ? what is the file name of the users and plans tables migrations ? – N69S Aug 07 '22 at 01:37
  • In additional to checking table migration order, why are you using `$table->unsignedBigInteger("plan_id")->nullable();` instead of proper methods to build the foreign key? https://laravel.com/docs/9.x/migrations#foreign-key-constraints – miken32 Aug 07 '22 at 02:17
  • Thanks @N69S the problem was in the order I was running the migrations after I fixed that order it works – Mohammed El Ouali Aug 07 '22 at 08:33

2 Answers2

0

You can simply use this method to assign foreign keys:

$table->foreignId('user_id')
  ->nullable()
  ->constrained()
  ->onUpdate('cascade')
  ->onDelete('set null');

This is way better than other methods.

Check Documentation

0

In Laravel you can use nullOnDelete()

$table->foreignId('plan_id')
        ->nullable()
        ->constrained('plans')
        ->nullOnDelete();

Go through the Laravel Foreign Key Constraints to get some idea on Foreign Key declarations

Abdulla Nilam
  • 36,589
  • 17
  • 64
  • 85