I have two tables, categories and product. I'm trying to add a query to count products of each category without foreach of the categories. I reached the query below:
DB::table('categories')->leftJoin('product', 'categories.id', '=','categpry_id')
->selectRaw('categories.*, count(product.id) as Count')
->where('product.status',1)
->groupBy('categories.id')
->get();
But the problem I'm facing is when a category doesn't have products it's not showing. I want to show the categories with no product with 0 product in the array.
You can use Eloquent's withCount method:
$categories = Category::withCount('products')->get();
Each $category will have a products_count attribute.
I'm pretty sure you can use rightJoin for this.
Not 100% sure though.
The first line in your code is bad in a few ways
'categories.id', '=','categpry_id')
You have a typo
Add the table also 'categories.id', '=', 'products.category_id'
If you're using models, which you should be... #DigitalDrifter has an "Eloquent" solution.
Two asides
I would refrain from using mysql reserved words in your aliases. COUNT(product.id) AS product_count instead.
You can minimize your raw statement with an array passed to select instead.
Here is what I would use
Categories::leftJoin(Product::class, 'product.category_id', '=', 'categories.id')
->select([
'categories.*',
DB::raw('COUNT(products.id) AS product_count')
])
->where('product.status', 1)
->groupBy('categories.id')
->get();
TIL.
Model::withCount('relation') that is pretty darn cool too.
Related
I'm trying to check the another table to remove the matches from the results but unable to figure this out.
$value = people::select(array('people.blog_id'))
->join('blocks', 'people.user_id', '=', 'blocks.blocker')
->where('people.user_id', $user->id)
->where('blocks.blocker', '!=', 'people.user_id')
->get()
->toArray();
What I am trying to achieve, is to strip away the results when getting user_id from people where blocker is found as well in the blocks table, but the following returns an empty array.
As per laravel doc
You may use the table method on the DB facade to begin a query. The table method returns a fluent query builder instance for the given table, allowing you to chain more constraints onto the query and then finally get the results using the get method.
Change your query statement like bellow-
$articles = DB::table('people')
->join('blocks', 'people.user_id', '=', 'blocks.blocker')
->where('blocks.blocker', '<>', 'people.user_id')
->select('people.blog_id')
->get();
I think you should use right join here instead of simple join,
You can do it like.
$value = people::select(array('people.blog_id'))
->join('blocks', 'people.user_id', '=', 'blocks.blocker', 'right')
->where('people.user_id', $user->id)
->get()->toArray();
Please notice the fourth parameter in the join statement, this will include only the results where blocks will find.
Hope this will help
I'm trying to get data from the database filtered by some categories
This is my code in CodeIgniter
$this->db
->select('*')
->from($this->table)
->join('sites','sites.id = categories_by_site.site_id')
->where('categories_by_site.category_id', $categories[0])
->or_where('categories_by_site.category_id', $categories[1])
->order_by('id', 'ASC')
->get()
->result();
I simplify my code for the sake of this question, the above query take the categories as a search filter and used it to get result from the database.
There can be many categories filter to search at the same time, that's why I am using or_where() method.
The problem with this, when I got the result data, it has duplicate row of entries in object array.
Anyone can suggest how to prevent from getting a duppicate data from the database using above query?
Thanks
You can use group_by to solve this issue
Replace your code with
$this->db
->select('*')
->from($this->table)
->join('sites','sites.id = categories_by_site.site_id')
->where('categories_by_site.category_id', $categories[0])
->or_where('categories_by_site.category_id', $categories[1])
->order_by('id', 'ASC')
->group_by('categories_by_site.category_id')
->get()
->result();
You can eleminate duplicate values using distinct or group by
As you select all fields a group by is better in my opinion. Example to group by category_id
$this->db->group_by('category_id');
In a nutshell I am trying to do a join with more than one condition. We are using legacy Laravel 4, and the actual class I've tracked it to is Illuminate\Database\Query\Builder. Here is what I am adding:
->leftJoin('node_fields AS visible_for_categories', function($join){
$join->on('nv2.id', '=', 'visible_for_categories.node_version_id');
$join->on('visible_for_categories.name', '=', 'visible_for_categories');
})
It works fine with the first $join->on( ) call, but the page fails if the second on is called. why is this and what is the proper way to do this in Laravel 4?
The way that worked for me on the above query is as follows:
->leftJoin('node_fields AS visible_for_categories', function($join){
$join->on('nv2.id', '=', 'visible_for_categories.node_version_id');
$join->on('visible_for_categories.name', '=', DB::raw("'visible_for_categories'"));
})
The query builder will assume all three values in the on() function are fields, not strings, and will parse out the . period as well.
The general assumption is that JOINS will have the relational field joins to create structure, and the WHERE conditionals will provide the desired filter. However anyone who's worked esp. with LEFT or RIGHT joins knows this is not always possible.
Be careful for SQL injection using DB::raw but in this case, an EAV table, we're dealing with a fixed string, not a variable.
Try
->leftJoin('node_fields AS visible_for_categories', function($join){
$join->on('nv2.id', '=', 'visible_for_categories.node_version_id')
->on('visible_for_categories.name', '=', 'visible_for_categories');
})
Is it possible to limit the result while doing a leftjoin?
(Laravel 4.2) - Querybuilder
I've got the following query with laravel's querybuilder:
DB::table('part')
->leftjoin('model', 'model.model_id', '=', 'part.model_id')
->leftjoin('make', 'model.make_id', '=', 'make.make_id')
->leftJoin('photo', 'photo.part_id', '=', 'part.part_id')
->select( 'part.part_id',
'part.model_id',
'make.desc as make_desc',
'model.desc as model_desc',
'photo.local as local_img',
'photo.cdn as cdn_img')
->take(8)->get();
Every part has more then 4 photos, but i only want the first photo to be included in the join. The problem is that when i use this query, i get 8 part objects (results). But the 8 results are not 8 parts, but 2 parts. This query creates 4 of the same part objects, with the only difference being the photo (the join includes every photo).
I tried things like:
->select( '(photo.local LIMIT 1) as local_img',
'(photo.cdn LIMIT 1) as cdn_img')
But this doesn't work. I also tried to do raw query's. Also i tried to use the '->take(1)' in a leftjoin closure, like this:
->leftjoin('photo', function($q){
$q->on('photo', 'photo.part_id', '=', 'part.part_id')->take(1);
});
But this is not possible.
I`m searching for a solution to only include the first photo row in a leftjoin.
Edit: Following up on mgrueter's answer. I know that a groupby would do the trick, but this makes the query very slow. So i want to do it in a different way so the query doesn't get to slow.
Group the results by 'part.id':
DB::table('part')
->leftjoin('model', 'model.model_id', '=', 'part.model_id')
->leftjoin('make', 'model.make_id', '=', 'make.make_id')
->leftJoin('photo', 'photo.part_id', '=', 'part.part_id')
->select( 'part.part_id',
'part.model_id',
'make.desc as make_desc',
'model.desc as model_desc',
'photo.local as local_img',
'photo.cdn as cdn_img')
->group_by('part.part_id')
->take(8)->get();
And also order the results by 'photo.created' or whatever column you have to determine, which is the first photo.
I have a large query. For all intents and purposes, I'll recreate the issue using a smaller query as an example:
DB::table('cases')
->join('contacts', 'cases.id', '=', 'contacts.id')
->select('cases.name', 'contacts.name')
->get();
And then in laravel's templating (Blade) when I do a foreach loop and print the results, it's printing the results from the cases table for {{ $case->name }}
I understand the problem and why it is happening, but how can I make it work properly?
This is a PDO restriction and has nothing to do with laravel. If you need them both, you'll have to alias it in your query:
DB::table('cases')
->join('contacts', 'cases.id', '=', 'contacts.id')
->selectRaw('cases.name as cases_name, contacts.name as contacts_name')
->get();