Laravel Eloquent - Many to Many, Through, Where - php

I have three tables: products, categories and product_has_category
The categories table has a "type" field, which is either "gender" or "garment". So a product has many categories and a category has many products.
The trickier part comes in with how I have two different types of categories (i.e. gender and garment). A product can have only one "gender" category and only one "garment" category.
products table:
---------------
| id | style |
---------------
| 1 | Style 1|
---------------
| 2 | Style 2|
---------------
categories table:
----------------------------
| id | type | name |
----------------------------
| 1 | gender | men's |
----------------------------
| 2 | gender | women's |
----------------------------
| 3 | garment | crew neck |
----------------------------
| 4 | garment | v neck |
----------------------------
| 5 | garment | tank top |
----------------------------
product_has_category table:
----------------------------
| product_id | category_id |
----------------------------
| 1 | 1 |
----------------------------
| 1 | 3 |
----------------------------
| 2 | 2 |
----------------------------
| 2 | 5 |
----------------------------
So, with the above data, we have:
Style 1 is a men's crew neck, and Style 2 is a women's tank top.
I'd like to be able to retrieve products in this type of manner:
// returns Style 1, men's, crew neck
$product = Product::with(['gender', 'garment'])->find(1);
// returns Style 2, women's, tank top
$product = Product::with(['gender', 'garment'])->find(2);
I think I understand how I can set up a standard many-to-many relationship in my models using a belongsToMany() method setting the junction table as 'product_has_category'.
In my category model I have the following relationship:
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product', 'product_has_category', 'category_id', 'product_id');
}
}
But I'm not sure how to set the relationships in the product model to get the categories by a given category type. Here's what I have in my product model, which makes sense in a way, but laravel is throwing an error about the category.type being an unknown column.
class Product extends Model
{
public function gender()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')->where('type', '=', 'gender');
}
public function garment()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')->where('type', '=', 'garment');
}
}
Can anyone point me in the right direction for how to set up these types of data relationships?

I'm assuming your relationships work as intended.
Here's your problem:
public function gender()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('category.type', '=', 'gender'); // Here
}
public function garment()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('category.type', '=', 'garment'); // And here
}
When you chain query off of a relationship (in your case ->where('category.type'...), you're working on a related model's query. And as such you need to remove the category. part, since you're already working on a category query.
Like this:
public function gender()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('type', '=', 'gender'); // Removed 'category.'
}
public function garment()
{
return $this->belongsToMany('App\Category', 'product_has_category', 'product_id', 'category_id')
->where('type', '=', 'garment'); // Removed 'category.'
}
Now if you call Product::with(['gender', 'garment'])->first() you will have these 2 categories separated.

Related

How to query with nested distinct records in Laravel/Eloquent?

I have the following table structure:
Products
========
id | name
------|-------
1 | A
2 | B
Stocks
========
id | product_id | color_id | size | qty
------|------------|----------|----- |-----
1 | 1 | 1 | S | 37
2 | 1 | 1 | XL | 89
3 | 1 | 2 | S | 6
4 | 1 | 2 | L | 8
Colors
========
id | name | hex
------|-------|-------
1 | Red | #ff0000
2 | Green | #00ff00
What I want is to get the list of products with each of its available colors and sizes, so:
Product {
id: string
name: string
available_sizes: string[]
available_colors: Color[]
}
I looked up the Laravel documentation and figured that I need to use hasManyThrough (probably) to make a relation between Product <==> Color. But since my colors table doesn't have a stock_id, I am not understanding how can I connect them.
How can I achieve this?
As I understand, hasManyThrough works in a scenario that a product has many stocks and a stock has many colors. This is not the case.
You can simply eager load the relationship and present the data the way you want, using collection and higher order messages:
public function index()
{
$products = Product::with('stocks.color')->get();
return ProductResource::collection($products);
}
class ProductResource extends JsonResource
{
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'available_sizes' => $this->stocks->map->size->unique()->toArray(),
'available_colors' => $this->stocks
->map->color
->map->name
->unique()
->toArray()
]
}
}
here you will use models
ProductModel
public function stocks()
{
return $this->belongsto(StockModel::class,'product_id');
}
StockModel
public function colors()
{
return $this->hasmany(ColorModel::class,'color_id');
}
Controller
$product=Product::with('stocks.colors')->find($id);
dd($product);
and check if the data are available.
or according to the documentation hasmanythrough
ProductModel:
public function colors(){
return $this->hasManyThrough(
Stock::class,
Color::class,
'product_id', // Foreign key on the product table...
'color_id', // Foreign key on the colors table...
'id', // Local key on the products table...
'id' // Local key on colors table...
);
}
Controller:
$product=Product::with('colors')->find($id);

Eloquent Count nested relationships

