Search code examples
laravelmigrationlaravel-10

1452 - Cannot add or update a child row: a foreign key constraint fails in Laravel 10 - SQLSTATE[23000]: Integrity constraint violation


Hi I am facing an issue to store record in categories table having its own id(Primary Key) as parent_id as foreign key (check the migration for details), when i save the data it shows following error.

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`laravel`.`categories`, CONSTRAINT `categories_parent_id_foreign` FOREIGN KEY (`parent_id`) REFERENCES `categories` (`id`))

My Migration Code is

Schema::create('categories', function (Blueprint $table) {
            $table->id();
            //$table->integer('parent_id')->default(0);
            $table->bigInteger('parent_id')->nullable()->unsigned()->default(0);
            $table->string('name');
            $table->string('slug');
            $table->text('description')->nullable();
            $table->string('image');
            $table->string('meta_title')->nullable();
            $table->string('meta_description')->nullable();
            $table->string('meta_keywords')->nullable();
            $table->integer('navbar_status')->nullable()->default(0);
            $table->integer('is_active')->nullable()->default(0);
            $table->timestamps();
            //$table->foreign('parent_id')->references('id')->on('categories');
        });
        Schema::table('categories', function (Blueprint $table) {
            $table->foreign('parent_id')->references('id')->on('categories');
        });

My Controller Code to Store the Record is

public function store(CategoryFormRequest $request){

        $data = $request->validated();

        $category = new Category();

        $slug = Str::of($data['name'])->slug('-');
        

        if ($request->hasFile('image')) {
            $file = $request->file('image');
            $category_image = date('ymd')."-".time()."c.".$file->getClientOriginalExtension();
            $file->move('uploads/category/', $category_image);
        }

        if($request->hasFile('banner_image')){
            $bnr_file = $request->file('banner_image');
            $cat_banner_image = date('ymd')."-".time().'b.'.$bnr_file->getClientOriginalExtension();
            $bnr_file->move('uploads/category/',$cat_banner_image);
        }

        $category->parent_id        = $data['parent_id'];
        $category->name             = $data['name'];
        $category->slug             = $slug;
        $category->description      = $data['description'];

        $category->image            = $category_image;
        $category->banner_image     = $cat_banner_image;

        $category->meta_title       = $data['meta_title'];
        $category->meta_description = $data['meta_description'];
        $category->meta_keywords    = $data['meta_keywords'];
        $category->navbar_status    = $request->navbar_status == true ? 1 : 0;
        $category->is_active        = $request->is_active == true ? 1 : 0;
        $category->is_featured      = $request->is_featured == true ? 1 : 0;
        

        $category->created_by       = Auth::user()->id;

        $category->save();

        return redirect()->route('category-create')->with('success', 'Category Added Successfully');
    }

Solution

  • in your migration file use this instead of yours

    Schema::create('categories', function (Blueprint $table) {
                $table->id();
    
               $table->foreignId('parent_id')
                    ->nullable()
                    ->references('id')
                    ->on('categories');
    
                $table->string('name');
                $table->string('slug');
                $table->text('description')->nullable();
                $table->string('image');
                $table->string('meta_title')->nullable();
                $table->string('meta_description')->nullable();
                $table->string('meta_keywords')->nullable();
                $table->integer('navbar_status')->nullable()->default(0);
                $table->boolean('is_active')->default(false);
    
                $table->foreignId('created_by')
                    ->references('id')
                    ->on('users');
    
                $table->foreignId('updated_by')
                    ->nullable()
                    ->references('id')
                    ->on('users');
                $table->timestamps();
    });
    

    when you are injecting a FormRequest class as dependency in Laravel, Laravel will validate the request automatically for you thus you dont need to validate it again in your controller. In your code use this: Dont forget if $request->all() is not suitable then use $request->only(['name', 'description', ...]). Try dd($request->all()) and dd($request->only(['name', 'description', ...]) to see the content to make sure every thing is fine.

    public function store(CategoryFormRequest $request, Category $category){
            
            if ($request->hasFile('image')) {
                $file = $request->file('image');
                $category_image = date('ymd')."-".time()."c.".$file->getClientOriginalExtension();
                $file->move('uploads/category/', $category_image);
            }
    
            if($request->hasFile('banner_image')){
                $bnr_file = $request->file('banner_image');
                $cat_banner_image = date('ymd')."-".time().'b.'.$bnr_file->getClientOriginalExtension();
                $bnr_file->move('uploads/category/',$cat_banner_image);
            }
    
            Category::create($request->all());
    
            return redirect()->route('category-create')->with('success', 'Category Added Successfully');
        }
    

    next step is to create a listener for your model which called observer in artisan command php artisan make:observer CategoryObserver

    namespace App\Observers;
    
    use App\Models\Category;
    
    class CategoryObserver
    {
        public function creating(Category $category)
        {
            $category->created_by = auth()->id();
            $category->slug = Str::of($category->name)->slug('-');
        }
        
        public function updating (Category $category)
        {
            $category->updated_by = auth()->id();
        }
    }
    

    and now you have to register your observer to your model, open App\Providers\EventServiceProvider and add this line of code to the boot method like so:

    
    namespace App\Providers;
    
    
    use App\Models\Category;
    use App\Observers\CategoryObserver;
    use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
    
    
    class EventServiceProvider extends ServiceProvider
    {
        protected $listen = [
        ];
    
        public function boot(): void
        {
            Category::observe(CategoryObserver::class); // add this line of code
        }
    
        public function shouldDiscoverEvents(): bool
        {
            return false;
        }
    }