I wonder if anyone can help, as I've hit a wall and still learning Laravel ORM. Can anyone explain why, when I run:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id');
}
I get this result set: (snipped...)
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": [
{
"user_id": 1,
"location_tag_id": 1,
"id": 1,
"created_at": "2015-05-13 13:06:28",
"updated_at": "2015-05-13 13:06:28",
"name": "Test Tag 0",
"location_id": 1,
"deleted_at": null
},
{
"user_id": 1,
"location_tag_id": 2,
"id": 2,
"created_at": "2015-05-13 11:40:21",
"updated_at": "2015-05-13 12:56:13",
"name": "Test Tag 123",
"location_id": 1,
"deleted_at": null
}
]
}
Which is ace! However, when I start to select the columns I want from the location_tags join, with:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id')
->select('lt.id', 'lt.name');
}
I end up with:
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": []
}
Can someone explain what's going on? And possibly point me in the right direction to limit the selects? Thanks!
Update
I've also tried:
$query = \App\User::with(['permissions', 'locationTags' => function($query){
$query->select('lt.id', 'lt.name');
}]);
Which returns the same result :(
Figured it out. The key here was that you must include a select() value of at least one key that Laravel can use to map the result set. In my case it was user_id, like so:
public function locationTags(){
return $this->hasMany('App\UserHasLocationTags', 'user_id')
->join('location_tags AS lt', 'lt.id', '=', 'location_tag_id')
->select('user_id', 'lt.name', 'location_tag_id');
}
Which then returns a much nicer results set:
{
"id": 1,
"created_at": "2015-05-13 13:04:56",
"updated_at": "2015-05-13 13:04:56",
"email": "REMOVED",
"firstname": "REMOVED",
"lastname": "REMOVED",
"location_id": 0,
"deleted_at": null,
"permissions": [],
"location_tags": [
{
"user_id": 1,
"name": "Test Tag 0",
"location_tag_id": 1
},
{
"user_id": 1,
"name": "Test Tag 123",
"location_tag_id": 2
}
]
}
Hope this helps someone out in the future, because it kept me guessing for a good couple of hours.
I'm sorry but you've gone a little sideways here. The relationship definition should just define the Relationship. It's a powerful feature that supports many other aspects of the ORM. What you've done here is more or less build a custom Query, severely limiting the effectiveness of the relationship.
Up until recently, the proper way would have looked something like this.
// class User
public function userHasLocationTags() {
$this->hasMany('App\UserHasLocationTags', 'user_id');
}
// class UserHasLocationTags
public function locationTags() {
$this->hasMany('App\LocationTags', 'location_tag_id');
}
And you would eager load all the results like this.
$user = User::where('id', 1)->with('userHasLocationTags.locationTags')->first();
The code above produces in 3 queries. One to get the User, one to get all the UserHasLocationTags and a one to get all the LocationTags. This may seem wasteful on first blush but consider the following.
$users = User::take(100)->with('userHasLocationTags.locationTags')->get();
Again, this is only 3 queries but now you have loaded 100 users with all their location tags.
But I can see you are a man with an eye for efficiency, and loading all the intermediate relationships and the whole nested hierarchy probably doesn't sit well with you. Well good news! Laravel 5 has added another relationship type for just this situation. hasManyThrough (scroll down a bit to find it).
Has Many Through
The "has many through" relation provides a convenient short-cut for accessing distant relations via an intermediate relation. For example, a Country model might have many Post through a User model.
So in your case it may look something like this...
// class User
public function locationTags()
{
return $this->hasManyThrough('App\LocationTags', 'App\UserHasLocationTags');
}
$users = User::take(100)->with('locationTags')->get();
So now we are down to two queries. If you feel the need to optimize this further and only select specific columns, you can modify the eager load.
$users = User::take(100)->with(['locationTags' => function ($query)
{
$query->select('user_id', 'name', 'location_tag_id');
})->get();
And if you do that frequently, you should wrap it up in a scope.
Related
Hello everyone i want to ask about my problem. I want to get which data if column a is b or column b is a for a messaging app. I think you will understand when you see the code. And I also think I did nothing wrong in writing the code. this is my code and the response
Variable for this test
var uid: "QqAIR5Qwd6erFQIgSG"
var friend_uid: "Pk6R56XmWMywcObV3a"
Test 1
Chat::where(['sender'=>$request->uid, 'receiver'=>$request->friend_uid])->get();
Response
"data": [
{
"id": 4,
"sender": "QqAIR5Qwd6erFQIgSG",
"receiver": "Pk6R56XmWMywcObV3a",
"message": "eyJpdiI6Ik8xSld4QlQ3bmMxaWxBMVlua3B0WXc9PSIsInZhbHVlIjoidGMvNGpXVmowVDczRUhSdGM0UkFQNHNjSVVZcFEyeFJlRThWNGdhMWZQMUFodkIwNWJtNmg4cmg1T2NOTm5ZdXBkdzdWWWhUY3hDQUtnVFBGcHllMkE9PSIsIm1hYyI6IjRkYTI5OTk3ZTRhMDAwZTdkYzgwODVmOTgyYzdlYTY2NDg0ZGM2YmExMDVkYzdiMzRhOTliYjA2MjU1ZTc3ZTkiLCJ0YWciOiIifQ==",
"created_at": "2022-03-02T09:29:44.000000Z",
"updated_at": "2022-03-02T09:29:44.000000Z"
}
]
This is valid response
Test 2
Chat::where(['sender'=>$request->friend_uid, 'receiver'=>$request->uid])->get();
Response
"data": []
This is valid because it doesn't exist in the database, if there is then he will appear I've tried it.
Test 3 (i want to do this)
Chat::where(['sender'=>$request->uid, 'receiver'=>$request->friend_uid])->orWhere('sender'=>$request->friend_uid, 'receiver'=>$request->uid)->get();
Response
"data": [
{
"id": 2,
"sender": "30B67S1tV7O7sJRG2l",
"receiver": "QqAIR5Qwd6erFQIgSG",
"message": "eyJpdiI6IkFDaFNMNjZESk96ZU5rSTQyMVhOUEE9PSIsInZhbHVlIjoiaDZyZU9TRSs2c0VTdkVlU2ZDd2JvZHpvQTh3KzZIYjk0NWJrbm90Tk9WREhBV2lrTS9xQW9KOXIza0MwaHUyMGUrTnlBS21aNXBRd1BweXJldlhiOWkzR0hqeHlFWERpdXczZ1E4NTJCSUU9IiwibWFjIjoiMzA3YmQwZmM4NjFlOTk3YmQwNzMwMzgxMTQ4ZmRhZDBhY2IzOWJkZWMzYWVhZmQyNjQ2NGI1MzM4NDQ2ZWY4YiIsInRhZyI6IiJ9",
"created_at": "2022-03-02T09:29:44.000000Z",
"updated_at": "2022-03-02T09:29:44.000000Z"
},
{
"id": 4,
"sender": "QqAIR5Qwd6erFQIgSG",
"receiver": "Pk6R56XmWMywcObV3a",
"message": "eyJpdiI6Ik8xSld4QlQ3bmMxaWxBMVlua3B0WXc9PSIsInZhbHVlIjoidGMvNGpXVmowVDczRUhSdGM0UkFQNHNjSVVZcFEyeFJlRThWNGdhMWZQMUFodkIwNWJtNmg4cmg1T2NOTm5ZdXBkdzdWWWhUY3hDQUtnVFBGcHllMkE9PSIsIm1hYyI6IjRkYTI5OTk3ZTRhMDAwZTdkYzgwODVmOTgyYzdlYTY2NDg0ZGM2YmExMDVkYzdiMzRhOTliYjA2MjU1ZTc3ZTkiLCJ0YWciOiIifQ==",
"created_at": "2022-03-02T09:29:44.000000Z",
"updated_at": "2022-03-02T09:29:44.000000Z"
},
{
"id": 87,
"sender": "chT9eSJ0N6HAXlkUUL",
"receiver": "QqAIR5Qwd6erFQIgSG",
"message": "eyJpdiI6Ikd4ZFZZTE1IVXRGQXNCVmUxUmJhcUE9PSIsInZhbHVlIjoiNWNUZE91Mk41K2o5Si9TSktOdEpNVSswYlVXZDlQd2hyTWY0d256cEJ4YkZKNVg0Y2hwbmNiUTJkZFhMSGxzVSIsIm1hYyI6IjA5ZDk2MDNkYzFhYzY0MTNlNTM3ODNmZDFiZDU3OTc2NThkODg4MGQzZGM5ZWQyYWRjM2Q1YzJmODkwNmUwOTYiLCJ0YWciOiIifQ==",
"created_at": "2022-03-02T09:29:44.000000Z",
"updated_at": "2022-03-02T09:29:44.000000Z"
}
]
why when I try with a query like this I get data which doesn't match the uid or friend_uid? what happened and if where is the error in my program?, please help, thank you.
SOLVED BY RUN php artisan migrate:refresh
That is very odd. I could not see any issues in your code. If you can't find a fix and just want to get it over with using a quick solution, simply merge the two working collections you have created:
$test1 = Chat::where(['sender'=>$request->uid, 'receiver'=>$request->friend_uid])->get();
$test2 = Chat::where(['sender'=>$request->friend_uid, 'receiver'=>$request->uid])->get();
$test3 = $test1->merge($test2);
Not very clean, I know, but it's an okay solution if you can't solve the problem from its root.
I have the following table relationship:
Results ->
Objectives ->
Users ->
User
And my current query is the following:
$results = \App\Models\Results::with('objectives')
->with('objectives.users')
->with('objectives.users.user')
->get();
It works great. But now, I'd like to return only the users that are active.
$results = \App\Models\Results::with(['objectives' => function($query)
{
$query->with(['users' => function ($subQuery)
{
$subQuery->with(['user' => function($thirdQuery)
{
$thirdQuery->where('active', true);
}]);
}]);
}]);
But the result of this query returns an empty user (which is the one that is no longer active).
{
"id": 267068,
"objective_id": 9291,
"user_id": 79598,
"created_at": "2020-11-04 00:52:32",
"updated_at": "2020-11-04 00:52:32",
"user": null
},
{
"id": 267069,
"objective_id": 9291,
"user_id": 79599,
"created_at": "2020-11-04 00:52:32",
"updated_at": "2020-11-04 00:52:32",
"user":
{
"id": 79599,
"name": "John Doe",
"created_at": "2020-10-01 05:53:34",
"updated_at": "2020-10-01 05:53:34",
}
},
Applying ->whereHas('objectives.users.user') also doesn't seem to do anything!
I'm fetching some models from database using Laravel Eloquent. The code is like this-
$jobs = User::find($request->user_id)
->jobs
->where('status', $request->status);
return $jobs;
For the value of status = 0, the output is an array of objects-
[
{
"id": 6,
"name": "Mwkkdndnnd",
"desc": ".amsmsmskxnndsmsms",
"credits": "100",
"category_id": "1",
"views": "0",
"user_id": "11",
"deadline": "2017-07-19",
"status": "0",
"assigned_to": null,
"assigned_on": null,
"closed_at": null,
"created_at": "2017-07-17 09:57:28",
"updated_at": "2017-07-17 09:57:28"
}
]
But for the value of status = 1, the output is an object containing multiple objects-
{
"1": {
"id": 7,
"name": "Promote my company ",
"desc": "My brand needs to be promoted.",
"credits": "100",
"category_id": "1",
"views": "0",
"user_id": "11",
"deadline": "2017-07-20",
"status": "1",
"assigned_to": "12",
"assigned_on": "2017-07-18 09:32:51",
"closed_at": null,
"created_at": "2017-07-17 18:43:50",
"updated_at": "2017-07-18 09:32:51"
}
}
What is wrong the code?
Any help would be appreciated
Based on my experience with chaining another where with find, there can be unexpected behavior. So you should attempt to simply change everything to where, i.e,
$jobs = User::where('id', $request->user_id)->jobs->where('status', $request->status);
Because generally, the find method will return One model that matches, and calling a where on that model the key is not preserved so it just does a query on all the records instead of one. I may not be entirely correct, but I am pretty sure about find with where in this situation.
I would propose in order to actually see whats wrong, simply call the Job model directly and find where you have that user id (in case the relationship exists in this direction)
Job::where('user_id', $request->user_id)->get();
In fact, my observation is that you might be nursing a potential issue where if user with that id does not exist, then one uncaught error is triggered to the user. Do a check somehow in that place. findOrFail() might be helpful.
I am new to cakephp 3.
Below is my code.
$getFlags = $this->Posts->find('all')->contain(['Users', 'Flags.Users', 'Flags.FlagReasons', 'Flags.Users.Schools']);
output
{
"id": 114,
"title": "",
"allow_comment": 0,
"owner_id": 84,
"created": "2016-04-08T04:41:08+0000",
"status": 1,
"flags": [],
"user": {
"id": 84,
"profile_image": "bc7484cd8caad0de055f8d7ef15551f5.png",
"is_active": 1
}
Here i want to give a condition,when there is an empty flag ,it will not be listed in result.
Only post having flag will come.
Please suggest me.
ANy suggstion will highly appreciate.
There are two ways to get this done:
Use a counter cache and filter by that field (flag_count > 0)
Do a subquery to get the count (inefficient)
I need to update my code to include Media, which is oneToMany unidirectional mapped in Object.php Entity. Ideally I need my code to return all objects even though without media, currently it only returns objects WITH media but it returns multiple results if there are multiple media items.
If you think I refactor the code and use something much simpler, than let me know - I didn't find a way to get the feed using the getRepo() and findBy() functions (no serialization?)
Here's my code:
// Build object QB
$emObject = $this->objectService->getManager();
$qbObject = $emObject->createQueryBuilder();
$qbObject->addSelect(['o.id', 'o.title', 'o.description', 'm as multimedia', 'c.id as category_id', 'c.name as category_name'])
->from('Application\Entity\Cms\Object', 'o')
->leftJoin('Application\Entity\Cms\Media', 'm', 'WITH', '1 = 1')
->leftJoin('o.media', 'om')
->leftJoin('Application\Entity\Cms\Category', 'c', 'WITH', '1 = 1')
->leftJoin('o.category', 'oc')
->andWhere('om.id = m.id')
->andWhere('oc.id = c.id')
->andWhere('o.member = :member')
->setParameter('member', $member);
$objectList = $qbObject->getQuery()->getResult(Query::HYDRATE_ARRAY);
$viewModel->setVariables(['object' => $objectList]);
return $viewModel;
Which returns:
{
"object": [
{
"multimedia": {
"id": 22,
"title": null,
"url": "\/srv\/pois\/cms\/e9049aaac93ce96c81e16340027a804b.png"
},
"id": 34,
"title": "imgTest",
"description": "f",
"category_id": 1,
"category_name": "Chambres"
},
{
"multimedia": {
"id": 23,
"title": null,
"url": "\/srv\/pois\/cms\/ce0c7d669c567369485a01c65197943f.png"
},
"id": 34,
"title": "imgTest",
"description": "f",
"category_id": 1,
"category_name": "Chambres"
}
]
}
However, I need the multimedia objects to be in one array and only return 1 object, like:
{
"object": [
{
"multimedia": {
1: {
"id": 22,
"title": null,
"url": "\/srv\/pois\/cms\/e9049aaac93ce96c81e16340027a804b.png"
},
0: {
"id": 23,
"title": null,
"url": "\/srv\/pois\/cms\/ce0c7d669c567369485a01c65197943f.png"
},
}
"id": 34,
"title": "imgTest",
"description": "f",
"category_id": 1,
"category_name": "Chambres"
}
]
}
I always work with DQL queries without QueryBuilder. I think this is what you want:
$query = $this->entityManager->createQuery(
"SELECT o, m FROM 'Application\Entity\Cms\Object' o JOIN o.multimedia m WHERE o.member = :member"
);
$query->setParameters(array('member' => $member));
$objects = $query->getResult();
I don't think it is more complicated than that.
In QueryBuilder it probably looks like this:
$qb = $this->entityManager->createQueryBuilder();
$qb->select('o', 'm')
->from('Application\Entity\Cms\Object', 'o')
->leftJoin('o.multimedia', 'm')
->where('o.member = :member')
->setParameter('member', $member);
return $qb->getQuery()->getResult();
Please come back with details if this doesn't return what you want. I might be able to help out to get what you want.