laravel orm : where condition on table -> related table -> related table - php

so here is my database for a book store
books : id ,title
category : id, title
book_category : id , book_id, category_id
book_stock : id , book_id , quantity , price
considering all the relations are defined in the model , i can query book_stock it goes something like this
Stock::with('Book')->get();
but what if i want to get stock of a book in the category = 1
i can use use condition on book
Stock::with('Book' , function($q){
$q->where(['title'=>'abc']);
})->get();
but how can i filter related table of book ?
basically i want to get book_id from book_category where category_id = 1 and then use those ids to filter my books finally get stock
ps : i don't want to use query builder

This will return you all books belonging to category=1 with their stock information:
$categoryId = 1;
$books = Book::with('stock')->whereHas('category', function($query) use ($categoryId) {
return $query->where('id', $categoryId);
})->get();

You can do:
Stock::with('Book.stock', 'Book.category')->get();
You can access any number of nested relations within a with statement.
Related question:
Laravel nested relationships
Armin Sam's answer should also be a viable option.

Related

laravel get data when give id not match with join table id

here is my category table data
id
1
2
6
7
when in my post bale I join with this category table
here is my post table sample data
ID = 1,
name = 'hellow'
category id = 4 (i join with category table but selected category is
deleted)
here is my index SQL query (when categy_id match with the category.id) then only its fetch
$post = DB::table('posts)->join('category','posts.category_id','categories.id')-.paginate(10);
for some reason, the selected category can be deleted so I try to get category deleted post data
here is my query
$cpost = DB::table('posts')->join('categories','posts.category_id', '!=' ,'categories.id')->select('post.*')->paginate(5);
but above query duplicate post data based on available category data
i want all post data which are category id is not matched with in category table id how can i get that ?
Try this. Key is the leftJoin instead of default innerJoin (join).
// posts without assigned or existing category
$posts = \DB::table('posts')
->leftJoin('category','posts.category_id','categories.id')
->whereNull('categories.id')
->paginate(10);
why are you doing a join for this? you already have category id stored in your post table.
$cpost = DB::table('posts')->where('category_id','!=', $category_id)->paginate(5);
Just try it:
$cpost = DB::table('posts')
->join('categories','posts.category_id', '=' ,'categories.id')
->select('post.*', 'categories.*')
->whereNotIn('posts.category_id', DB::raw('select id from categories'))
->paginate(5);

Laravel eloquent get items belonging to a certain category

I have the following table structure
tables
tbl_items
id
sub_category_id //foreign key
table subcategories
tbl_subcategories
id
category_id
name
table categories
tbl_categories
id
name
As you can see from the above the sub_category_id is a foreign key in products table which relates to the id in the subcategories table. the subcategories table have a category_id which is a foreign key from the categories table
Now i wanted to fetch all items belonging to a certain category.
so i have tried
$categories = Categories::where('id',7)->first();
//from categories get subcategoryid
$allsubcategories = Subcategories::where('category_id',$categories->id)->pluck('id')
$allitems = Items::where('status',1)->wherein('sub_category_id',$allsubcategories)
The above works. but is there a neater way to do this?
define a hasManyThrough relationship in the Category model to the Item model.
// on the Category Model
public function items() {
return $this->hasManyThrough(Items::class, Subcategories::class);
}
then you can access all the items belongs to a particular category
// On your Controller
$category = Categories::where('id',7)->first();
$items = $category->items
official documentation - https://laravel.com/docs/5.7/eloquent-relationships#has-many-through
Well you could probably try using Nested Eager Loading, which should be a little more efficent:
//Assuming the relationships are named subCategories() on Categories model & items() on Subcategories model
$cat = Categories::where('id', 7)
->with('subCategories.items')
->get()
->first();
That should load the category w/ all the subCategories and all the items within each subCategories with the least amount of queries.

How to build complex query that has join query and subquery using query builder Laravel

Good evening my coder friends,
As the title of this Question says, i have a little bit complex mysql query that i am trying to use query builder in Laravel to write it down, but every time i try to write it i stop when reach to the subquery. i will write down the query and also will let you know what the query will do .
SELECT
brands.name_en as brand_name_en,
brands.name_ar as brand_name_ar,
brands.brand_url as brand_url,
brands.description_ar as description_ar,
brands.description_en as description_en,
categories.name_en as category_name_en,
categories.name_ar as category_name_ar
FROM
brands
INNER JOIN categories ON brands.category_id = categories.id
WHERE
(brands.status = "1")
AND
(
brands.id IN (
SELECT
distinct(items.brand_id)
FROM
items
WHERE
items.in_stock = "1"
GROUP BY
(items.brand_id)
)
)
The query explanation :
I have three tables :
Brand (id,name,category_id)
Categories (id,name,brand_id)
Items (id,brand_id,in_stock,sold_date)
Now i want to do the following :
i want to retrieve (brand name , and the category name ) and sort them based on the highest sales in the last month
This is example on the tables with data in them :
Brands Table
------------
id name category_id
1 googleplay 1
Categories Table
--------------------------
id name brand_id
1 cards 1
items Table
-----------
id brand_id in_stock selling_date
1 1 0 null
1 1 1 2017-02-02 04:04:49
Been trying for almost 2 days but no luck as its really complicated when it reaches the subquery part .
Really hope to find help from you guys . and let me know if you guys need more explanation please , i might forgot something while writing the questions .
Thank you in advance.
If you change your sql a little, you can make it with inner joins:
SELECT brands.name, categories.name
FROM brands
INNER JOIN categories ON brands.category_id = categories.id
INNER JOIN items ON categories.id = items.brand_id
WHERE brands.status = 1 AND items.in_stock = 1
Translating this to Laravel is simple:
\DB::table('brands')
->join('categories', 'brands.category_id', '=', 'categories.id')
->join('items', 'brands.id', '=', 'items.brand_id')
->select('brands.name as brand', 'categories.name as category')
->where('brands.status', 1)
->where('items.in_stock', 1)
->get();
Conceptually speaking, How about we select the items from the item tables for the last month, sort them and fetch the brand and category via a join on that.
It can be written in a single query as follows:
DB::table('items')
->join('brands', 'items.brand_id', '=', 'brands.id')
->join('categories', 'brands.category_id', '=', 'categories.id')
->select('brands.name as brand', 'categories.name as category')
->where('brands.status', 1) // as per your query
->whereRaw(YEAR(items.selling_date) = YEAR(CURRENT_DATE - INTERVAL 1 MONTH)) // year condition
->whereRaw(MONTH(date_created) = MONTH(CURRENT_DATE - INTERVAL 1 MONTH)) // last month condition
->orderBy('items.in_stock', 'desc') // orderBy the sales
->distinct() // Since you only require brand names and category names, choosing distinct will avoid the need of the GROUP BY eliminating the multiple occurances of a brand in the items table
->get()
I haven't tried running this query, but hope it helps. If I have misunderstood the question feel free to correct me.

Laravel belongsToMany where doesn't have one of

I have two tables: categories and videos, I then have a pivot table for these as it's a belongsToMany relationship.
What I'm trying to do is get all of the videos where there isn't a single instance of the video being in one of many categories.
e.g.
Video 1 is in category 1, 2 and 3.
Video 2 is in category 1 and 3.
Video 3 is in category 1.
I want to get the video which is NOT in category 2 or 3, meaning this will return Video 3.
What I've tried so far, which doesn't give the intended result, this is because another row is still found for Video 1 and 2, as they are in Category 1:
Video::whereHas('categories', function($query) {
$query->whereNotIn('category_id', [2,3]);
})->take(25)->get();
The query populated from this is:
select * from `videos` where exists (select * from `categories` inner join
`category_video` on `categories`.`id` = `category_video`.`category_id` where
`videos`.`id` = `category_video`.`video_id` and `category_id` != ? and
`category_id` != ? and `categories`.`deleted_at` is null) and `videos`.`deleted_at`
is null order by `created_at` desc limit 25
You can use Eloquent's whereDoesntHave() constraint to get what you need:
// get all Videos that don't belong to category 2 and 3
Video::whereDoesntHave('categories', function($query) {
$query->whereIn('id', [2, 3]);
})->get();

display multiple categories codeigniter

I have a relational database in this format
Table: posts
Columns: post_id,post_title,post_content
Table: categories
Columns: category_id,category_name
Table: posts_categories
Columns: post_id,category_id
Posts can have multiple categories so i store them in posts_categories using post and category id, when i get results from database using below query, it just display the last category, Is it possible to display all categories otherwise i have to run a separate query, here my code.
$this->db->select("p.*,pc.*,c.*");
$this->db->where('post_id', $id);
$this->db->from('posts AS p');
$this->db->join('posts_categories AS pc', 'pc.post_id = p.post_id', 'inner');
$this->db->join('categories AS c', 'pc.category_id = c.category_id', 'inner');
$q = $this->db->get();
Thanks for any help.
You didn't mention what fields you actually select. However, you can SELECT p.title, c.category_name and after doing your query (mentioned in the question), you should have multiple rows in your result, containing the posts title and a category name for that post.
Now if you want you can group these categories by posts in php, building a new array from the db result.

Categories