Retrieve all models that are not associated with another model through pivot - php

I have three tables: users, organizations, organization_user. organization_user is a pivot table for the many-to-many relationship between users and organizations. The many-to-many relationships have been setup correctly in the corresponding models.
I need to obtain all users who are not associated with a given organization. How should this be done using eloquent. Below is what I have tried, but it is returning no results:
public function associate_user($organization_id){
$data = [
'organization' => \App\Organization::find($organization_id),
'users' => \App\User::whereDoesntHave('organizations', function($query) use ($organization_id){
$query->where('organization_id', $organization_id);
})
];
return view('admin.associateUser', $data);
}

You are never actually executing the query.
You need to call get() at the end of the query builder.
public function associate_user($organization_id) {
$data = [
'organization' => \App\Organization::find($organization_id),
'users' => \App\User::whereDoesntHave('organizations', function($query) use ($organization_id){
$query->where('organization_id', $organization_id);
})->get(); // Add the call to get()
];
return view('admin.associateUser', data);
}

Related

Getting specific column in Laravel relation returning empty array

I want to get specific column from Model relation in Laravel, but the relation model returning empty array.
Eloquent Query
$medicines = Medicine::with(['weightage' => function($query) {
$query->select('name');
}])->get(['name', 'description']);
Medicine Modal
public function weightage()
{
return $this->hasMany('App\MedicineWeightage', 'medicine_id');
}
You always need to also select the primary and foreign keys of the table to make the relation work:
$medicines = Medicine::with(['weightage' => function($query) {
$query->select(['id', 'name', 'medicine_id']);
}])->get(['id', 'name', 'description']);

How to get pivot data inside eloquent withCount function callback?

I'm trying to use a pivot variable of a parent relationship inside the eloquent withCount() method.
Background:
There is a application with a ManyToMany relationship between Users and Clusters. Users can send messages within a cluster. To keep track of the unread message count for a user in a specific cluster i keep track of the last read message id in the join table, like so:
table: cluster_user
cluster_id | user_id | last_read_message_id
-------------------------------------------
1 | 59 | 3
2 | 62 | 8
The User() model has a belongsToMany() relation with the Cluster() model
The Cluster() model has a belongsToMany() relation with the User() model
The Cluster() model has a hasMany() relation with the Messages() model
The Message() model has a belongsTo() relation with the Cluster() model
Now I would like to list all the clusters of the authenticated user including a unread message count.
Currently I'm stuck on this:
$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', '???');
}])->get();
I've already tried:
$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', 'cluster_user.last_read_message_id');
}])->get();
But this gives me a total count of all the messages in stead of the ones with an id higher than x.
If I hardcode an id, like this:
$clusters = Auth::user()->clusters()->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', '3');
}])->get();
Then I get the correct unread message count.
So can somebody tell me how to use the pivot variable 'last_read_message_id' of the user()->cluster() relationship inside the withCount() callback function with the following in mind:
I'ts crucial to use as little queries as possible.
The unread message count must be a part of the cluster() collection because I'm returning a ClusterResource later on, like so:
return ClusterResource::collection($clusters);
which includes the unread message count.
class ClusterResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* #param \Illuminate\Http\Request $request
* #return array
*/
public function toArray($request)
{
return [
'name' => $this->name,
'unread_messages_count' => $this->whenPivotLoaded('cluster_user', $this->messages_count)
];
}
}
Thnx!
Found the answer due to a comment of #cbaconnier.
Placing DB::raw('cluster_user.last_read_message_id') on the spot is working. I't not neat, but it works.
Full example:
$clusters = Auth::user()
->clusters()
->withCount(['messages' => function ($query) {
$query->where('messages.id', '>', DB::raw('cluster_user.last_read_message_id'));
}])
->get();
Good question! I think you should be able to do use the withPivot method on your relationship and then use the pivot attribute in your query.
For example, in your Cluster model where you define the cluster_user relationship, do:
function cluster_user() {
return $this->belongsToMany('App\User')
->withPivot('last_read_message_id');
}
And then in your query you could use whereColumn to compare the columns. Something like this:
$clusters = Auth::user()
->clusters()
->withCount(['messages' => function ($query) {
$query->whereColumn('messages.id', '>',
'pivot.last_read_message_id');
}])
->get();
Search for "Retrieving Intermediate Table Columns" on the Eloquent relationships documentation for more information.

How to select aggregates with Eloquent

