0

I am trying to create a relationship between categories and products. I have provided the various columns for both tables and I am trying to link them using a foreign id (the category_id from the category table) but I keep getting this error.

Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint failsIntegrity constraint violation: 1452 Cannot add or update a child row but it does not answer the question

This is my Category table

public function up()
{
    Schema::create('categories', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->timestamps();
    });
}

And this is the product table

public function up()
{
    Schema::create('products', function (Blueprint $table) {
        $table->increments('id');
        $table->unsignedInteger('category_id');
        $table->string('name');
        $table->string('description');
        $table->string('image');
        $table->integer('price');
        $table->foreign('category_id')
            ->references('id')
            ->on('categories')
            ->onDelete('cascade');
        $table->timestamps();
    });
}

This is the category model.

class Category extends Model
{
 use HasFactory;

protected $fillable = [
    'name',
];


public function products()
{
    return $this->hasMany(Product::class);
}

}

This is the product model

class Product extends Model
{
    use HasFactory;

    protected $fillable = [
        'name',
        'description',
        'image',
        'price',
        'category_id',
    ];

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

    public function setPriceAttribute($value)
    {
        $this->attributes['price'] = $value * 100;
    }
}

This is my Product Factory class

<?php
    
    namespace Database\Factories;
    
    use Illuminate\Database\Eloquent\Factories\Factory;
    
    class ProductFactory extends Factory
    {
       
        public function definition()
        {
            return [
                'name'=>$this->faker->unique()->word(),
                'description'=>$this->faker->text(),
                'image'=>$this->faker->imageUrl(100, 100),
                'price'=>$this->faker->numberBetween($min = 50, $max = 100),
                'category_id'=>$this->faker->randomDigit()
            ];
        }
    }

This is my db seeder class

class DatabaseSeeder extends Seeder

    {
       
        public function run()
        {
    
            Product::factory(20)->create();
        }
    }

This is the full error code I am getting please after running the db:seed

 INFO  Seeding database.  

Illuminate\Database\QueryException

SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`econs`.`products`, CONSTRAINT `products_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE) (SQL: insert into `products` (`name`, `description`, `image`, `price`, `category_id`, `updated_at`, `created_at`) values (omnis, Facere autem excepturi velit dolorem voluptas. Dignissimos laboriosam quia numquam sint harum officia eum. A aspernatur ratione fuga ut nesciunt sit. Ex nisi maxime quas., https://via.placeholder.com/100x100.png/006611?text=aut, 5700, 4, 2022-10-10 11:49:55, 2022-10-10 11:49:55))

  at vendor/Laravel/framework/src/Illuminate/Database/Connection.php:759
    755▕         // If an exception occurs when attempting to run a query, we'll format the error
    756▕         // message to include the bindings with SQL, which will make this exception a
    757▕         // lot more helpful to the developer instead of just the database's errors.
    758▕         catch (Exception $e) {
  ➜ 759▕             throw new QueryException(
    760▕                 $query, $this->prepareBindings($bindings), $e
    761▕             );
    762▕         }
    763▕     }

      +16 vendor frames 
  17  database/seeders/DatabaseSeeder.php:26
      Illuminate\Database\Eloquent\Factories\Factory::create()

      +23 vendor frames 
  41  artisan:37
      Illuminate\Foundation\Console\Kernel::handle()
Kennedy Owusu
  • 5,690
  • 4
  • 15
  • 20
  • 1
    Are you creating the categories table before the products table? – Oskar Mikael Oct 10 '22 at 12:01
  • Yes. I have created the categories table first – Kennedy Owusu Oct 10 '22 at 12:03
  • 2
    What are the categories that you have currently? Just check the id and name, and make sure you have a category id of 4. – aynber Oct 10 '22 at 12:06
  • make sure category id 4 is present in categories table – Ariful Islam Oct 10 '22 at 12:09
  • I am confuse about the category Id 4? Can any explain why that category id 4? – Kennedy Owusu Oct 10 '22 at 12:11
  • 2
    Because you're trying to insert a category ID of 4. Look at your insert query: `insert into \`products\` (\`name\`, \`description\`, \`image\`, \`price\`, \`category_id\`, \`updated_at\`, \`created_at\`) values ('omnis', 'Facere autem excepturi velit dolorem voluptas. Dignissimos laboriosam quia numquam sint harum officia eum. A aspernatur ratione fuga ut nesciunt sit. Ex nisi maxime quas.', 'https://via.placeholder.com/100x100.png/006611?text=aut', 5700, 4, '2022-10-10 11:49:55', '2022-10-10 11:49:55')` – aynber Oct 10 '22 at 12:11
  • I have run php artisan migrate: fresh to erase everything from the db. I am surprise it keeps increasing the category Id. Do you know why this is happening? – Kennedy Owusu Oct 10 '22 at 12:15
  • 1
    It seems to be happening during the seeder, so check both your category and product seeders. – aynber Oct 10 '22 at 12:20
  • I have added the factory and db seeder classes to the question. @aynber – Kennedy Owusu Oct 10 '22 at 12:27
  • 1
    `$this->faker->randomDigit()` creates a random integer from 0 to 9. Make sure categories already exist with those `id`s when running that factory. Or don't use `$this->faker->randomDigit()` (You won't have a category with `id=0` though so I suggest to use something different here) – brombeer Oct 10 '22 at 12:32
  • How do you suggest I go about this 'category_id'=>$this->faker->randomDigit() I want to create an auto-increment id for each product when the seeder runs – Kennedy Owusu Oct 10 '22 at 12:38
  • Get a random `id` from your already existing `categories` – brombeer Oct 10 '22 at 12:45

3 Answers3

1

You're using $this->faker->randomDigit() which creates a random digit, and doesn't care if the category ID exists or not. For your factory, you can grab a random category and use it's id:

'category_id'=>Category::inRandomOrder()->first()->id
aynber
  • 22,380
  • 8
  • 50
  • 63
1
'category_id' => Category::inRandomOrder()->select("id")->first()->id ?? 1;

It will fetch only one category in random order. After that give you the id attribute, If anything issue happens, then it will insert 1 as category id. But it will not through any exception.

S Ahmed Naim
  • 315
  • 1
  • 8
0

Try changing $table->unsignedInteger('category_id'); to $table->unsignedBigInteger('category_id'); and also confirm that there is category id 4 present in Categories table

  • 1
    The first half would have cropped up as an error during the migration, not an insert. They've already migrated successfully, they're just having problems with the insert. – aynber Oct 10 '22 at 12:11
  • `->increments()` "_creates an auto-incrementing UNSIGNED INTEGER equivalent column as a primary key_" though, so `unsignedBigInteger` wouldn't fit – brombeer Oct 10 '22 at 12:11