سؤال

Logic

A company has many users. Each user has many alerts. Each alert belongs to one location.

I want to display each user's alerts, and the alert's respective location, in a table. Preferably with the user's name at the side of each alert.

The company and user is linked through a pivotTable, a belongsToMany relationship. The alert belongsTo a location. The location hasMany alerts.

Alert model:

public function location()

    {
        return $this->belongsTo('Location');
    }

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

User model:

public function companies()
    {
        return $this->belongsToMany('Company')->withTimestamps();
    }

    public function alerts()
    {
        return $this->hasMany('Alert');
    }

Company model:

public function users()
{
    return $this->belongsToMany('User')->withTimestamps();
}

Location model:

public function alerts()
{
    return $this->hasMany('Alert');
}

So far, I have this within the controller:

public function getAdminIndex()
{   
    $company_id = User::find(Auth::user()->id)
        ->companies()
        ->first()
        ->id;
    $alerts = Company::with('users.alerts.location')
        ->where('id', '=', $company_id)
        ->get();
    $this->layout->content = View::make('agents.admin.index',
         array('alerts' => $alerts));
}

How I interpret the query

  1. Get the company ID of the authenticated user.
  2. Get each user that belongs to that company, along with any alerts they may have and their respective location.

Going over to the view, the MYSQL queries that have been executed seem to be correct, however, I cannot for the life of me print out the data, say for instance, in each alert.

I imaging the query would be something along the lines of (based on the models):

@foreach($alerts as $alert)
    <td>{{$alert->users->alerts->location->address_1}}</td>
@endforeach

But each time I am presented with one error or another - the most common one is Trying to get property of non-object.

Any help would be hugely appreciated on how I can display each user's alert and their respective location.

هل كانت مفيدة؟

المحلول

As I stated in the comment as may be seen in your previous questions: You must learn what is returned from the queries (Collection of models or Model) and how dynamic properties (loading relations) works. To make it perfectly clear for you, a little guidance first:

// Company model
public function users()
{
    return $this->belongsToMany('User')->withTimestamps();
}

// now you can do this:
$company = Company::find($someId);

$company->users;
// which does behind the scenes:
$company->users()->get();
// thus returned Collection

// Alert model
public function location()
{
  return $this->hasOne('Location');
}

// calling relation:
$alert = Alert::find($id);

$alert->location; // single model because it does the same as:
$alert->location()->first();

That being said, you may access related models like this:

$company->users->first()->alerts->first()->location->address_1;

// and for your needs use of course loops:

// don't call it $alerts as it returns company with some relations
$company = Company::with('users.alerts.location')
    ->where('id', '=', $company_id)
    ->first(); // use first not get for the reason I mentioned above

  @foreach ($company->users as $user)
    @foreach ($user->alerts as $alert)
      {{ $alert->location->address_1 }}
      ...
    @endforeach
  @endforeach

Trashed alerts:

// User model
// additional relation for ease of use
public function deletedAlerts()
{
   return $this->hasMany('Alert')->onlyTrashed();
}

// only change in the call is replacing alerts with deletedAlerts:
$company = Company::with('users.deletedAlerts.location')
    ->where('id', '=', $company_id)
    ->first();

@foreach ($company->users as $user)
  @foreach ($user->deletedAlerts as $alert)
    {{ $alert->location->address_1 }}
     ...
  @endforeach
@endforeach

نصائح أخرى

As log as your models have the correct relationship methods, I think that all your missing in your query (and hence the object error) is first(). Like this:

@foreach($alerts as $alert)

    <td>{{$alert->users->alerts->first()->location()->address_1}}</td>

@endforeach

It's not working because $alert->users is a collection and each $user has $alerts which is also another collection and you are trying to use a property from the collection, in this case, you need to loop all alerts:

@foreach($alerts as $alert) @foreach($alert->users->alerts as $alert1) {{ $alert1->location->address_1 }} @endforeach @endforeach

Actually, $alert->users is a collection so:

@foreach($alerts as $alert)
    @foreach($alert->users as $user)
        @foreach($user->alerts as $anAlert)
            {{ $anAlert->location->address_1 }}
        @endforeach               
    @endforeach       
@endforeach

Well, lets re-write the whole thing:

$companies = Company::with('users.alerts.location')
                    ->where('id', '=', $company_id)
                    ->get();

Now you have a collection of Company models with users (collection) and each users has another collection of Alert so multiple users and multiple alerts and then each Alert has one Location, so:

@foreach($companies as $company)
    // Each User belongs to multiple company, so multiple users here
    @foreach($company->users as $user)
        // Each User has multiple alerts
        @foreach($user->alerts as $anAlert)
            // Each location belongs to one alert
            {{ $anAlert->location->address_1 }}
        @endforeach
    @endforeach
@endforeach

To be honest you don't want the database calls in your controller but in your model to stay true to the MVC paradigm. I'm trying to wrap my head around this. I think you can restructure this to just relations and call everything inside your foreach loop.

@foreach($user->company()->users as $company_user)
<tr>
   <td>
   {{ $company_user->username }}
   </td>
   <td>
   @foreach($company_user->alerts() as $alert)
      {{ $alert->message }} - {{ $alert->location()->name }}
   @endforeach
   </td>
</tr>
@endforeach

With this setup and the right relations this should work out. It's a complicated structure. The pitfall is that the amount of alerts is probably variable (can be 0 or more right)? So you have to do this in one <td>. Hope you will get it done! Let us know if it worked out or made progression!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top