Search code examples
laraveleloquentlaravel-8eloquent-relationship

Laravel relationships, Many to Many or Has Many Through?


I'm building a web app using Laravel 8 and one thing I tend to struggle with is the complex relationships and accessing the data. I've started reading on hasManyThrough relationships but I'm not convinced that's the correct way to do it for my scenario.

The app is used by travelling salesmen to check in to a location when they arrive safely.

I have three main tables:

  • Locations (where they are visiting),
  • Checkins (where their check in data is stored, e.g. time),
  • Users

This is where it gets a little complex and where I find myself not being able to see the wood for the trees...

  • Locations have many users, users have many locations. Many-to-many pivot table created.
  • Locations have many check ins, check ins have many locations. Many-to-many pivot table created.
  • Check ins have many users, users have many check ins. Many-to-many pivot table created.

As such they're all created using a belongsToMany relationship.

Now what I'd like to do is access the user of the check in so I can call something similar to on my show.blade.php:

<tbody>
 @foreach($location->checkins as $checkin)
  <tr>
   <td>{{ $checkin->id }}</td>
   <td>{{ $checkin->users()->name }}</td> // I'd like to get the users name
   <td>{{ $checkin->longitude }}</td>
   <td>{{ $checkin->latitude }}</td>
   <td>{{ \Carbon\Carbon::parse($checkin->created_at)->format('jS F Y') }}</td>
  </tr>
 @endforeach
</tbody>

Is a hasManyThrough relationship the only way I can achieve this? Or should I get a bit more creative with my controller and bring users through on their own collection?

My current Location controller looks like this:

public function show(Location $location)
 {
  $page_title = "Locations";
  $page_description = "Event locations.";

  return view('pages.admin.locations.show', compact('page_title', 'page_description'))
    ->with('location' => $location);
 }

Any direction or best practice advice is appreciated.


Solution

  • You said "Check ins have many users", you seem to want the singular user for a check-in, but currently that would result in many users. It sounds like users check-in in a many to one relationship

    Also $checkin->created_at will be by default a carbon object, so you can just go $checkin->created_at->format('jS F Y')

    Also don't mix using compact and with, stay consistent and use only 1 as they achieve the same thing

    Also $checkin->users()->name won't work, if you use the brackets on syntax is only returns a query builder instance, which you would need to call get on like $checkin->users()->get(), or you could use $checkin->users which will fetch them anyway. You may want to look into using with('relation') on query builder instances to stop N+1 queries if you want to dive a little deeper. Lastly $checkin->users()->get()->name also won't work as your user relation is many to many which returns a collection, which again points to you should have a belongsTo relationship called user without a pivot table