Categories and Sub Categories Query? - laravel

Im trying to set up a database so that I can query it and get all products out for a category and query it for a particular sub category.
I have a products table
id | title | category_id (fk)
And a categories table:
id | title | parent
So if the categories looked like this:
id | title | parent
1 | books | null
2 | crime | 1
3 | spy | 2
4 | dvd | null
5 | cd | null
And products:
id | title | category_id (fk)
1 | 007 | 3
1 | Murder| 2
A product would belong to one category. Above the '007' product belongs to the 'Spy' sub category. 'Murder' belongs to the 'Crime' sub category. Both belong to the parent 'books' category.
How would I query the database to:
Get all products for a sub category (in example for spy I would get '007')
Get all products for a parent category, so if I want all products for books I would get both '007' and 'Murder'.

You can just find all subcategories of a parent category and then use that to fetch the associated products. The solution below assumes you have Eloquent Models for each table you described, so a Categoryand a Product model.
The Product model doesn't require any additional code, it just needs to exist. The Category model will have to look as follows:
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
public function products()
{
// Build an array containing the parent category ID and all subcategory IDs found
$categoryIds = array_merge([$this->id], $this->subcategoryIds());
// Find all products that match the retrieved category IDs
return Product::whereIn('category_id', $categoryIds)->get();
}
protected function subcategoryIds($id = null, &$ids= [])
{
// If no ID is passed, set the current model ID as the parent
if (is_null($id)) {
$id = $this->id;
}
// Find subcategory IDs
$categoryIds = $this->query()->where('parent', $id)->lists('id');
// Add each ID to the list and recursively find other subcategory IDs
foreach ($categoryIds as $categoryId) {
$ids[] = $categoryId;
$ids += $this->subcategoryIds($categoryId, $ids);
}
return $ids;
}
}
Now to find the products within the Books category, you just need to find it by id and call the products method on the model:
$products = App\Category::find(1)->products();

Related

Getting data from pivot table using eloquent

I have two tables with a pivot table
Table user
id | name | email
Table drinks
id | name
Pivot Table user_drinks
id | user_id | drink_id | quantity | price | status
I want to get all users and drinks with status set to 1 and their latest details from the pivot table, that is use the user_Id to get the username and drink_Id to get the drink name then the price and quantity from the pivot table.
In your User Model put this //App\User.php
public function drinks()
{
return $this->belongsToMany(Drink::class)->where('status',1);
}
In your DrinkModel put this //App\Drink.php
public function users()
{
return $this->belongsToMany(Drink::class)->where('status',1);
}
Then in your controller you can use this like
$users = User::has('drinks')->get(); //to get all the user that has drinks column status set to 1
foreach ($users as $user) {
//you rest of code
}

How should I use HasManyThrough connecting 3 tables via 2 pivot tables in Laravel?

I am having trouble wrapping my head around the HasManyThrough relationship in Laravel 5.8 which I think is what I need to use, but I am not sure how to implement. I've tried following along a Laracasts video where Jeffrey describes it and read the docs, but I can't seem to grasp the concept and feeling rather stupid right now.
How should I define the relationships in my models and write the correct Eloquent query in my ProductsController to display posts in a product view that shares the same tag id?
For example I have a post tagged with "mbti" and I have a product tagged also with "mbti". How would I go about displaying those related posts in the relevant product view? I've managed to display the tags in the view so far, but I want the posts associated with that tag to display as well. I appreciate any guidance on how I should approach this.
I have 3 tables and 2 pivot tables (some column names removed for brevity):
| products |
+----------+
|id |
------------
| posts |
+----------+
|id |
------------
| tags |
+----------+
|id |
------------
| post_tag |
+----------+
|post_id |
|tag_id |
------------
| product_tag |
+-------------+
|product_id |
|tag_id |
---------------
My models:
Post.php
public function tags() {
return $this->belongsToMany('App\Tag')->withPivot('tag_id');
}
Tag.php
public function posts()
{
return $this->belongsToMany('App\Post')->withPivot('post_tag');
}
public function products()
{
return $this->belongsToMany('App\Product')->withPivot('product_tag');
}
Product.php
public function tags()
{
return $this->belongsToMany('App\Tag')->withPivot('product_id');
}
I ended up adding a tag_id column to my products table and referencing the tag id there. A product in my application can only be assigned one tag, so the need for a pivot table was kind of redundant and added unnecessary complexity.
In my Tag.php model, I defined the relationship as:
public function products()
{
return $this->hasManyThrough('App\Post', 'App\Product');
}
and in my ProductsController.php I select the tags as such in mthe show method:
$tag = Tag::where('id', '=', $product->tag_id)->first();
if($tag) {
$tags = Tag::whereName($tag->name)->first();
}

How to query a table based on criteria of another table in Laravel?

