Laravel - retrieve direct/indirect pivot table subscribers - laravel

I am having a hard time figuring this out in laravel; for a subscriber relationship we have 2 pivot tables:
A subscriber can subscribe to a question:
question_subscriber
id question_id subscriber_id
1 2 3
2 2 4
3 3 1
4 3 2
A subscriber can subscribe to a user:
user_subscriber
id user_id subscriber_id
1 1 6
2 1 7
3 2 1
Normal questions table with question owner:
questions
id question user_id
1 abc? 1
2 xyz? 1
3 123? 2
The pivot relationships are setup correctly in their models, and I can pull out with a foreach, subscribers for a question, $question->subscribersQuestion as $subscriber, or subscribers for a user, $user->subscribersUser as $subscriber:
But how can we pull out all subscribers that belong to a user (directly or indirectly (through a question they own))?
For example, these should be all retrieved subscribers for user 1:
6 // subscribed to directly
7 // subscribed to directly
3 // subscribed to through question
4 // subscribed to through question
Any idea on the most efficient way to do this in Laravel?

This will be the easiest way:
// assuming this setup:
User hasMany Question
User belongsToMany User (subscribers - direct)
Question belongsToMany User (subscribers - indirect)
// we don't need this, since we pass it by reference to the closure,
// but for clarity let's initialize this variable:
$indirectSubscribers = null;
$user = User::with(['questions.subscribers' => function ($q) use (&$indirectSubscribers) {
// get all indirect subscribers (cost of additional query)
$indirectSubscribers = $q->get()->unique();
}])->find($userId);
// merge with directs subscribers
$allSubscribers = $user->subscribers->merge($indirectSubscribers);

Related

Laravel - Add additional where constraints from Eloquent Model belongsToMany relationship

I am new to Laravel (using v7) so I apologise if I am doing this incorrectly.
I have a custom subscription setup for my users.
I have my users table
id
first_name
last_name
1
John
Doe
2
Jane
Doe
I have my subscriptions table
id
name
1
Basic
2
Bronze
3
Silver
4 ​
Gold
5 ​
Platinum
And a subscription_user pivot table
id
user_id
subscription_id
created_at
expired_at
1
1
1
2021-02-01 23:22:12
2021-03-21 08:22:12
2
1
5
2021-03-21 08:22:12
2021-04-04 09:03:21
2
1
3
2021-04-04 09:03:21
2
2
1
2021-01-01 01:00:00
2021-01-05 05:30:00
2
2
2
2021-01-05 05:30:00
2021-01-06 08:34:10
So as per the above entries, John Doe subscribed on the Basic subscription, then upgraded to the Platinum and then finally downgrading to the Silver. Which is his current subscription.
I have the following methods (within User.php) for first getting a list of the users subscriptions
public function subscriptions()
{
return $this->belongsToMany('App\Subscription')
->orderby('pivot_created_at', 'desc')
->withPivot('created_at', 'expired_at')
->withTimestamps();
}
And then to get the current subscription
public function subscription()
{
return $this->subscriptions->first();
}
However there could be instances where by a user has all subscriptions expired, therefore they have no current subscription in place. i.e in the above records Jane Doe who subscribed to 2 plans but both have expired.
If i call $user->subscription() on Jane Doe it still returns the latest expired entry.
I have tried added where() clauses into my subscription method but it has no effect:
public function subscription()
{
return $this->subscriptions->where('expired_at', NULL)->first();
}
Is there a better way to do what I am trying to achieve? I need a method to fetch all the users subscriptions, whether they are active or expired. (which I have - is this the best way?)
But then also fetch the current subscription which should always not have an expired_at.
It seems I cannot add any further conditions within mt subscription method.
You need to use the query builder for the subscription method using subscriptions() instead of the collection subscriptions
public function subscription()
{
return $this->subscriptions()->whereNull('expired_at')->first();
}
calling $this->subscriptions is equivalent to $this->subscriptions = $this->subscriptions()->get(). where $this->subscriptions returns a collection.
$this->subscriptions(), using the method, return a query builder.
PS: it did not trigger an error calling first() because a Collection has a method called first() too.

Laravel : How to show a multiple rows from DB in one row in view blade

