Querying to fetch relationship data in laravel - laravel

I am fetching post with the specific tag id and I have following code so far.
public static function getPostByTag($id) {
return Tag::with('posts')->whereHas('posts', function($q) use(&$id) {
$q->where('tags.id',$id);
})->get();
}
The data I am getting is working perfectly so far but now, I want to limit the data. What should I do to limit data?
I have tried following:
public static function getPostByTag($id) {
return Tag::with('posts')->whereHas('posts', function($q) use(&$id) {
$q->where('tags.id',$id);
})->take(5)->get();
}
But, it does not seem to work. Also, I want to list my post by according to latest date.

It seems you want to get post by the tag_id, you can just use Tag::where('id', $id)
If you want the latest posts, you can use ->latest() method, it will automatically order by created_at desc.
And you want to limit the posts, you need to limit it in with closure:
public static function getPostByTag($id){
return Tag::where('id', $id)->has('posts')
->with(['posts' => function($q) {
$q->latest()->limit(5);
}])->get();
}

You need to do in the following ways:
public static function getPostByTag($id){
return Tag::whereHas('posts', function($q) use(&$id){
$q->where('tags.id',$id);
}->with(['posts' => function($q) {
$q->orderBy('created_at', 'DESC')->take(5);
}])->get();
}

Related

Eloquent `with()` with filtering based on relation

I have this tables.
And this model relations, this relations works fine.
class Item extends Model
{
public function translations()
{
return $this->hasMany(ItemTranslations::class);
}
}
class ItemTranslation extends Model
{
public function language()
{
return $this->belongsTo(Language::class);
}
}
I need to return a list of items with the translations, but only the translations related to a specific language.
I can't have this query working, im getting all translations of each item, not only the one filtered with this query. The language related to the translation is not needed on the result.
$query = Item::query();
$query->with('translations')->when('language',function($query) use ($ISOlanguage) {
return $query->where('languages.ISO_code', '=', $ISOlanguage);
});
return $query->paginate();
Any idea who i can have this working? Thanks!
So what you want to do is constraining eager loading
Item::with(["translations" => function ($query) use ($ISOlanguage) {
$query->where('language.ISO_code', $ISOlanguage);
}])->get();
https://laravel.com/docs/5.8/eloquent-relationships#constraining-eager-loads
I finally have it working
Item::with(['translations' => function($query) use ($ISOlanguage) {
$query->whereHas('language', function($query) use ($ISOlanguage) {
$query->where('ISO_code', '=', $ISOlanguage);
});
}])->get();
Thanks #julian-s for your help!

How To Use groupBy in whereHas laravel

i am using Metable in my project for creating meta for orders
but i have one problem
i want group orders that have same email
this is metable table image : image 1 image 2
i need code like this can work :)
Order::whereHas( 'meta', function($query) {
$query->where("key" , "gmail");
})->groupBy('meta.value')->get();
and this is meta relation that called by trait 'use Metable' in Order Model:
public function meta(): MorphMany
{
return $this->morphMany($this->getMetaClassName(), 'metable');
}
Thanks
This query may work, we start querying from the Meta model:
Meta::with('metable')
->whereHasMorph('metable', Order::class)
->where('key', 'gmail')
->get()
->groupBy('value')
->map(function ($metaCollection) {
return $metaCollection->map->metable->unique('id');
});
This is untested, but I would give it a try to retrive the models from the Order class:
Order::whereHas('meta', function($query) {
$query->where('key', 'gmail');
})->with(['meta' => function($query) {
$query->groupBy('value')
}])->get();
Try this solution instead:
Order::whereHas( 'meta', function($query) {
$query->where("key" , "gmail")
$query->groupBy('meta.value');
})->get();

Laravel : Get all models that their last relationship have some condition

I have two models Post and Comment, i'd like to get all posts that their last comment is active:
// Model Post
public function comments()
{
return $this->hasMany('comments');
}
//Model Comment
public function post()
{
return $this->belongsTo('post');
}
i tried this solution :
public function lastComment()
{
return $this->hasOne('comment')->latest()
}
and in my controller :
$postsWithLastActiveComment = Post::whereHas('lastComment', function($q){
$q->where('active',1);
})->all();
but in this solution if the last comment is not active Previous comment will be taken
I am not sure if there's another simpler way of doing this, but maybe you can try it with a sub-query?
$lastComment = Comment::select('active')
->whereColumn('post_id', 'posts.id')
->latest()
->limit(1)
->getQuery();
$posts = Post::select('posts.*')
->selectSub($lastComment, 'last_comment_is_active')
->having('last_comment_is_active', 1)
->get();
->latest() only orders the posts by created_at so to get only the latest comment you need ->latest()->first()
I think the code below should work !
public function comments()
{
return $this->hasMany('comments');
}
public function lastComment()
{
return $this->comments()->latest()->first();
}
Shouldn't this
$postsWithLastActiveComment = Post::whereHas('lastComment', function($q){
$q->where('active',1);
})->all();
be
$postsWithLastActiveComment = Post::whereHas('lastComment', function($q){
$q->where('active',1);
})->get();
According to your question, the Post model has many Comments. And you want to get the comment from the post where active is one and must be the lastest id.
Get the last comment like the following
public function lastComment()
{
return $this->hasOne('comment')->latest()->take(1);
}
Get all posts which had the lastComment like the following
$latestCommentPosts = Post::whereHas('lastComment')->get()
And filter the latestCommentPosts like the following
$latestCommentPosts->where('active', 1)->get()
Or, you can archive by one query like the following as well.
Post::whereHas('comments', function($q) {
$q->where('active', 1);
})->get()
Like that, you got all the latest comment with active is 1.

