How to count relations in laravel - php

How can I count for example the number of roles for a user?
When I try this:
User::with('roles')->count();
it just counts the number of users.
What I need is to return the number of roles per user. For example:
[
{
"id": 2,
"name": "user",
"roles": 2
},
{
"id": 3,
"name": "user",
"roles": 1
}
]

Eloquent does not support this out-of-the-box.
You can read this great article on how to achieve this:
How to get hasMany relation count efficiently?

If you already have the $user object, you can do the following:
$rolecount = $user->roles()->count();
Or if you are using eager loading you can drop the ( & ) at roles
$rolecount = $user->roles->count();

Related

Retrieve array object in a table from another table data id using a where clue in laravel

Question: I have two tables how to fetch a customer's data using order's table user_id
"order": [
{
"id": 1,
"user_id": 4
},
{
"id": 2,
"user_id": 5
}
],
"customers": [
{
"id": 5,
"name": "Mohamed Raazi",
"phone": "777",
}
],
Following code will return only the last object of an array, i need to display all the objects from user table using a where condition in customers table
for ($x=0; $x<count($orders); $x++){
$customer = User::where('id',$orders[$x]->user_id)->get();
}
I would use the ::whereIn-method where you can provide an array of IDs to and get all the users with the provided IDs.
First you would have to reformat the $orders array that you have so that it contains only user IDs. I am guessing that you are using Eloquent to fetch the orders, then you can use the pluck-function in your Eloquent-statement:
$userIds = Order::where('statement', true)->pluck('user_id')->toArray();
Please note that the where-statement is not real, I just want to illustrate how you can call the pluck-method.
Another alternative is that you use the map function on the $orders-collection and return only user IDs:
$userIds = $orders->map(function ($order) {
return $order->user_id;
});
Once you have your user IDs in an array/collection you can use it in the whereIn-statement:
$users = Users::whereIn('id', $userIds)->get();
Then this would give you all the users that that are connected to the orders you have in your order-collection.

laravel 5.3 Many-to-Many relationship returns associated Role only when queried

I am just confused on how this thing is working .
I have a M-M relationship between by Users and Roles. If I retrieve my user like the following :-
$user = Auth::User();//->with('roles')->get();
$roleName = $user->roles[0]->name;
return $this->sendResponse('User retrieved successfully',$user);
I get the following response :
{
"success": true,
"message": "User retrieved successfully",
"data": {
"id": 2,
"name": "dummy",
"email": "dummy#dummy.com",
"created_at": "2017-05-06 09:49:50",
"updated_at": "2017-05-06 09:49:50",
"tenant_id": 2,
"roles": [
{
"id": 1,
"created_at": "2017-05-06 06:26:55",
"updated_at": "2017-05-06 06:26:55",
"name": "Admin",
"permissions": null,
"pivot": {
"user_id": 2,
"role_id": 1
}
}
]
}
}
But, if I retrieve my user as :-
$user = Auth::User();//->with('roles')->get();
return $this->sendResponse('User retrieved successfully',$user);
I get the following resut :-
{
"success": true,
"message": "User retrieved successfully",
"data": {
"id": 2,
"name": "Ali",
"email": "ali#and-or.com",
"created_at": "2017-05-06 09:49:50",
"updated_at": "2017-05-06 09:49:50",
"tenant_id": 2
}
}
Why is this happening ? I expected the "first" posted result to the latter query.
Secondly, I did not modify the $user after the query in the first "method" how did it get its Roles attachment ?
I am sure there is an explanation, but I couldn't put my finger on it.
When retrieving a model, the User in this instance, relationships are not automatically also retrieved (Since on the database side this would require a second query while you might not even need the roles in a certain situation).
In your first example, by accessing the roles through $roleName = $user->roles[0]->name;, Laravel does the roles() query automatically, and also adds the roles object to the User (So it can be accessed again at a later point without needing to redo the query). This explains why the roles are 'magically' attached to your User model in the first example.
In your second example this query is not done automatically, so you do not get the roles relation in your response.
If you want to have access to the Users roles, then you could use the with() method like in your comment to eager load the relationship, but keep in mind that this implies doing the second query in order to get this data from the database.
Another option, if you always want the User model to have its Roles attached, would be to add roles to the $appends array of the model:
protected $appends = ['roles'];
This tells Laravel that the roles attribute is one which you always want available on your model, and it then does what is necessary to make this happen (In this case, query the relationship).