In my Laravel project, I have a multiple rows in my DB table, similar to each other in every thing except ID, other column called stage
Stages table
ID
name
1
first
2
second
3
third
subjects Table
ID
name
stage_id
1
English
1
2
English
2
3
English
3
I need to show em in a blade like this
#
name
stage
1
English
first, two, three, etc
I already have the relations in my modals, and I am already showing them in my blade by loop, but I just want to group the rows by name and show the different stages
I hope that I explained the case well, I am not very good at explaining
Updates : before editing I wrote a dummy data now I hope I explained the issue in more details
The project idea is that you can create more than faculty
and every faculty has Stages and sections and every section has specialties and every specialty has subjects
now about subjects and stages
user can create subject like English and make it available to the first stage and 2nd and third ..etc ( the available stages in this faculty)
The Stages belongs to Faculty
The Subjects belongs to Stage
so the relations are
Subject Model
public function stage()
{
return $this->belongsTo(Stage::class);
}
Stage Model
public function subjects()
{
return $this->hasMany(Subject::class);
}
As you have "English" Multiple Times, you should have a subject Table :
Stages table
ID
name
1
first
2
second
3
third
Subject table
ID
name
1
English
stage_subject Table :
ID
stage_id
subject_id
1
1
1
1
2
1
1
3
1
Subject Model
public function stage()
{
return $this->belongsToMany(Stage::class);
}
Stage Model
public function subjects()
{
return $this->belongsToMany(Subject::class);
}
Then in your foreach:
#php($subjects = Subject::with('stages')->get())
#foreach($subjects as $subject)
{{ $subject->id }} | {{$subject->name}} | {{$subject->stages->pluck('name')->implode(', ')}}
#endforeach
If you don't / can't do a pivot table, then you can use collection :
#php($subjects = Subject::with('stages')->get())
#foreach($subjects->groupBy('name') as $name => $subjects)
{{ $subjects->first()->id }} | {{$name}} | {{$subjects->pluck('stage.name')->implode(', ')}}
#endforeach

Get ID from while looping

I want to make an employee leave application, where when an employee submits leave request, his boss will immediately get a notification in the email
My table in database like this
id | name | email | person_in_charge |
1 Michael michael.com 2
2 Johan johan.com 4
3 Lorem lorem.com 2
4 Ipsum ipsum.com 5
5 Dolor dolor.com
I want to, when user id 1 sends an email, then the one who gets the email is user id 2, 4 and 5, because user id 2 is the boss of user id 1, and user id 4 is the boss of user id 2, and user id 5 is boss all of them.
I've doing using while looping, but the result is the one who gets the email is user id 4 and 5, user id 2 not get the email
Here's the code
$currentuserid = '1';
$show= Users::where('id',$currentuserid)->first();
if($show->pic_for!=null)
{
$pic_for = $tampil->pic_for;
while($pic_for!=null)
{
$query = Users::where('id',$pic_for)->first();
if($query->pic_for==null)
{
break;
}else
{
$pic[]=$query->pic_for;
$pic_for = $query->pic_for;
}
}
dd($pic);
Do you know where I missed ?
Thank you
You simply do not save the first reference, when you enter the loop. The first $pic_for is never added to the array $pic.
EDIT/BTW: This code can basically DDoS your own database with that loop. Just an idea, but instead of looping through single instances, you could create a HABTM relation between them. So you could query like $user->usersInCharge() and iterate over this collection.

Getting the sum of numbers

Answer has many rates:
Answer Model:
public function rates()
{
return $this->hasMany('App\Rate');
}
Rates table:
id answer_id user_id rating
1 3 1 5
2 3 2 8
How do i get the specific answer with sum of rating?
From your question, you have two classes Answer and Rate. Also, there is hasMany relationship from Answer to Rate named 'rates'.
So for the sum of ratings of a specific answer, you can call 'sum' method from 'rates' relationship on 'Answer' model.
$answer = App\Answer::find(1);
$sum_of_rating = $answer->rates->sum('rating');

Does Eloquent relation sync also remove?

When updating a model and I sync a relationship, if I don't pass in all the ids that already exist, will that relationship be removed?
You decide: sync has 2nd parameter that defaults to true and is responsible for detaching:
$model->relationship()->sync([1,2,3]);
$model->relationship()->sync([4,5,6]); // attached [4,5,6], detached [1,2,3]
$model->relationship()->getRelatedIds(); // [4,5,6]
// but:
$model->relationship()->sync([4,5,6], false); // attached [4,5,6], detached []
$model->relationship()->getRelatedIds(); // [1,2,3,4,5,6]
The answer is Yes it does. I could not find any documentation that in fact stated that.
lets say you have 2 tables: "authors" and "books", with a pivot table "book_authors".
when creating a new author:
$author_id =2;
$author->books()->sync(array(1,4,5,15));
Now you have a 4 entries in that pivot table "book_authors":
author_id book_id
2 1
2 4
2 5
2 15
Now update:
$author_id =2;
$author->books()->sync(array(1,15));
now "book_authors" is:
author_id book_id
2 1
2 15

Resources