How to query whereIn inside json column in laravel 5.3? - php

I have been working with a laravel 5.3 version and in a table i have a json column where i have saved average rating of a category table.
so, A product category table has a column "detail" as a json data type which saves avgRating.
I want to run a query inside that json column. And i want to filter that category with a average rating. So, from the frontend side rating comes in a parameter with a comma seperated so that category can be filtered with multiple ratings.
$productCategory = ProductCategory::query();
$ratings = explode(',',$params['ratings']);
$productCategory = $productCategory->whereIn('detail->avgRating',$ratings)->get();
I want to achieve something like this.
I am using Postgres

It turns out that there was too much uncertainty at the time when the question was asked. Once the asker separated the problems and figured out that the raw query of
DB::select(DB::raw("SELECT * FROM product_categories where detail->>'avgRating' in ('2.0','4.0')"));
works in Postgres, the asker from that point onwards had a much easier time figuring out the actual solution in the where clause. I presuppose that it was
detail->>avgRating
instead of
detail->avgRating
but from the comment section that was not yet confirmed. But the moral of the story is that whenever one has an eloquent problem that might be related to the RDBMS, then it makes a lot of sense to first sort out the raw query and then, having solid knowledge about what should be generated, at that point one can switch to the Eloquent code and apply the solution there.

You should use whereJsonContains or whereRaw:
$productCategory->whereJsonContains('detail->>avgRating',$ratings)->get();
OR
$productCategory->whereRaw('JSON_CONTAINS(detail->>avgRating, ?)', $ratings)->get();

Related

Can't get result from where condition with variables in laravel

this is how i'm trying to get the type of certificate with where condition , but still recieve
nothing from this query:
$res= student::find($student, ['typecertificate']);
$k = certificate::select('id-cer')->where('name-cer','=',$res)->get();
return $k;
Based on your comments, I'm assuming you want to retrieve the field certificateType from the latest record that was inserted in the students table.
You can achieve that without a where clause, by directly using the Eloquent Builder to retrieve only that specific field like this:
Student::latest()->first('certificateType');
But this would give you an Eloquent Collection with one element. If you just want the value (not wrapped in a collection), you can simply retrieve the latest student and get the corresponding field directly:
$certificateType = Student::latest()->first()->certificateType;
I could explain more, but your question is vague and your database schema isn't clear either, so I'd need more information on that as well as what you intend to achieve.
In any case, Laravel's documentation is often a big help: https://laravel.com/docs/9.x/eloquent#retrieving-single-models

Laravel Eloquent: get unique models between relationships

I have a scenario like this:
I have User model that has an OneToMany relationships with the Post model.
I have a Hashtag model that has an OneToMany relationships with the Post model.
Recap: ONE user has MANY posts, ONE post belongs to ONE hashtag, ONE hashtag has MANY posts.
I would like to fetch only unique users records of an hashtag.
I'm able to do it in non scalable way (fetching all the posts first and then iterate filtering by user id), but I need to maintain scalability for large record numbers.
Edit: I saw a partial solution in Laravel docs.
Laravel eloquent has a method called unique().
With that method I can specify the parameter which should be unique in query.
In my case figured it out with:
$users = $hashtag->posts->unique('user_id');
But I can't paginate query in this way...
Has anyone a solution for that?
You have to paginate your main model caller.
I don't know exactly if this is can reproduce your actual scenario, but let's see this example:
Let's say you have the model hashtags
So let's say the code could be something like this:
$users = DB::table('hashtags AS tags')
->select('tags.*')
->join('[posts AS post','post.id','=','tags.id')
->distinct()
->paginate(5, ['tags.*']);
I don't know if your query it'll be very accurate, but for this, I believe the best approach it'll be you do a raw query, could look like it'll be costly to your database and the operation, but you can work around this approach indexing and partitioning your database.
Remembering always the eloquent sometimes even when we're building join queries could be more even costly to your database.
Since you're worried about scalability so the best thing could be for too could write a view to fetch all the data with proper indexing and partitioning.
Try to use DISTINCT
$users = $hashtag->posts()->selectRaw('DISTINCT(user_id) AS unique_user_id')->paginate(10);
OR
use groupBy()
$users = $hashtag->posts()->groupBy('name')->select('user_id')->paginate(10);

