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

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;

Related

how to use whereHas in laravel

I am new to laravel,
I need to create a query for the db,
$query = Deal::query();
I want to use the wherehas operator.
this is my code that is worked.
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus);
// \Log::info('The request precedence: '.$precedenceStatus);
}
I want to add this code also to the query
if($person) {
$query->whereHas('personnel', function ($subQuery) use ($person) {
$subQuery->where('id', '=', $person);
});
}
So I need to change the first code?
how I can convert the first code to wherehas?
the first code is from table called deal, the second section is from realtionship called personnel.
the second section worked in other places in the code, I just need to fix the first section and not understand what to write in the use
I try this and get error on the last }
else if ($group_by == 'precedence') {
if($get_deals_for == 'noprecedence'){
$get_deals_for = 0;
}
$precedenceStatus = $get_deals_for;
$query-> where('precedence', '=', $precedenceStatus)
-> when ($person, function($query) use($person) {
$query->whereHas('personnel', function ($query) use ($person) {
$query->where('id', '=', $person);
});
})
}
There is a method you can use called when(, so that you can have cleaner code. The first parameter if true will execute your conditional statement.
https://laravel.com/docs/9.x/queries#conditional-clauses
$result = $query
->where('precedence', '=', $precedenceStatus)
->when($person, function ($query) use ($person) {
$query->whereHas('personnel', fn ($q) => $q->where('id', '=', $person));
})
->get();
You should also be able to clean up your precedence code prior to that using when( to make the entire thing a bit cleaner.
Querying to DB is so easy in laravel you just need to what you want what query you want execute after that you just have to replace it with laravel helpers.Or you can write the raw query if you cant understand which function to use.
using,DB::raw('write your sql query').
Now Most of the time whereHad is used to filter the data of the particular model.
Prefer this link,[Laravel official doc for queries][1] like if you have 1 to m relation ship so u can retrive many object from one part or one part from many object.like i want to filter many comments done by a user,then i will right like this.
$comments = Comment::whereHas('user', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
$comments = Here will be the model which you want to retrive::whereHas('relationship name', function (Builder $query) {
$query->where('content', 'like', 'title%');
})->get();
you can also write whereHas inside whereHas.
[1]: https://laravel.com/docs/9.x/eloquent-relationships#querying-relationship-existence

How to write a query conditionally in laravel?

I am new to the laravel, i am joining three tables based on conditions every thing is working fine but i need to write conditionally if the $field is array it should run with whereIn otherwise it should run where condition,can you please help me to acheive this thing
//$field sometimes it's an array sometimes it's a string.
public function find($field){
}
For conditional constraints, you can use the when() clause:
$query = DB::table(...);
$data = $query
->when(is_array($field), function ($query) use ($field) {
$query->whereIn('my_field', $field);
}, function ($query) use ($field) {
$query->where('my_field', $field);
})
->get();
Now, as a tip, you could do this: Wrap the $fields variables to an array and then use always the whereIn clause. You can achieve this with the Arr::wrap() function:
use Illuminate\Support\Arr;
// ...
$query = DB::table(...);
$data = $query
->whereIn('my_field', Arr::wrap($field))
->get();
PS: I have linked the relevant functions to the docs so you can know how they work and their params.

Retrieving unique results from a relationship

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();

Have select() and/or pluck() been broken in Laravel 6?

The following code does not pluck the name column of the selected user record. Rather, returns the entire row. Before I make a re-creatable example: Is this the expected behaviour here?
I want to explicitly select columns across joins to reduce my JSON payload size, and to return a nested model hierarchy to my clients.
I should add that I'm experiencing the same behaviour when using the pluck() function as well, on the same line. Perhaps I've done something wrong.
There's tons of examples showing this approach with earlier versions of Laravel. Version 6 may have broken this.
$query = Post::whereHas('user.address', function ($query) use ($lat, $lon, $distance) {
$query->distance($lat, $lon, $distance);
})->with([
'user' => function ($query) {
$query->select('name'); // TODO: Report this bug. I've also tried pluck()
},
'user.address' => function ($query) use ($lat, $lon, $distance) {
$query->distance($lat, $lon, $distance);
},
'user.address.city',
'bids' => function ($query) {
$query->orderBy('amount', 'DESC');
},
'bids.user',
'images',
]);
pluck() is a collection method, it executes the query and returns a simple Collection object of the field you specify.
Using pluck() inside your subquery builder executes it (returning nothing, because you are assigning it to nothing) while the $query variable is unmodified and behaves as normal returning all columns.
If you were to dump the value of the pluck() inside this query, you would see it is an array of just names, and because of that, it has no affect on the query itself.
'user' => function ($query) {
dd($query->pluck('name'));
}
select() should work fine in this case. You just need to also provide the relationship key or else it will just return a null object.
'user' => function ($query) {
$query->select(['id', 'name']);
},

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