Where condition on laravel model relationship - php

I have two models. i.e, Posts and Tags.
Post Model
public function tags()
{
return $this->belongsToMany('App\Models\Tag', 'tbl_post_tags', 'in_post_id', 'in_tag_id');
}
Tag Model
public function posts()
{
return $this->belongsToMany('App\Models\Post', 'tbl_post_tags', 'in_tag_id', 'in_post_id');
}
I want to fetch those posts which have php tag.
This is what I have done till now.
Search Controller
Post::with(['tags'])->skip(0)->take(5)->get();
I'm getting first five posts from table. But there is a post in these list which doesn't have PHP tag. So as per my requirement, I must get next post from table which have PHP tag.
I can't find any document on official site of laravel. If anyone knows the answer, it will be appreciated.

Assuming that you search the tags by name, here is the code.
Post::with(['tags'])->whereHas('tags', function($query){
$query->where("name", 'PHP');
})->skip(0)->take(5)->get();

Post::whereHas(['tags' => function($q) use($name){
$q->where('name', $name); // $name = 'PHP'
}])->take(5)->get();

Related

Get comment sub-comments when only one sub-comment

I have comments table where has parent_id
This is Comment table sub_comments relation.
public function sub_comments()
{
return $this->hasMany(self::class, 'parent_id');
}
This code return all comments with related all sub-comments
Comment::with('sub_comments')->get();
But I want to get all comments also sub-comments when sub-comments is single. That mean if comment have 2 or more comments for that comment I did not want get that sub-comments.
Now I use this code
$oneSubcommentCommentIds = Comment::has('sub_comments', '=', 1)->pluck('id');
Comment::with([
'sub_comments' => function ($q) use ($oneSubcommentCommentIds) {
$q->whereIn('parent_id', $oneSubcommentCommentIds);
}
])->get();
but this make one additional query.
Try this:
Comment::with('sub_comments')->has('sub_comments', '=', 1)->get();
Update
Your question wasn't clear, I can't imagine another way to doing this without previosly loaded the relationship or the count of the relationship.. so I'd do this:
// First get all your comments with an aditional count field
$comments = Comments::withCount('sub_comments')->get();
// separate the ones with just one sub_comment from the rest
list($oneSubComment, $theRest) = $collection->partition(function ($comment) {
return $comment->sub_comments_count == 1;
});
// Then load the relationship on just the selected elements
$oneSubComment->load('sub_comments');
// re-join the collection
$comments = $oneSubComment->union($theRest);
What am I doing here?
Adding an additional field to each $comment with the relationship count (it should be something like sub_comments_count)
Partition the resulting collection in two parts: the ones with one comment and the rest. Using the partition() method.
Lazy eager loading the collection.
Re-joining the two collections using the union() method.

Return belonging name with ID form Laravel, check for the type?

sorry for the title of this question but I am not sure how to ask it...
I am working on a project where I have two Models Trains and Cars, to this model I have a belonging Route.
I want to make a query and check if the routeable_type is App\Car than with the selected routeable_id to get the data from the Car. And if the routeable_type is Train then with the ID to get the data from the Tran.
So my models go like this:
Train:
class Train extends Model
{
public function routes()
{
return $this->morphMany('App\Route', 'routeable');
}
}
Car:
class Car extends Model
{
public function routes()
{
return $this->morphMany('App\Route', 'routeable');
}
}
Route:
class Route extends Model
{
public function routeable()
{
return $this->morphTo();
}
}
And the query I have at the moment is:
$data = Route::leftjoin('cars', 'cars.id', '=', 'routes.routeable_id')
->leftjoin('trains', 'trains.id', '=', 'routes.routeable_id')
->select('routes.id', 'cars.model AS carmodel', 'trains.model AS trainmodel', 'routeable_type', 'routes.created_at');
With this query if I have the same ID in cars and trains I get the data from both and all messes up. How do I check if routeable_type is Car ... do this, if routeable_type is Train .. do that?
Will something like this be possible in a 1 single query:
$data = Route::select('routes.id', 'routeable_type', 'routes.created_at');
if(routeable_type == 'Car'){
$data = $data->leftjoin('cars', 'cars.id', '=', 'routes.routeable_id')->select('routes.id', 'cars.model AS carmodel', 'routeable_type', 'routes.created_at');
}else{
$data = $data->leftjoin('trains', 'trains.id', '=', 'routes.routeable_id')->select('routes.id', 'trains.model AS trainmodel', 'routeable_type', 'routes.created_at');
}
Maybe this is what you are looking for?
DB::table('routes')
->leftJoin('cars', function ($join) {
$join->on('cars.id', '=', 'routes.routeable_id')
->where('routes.routeable_type', 'App\Car');
})
->leftJoin('trains', function ($join) {
$join->on('trains.id', '=', 'routes.routeable_id')
->where('routes.routeable_type', 'App\Train');
})
->select('routes.id', 'cars.model AS car_model', 'trains.model AS train_model', 'routes.routeable_type', 'routes.created_at');
->get();
I think you may want to follow the morphedByMany design.
https://laravel.com/docs/5.7/eloquent-relationships#many-to-many-polymorphic-relations
This was also a neat visual for the different relation types.
https://hackernoon.com/eloquent-relationships-cheat-sheet-5155498c209
I was faced with a similar issue though I failed to follow the correct design initially and was forced to query the many possible relations then wrote custom logic after to collect the relation types and ids then do another query and assign them back through iteration. It was ugly but worked... very similar to how Eloquent does things normally.
i don't have enough repo, so i can't comment. that's why i am putting as an answer.
You should use 2 different queries, for each model.
This will be better, code wise as well as performance wise. also if both models have similar fields you should merge them to 1 table and add a 'type' column.
and put non-similar fields in a 'meta' column.
( in my opinion )

