简体   繁体   中英

Laravel Many-To-Many Relationship Fails For One Model But Not The Other

I have the beginnings of a Laravel application with only two models: User and Project. There is a many-to-many relationship between Users and Projects, A single User can be working on many Projects, and a single Project can be worked on by many Users.

The problem I'm experiencing has manifested itself in two ways:

  1. After migrating and seeding my DB with basic data, I manually create a row in the project_user pivot table, linking references to the first entries of the users and projects tables: INSERT INTO project_user (user_id, project_id) VALUES (1, 1) . I then boot up Tinker in order to test the relationship. Accessing all Projects through the User model works as expected: User::find(1)->projects (returns a valid collection of Project models), however, the inverse relationship: Project::find(1)->users returns an empty collection, when it should be returning the User model referenced in the pivot table.
  2. And, when using a Seeder file to seed the project_user table like so:
public function run()
{
    $project = Project::first();
    $admin = User::first();

    if (isset($project) && isset($admin)) {
        echo "Project: $project\n";
        echo "User: $admin\n";
        // FIXME: Why isn't this working?
        $project->users()->attach($admin);
        // The inverse below also fails for the same reason
        // $admin->projects()->attach($project);
    }
}

Which causes the seed file to fail with the error: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'project_id' cannot be null (SQL: insert into project_user ( created_at , project_id , updated_at , user_id ) values (2020-02-02 17:01:04, ?, 2020-02-02 17:01:04, 1))

I've scoured my code, as well as the internet looking for a solution, to no avail. The problem must either be some very tiny mistake in my code, improper use of some function, or invalid order of operations. It's clear to me that there must be a problem with the association on the Project side, but I've literally compared Users and Projects side-by-side at every step and I can't figure out what's wrong. I'll paste the relevant code sections below, and if there's any additional information or code I can provide, I'm more than happy to do so. Thanks in advance for your help!

Here are the model definitions:

class User extends Authenticatable
{
    public $type;

    // Here is the relationship to the Projects this User is working on
    public function projects()
    {
        // SELECT * FROM project_user WHERE user_id = $user->id
        return $this->belongsToMany(Project::class)->withTimestamps();
    }


    use Notifiable;

    protected $fillable = [
        'name', 'email', 'password',
    ];

    protected $hidden = [
        'password', 'remember_token',
    ];

    protected $casts = [
        'email_verified_at' => 'datetime',
    ];
}
class Project extends Model
{
    public $id;

    public $name;

    public $deleted_at;

    // Here is the relationship to the Users working on this Project
    public function users()
    {
        // SELECT * FROM project_user WHERE project_id = $project->id
        return $this->belongsToMany(User::class)
            ->withTimestamps();
    }

    protected $fillable = [
        'name',
    ];

    protected $casts = [
        'deleted_at' => 'datetime',
    ];
}

The users and projects table migrations:

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->enum('type', ['admin', 'client', 'editor'])->default('client');
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->rememberToken();
        $table->timestamps();
    });
}
public function up()
{
    Schema::create('projects', function (Blueprint $table) {
        $table->bigIncrements('id');
        $table->string('name')->unique();
        $table->integer('memberCount')->default(0)->nullable(false);
        $table->timestamp('deleted_at')->nullable();
        $table->timestamps();
    });
}

And here is the pivot table project_user migration that sets the references:

public function up()
{
    Schema::create('project_user', function(Blueprint $table) {
        $table->bigIncrements('id');
        $table->unsignedBigInteger('user_id');
        $table->unsignedBigInteger('project_id');
        $table->timestamps();

        $table->unique(['user_id', 'project_id']);
        $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
        $table->foreign('project_id')->references('id')->on('projects')->onDelete('cascade');
    });
}

Try removing public $id; from your Project model (and the other redundant properties). With Eloquent, you don't need to declare the model properties that exist as columns in the backing table of the model. I believe what's happening is the $id property on your Project model is being called, which is null, because it is overriding the magic getter that Eloquent will provide for this column out of the box.

If you're declaring the properties on your model so your IDE picks them up, consider using the barryvdh/laravel-ide-helper package instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM