Eloquent relation query not functioning - mysql

I'm scratching my head over this database query and couldn't find out why it is not working.
It actually works if I try it directly on the plays table :
Play::select(\DB::raw('COUNT(*) as plays'), \DB::raw('DATE(created_at) as date'))
->whereDate('created_at', '>', Carbon::now()->subWeek())
->groupBy('date')->orderBy('date')->get();
// returns the expected results
However, it does not work when I try to use it inside relation function ( returns empty array, while there is actually data ):
$artist->songs()->with(['plays' => function($q){
$q->select(\DB::raw('COUNT(*) as plays'), \DB::raw('DATE(created_at) as date'))
->whereDate('created_at', '>', Carbon::now()->subWeek())
->groupBy('date')->orderBy('date');
}])->get()->pluck('plays')->toArray();
NB: $artist->songs()->with('plays')->get() return the expected results

I guess you need to send id inside select statement.
$artist->songs()->with(['plays' => function($q){
$q->select(\DB::raw('COUNT(*) as plays'), \DB::raw('DATE(created_at) as date'), 'id')
->whereDate('created_at', '>', Carbon::now()->subWeek())
->groupBy('date')->orderBy('date');
}])->get()->pluck('plays')->toArray();

Related

Laravel Query builder where() for when Product has to have multiple tags (with product_tags pivot table many-to-many)

I am new to Laravel and I got a complicated query to build. I managed to sort it except for when a user asks for multiple tags (tags = 1, 2, 3). Product shown has to have all tags that the user asks (not only one or two but all of them).
I have the query in SQL (this example is two tags, I would switch it to different numbers based on how many tags are passed):
SELECT m.*
FROM meals m
WHERE EXISTS (
SELECT 1
FROM meals_tags t
WHERE m.id = t.meals_id AND
t.tags_id IN (227,25)
HAVING COUNT(1) = 2
);
This one works perfectly, but I have an issue when translating it to an Eloquent query builder where method.
I have another where method included in the same query so I want to attach this one as well.
I have tried this:
DB::table('meals')
->select('id')
->where(function ($query) use ($parameters) {
if (isset($parameters['tags'])) {
$array = explode(',', $parameters['tags']);
$query->select(DB::raw(1))
->from('meals_tags')
->where('meals.id', '=', 'meals_tags.meals_id')
->whereIn('meals_tags.tags_id', $array)
->having(DB::raw('COUNT(1)'), '=', count($parameters['tags']));
}
});
But I can't find a way. New to Laravel and PHP.
Let's say I have table meals and tags with meals_tags to connect them (many to many).
$paramaters are comming from GET (...?tags=1,2,3&lang=en&another=something...), they are an array of key-value pairs (['tags' => '1,2,3', 'lang' => 'en'])
$parameters['tags'] is of type string with comma separated numbers (positive integers greater than 0) so that I have the option to explode it if needed somewhere.
Assuming that you have defined belongsToMany (Many-to-Many) meal_tags relationship on the Meal model, you can try
Meal::select('id')
->when(
$request->has('tags'),
function($query) use ($request) {
$requestedTagIds = explode(',', $request->tags);
return $query->whereHas(
'meal_tags',
fn ($query) => $query->whereIn('tags_id', $requestedTagIds),
'=',
count($requestedTagIds)
);
}
)
->get();

Why 'where' return all data in laravel

I am applying search filter in my project so first of all I get data from multiple tables and store in two different variables and then merge these two variable into one so I can filter data from that merged variable. So my code is like that
$data1=Model::query()
->Join('...')
->leftJoin('...')
->where('id',login_user)
->select(...)
->whereRaw('id IN (select MAX(id) FROM table GROUP BY name)')
->groupBy('name')
->get();
$data2=Model2::query()
->leftJoin(...)
->select(...)
->where('id',login_user)
->whereNotIn(..)
->get();
both data1 and data2 return same column with different values so I merge both variable like that
$results = $data1->concat($data2);
No when I already get data so now I need to add filter data from $results so i make post method for that .
so When user request to filter data with name I write query like that
if ($request->name!="") {
$results->when(request('name'), function($q){
$q->Where('name', request('name'));
});
}
$records = $results;
return response()->json(['success'=>true,'message'=>'success', 'data' => $records]);
But that query is not filtering the data and return me all data.I am new in laravel so I don't know what I have done wrong in that any favour will be helpful for me ,thanks.
if (request()->has('name')) {
$results->when(request()->get('name'), function($q){
return $q->where('name', request()->get('name'));
});
}
$records = $results;
return response()->json(['success'=>true,'message'=>'success', 'data' => $records]);
As you use 'when()', you can drop the if expression all together:
$results->when(request()->has('name'), function($q){
return $q->where('name', request()->get('name'));
});
$records = $results;
return response()->json(['success'=>true,'message'=>'success', 'data' => $records]);
request() is a Laravel helper for Request $request
Edit: the where() clause in the ORM is with a small 'w', not 'W' as in orWhere

Laravel: DB Query returns stdclass instead of array

I've got the following:
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->get();
Log::info($srv);
and log info gives me
49 => stdClass::__set_state(array(
'id' => '46531',
)),
50 =>
stdClass::__set_state(array(
'id' => '46531',
)),
51 =>
stdClass::__set_state(array(
'id' => '46531',
)),
What I would like to have:
return the entry ONCE, there are multiple entries with this id in the "ad_service" table , and i would like to retrieve it once.
its a many to many relationship.
also biggest problem, what is this stdclass set state?
I was expecting something like:
array('id' => '46531', ...,)
thanks guys.
EDIT:
Solved it using Eloquent:
$srv = Ad::whereIn('id',[46696,48982,...MORE IDS HERE])->get();
this returns not an object but a collection itself. i do not really know what #ourmandave meant with "you get a collection with std class objects" i mean obviously i could see that but i do not really know why i got it, and why i get it as i wish using eloquent.
Any answer to that would be appreciated:
Why does DB:: return this std class stuff and Eloquent returns the "wanted format"?
If you just want one record you could try:
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->first();
Change the get to a first(). The reason you are getting multiple records is because get() returns a laravel collection of your ads.
You're actually getting back a lararvel Collection.
From the docs (https://laravel.com/docs/5.8/queries#retrieving-results)
The get method returns an Illuminate\Support\Collection containing the
results where each result is an instance of the PHP stdClass object.
You may access each column's value by accessing the column as a
property of the object:
foreach ($users as $user) {
echo $user->name;
}
There's a lot of methods they provide for collections to treat them like arrays.
(https://laravel.com/docs/5.8/collections#available-methods)
You might also try the distinct() method to get back one of multiples.
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->distinct()
->get();
To convert a model and its loaded relationships to an array, you should use the toArray method. This method is recursive, so all attributes and all relations (including the relations of relations) will be converted to arrays
$srv = DB::table('ads')
->join('ad_service','ad_service.ad_id', '=', 'ads.id')
->select('ads.id')
->whereIn('ads.id',[45789,46531])
->get()
->toArray();
Print array:
Log::info($srv);

Eloquent: Select field from whereHas block fails

I have got a slightly complex SQL query using a combination of where, whereHas, orWhereHas etc.
Everything goes well but when I add 'custom_records.custom_title' (see below) into the Select fields it fails with:
The Response content must be a string or object implementing __toString(), "boolean" given.
Any ideas?
Here it's the snippet:
`
$record = $this->record->newQuery();`
$record->whereHas('customRecords', function ($query) use ($searchTerm) {
$query->where('custom_title', 'like', '%'.$searchTerm.'%');
});
return $record->get([
'records.id',
'records.another_field',
'records.another_field_2',
'custom_records.custom_title',
]);
Update
When I run the produced SQL query on a mysql client it comes back with:
Unknown column 'custom_records.custom_title',' in 'field list'
You can't select custom_records.custom_title like that. Since it's a HasMany relationship, there can be multiple custom_records per record.
You have to do something like this:
$callback = function ($query) use ($searchTerm) {
$query->where('custom_title', 'like', '%'.$searchTerm.'%');
};
Record::whereHas('customRecords', $callback)
->with(['customRecords' => $callback])
->get(['id', 'another_field', 'another_field_2']);

Translate Laravel function to SQL request to test

I would like to test the output of Laravel function in MySQL, for this I need to translate the code to SQL request.
Function :
return DB::table('products')
->leftJoin('product_locations', 'products.id', '=', 'product_locations.productId')
->select(DB::raw('IF(product_locations.id is not null,concat(product_locations.id,"-","M"),concat(products.id,"-","N")) as id'), DB::raw('IF(product_locations.id is not null,1,0) as multilocation'), 'products.productName', 'products.productName as text', 'product_locations.productLocationName', 'product_locations.binLocationName', 'product_locations.unitCost', 'product_locations.amount')
->orderBy('products.productName')
->distinct()
->get();
Use query logging:
DB::connection()->enableQueryLog();
DB::table('products')
->leftJoin('product_locations', 'products.id', '=', 'product_locations.productId')
->select(DB::raw('IF(product_locations.id is not null,concat(product_locations.id,"-","M"),concat(products.id,"-","N")) as id'), DB::raw('IF(product_locations.id is not null,1,0) as multilocation'), 'products.productName', 'products.productName as text', 'product_locations.productLocationName', 'product_locations.binLocationName', 'product_locations.unitCost', 'product_locations.amount')
->orderBy('products.productName')
->distinct()
->get();
dd(DB::getQueryLog())
It is not the way, but still you can get the same result with less effort.
Make a small mistake in you query, for example use remove last character of a table name or a column name. Then it raise an error that shows the query. Then you can easily get your query on the screen.
In your query, use product instead of products. This will cause an error in query and display it.