Exclude items from a collection in Laravel - php

In my Laravel app, I have the concept of users, friends, and groups. A user can create and sort friends into groups.
A user can have many friends:
function friendsOfMine()
{
return $this->belongsToMany('App\User', 'helpers', 'user_id', 'helper_id')
->wherePivot('accepted', '=', 1);
}
A group can have many users:
public function groupMembers()
{
return $this->belongstoMany('App\User')->withTimestamps();
}
In the UI for adding friends to a group, I want to show the full list of a user's friends, but exclude those friends that have already been added to the group. My Groups Controller function looks like this, though I'm positive I'm off-base.
public function add($id)
{
$group = Group::findOrFail($id);
$helpers = $group->groupMembers;
$id = $helpers->lists('id');
$invitees = $this->user
->friendsOfMine()
->where('helper_id', '!=', $id)
->Paginate(5);
return view('groups.add', compact('group','helpers','invitees'));
}
Ideally, what I'd love is some way to write:
$helper = $group->friendsOfMine->not->groupMembers->Paginate(5);
Is there anyway to filter data using functions from two different models?

With your approach you would have to use whereNotIn() since you have an array of ids:
$id = $helpers->lists('id');
$invitees = $this->user
->friendsOfMine()
->whereNotIn('helper_id', $id)
->Paginate(5);
However you probably could something like this as well (assuming the relation group)
$invitees = $this->user
->friendsOfMine()
->whereDoesntHave('group', function($q) use ($id){
$q->where('group_id', $id); // Note that id is the group id (not the array of ids from the helper)
})
->Paginate(5);
For a nicer syntax (like in your example) you should look into Query Scopes

Related

Laravel return related model beside the main model

I have a User model which has the following method:
public function articles()
{
return $this->hasMany('App\Article');
}
And also Article model, which has:
public function user()
{
return $this->belongsTo('App\User');
}
I want to return a list of users that contains each of thems articles. So with this one:
// only returns list of users
return $user = User::all();
And with the following:
$user = User::all();
foreach ($user[0]->articles as $article) {
return $article;
}
I only get the first one's articles. Is there any way (in laravel) to retrieve the whole list with each one's articles?
you need to call with method to eager load all articles.
$user = User::with('articles')->get();
dd($user);
or if you want to lazy load then.
$user = User::get();
dd($user->articles());
you can use select() to select specific columns - for example
$user = User::select('name','email')->get();
if you want to select specific columns in with() then
$user = User::with(['articles' => function($query){
$query->select('id','title');
}])->get();
or
$user = User::with(['articles' => function($query){
$query->select('id','title');
}])->select('name','email')->get();
The issue here is that you return inside the foreach loop, which will stop the loop after the first iteration, and therefore only return the first article.
If you want to show the user and articles in a JSON response, you should look at Eloquent Resources, Writing Resources, especially the Relationships part of that chapter.

Laravel - Access model attribute dynamically inside whereHas Closure

Let's say I have the relation: Role has many Users. Role and user stores a code value.
If I want to select all roles that have users with same code, how would be this query using whereHas clause?
What I tried:
$roles = Role::whereHas('users', function ($users) {
// Obviously doesn't work but it is what I need to access.
$code = $users->first()
->role
->code;
return $users->where('code', $code);
})->get();
Use this:
$roles = Role::whereHas('users', function ($query) {
$query->whereColumn('users.code', 'roles.code');
})->get();

Displaying posts of users the user follows through Laravel relationships

I would like to display the posts of everyone the current user follows, ordered by date desc.
I have a many to many relationship supplying all the people the user is following.
$users = User::find(Auth::user()->id)->follow()->get();
I have a one to many relationship displaying the posts for any user.
$updates = App\User::find(?????)->updates()->orderBy('created_at', 'desc')->get();
The question mark's shows where the followers ID's need to be placed.
I can put the above query inside the for each loop but that obviously works its way through each follower rather than all posts in date order.
I suspect I may need to set a new relationship and work from the beginning. Can anyone advise.
User Model
public function updates()
{
return $this->hasMany('App\update');
}
/**
* User following relationship
*/
// Get all users we are following
public function follow()
{
return $this->belongsToMany('App\User', 'user_follows', 'user_id', 'follow_id')->withTimestamps()->withPivot('id');;;
}
// This function allows us to get a list of users following us
public function followers()
{
return $this->belongsToMany('App\User', 'user_follows', 'follow_id', 'user_id')->withTimestamps();;
}
}
Update Model
public function user_update()
{
return $this->belongsTo('App\User');
}
Thank you.
Since you want the posts, it is probably going to be easier starting a query on the Post model, and then filter the posts based on their relationships.
Assuming your Post model has an author relationship to the User that created the post, and the User has a follower relationship to all the Users that are following it, you could do:
$userId = Auth::user()->id;
$posts = \App\Post::whereHas('author.follower', function ($q) use ($userId) {
return $q->where('id', $userId);
})
->latest() // built in helper method for orderBy('created_at', 'desc')
->get();
Now, $posts will be a collection of your Post models that were authored by a user that is being followed by your authenticated user.

