Laravel 5, get only relationship for eloquent model - laravel

I have a user and article models in my app. The relation between them is straightforward:
//article.php
use Taggable;
public function user()
{
return $this->belongsTo('App\User');
}
Article uses the Taggable lib which provides me with a variety of methods like Article::withAnyTags (returns all articles tagged with 'xyz' string). Now I'd like to get all users who posted an article/s tagged as 'xyz'. I know how to do this in two lines but something tells me that this is not right. Ideally I'd like to do sth like:
$users = Article::withAnyTags('xyz')->with('user')->select('user'); --pseudocode
Is it possible to do sth like this in eloquent?
Side note: I knot that I could do this with DB::table but this is not an option for me. Please note that there is no "->get()" at the end on my pseudocode. It's so, because I'm paginating the users' results set with lib which works only with eloquent queries.

You could use whereHas():
User::whereHas('articles', function ($query) {
$query->withAnyTags('xyz');
})->paginate();
Or if you need to pass a variable of the tags to the closure you could do:
$tag = 'xyz';
User::whereHas('articles', function ($query) use($tag) {
$query->withAnyTags($tag);
})->paginate();

Related

Laravel how to use model scope method inside join query

i want to use scope method inside join subquery so is there any way I can do that in laravel?
Post Model
public function scopePublished($query)
{
return $query->where('published',true);
}
Now I want to join user table with post table but in join I want to use this scope method directly but its giving me error.
Users::join('posts',function($q){
$q->on('posts.user_id','users.id');
$q->published();
})->get();
So is there any way I can use scope directly inside join subquery ?
First, you need to add the relation between posts and users to the User model like so:
User Model
public function posts()
{
return $this->hasMany(Post::class);
}
and then your scope stays as it is, and your query if you wanna get users with their published posts:
return User::with(['posts' => function ($query) {
$query->published();
}])
->get();
and if you want to get only users that have published posts:
return User::whereHas('posts', function ($query) {
$query->published();
})
->get();
Note that while with('posts') will include the related table's data in the returned collection, whereHas('posts') will not include the related table's data.
Hence sometimes you may need to call both together, I mean, only with('posts') will eager load relations (in this case posts).

Best way to query model relationship in Eloquent