One SELECT (to fetch/ rule them all!) then handle the entire collection using Ruby array functions?

I'm wondering, when looking through a set of rows and for each row fetching another tables set of rows. For example, looping through a series of categories and for each category, fetching all their news articles. Perhaps to display on a single page or such. It seems like a lot of SELECT queries - 1 to get all categories, and one for each category (to get it's articles). So, my question is - is it quicker to simple do two fetches at the start:
categories = Category.all
articles = Articles.all
...and then just use select() or where() on articles by category id to only take those from the articles array? Replacing multiple SELECT queries with multiple array functions, which is quicker? I imagine also that each app, depending on number of rows, may vary. I would be interested to hear what people think, or any links that clarify this as I ddin't find much on the matter myself.
My code example above is Ruby on Rails but this question might actually apply to any given language. I also use PHP from time to time.
It depends on what you want to do with your data. You could try eager loading.
categories = Category.includes(:articles)
Here's the documentation. http://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations
I think you're describing what's called the N+1 problem (I'm new to this too). Here's another stack overflow question that addresses this issue generally: What is SELECT N+1?
n+1 is the worst, especially when you think about 10k or 10M articles like timpone pointed out. For 10M articles you'll be hitting the DB 10,000,001 times for a single request (hence the name n + 1 problem). Avoid this. Always. Anything is better than this.
If Category has a has_many relation to Article (and Article has a belongs_to relation to Category) you could use #includes to "pre-fetch" the association like so:
categories = Category.includes(:articles)
This will two two queries, one for the Category and one for the Article. You can write it out in two explicit select/where statements but I think doing it this way is semantically clearer. If you want to retrieve all the categories and then for each category get all the articles. You can write code like this (in Ruby):
categories.articles.each do |article|
# do stuff...
end
and it's immediately clear that you mean "all the articles for this category instance".

PHP & MySQL Tickers..Is this standard practice?

I currently have about 4 different database tables which output to html tables. Each of these tables uses a count query to calculate data from a 5th table.
That's no problem, but what about when I want to sort and order the data, and paginate etc (like with zend). If it were a one page table, I could probably sort an array.
My thought was, to use a ticker. But that would require a new column in all 4 tables and seems like overkill or like there could be a better way.
Sadly, I can't find much info on it (likely because I don't know what to search for).
Advice?
..and please take it easy, I'm new and learning.
Assuming youre using Zend_Db_Table_Row and that you dont need to persist any modifications you might make to these rowsets then you can just append the virtual columns to the row object and have them be accessible via array notation. So if youre doing it all in one query now just use that same query, and the column should be there.
OTOH, if youre using a Data Mapper pattern then simply adjust your hydration to look for this "virtual column" and hydrate it if it exists in the result data. Then in your getter for this property have it see if the property is null or some other negative specification, and if it is, to execute a calculation query on that single object or return the already calculated result.

SQL query to collect entries from different tables - need an alternate to UNION

I'm running a sql query to get basic details from a number of tables. Sorted by the last update date field. Its terribly tricky and I'm thinking if there is an alternate to using the UNION clause instead...I'm working in PHP MYSQL.
Actually I have a few tables containing news, articles, photos, events etc and need to collect all of them in one query to show a simple - whats newly added on the website kind of thing.
Maybe do it in PHP rather than MySQL - if you want the latest n items, then fetch the latest n of each of your news items, articles, photos and events, and sort in PHP (you'll need the last n of each obviously, and you'll then trim the dataset in PHP). This is probably easier than combining those with UNION given they're likely to have lots of data items which are different.
I'm not aware of an alternative to UNION that does what you want, and hopefully those fetches won't be too expensive. It would definitely be wise to profile this though.
If you use Join in your query you can select datas from differents tables who are related with foreign keys.
You can look of this from another angle: do you need absolutely updated information? (the moment someone enters new information it should appear)
If not, you can have a table holding the results of the query in the format you need (serving as cache), and update this table every 5 minutes or so. Then your query problem becomes trivial, as you can have the updates run as several updates in the background.

Categories