Laravel 4 whereIn and many to many

I have a tag system, where you can add tags to photos and users.
I have a function where the users are able to add their favorite tags, and select images based on those tags
But my problem i am a really big beginner with php and laravel and i do not know how to pass the values to the whereIn function
Model
public function tag()
{
return $this->belongsToMany('Tag', 'users_tag');
}
Controller
// get the logged in user
$user = $this->user->find(Auth::user()->id);
// get tags relation
$userTags = $user->tag->toArray();
// select photos based on user tags
$photos = Photo::whereHas('tag', function($q) use ($userTags)
{
$q->whereIn('id', $userTags);
})->paginate(13);
$trendyTags = $this->tag->trendyTags();
$this->layout->title = trans('tag.favorite');
$this->layout->content = View::make('main::favoritetags')
->with('user', $user)
->with('photos', $photos)
->with('trendyTags', $trendyTags);
When i pass i get an error
preg_replace(): Parameter mismatch, pattern is a string while replacement is an array
than i tried to use array_flatten() to clean my array
// get the logged in user
$user = $this->user->find(Auth::user()->id);
// get tags relation
$userTags =array_flatten($user->tag->toArray());
// select photos based on user tags
$photos = Photo::whereHas('tag', function($q) use ($userTags)
{
$q->whereIn('id', $userTags);
})->paginate(13);
$trendyTags = $this->tag->trendyTags();
$this->layout->title = trans('tag.favorite');
$this->layout->content = View::make('main::favoritetags')
->with('user', $user)
->with('photos', $photos)
->with('trendyTags', $trendyTags);
This way it works but not returning the correct tags.
Could please someone could lend me a hand on this?
Sure thing and I'll make a couple recommendations.
To get the user model, you simply have to use $user = Auth::user().
To use whereIn(), it's expecting a 1 dimensional array of user id's. The toArray() function is going to return an array of associative arrays containing all the users and their properties, so it's not going to work quite right. To get what you need, you should use lists('id').
And one last thing that has really helped me is when you are setting up a relation that's going to return a collection of objects (hasMany, belongsToMany()), make the relation name plurual, so in this case you would modify your tag() function to tags().
So with all that in mind, this should work for you.
// get the logged in user
$user = Auth::user();
// get tags relation
$userTags = $user->tags()->lists('id');
// select photos based on user tags
$photos = Photo::whereHas('tags', function($q) use ($userTags)
{
$q->whereIn('id', $userTags);
})->paginate(13);
$trendyTags = $this->tags->trendyTags();
$this->layout->title = trans('tag.favorite');
$this->layout->content = View::make('main::favoritetags')
->with('user', $user)
->with('photos', $photos)
->with('trendyTags', $trendyTags);
And I'd suggest to modify your relation to... though not hugely important.
public function tags()
{
return $this->belongsToMany('Tag', 'users_tag');
}

Creating a query in Laravel by using `with` and `where`

I'm wondering it would be possible to add a where condition to a with.
Such as:
Comment::with('Users')->where('allowed', 'Y')->get();
I was trying to find a more simple way to make queries avoiding the whereHas method which looks quite verbose:
$users = Comment::whereHas('users', function($q)
{
$q->where('allowed', 'Y');
})->get();
The raw query I want internally to generate should be like so:
select * from comments, users
where users.id = comments.user_id and
users.allowed = 'Y'
I'm used to work with CakePHP in which this queries look very simple:
$this->Comments->find('all', array('Users.allowed' => 'Y'));
The relationships I have defined are:
//Comments.php
public function Users()
{
return $this->belongsTo('Users');
}
//Users.php
public function Comments(){
return $this->hasMany('Comments');
}
You may try this
$users = User::with(array('comments' => function($q)
{
$q->where('attachment', 1);
}))->get();
Update : Alternatively you may use a where clause in your relationship in your User model
// Relation for comments with attachment value 1
// and if hasMany relation is used
public function commentsWithAttachment()
{
return $this->hasMany('Comment')->where('attachment', 1);
}
// Relation for all comments
// and if hasMany relation is used
public function comments()
{
return $this->hasMany('Comment');
}
So, you can just use
// Comments with attachment value 1
User::with('commentsWithAttachment')->get();
// All comments
User::with('comments')->get();
Update : I think you want all comments with users where attachment is 1, if this what you want then it should be Comment not User
Comment::with('user')->where('attachment', 1)->get();
In this case your relation should be
public function user()
{
return $this->belongsTo('User'); // if model name is User
}
Because one comment belongs to only one user.

Categories