I have 2 models: User and Transaction with user_id, type, and amount fields. I want to be able to select users with array of transactions grouped by type like so:
{ // User instance
'id': 123,
'username': 'John'
'transactions': [
'type1': '231', // `type`: `sum(amount)`
'type2': '543'
]
}
I will select arrays of users so I need this to load eagerly. How should I write an Eloquent model for that? I've read all docs but still don't know how to approach this.
You Can try Like :
$column = [DB::raw('SUM(order_qty) as volume'),
DB::raw('SUM(order_qty*price) as value')];
$oldValueVolume = OrderProduct::select($column)
->where('order_id', $ordersId)
->first();
User model:
public function transactions()
{
return $this->hasMany(Transaction::class, 'user_id');
}
So then the call:
User::with(
[
'transactions' => function ($q) {
return $q->groupBy(['type', 'user_id'])
->selectRaw('sum(amount) as sum, type, user_id');
}
]
)->get();
Notes
I used 'with' for eager loading.
You mentioned a custom structure (pairs) in a question:
This part
'transactions': [
'type1': '231', // `type`: `sum(amount)`
'type2': '543'
]
That's not what Eloquent does by-default, but you can always remap it.

Laravel Eloquent: Relationships multiple have issue & Eloquent “select” method not working with using “with” method

I am try to made Eloquent: Relationships for three model. Please have a look on my code.
$account = Account::select(['id', 'is_sign_contract', 'restaurant_name', 'state', 'phone_no', 'email', 'one_time_pick_up', 'sign_contract_date', 'created_at', 'oil_on_hand', 'binsize'])
->with(['completedservice' => function($c) {
//$c->select('id');
}])
->with(['accountService' => function($q) {
$q->with(['serviceProvider' => function($qs) {
$qs->select('id', 'company_name');
}])->select('account_id', 'service_provider_id', 'service_id');
}])
->whereRaw($where)
->orderBy('id', 'ASC')->offset(54)->limit(1)->get();
if i remove that //$c->select('id'); select form above relation then i get data if i am using it display blank relationship block.
below image for response in last image whole function there
In short without select it works fine, but if I am using select then not working.
Laravel loads the relations after the first query is run. In order for it to attach the related model to the parent you need to select the foreign key on the related table so Laravel knows which model to attach the child to after the query is run.
The accountService works because ytou are selecting account_id on that model to attach it to Account and serviceProvider works because you are selecting id on serviceProvider as well as service_provider_id on accountService so after all queries are run Laravel knows which models get attached where.
Your query is not working because you are not selecting the account_id on the child model.
The following will work:
$account = Account::select(['id', 'is_sign_contract', 'restaurant_name', 'state', 'phone_no', 'email', 'one_time_pick_up', 'sign_contract_date', 'created_at', 'oil_on_hand', 'binsize'])
->with(['completedservice' => function($c) {
$c->select('id', 'account_id');
}])
->with(['accountService' => function($q) {
$q->with(['serviceProvider' => function($qs) {
$qs->select('id', 'company_name');
}])
->select('account_id', 'service_provider_id', 'service_id');
}])
->whereRaw($where)
->orderBy('id', 'ASC')->offset(54)->limit(1)->get();
You need to have the foreign key selected along with any other fields you wish to select. Without which you'll get an empty relationship. So it should be something similar to $c->select('id', 'account_id');

CakePHP 3 - Assocation BelongsToMany and hasMany - limit columns by select

Good day!
I have small problem with CakePHP 3. I have association like that:
$this->belongsToMany('AdminUserGroup',[
'classname' => 'AdminUserGroup',
'foreignKey' => 'admin_user_id',
'targetForeignKey' => 'group_id',
'joinTable' => 'AdminUsersGroups'
]);
I am returning records with this code:
public function getAll()
{
$ble = $this->find()
->contain(['AdminUserGroup' ]);
return $ble;
}
Until that it works, but when i want to select specified fields i have problem. When I add select method i don't see columns from contained table:
public function getAll()
{
$ble = $this->find()->select(['id', 'name', 'surname'])
->contain(['AdminUserGroup']);
return $ble;
}
So I added callback query:
public function getAll()
{
$ble = $this->find()->select(['id, name, surname'])
->contain(['AdminUserGroup' => function ($q) { return $q->select(['group_name']);}]);
return $ble;
}
But it still dont work. I can see only fields from main table. Fields with contained table doesn't appear.
{
"id": "8",
"name": "Ola",
"lastname": "BleBle",
"admin_user_group": []
},
Haw can I repair it?
The manual includes the following comments:
When you limit the fields that are fetched from an association, you must ensure that the foreign key columns are selected. Failing to select foreign key fields will cause associated data to not be present in the final result.
and
If you have limited the fields you are loading with select() but also want to load fields off of contained associations, you can pass the association object to select().
and
Alternatively, if you have multiple associations, you can use autoFields().
And there are helpful examples there. So:
$ble = $this->find()
->select(['id', 'name', 'surname'])
->contain(['AdminUserGroup' ])
->autoFields(true);
or if you're using 3.1 or higher:
$ble = $this->find()
->select(['id', 'name', 'surname'])
->select($this->AdminUserGroup);
From the example, it looks like the contain call may not be required in the second version, so I've left it out.
Try something like this.
$ble = $this->find()->select(['Table.id, Table.name, Table.surname'])->contain(['AdminUserGroup']);

Categories