Retrieving unique results from a relationship - laravel

I am developing a page where I want to display unique results from a relation and the entire result.
The entrie result I retreive it as follows:
$media = Media::whereHas('block', function ($query) {
$query->where('identifier', "page");
})->with(["texts" => function ($query) use ($language) {
$query->where("language_id", $language->id);
}])->get();
In the texts relation is a title field where I want to get only the unique results from.
I tried to do another query for it but it didn't work
$media = Media::whereHas('block', function ($query) use ($blockId) {
$query->where('identifier', "page");
})->with(["texts" => function ($query) use ($language) {
$query->where("language_id", $language->id);
}])->distinct("texts.title")->get();
How can I achieve that? and can I do it from the same result ( not another query )
edit:
what I want is a list of unique titles

Relationship queries are performed separately from the main query, any logic in retrieving relationships must go in the with function:
$media = Media::whereHas('block', function ($query) use ($blockId) {
$query->where('identifier', $blockId);
})->with(["mediaTexts" => function ($query) use ($language) {
$query->where("language_id", $language->id)
->select('title')
->distinct();
}])->get();
Here each Media object retrieved should have distinct titles in the mediaTexts relationship. If you want the entire relationship data things will get more complicated.
To get distinct titles of media texts among all Media objects after you've retrieved them you can do:
$media = Media::whereHas('block', function ($query) use ($blockId) {
$query->where('identifier', $blockId);
})->with("mediaTexts")->get();
$titles = $media->pluck('mediaTexts.title')->unique();

Related

Data filtering query in Laravel many-to-many relationship

there is a table named products and another table named features that have a many-to-many relationship.
I want to write a query that returns products that, for example, have features 2, 3, and 11 at the same time. Now I wrote the following command
$params = [2,3,11];
$products = Product::with('photos')
->whereHas('features', function ($q) use ($params) {
$q->whereIn('feature_id' , $params);
})
->whereIn('category_id', $ids)
->orderByRaw($sortBy)
->paginate($paginate);
when I use whereIn, it works in the form of OR and shows me products that either have the feature 2 or 3 or 11..
I want it to be in the form of AND, which means it will only find products that have all the desired features together
$products = Product::with('photos')
->whereHas('features', function ($q) use ($params) {
foreach($params as $p) {
$q->where('feature_id', $p);
}
})
->whereIn('category_id', $ids)
->orderByRaw($sortBy)
->paginate($paginate);
and this query that I wrote returns empty data.

Laravel, eloquent, query: problem with retriving right data from DB

I'm trying to get the right data from the database, I'm retriving a model with a media relation via eloquent, but I want to return a photo that contains the 'main' tag stored in JSON, if this tag is missing, then I would like to return the first photo assigned to this model.
how i assign tags to media
I had 3 ideas:
Use orWhere() method, but i want more likely 'xor' than 'or'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main')->orWhere();
}]);
return $models->paginate(self::PER_PAGE);
Raw SQL, but i don't really know how to do this i tried something with JSON_EXTRACT and IF/ELSE statement, but it was to hard for me and it was a disaster
Last idea was to make 2 queries and just add media from second query if there is no tag 'main'
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}]);
$models_all_media = Model:: with(['media']);
return $models->paginate(self::PER_PAGE);
but i tried something like
for($i=0; $i<count($models); $i++) {
$models->media = $models_all_media
}
but i can't do this without get() method, beacuse i don't know how to change this to LengthAwarePaginator class after using get()
try using whereHas https://laravel.com/docs/9.x/eloquent-relationships
Model::with('media')
->whereHas('media',fn($media)=>$media->whereJsonContains('custom_properties->tags', 'main'))
->paginate(self::PER_PAGE);
as per your comment you can use
$models = Model::with(['media' => function ($query) {
$query->whereJsonContains('custom_properties->tags', 'main');
}])
->leftJoin('media', function ($join) {
$join->on('models.id', '=', 'media.model_id')
->whereNull('media.custom_properties->tags->main');
})
->groupBy('models.id')
->paginate(self::PER_PAGE);
return $models;

API filtering with pivot table

I'm using laravel 7 and i've made an API to fetch data with a VUE component.
I want to filter restaurants based on their type of food. So if i query "pizza" i want to show only restaurants that make pizza.
getTypes is a many to many relation.
public function restaurants(Request $request)
{
$ricerca = $request->input('query');
$users = Restaurant::with(array('getTypes' => function ($query) use ($ricerca) {
$query->where('name', "LIKE", "%" . $ricerca . "%");
}))->get();
return response()->json($users);
}
I tried this, but this way i don't have access to other genres, if a restaurant make pizza and meat, i don't see meat anymore.
Any advice?
create a many to many relation with restaurant and pizza.
get an array which restaurant make pize,
$array = [];
or simple var
$array = $request('query');
and put this array in query.
$data = Project::whereHas('restorant_pizza', function ($q) use ($array) {
$q->whereIn('restorant_pizza.pizza_id', $array);
})->get();
I think this idea will help you

Laravel Query Builder relationship plus no relationship

I currently have this which works for querying a relationship:
$users = User::query();
$post_id = $request->input('post_id');
$users->whereHas('posts', function ($query) use ($post_id) {
$query->where('id', $post_id);
});
return $users->get();
But in the results of this query I would also like to include Users that do not have any posts connected to them. So the result becomes: users without posts + users with a specific post (code above). Is this possible?
Use doesntHave():
$users->whereHas('posts', function ($query) use ($post_id) {
$query->where('id', $post_id);
})->orDoesntHave('posts');

Laravel eloquent: get data with model wherePivot equal to custom field

I have an eloquent object Performer that has Albums and Albums have Images
Here is setup:
Model Performer->albums():
public function albums()
{
return $this->belongsToMany('Album','performer_albums','performer_id','album_id');
}
Model Album->images()
public function images()
{
return $this->belongsToMany('Image','album_images','album_id','image_id')->withPivot(['type','size']);
}
I have performer object stored as such:
$performer = Performer::where...->first();
Now I need to get Performer's Albums with images where size is 'large'
So to avoid nesting queries, can I use with()?
I tried
$performer->albums()
->with('images')
->wherePivot('size','large')
->get();
But laravel tells me it's trying to use wherePivot for Performer-Album relationship (M-2-M)
PS. I am also aware that I can do this,
$performer = Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
but question remains the same.
You need eager load constraints:
$performer->albums()
->with(['images' => function ($q) {
$q->wherePivot('size','large');
}])
->get();
And btw, no, you can't do this:
Performer::with('albums')
->with('albums.images')
->.....-conditions for additional fields in album_images....
->get();
instead you could do:
Performer::with(['albums.images' => function ($q) {
$q-> .....-conditions for additional fields in album_images....
}])->get();

Resources