In my laravel project I have an inventory and a medicine table which the formats are as the following:
Inventory Table
id | medicine_id | expiry_date
-------|-----------------|-------------
1 | 1 | 13-11-2021
2 | 2 | 01-01-2020
3 | 2 | 23-05-2024
Medicine Table
id | name | category
-------|-----------------|-------------
1 | Hemophine | Syringe
2 | andrenalin | Tablet
3 | aspirin | Capsule
The models are set as below:
Inventory Model
class Inventory extends Model
{
public $incrementing = false;
public function medicine(){
return $this->belongsTo('App\Medicine', 'medicine_id');
}
}
Medicine Model
class Medicine extends Model
{
public $incrementing = false;
public function inventory(){
return $this->hasMany('App\Inventory', 'medicine_id');
}
}
For retrieving all the inventories with a specific category such as Syringe, I tried eager loading, but couldn't figure out the flaws. Here is what I did to get all the inventories with a Syringe category, but it didn't work.
public function index()
{
$medicines = Inventory::where('medicine_id'->category, "Syringe")->get();
foreach($medicines as $medicine){
echo $medicine->medicine->name ." ".$medicine->expiry_date;
echo "<br>";
}
}
Now, Is there anyway to get all the inventory items based on their categories they are in? in this case in "Syringe" category?
Your syntax is a bit wrong, in order to load the medicine for each inventory you need to use with and the where function is where('column', value)
So change it to this:
$medicines = Inventory::with([
'medicine' => function($query) {
$query->where('category', "Syringe");
}
])->get();
or even better the other way around:
$medicines = Medicine::with('inventory')->where('category', 'Syringe')->get();

Laravel 5 multi level category

i currently i have 2 tables category & subcategory and everything works just fine, but i need another level of sub category which will be like category->sub->sub for that matter i tried to find a reasonable solution and i end up with bunch of packages to handle that for me.
now the question is
do i have to delete my category tables and their relations to my
another modes before i try any package or i should just add package top of my current tables?
what is the best package for my purpose base on your experience?
thanks in advance.
You don't need to depend on packages to implement this.
You can design your categories table like following:
|----------|------------|---------------|
| id | name | category_id |
|----------|------------|---------------|
Here category_id is nullable field and foreign key referenced to id of categories table.
For category category_id field will be NULL and for sub-category category_id will be it's parent category id. For sub sub category, category_id will be parent sub category id.
In model, you can write relation like following:
Category.php
/**
* Get the sub categories for the category.
*/
public function categories()
{
return $this->hasMany(Category::class);
}
Now you can get your sub categories like $category->categories.
N.B: You don't need the subcategory table, Only one table will do the work.
Update- Show product categories
Update Category.php:
/**
* Get the parent category that owns the category.
*/
public function parent()
{
return $this->belongsTo(Category::class);
}
In Product.php:
/**
* Get the category that owns the product.
*/
public function category()
{
return $this->belongsTo(Category::class);
}
Now, you need to get product category and all of its parents. It'll be an array of categories from parents to child. Then you can show as you wish.
$category = $product->category;
$categories = [$category];
while (!is_null($category) && !is_null($category = $category->parent)) {
$categories.unshift($category);
}
// $categories = ['parent category', 'sub category', 'sub sub category' ..]
Show category title sequentially
foreach ($categories as $category) {
echo $category->title . '<br>';
}

Joining pivot table to access data

I am trying to access data from a database query which I think will need a join. I have users that can be apart of many groups. I am using a belongsToMany
relationship. My Models are like so
class User extends Model
{
protected $table = 'users';
protected $guarded = [];
public function group()
{
return $this->belongsToMany('App\Group', 'users_user_groups')->withPivot('user_id', 'group_id');
}
}
class Group extends Model
{
protected $table = 'user_groups';
protected $guarded = [];
use SoftDeletes;
public function user()
{
return $this->belongsToMany('App\User', 'users_user_groups')->withPivot('user_id', 'group_id');
}
}
When I run everything I need too, I might get data like the following.
users
+----+---------------+
| id | name |
+----+---------------+
| 1 | John Doe |
+----+---------------+
user_groups
+----+---------------+-----------------+
| id | name | description |
+----+---------------+-----------------+
| 1 | Group AA | Something |
+----+---------------+-----------------+
| 2 | Group BB | Something |
+----+---------------+-----------------+
users_user_groups
+----+---------------+-----------------+
| id | user_id | group_id |
+----+---------------+-----------------+
| 1 | 1 | 1 |
+----+---------------+-----------------+
| 2 | 1 | 2 |
+----+---------------+-----------------+
So I know user with the id 1 belongs to the user_groups with ids of 1 and 2. What I am trying to do is grab all the users within my database who
belong to a user_group with the name admin. So I am trying something like this
DB::table('users')->select('userName')
->join('user_groups', 'users_user_groups')
->where('name', '=', 'admin')->get();
This I know is all wrong, how can I get all users within a group when using belongsToMany and a pivot table?
Thanks
Eloquent uses relations, not the query builder.
You can achieve what you're aiming for by doing something like this:
$group = Group::where('name', 'admin')->first();
$users = $group->users; // Where users is the name of your relationship (At the moment you have user)
Under the hood that will do two SQL statements and map them together in eloquent objects, rather than a join. The statements will look something like this:
select * from user_groups where name = ? and deleted_at is not null limit 1
select * from users where id in (?, ?)
When you have an instance Group you execute the relationship by calling it as if it was a property. So after that $users will contain a collection of User instances, so you can just loop through them:
foreach ($users as $user) {
// Do something with $user
}

Resources