1

you can see a part of my database in the image below:

the goal of this design was to able the admins send alerts to users filtered by center and field(dynamically). if you think this is a bad design tell me (please say why and how should I improve my design).

enter image description here

now if I want to get the alerts for a user I should do this:

    $userAlerts = collect();

    auth()->user()->branch()->first()->groups()->get()->each(function ($item, $key) use ($userAlerts) {
        $item->alerts()->get()->each(function ($item, $key) use ($userAlerts) {
            $userAlerts->push($item);
        });
    });

this code is ridiculous and I don't want to do it in this way.

instead i want to do something like this:

    $alerts = auth()->user()->branch()->groups()->alerts() // i want this // method 1

or

    $alerts = auth()->user()->alerts() //or maybe this // method 2

can I do something like this without changing the database? (method 1 or 2).

Mohammad Hossein
  • 404
  • 7
  • 21

1 Answers1

2

You can use laravel's default Has Many Through on branch model to pick associated alerts

class Branch extends Model
{

    public function alerts()
    {
        return $this->hasManyThrough(
            'App\Models\Alert',
            'App\Models\Group',
            'branch_id', // Foreign key on alert table...
            'group_id', // Foreign key on group table...
            'id', // Local key on branch table...
            'id' // Local key on group table...
        );
    }

}

Uisng this way you can query user alerts as

$alerts = auth()->user()->branch()->alerts()

Or you can use third party package eloquent-has-many-deep which provides an ease to extend hasManyThrough on multiple related models

class User extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function alerts()
    {
        return $this->hasManyDeep(
            'App\Models\Alert',
            ['App\Models\Branch', 'App\Models\Group'], // Intermediate models, beginning at the far parent (User).
            [
               'user_id', // Foreign key on the "branch" table.
               'branch_id',    // Foreign key on the "group" table.
               'group_id'     // Foreign key on the "alert" table.
            ],
            [
              'id', // Local key on the "user" table.
              'id', // Local key on the "branch" table.
              'id'  // Local key on the "group" table.
            ]
        );
    }
}

And you can directly get user alerts as

$alerts = auth()->user()->alerts()

Lastly you can define alerts relation in user model by adding some joins to directly pull related alerts

class User extends Model
{
  
    public function alerts()
    {
        return $this->belongsTO('App\Branch', 'branch_id')
            ->join('branch_group', 'branches.id', '=', 'branch_group.branch_id')
            ->join('alerts', 'branch_group.group_id', '=', 'alerts.group_id')
            ->select('alerts.*');
    }

}
Mohammad Hossein
  • 404
  • 7
  • 21
M Khalid Junaid
  • 63,861
  • 10
  • 90
  • 118
  • does your first solution (Has Many Through) work on **many to many** relations? (in this case groups to branches) because there is a pivot table and no `group_id` on `groups` table and no `branch_id` on `alerts` table. – Mohammad Hossein Aug 22 '20 at 11:17
  • **_thanks for your answer_**. your last solution is very interesting I like it. – Mohammad Hossein Aug 22 '20 at 11:18
  • 1
    @alien not quite sure for `hasManyThrough` but `hasManyDeep` (third party package) can handle many-to-many relations – M Khalid Junaid Aug 22 '20 at 11:35