Search code examples
laraveleloquenteloquent-relationship

Laravel: Saving a relationship when instanciating an Eloquent model rises this SQL error: "Integrity constraint violation"


Summary

  1. Context and Needs
  2. Minimal, Testable and Executable sources (with instructions for testing)
  3. Actual Results and Expected Results
  4. What I've tried
  5. The Question

Context and Needs

The relationship between both Eloquent models GalleryImage and GalleryGroup is: GalleryImage * <-> 1 GalleryGroup. I want to save an instance of GalleryGroup, then of GalleryImage.

Minimal, Testable and Executable sources

Instructions to test

  • I wanted to show you how to test my code in the case you really want to do it ;-) . However, I think you don't actually need to test. Indeed, the code is very simple. By reading it, if you know more than me Laravel, maybe you will find the problem and be able to bring me some help. I let you reading the following contents but I think you'll agree with me.

  • Create the tables for GalleryGroup and GalleryImage (out of topic). The fields to create and the name of the tables are contained in the following sources.

  • Copy/Paste the Eloquent models and the script that instanciates them and tries to save them in DB.

  • Creates the routes of your choice to run the script and then, run the script (ie.: access the Web page or use a REST client)

The Eloquent models

-- GalleryGroup.php

<?php

namespace App\Models;

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

class GalleryGroup extends Model
{
    use HasFactory;

    protected $primaryKey = 'group_id';
    private $name;
    
    public function images() {
        return $this->hasMany(GalleryImage::class);
    }
}

-- GalleryImage.php

<?php

namespace App\Models;

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

class GalleryImage extends Model
{
    use HasFactory;

    protected $primaryKey = 'image_id';     

    public function group() {
        return $this->hasOne(GalleryGroup::class, 'group_id', 'image_id');
    }
}

Instanciations and concretization of the relationship

The Eloquent model GalleryGroup is instanciated and saved in db; then, the Eloquent model GalleryImage is instanciated and should be saved in db:

$img_group = new GalleryGroup();
$img_group->name = 'foobar';
$img_group->save();

$image = new GalleryImage();
var_dump($img_group->group_id);  // It exists and it's not empty
$image->group()->save($img_group);
$image->save();

Actual Results and Expected Results

The last line is never executed because this error is raised at the line $image->group()->save($img_group);:

Illuminate\Database\QueryException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'group_id' cannot be null (SQL: update gallery_groups set group_id = ?, gallery_groups.updated_at = 2021-01-09 10:16:44 where group_id = 24) in file /var/www/html/api/vendor/laravel/framework/src/Illuminate/Database/Connection.php on line 671

I don't understand why it tries to update the group entry, and I don't understand why group_id is NULL or empty, because $img_group actually has a non-empty group_id (cf.: the line var_dump($img_group->group_id);).

The actual results are: 1) the model GalleryGroup is correctly instanciated and correctly saved in db and 2) the model GalleryImage is correctly instanciated and not saved in db because of the above SQL error is raised.

The expected results are: 1) the model GalleryGroup is correctly instanciated and correctly saved in db and 2) the model GalleryImage is correctly instanciated and saved in db.

What I've tried

I've tried to var_dump several times several variables but did not found any relevant information to help debugging this issue.

I've read and re-read the docs https://laravel.com/docs/8.x/eloquent-relationships#the-save-method and https://laravel.com/docs/8.x/eloquent#primary-keys but did not found any relevant information to help debugging this issue.

The Question

Why is this error raised and how to fix it?


Solution

  • One of these relationships needs to be a belongsTo as one of these tables has the foreign key on it that relates to the other table. I would assume a GalleryImage belongs to a GalleryGroup:

    GalleryGroup
        images
            hasMany GalleryImage
    
    GalleryImage
        gallery
            belongsTo GalleryGroup
    

    Once those are setup correctly you should be able to do this to save the relationship:

    $img_group->images()->save($image);