1
\$\begingroup\$

I'm building a Laravel app (with Vue3 and InertiaJS), and I'm attempting to create relationships between three models:

  • User
  • Area
  • WorkHours

where WorkHours is the primary model. In addition to the main data (date, hours, comments, reason), I need to create the following relationships:

  1. User - the WorkHours entry is for work performed by the member (aka User), so I need to be able to see total hours for the user for the year. One user can have many WorkHours records.
  2. Area - The part of the club that the work hours has been performed in/for. Each WorkHours entry has one approver, and one approver can be associated with many WorkHours entries.
  3. Approver - Every Area has specific people that can approve WorkHours entries, and these are defined as approvers in the Area model related to the User model as approver_id (a belongstoMany() relationship both ways). Each WorkHours entry has one approver, but one approver can be associated with many WorkHours entries.

It seems then that the work_hours table should have _id values for each of the three relations (user_id, area_id, and approver_id). However, as I read the Eloquent docs, that means that WorkHours would be a child of both Area and Approver via belongsTo(), since the child gets the _id value of the parent.

So with all that here's the code:

work_hours migrations

    public function up()
    {
        Schema::create('work_hours', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->unsignedBigInteger('area_id');
            $table->unsignedBigInteger('approver_id');
            $table->date('date');
            $table->decimal('hours', 5, 2);
            $table->string('reason')->nullable();
            $table->string('comments')->nullable();
            $table->timestamps();
            $table->softDeletes();

            $table->foreign('user_id')
                ->references('id')
                ->on('users');

            $table->foreign('approver_id')
                ->references('id')
                ->on('users');

            $table->foreign('area_id')
                ->references('id')
                ->on('areas');
        });
    }

WorkHours

class WorkHours extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'date',
        'hours',
        'reason',
        'comments'
    ];

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

    public function approver()
    {
        return $this->belongsTo(User::class, 'approver_id');
    }

    public function area()
    {
        return $this->belongsTo(Area::class);
    }
}

Area

class Area extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'name'
    ];

    public function approvers()
    {
        return $this->belongsToMany(User::class, 'area_user', 'area_id', 'user_id');
    }

    public function workHours()
    {
        return $this->hasMany(WorkHours::class);
    }
}

User

class User extends Authenticatable
{
    public function workHours()
    {
        return $this->hasMany(WorkHours::class);
    }

    public function areas() {
        return $this->belongsToMany(Area::class);
    }

    public function approval()
    {
        return $this->belongsTo(WorkHours::class, 'approver_id');
    }
}

Now where I'm running into the problem is saving everything when I submit my form to my controller. From my form I have the following pieces of info

  • user_id
  • area_id
  • approver_id
  • date
  • hours
  • reason
  • comments

So based on the docs, I would think I would do something like this in WorkHoursController:

    public function store(StoreWorkHoursRequest $request)
    {
        $validated = $request->validated();

        $user = User::find($validated['user']);
        $area = Area::find($validated['area']);
        $approver = User::find($validated['approver']);

        $wh = WorkHours::make([
            'date' => $validated['date'],
            'hours' => $validated['hours'],
            'reason' => $validated['reason'],
            'comments' => $validated['comments']
        ]);

        $wh->user()->associate($user);
        $wh->area()->associate($area);
        $wh->approver()->associate($approver);

        return Redirect::route('workHours.index')->with('success', 'Work Hours saved');
    }

but I get an error about user_id not having a default value, which is true, since my understanding is that Eloquent handles assigning that. What I can do is make user_id, approver_id, and area_id fillable in the WorkHours model and save like this:

        $wh = WorkHours::create([
            'date' => $validated['date'],
            'hours' => $validated['hours'],
            'reason' => $validated['reason'],
            'comments' => $validated['comments'],
            'user_id' => $validated['user'],
            'approver_id' => $validated['approver'],
            'area_id' => $validated['area']
        ]);

and it works, but it is cloogy, and bypasses Eloquent.

  1. Do I have my relationships set up properly for what I am trying to accomplish?
  2. How do I need to store the records in my controller to properly use Eloquent?
\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

My issue (as I suspected) was in a relationship definition. Once I changed the approval() definition in User from this

    public function approval()
    {
        return $this->belongsTo(WorkHours::class, 'approver_id');
    }

to this

    public function approvals()
    {
        return $this->haMany(WorkHours::class, 'approver_id');
    }

it worked fine. There was no user id value to write since the relationship for the approver was wrong, which was why it was looking for a default value.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.