I am working within a controller in a Laravel application. I am returning a table to the view. The table is based on my PlanSubmission model. I am receiving parameters through a GET request and using those parameters to return a filtered set of rows to my view.
The first part of my controller looks like this and is working fine:
public function index()
{
//Used for filter. The request is received in the URL
if (request()->has('status')) {
$plans = PlanSubmission::where('status', request('status'))->paginate(25)->appends('status', request('status'));
}
elseif (request('employer_name')) {
$plans = PlanSubmission::where('employer_name', request('employer_name'))->paginate(25)->appends('employer_name', request('employer_name'));
}
I have run into a problem because now I need to use a model relationship in the controller. I am receiving 'advisor_name' from the request. The 'advisor_id" column is the foreign key on the PlanSubmission model. The 'advisor_name' column exists in the Advisor model. I have a function on my PlanSubmission model that looks like this:
public function advisor()
{
return $this->belongsTo(Advisor::class);
}
Initially, I thought there was a way I could do this easily with something like:
$plans = PlanSubmission::where(advisor->name, request('advisor_name'))->paginate(25)->appends('advisor_name', request('advisor_name'));
Of course, this will not work because I cannot enter a relationship into the first parameter in the Where Clause.
I do not know where to go from here. My other thought is to return all the advisors first from the Advisor model like this:
$advisors = Advisor::where('name', request('advisor_name'));
Then, I imagine I would have to somehow loop through that and get the id (primary key) for each of the objects in $advisors and somehow get that into the PlanSubmission where clause. I'm totally lost.
Like Victor mentions in his answer you can use whereHas like so:
PlanSubmission::whereHas('advisor', function ($query) {
$query->where('name', request('advisor_name'));
});
You didn't asked this directly, but I noticed that you use conditionals to make different queries. Eloquent provides a few way to make this a bit nicer to deal with.
The first which is kind of obvious is that that whatever method you call a builder (query) is returned that you can just add on to. It could be there were some common restrictions in your two cases:
public function index()
{
$query = PlanSubmission::where('something', 42);
if (request()->has('status')) {
$query = $query->where('status', request('status'));
} elseif (..) {
...
}
return $query->paginate(25);
}
Another way to do conditional queries in Laravel is using when. E.g. for status:
$query = $query->when(request->has('status'), function ($query) {
// note that you don't have to return the query
$query->where('status', request('status'));
});
// or PlanSubmission::>when(..)
In your example you cannot both filter by status AND advisor_name, but lets assume that would be okay, then you can combine everything like so:
public function index()
{
return PlanSubmission::query()
//->where('something', 42)
->when(request->has('status'), function ($query) {
$query->where('status', request('status'));
})
->when(request->has('advisor_name'), function ($query) {
$query->whereHas('advisor', function ($query) {
$query->where('name', request('advisor_name'));
});
})->paginate(25);
}
This approach may seem verbose for simple queries and then it is fine to use if conditions, but for complex queries when can be useful. Also the idea of "building up a query" also works nice in those situation. You can pass the query builder around and continuously build it up.
You can use whereHas for that
docs

Laravel Eloquent whereDoesntHave

I'm having issues using this method, I have read the docs but I am either doing something wrong or not understanding how it works or it's a bug.
I have the following code in my controller:
$books = Book::whereDoesntHave("author", function ($query) {
$query->whereNotNull("died_at");
})->get();
Now what this is supposed to do is return all books whose authors are still alive and also all books that do not have an author, however it does the exact opposite.
I assumed whereDoesntHave() is supposed to check whether the model doesn't have the specified relation, in this case an author model with the column died_at having a specific value.
Instead it checks the author model where the column died_at doesn't a value.
I'm very confused about this, how is this function supposed to work exactly? Can someone please explain this to me.
can you try this, doesntHave("author") check books that doesn't have author and whereHas with closure check live author
$books = Book::doesntHave("author")->orWhereHas("author", function ($query) {
$query->whereNotNull("died_at");
})->get();

Is there an Eloquent way of doing a leftjoin in Laravel 5.4

Is there a eloquent way to do a left join in Laravel?
We'd like to get all games and fill in the progress for each one if it exists in user_games.
Right now we've written the following solution, however this isn't eloquent at all, which we like it to be.
public function usergames($user_id) {
return DB::table('games')->leftJoin('user_games', function ($join) use ($user_id) {
$join->on('user_games.game_id', '=', 'games.id')->where('user_games.user_id', '=', $user_id); })->get();
}
DB model:
Thanks in advance!
A way to do this without you actually writing a left/inner join is to use the eloquent relationships.
In your case you will have 2 model classes: User and Game
class User extends Model {
public function games() {
return $this->belongsToMany(App\Game::class);
}
}
Now, you can access the user's games like so:
$user = App\User::find($user_id);
$usergames = $user->games; // Illuminate\Support\Collection
If you want to get a list of users with games, then look into eager loading. That would look something like this:
User::with('games')->get();
This way, Eloquent will know to lazy load the relationship meaning it will only run 2 queries. One to grab the users. and one to grab the games associated with the user, and then make them available for you in the 'games' property of the user object.

Laravel Relationships Conditions - 3 tables

I've got a situation where I've got Posts, Users and Comments.
Each comment stores a post_id and a user_id. What I want to do is get all of a user's comments on a particular post, so that I can do a call like this:
$comments = Auth::User()->comments(post_id=x)->text
(where I know what x is)
I have:
User->HasMany(comments)
Comments->HasOne(User)
Comments->HasOne(Project)
Project->HasMany(comments)
I feel like there needs to be a where or a has or a wherehas or something thrown in.. the best I can manage is that I pull Auth::User()->comments into an array and then search through the array until I find the matching post ID.. that seems wasteful.
with doesn't apply any join, so you can't reference other table.
You can use this:
// User model
public function comments()
{
return $this->hasMany('Comment');
}
// Comment model
public function scopeForPost($query, $postId)
{
$query->where('post_id', $postId);
}
// then you can do this:
Auth::user()->comments()->forPost($postId)->get();
Alternatively you can eager load comments with constraint:
User::with(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}])->find($someUserId);
// or exactly the same as above, but for already fetched user:
// $user .. or
Auth::user()->load(['comments' => function ($q) use ($postId) {
$q->where('post_id', $postId);
}]);
// then you can access comments for $postId just like this:
Auth::user()->comments; // collection
When you need to filter your relations, you just have to do it in your Eloquent query:
$data = User::with('posts', 'comments')
->where('users.id', Auth::User()->id)
->where('posts.id', $postID)
->get();
Then you can
foreach($data->comments as $comment)
{
echo $comment->text;
}
Your Comments table would have foreign keys Post_Id and User_ID
To Access all the comments of a particular post from a particular user , can you try this way?
Comment::select('comments.*')
->where('comments.user_id', Auth::user()->id)
->leftJoin('posts','posts.id','=','comments.post_id')
->leftJoin('users','users.id','=','comments.user_id')
->get();
Am sure there is better way to achieve it, but this should give you desired results.
Note use aliases if you have conflicting column names
Let me know if this worked.

Resources