Laravel: JSON and pivot table

Sorry about the non-explanatory title but I could not come up with a descriptive one.
I've got the following 3 tables:
- games
- platforms
- games_platforms
And I've got 2 Models in Laravel for both Platform and Game.
public function games()
{
return $this->belongsToMany('App\Game', 'games_platforms')->withPivot('release_date');
}
public function platforms()
{
return $this->belongsToMany('App\Platform', 'games_platforms')->withPivot('release_date');
}
Now this works like a charm, I get a JSON string with all the information in the 3 tables, like this.
[{
"id": 1,
"name": "Borderlands",
"short_description": "",
"created_at": null,
"updated_at": null,
"platforms": [{
"id": 4,
"name": "PC",
"pivot": {
"game_id": 1,
"platform_id": 4,
"release_date": "2016-03-03"
}
}]
}]
Now my problem is as follows. I don't want to show the whole 'pivot' information, just the 'release_date', like this:
"platforms": [{
"id": 4,
"name": "PC",
"release_date": "2016-03-03"
Is there an easy way in Laravel to do such a thing? As far as I can see right now, looking at other posts, is to either write a function that turns the json into an array and I can arrange it then. Or I can write my own query instead of letting Laravel do all that.
Hope you guys can help me with this issue. Thanks!
I would modify the data returned from the query via methods on the collection class:
//replace Game::all() with your actual query
return Game::all()->each(function($game){
$game->platforms->map(function($platform){
$platform->release_date = $platform->pivot->release_date;
unset($platform->pivot);
return $platform;
});
});
I know this is already answered but I believe the proper answer would be to add whatever you want to hide to your hidden attribute on the model.
<?php
class Games extends Eloquent
{
protected $hidden = ['pivot.game_id', 'pivot.platform_id'];
}
I am not sure what your keys are becuase they are different in your two examples.
Please see: https://github.com/laravel/framework/issues/745
A better way is to use Laravel resources,
First create Resource (php artisan make:resource)
Rresource GameResource extends Resource
{
public function toArray($request)
{
return [
'release_date' => $this->pivot->release_date,
'name' => $this->name,
/// All other fields or you can merge both here
];
}
}
Now use this resource;
$data = GameResource::collection(Game::all());

Tricky Eloquent query

I've been struggling to figure out a table relationship for the past two days, I'm sure that the solution is simple but it is alluding me.
Four tables/models are involved (including a pivot table):
Skill - skills table. A Skill belongs to a SkillGroup and belongs to many Candidates (candidate_skill pivot table)
Candidate - candidates table. Contains personal information on a candidate, not terribly related to the issue.
SkillGroup - skill_groups table. Each Skill Group has many Skills.
I want to be able to retrieve Skill objects possessed by a Candidate grouped by the SkillGroup. For example:
[
{
"id": 1,
"title": "Information Technology (Skill Group)",
"slug": "information-technology",
"created_at": "2016-05-07 23:58:23",
"updated_at": "2016-05-07 23:58:23",
"skills": [
{
"id": 1,
"title": "Web Development (Skill)",
"slug": "web-development",
"description": "Web developers primarily focus on the back-end of websites",
"created_at": "2016-05-07 23:58:55",
"updated_at": "2016-05-07 23:58:55",
"skill_group_id": 1,
"candidates": [
{
"first_name": "John (Candidate)",
"last_name": "Smith",
"pivot": {
"skill_id": 1,
"candidate_id": 6
}
}
]
}
]
}
]
This is easy to accomplish with the following code, but I want to retrieve only results (SkillGroup -> Skills -> Candidate) for a specific candidate.
\App\SkillGroup::with('skills.candidates')->get();
I have tried the following (and everything else I can think of), the candidate_id does not seem to affect the query - I see skills that are not possessed by the given candidate.
\App\SkillGroup::with(['skills.candidates' => function($query) {
$query->whereCandidateId(6);
}])->get();
Any help would be greatly appreciated, thanks!
EDIT
Thanks to #Giedrius Kiršys, I was able to come up with the following:
\App\SkillGroup::with(['skills.candidates' => function($query) {
$query->wherePivot('candidate_id', 8)->addSelect('candidates.id', 'first_name', 'last_name');
}])->whereHas('skills.candidates', function($q) {
$q->whereCandidateId(8);
})->get();
This only retrieves SkillGroup results with Skills with a Candidate with the given ID.
You want to query by pivot table attribute, but You are querying by candidates.candidate_id attribute.
You can do it like this:
\App\SkillGroup::with(['skills.candidates' => function($query) {
$query->wherePivot('candidate_id', 6);
}])->get();

How to join and echo 2 different MongoDB collections using PHP? [duplicate]

I'm using the Mongo PHP extension.
My data looks like:
users
{
"_id": "4ca30369fd0e910ecc000006",
"login": "user11",
"pass": "example_pass",
"date": "2010-09-29"
},
{
"_id": "4ca30373fd0e910ecc000007",
"login": "user22",
"pass": "example_pass",
"date": "2010-09-29"
}
news
{
"_id": "4ca305c2fd0e910ecc000003",
"name": "news 333",
"content": "news content 3333",
"user_id": "4ca30373fd0e910ecc000007",
"date": "2010-09-29"
},
{
"_id": "4ca305c2fd0e910ecc00000b",
"name": "news 222",
"content": "news content 2222",
"user_id": "4ca30373fd0e910ecc000007",
"date": "2010-09-29"
},
{
"_id": "4ca305b5fd0e910ecc00000a",
"name": "news 111",
"content": "news content",
"user_id": "4ca30369fd0e910ecc000006",
"date": "2010-09-29"
}
How to run a query similar like this, from PHP?
SELECT n.*, u.*
FROM news AS n
INNER JOIN users AS u ON n.user_id = u.id
MongoDB does not support joins. If you want to map users to the news, you can do the following
1) Do this at the application-layer. Get the list of users, and get the list of news and map them in your application. This method is very expensive if you need this often.
2) If you need to do the previous-step often, you should redesign your schema so that the news articles are stored as embedded documents along with the user documents.
{
"_id": "4ca30373fd0e910ecc000007",
"login": "user22",
"pass": "example_pass",
"date": "2010-09-29"
"news" : [{
"name": "news 222",
"content": "news content 2222",
"date": "2010-09-29"
},
{
"name": "news 222",
"content": "news content 2222",
"date": "2010-09-29"
}]
}
Once you have your data in this format, the query that you are trying to run is implicit. One thing to note, though, is that analytics queries become difficult on such a schema. You will need to use MapReduce to get the most recently added news articles and such queries.
In the end the schema-design and how much denormalization your application can handle depends upon what kind of queries you expect your application to run.
You may find these links useful.
http://www.mongodb.org/display/DOCS/Schema+Design
http://www.blip.tv/file/3704083
I hope that was helpful.
Forget about joins.
do a find on your news. Apply the skip number and limit for paging the results.
$newscollection->find().skip(20).limit(10);
then loop through the collection and grab the user_id in this example you would be limited to 10 items. Now do a query on users for the found user_id items.
// replace 1,2,3,4 with array of userids you found in the news collection.
$usercollection.find( { _id : { $in : [1,2,3,4] } } );
Then when you print out the news it can display user information from the user collection based on the user_id.
You did 2 queries to the database. No messing around with joins and figuring out field names etc. SIMPLE!!!
If you are using the new version of MongoDB (3.2), then you would get something similar with the $lookup operator.
The drawbacks with using this operator are that it is highly inefficient when run over large result sets and it only supports equality for the match where the equality has to be between a single key from each collection. The other limitation is that the right-collection should be an unsharded collection in the same database as the left-collection.
The following aggregation operation on the news collection joins the documents from news with the documents from the users collection using the fields user_id from the news collection and the _id field from the users collection:
db.news.aggregate([
{
"$lookup": {
"from": "users",
"localField": "user_id",
"foreignField": "_id",
"as": "user_docs"
}
}
])
The equivalent PHP example implementation:
<?php
$m = new MongoClient("localhost");
$c = $m->selectDB("test")->selectCollection("news");
$ops = array(
array(
"$lookup" => array(
"from" => "users",
"localField" => "user_id",
"foreignField" => "_id",
"as" => "user_docs"
)
)
);
$results = $c->aggregate($ops);
var_dump($results);
?>
You might be better off embedding the "news" within the users' documents.
You can't do that in mongoDB. And from version 3 Eval() is deprecated, so you shouldn't use stored procedures either.
The only way I know to achieve a server side query involving multiple collections right now it's to use Node.js or similar. But if you are going to try this method, I strongly recommend you to limit the ip addresses allowed to access your machine, for security reasons.
Also, if your collections aren't too big, you can avoid inner joins denormalizing them.

Categories