How to get latest eloquent relationship by column name in Laravel

I am building a small application on Laravel 5.6 where I am having two models Project and Status. In this I am having a relation as such:
In Project Model I am having:
public function statusUpdate()
{
return $this->hasMany('App\Status','project_id','id');
}
and to retrieve latest status I have:
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->latest();
}
In status I have columns: date, status, sub_status, comments.
I want to retrieve Status where I am having latest status by date mentioned in the column
I tried doing this in my model:
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->latest('date');
}
But this thing is not working out, help me out in this. Thanks
edit
I am using this relation in eager loading something like this:
Project::when( $request->name , function( $q) use( $request ) {
$q->where('name', 'like', '%' . $request->name .'%');
})->with('latestStatus')
->orderBy($request->sort_by_col, $request->order_by)
->paginate(30);
You can use orderBy in the relationship.
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->orderBy('date', 'desc');
}
Try it out.
You got your models wrong. This is what should be in the Project model
public function statuses() //plural because a project has many statuses
{
return $this->hasMany('App\Status','id','project_id');
}
If you want the latest status, call this in your controller:
Project::where('name', 'like', "%{$request->name}%")->with('statuses', function($q) {
return $q->orderBy('date', $request->order_by);
})->paginate(30);
If you want the latest project where the status has changed, first the Status model:
public function project()
{
return $this->hasOne('App\Project','project_id','id');
}
And in your controller:
$project = Status::latest()->first()->project;
Add first to the end of the query.
public function latestStatus()
{
return $this->hasOne('App\Status','project_id','id')->latest()->first();
}
This is how it works
The statusUpdate method builds the query and does the setup for has many relationship
The latest method adds the order by clause
The first methods adds the limit 1 clause, then executes the query and returns the first result

laravel search many to many Relashionship

I am testing eloquent for the first time and I want to see if it suit my application.
I have Product table:
id, name
and model:
class Produit extends Eloquent {
public function eavs()
{
return $this->belongsToMany('Eav')
->withPivot('value_int', 'value_varchar', 'value_date');
}
}
and eav table:
id, name, code, field_type
and pivot table:
product_id, eav_id, value_int, value_varchar, value_date
class Eav extends Eloquent {
public function produitTypes()
{
return $this->belongsToMany(
'ProduitType'
->withPivot('cs_attributs_produits_types_required');
}
All this is working.
But I want to search in that relashionship:
e.g: all product that have eav_id=3 and value_int=3
I have tested this:
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->get();
But I get all the product, and eav data only for these who have id=3 and value_int=3.
I want to get only the product that match this search...
Thank you
I know the question is very old. But added the answer that works in the latest versions of Laravel.
In Laravel 6.x+ versions you can use whereHas method.
So your query will look like this:
Produit::whereHas('eavs', function (Builder $query) {
// Query the pivot table
$query->where('eav_id', 3);
})->get()
My suggestion and something I like to follow is to start with what you know. In this case, we know the eav_id, so let's go from there.
$produits = Eav::find(3)->produits()->where('value_int', '3')->get();
Eager loading in this case isn't going to save you any performance because we are cutting down the 1+n query problem as described in the documentation because we are starting off with using find(). It's also going to be a lot easier to read and understand.
Using query builder for checking multiple eavs
$produits = DB::table('produits')
->join('eav_produit', 'eav_produit.produit_id', '=', 'produits.id')
->join('eavs', 'eavs.id', '=', 'eav_produit.eav_id')
->where(function($query)
{
$query->where('eav_produit.value_int','=','3');
$query->where('eavs.id', '=', '3');
})
->orWhere(function($query)
{
$query->where('eav_produit.value_int','=','1');
$query->where('eavs.id', '=', '1');
})
->select('produits.*')
->get();
Making it work with what you already have...
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
$query->orWhere('id', '1')->where('value_int', '1');
}))->get();
foreach($produits as $produit)
{
if(!produit->eavs)
continue;
// Do stuff
}
From http://four.laravel.com/docs/eloquent:
When accessing the records for a model, you may wish to limit your results based on the existence of a relationship. For example, you wish to pull all blog posts that have at least one comment. To do so, you may use the has method
$posts = Post::has('comments')->get();
Using the "has()" method should give you an array with only products that have EAV that match your criteria.
$produits = Produit::with( array('eavs' => function($query)
{
$query->where('id', '3')->where('value_int', '3');
}))->has('eavs')->get();

Resources