laravel pick last comment posted related to each post

I have got three tables in laravel like so:
Users, posts, and comments
I'm trying to come up with a query that fetches me all the user's posts, plus the date of last comment with each post.
Approach i've taken that's not working perfectly is:
$posts = User::find($userId)->posts()->with('latestComment')->get();
In my Post model I have:
public function latestComment()
{
return $this->hasOne(Comment::class)->latest();
}
In my findings, i haven't been to see a way to get the date from the lastComment load.
Any pointers welcome,
Thanks
Just discovered one needs to add the foreign key to the select method like so:
return $this->hasOne(Comment::class)->latest()->select('field','foreign_key');
You should use eager loading constraint. Code from the other answers will first load all comments, which you don't want.
$posts = Post::where('user_id', $userId)
->with(['comments' => function($q) {
$q->taletst()->take(1);
}])
->get();
You can use the existing relationship and get the latest comment.
public function comments() {
return $this->hasMany(Comment::class);
}
public function latestComment() {
return $this->comments()->last();
}

Combining query results in laravel

On my website, users can upload images and attach tags to those images.
I've got an images table,a tag table and an images_tag pivot table.
Images can have many tags, and tags can belong to many images.
I want to be able to generate a list of all the tags a user has used in his/her images.
$imageIDs = Images::where('created_by', Auth::user()->id)->lists('id');
So this would create a list of all the image IDs that a user has upload.
What I want is essentially "foreach $imageIDs, check the images_tag table and for every match go to the tags table and get me back the tagname value."
But I have no idea how I'd do that.
Maybe a foreach then use the merge method on all the results? Any help would be appreciated!
You need to use whereHas() to check the relationship:
$userTags = Tags::whereHas('images', function($q) {
$q->where('created_by', auth()->user()->id);
})->get();
Then just pass this data to a view:
return view('some.view', compact('userTags'));
And iterate over tags in a view:
#foreach ($userTags as $tag)
{{ $tag->name }}
#endforeach
What you could do is this.
class Tag extends Model
{
public function images()
{
return $this->belongsToMany(Image::class);
}
}
class SomeController
{
public function someMethod()
{
$tags = Tag::with(['images' => function ($image) {
return $image->where('created_by', Auth::user()->id);
}])->select('id', 'tagname')->get();
// these are your $tags
}
}
You should not use a query inside foreach(). Then it would result N+1 problem. What you instead do is eager loading using with() statement.

How do I take related posts (by category) with Eloquent?

I have a question on how to fetch the related posts of a particular post by category using Eloquent. I know how to do it in pure MySQL but am sure Eloquent will have nicer alternative to it.
My tables are: posts categories post_category (pivot)
I have made the neccessary Eloquent connections, so I want to do something like: $post->categories()->posts()->exclude($post)->get().
Of course this won't work. I get an error on posts() because "Builder doesn't have a method posts()", but hopefully you get the idea. How would you do it with Eloquent?
It's hard to say what you want to achieve, bot probably you want to get:
Posts::whereIn('id', $post->categories()->lists('id'))->whereNot('id',$post->id)->get();
One of the confusing parts about Eloquent's relations is that the method on a model that defines the relation will bring back a relation object when called like you're calling it:
$posts->categories();
To return a collection of category models attached to your posts you should use something like this:
Post::find(primary key of post)->categories;
Or get all posts and iterate through the models individually:
$posts = Post::all();
foreach($posts as $post) {
$post->categories;
}
Here's a resource I found very helpful in learning to use Eloquent's relation methods: http://codeplanet.io/laravel-model-relationships-pt-1/
I was trying to get my related posts by category and searched on google and got here.
I made this, and it worked fine.
public function getSingle($slug){
$post = Post::where('slug', '=', $slug)->first();
$tags=Tag::all();
$categories=Category::all();
$related= Post::where('category_id', '=', $post->category->id)
->where('id', '!=', $post->id)
->get();
return view('blog.show')
->withPost($post)
->withTags($tags)
->withCategories($categories)
->withRelated($related);
}
In my view('blog.show')
$post->title
$post->content
//related posts
#foreach($related as $posts)
$posts->title
$posts->category->name
#endforeach
I don't know if this is the right way, but it works for me. I hope this helps someone
Why don’t you define a ‘relatedposts’ relation where search for posts with the same category id?
Then you can simply do $post->relatedposts...
You’re making it overcomplicated imo...
if you have multiple category (Pivot ex:RelPortfolioCategory)
Portfolio Model:
public function getCats(){
return $this->hasMany(RelPortfolioCategory::class,'portfolioID','id');
}
controller:
public function portfolioDetail($slug){
$db = Portfolio::where('slug' , $slug)->with('getCats')->firstOrFail();
$dbRelated = RelPortfolioCategory::whereIn('categoryID' , $db->getCats->pluck('categoryID'))->whereNot('portfolioID' , $db->id)
->with('getPortfolioDetail')->get();
return view('portfolioDetail' , compact('db' , 'dbRelated'));
}

Categories