| Data | DataChildren | Category
----------------------------------
| id | id | id
| name | data_id | name
| | category_id |
| | name |
Data Model:
public function data_children() {
return $this->hasMany(DataChildren::class, 'data_id', 'id');
}
DataChildren Model:
public function category() {
return $this->belongsTo(Category::class, 'category_id', 'id');
}
I want to get count of Category based on Data id through DataChildren.
I just want to take the Category records from Data so the result should be like this
name from category | Count of category for Data
-------------------------------------------------
Unpublished | 1
Published | 3
I've tried using this but return null
Data::withCount(['category'=> function($query){return $query->groupBy('category_id');}])->find(1);
you need to used many to many relationship
in Category Model:
public function datas()
{
return $this->belongsToMany(Data::class, 'data_childerens', 'category_id', 'data_id');
}
Then run this Query withCount :
Category::withCount('datas')->get();
Set Data Model:
public function categories()
{
return $this->belongsToMany(Category::class, 'data_childerens', 'data_id', 'data_id');
}
Then run this Query With and withCount :
Data::with('categories')->withCount('datas')->get();

Eloquent resolve distant relationship

let's say I've this kind of structure.
| lines | | products | | orders |
|------------| |----------| |--------|
| id | | id | | id |
| product_id | |----------| |--------|
| order_id |
|------------|
Is there a way in Laravel Eloquent to load related products given an order? I've tried with hasManyThrough(Product::class, Line::class) with no luck, because on products table there's no reference to the order. Is there a method that can resolve this relationship or should I write a raw query?
You can create a many-to-many relationship in Order model:
class Order extends Model
{
public function products()
{
return $this->belongsToMany('App\Product','lines', 'order_id', 'product_id');
}
}
Then you can get products related to an order:
$order_id = 12;
$order = Order::find($order_id);
$order_products = $order->products;

Laravel ORM many to many relationship acts like one to many

My problem is I am trying to establish a many to many relationship between posts and genre. Here is what I have done so far.
class Post extends Model
{
public function genres()
{
return $this->belongsToMany('App\Genre');
}
}
class Genre extends Model
{
public function posts()
{
return $this->hasMany('App\Post');
}
}
genre Table
+----+--------+
| id | name |
+----+--------+
| 1 | News |
| 2 | Sports |
+----+--------+
post table
+----+----------------+
| id | title |
+----+----------------+
| 1 | Political News |
| 2 | Sport Update |
+----+----------------+
genre_post table
+----+---------+----------+
| id | post_id | genre_id |
+----+---------+----------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
| 3 | 2 | 2 |
+----+---------+----------+
When I am trying to acess genre list of a post, everything works fine.
Post::where('slug', '=', $id)->with("genres")->first(); // no problem
But when I tried the opposite it's not working.
$posts = Genre::where( "slug", "=", $id )->with("posts")->first();
I am getting the following error.
Column not found: 1054 Unknown column 'post.genre_id' in 'where
clause' (SQL: select * from post where post.genre_id
I understand the laravel is trying to aceess genre_id column from post table which doesn't exists since it's a many to many relation which means one post can contain more than one genre and one genre can contain more than one post.
Any idea how can I resolve this?
This is expected since hasMany itself an one to many relationship. Use belongsToMany instead.
class Genre extends Model
{
public function posts()
{
return $this->belongsToMany('App\Post');
}
}

Laravel - model modifications

I need to refactor project and I have problem. Below is old, working model, where 'active' column is in "people" table. I need to move 'active' column into "people_translations" table.
Do you have any Idea to modify scopeActive method?
Thanks a lot!
Old working model:
class BaseModel extends Eloquent
{
public function scopeActive($query)
{
return $query->where($this->table . '.active', '=', 1);
}
}
class People extends BaseModel
{
protected $table = 'peoples';
protected $translationModel = 'PeopleTranslation';
}
class PeopleTranslation extends Eloquent
{
public $timestamps = false;
protected $table = 'peoples_translations';
}
Old tables structure:
Table: peoples
id | type | date | active
-------------------------
7 | .... | ... | 1
Table: peoples_translations
id | people_id | language_id | name
-----------------------------------
1 | 7 | 1 | Ann
Old query:
$peoples = \People::active()->get();
New tables structure:
Table: peoples
id | type | date
----------------
7 | .... | ...
Table: peoples_translations
id | people_id | language_id | name | active
--------------------------------------------
1 | 7 | 1 | Ann | 1
Create a relation for translations inside People Model
public function translations()
{
return $this->hasMany('PeopleTranslation', 'people_id');
}
Create active scope in People model
public function scopeActive($query)
{
return $query->whereHas('translations', function($query) {
$query->where('active', 1);
});
}
It will make subquery for this table and as a result it will get where (count of translations with active = 1) > 0.
If you have one-to-one relation - look for hasOne relation method instead of hasMany.

Categories