My Laravel application has three models
Category
Subcategory
Channels
Category - Subcategory (one category has many subcategories)
Subcategory - Channels (many to many relation)
I have the following relations:
class Category extends Model
{
public function subcategories()
{
return $this->hasMany('App\Subcategory');
}
public function channels()
{
return $this->hasManyThrough('App\Channel','App\Subcategory');
}
}
class Subcategory extends Model
{
public function Category()
{
return $this->belongsTo('App\Category');
}
public function channels()
{
return $this->belongsToMany(Channel::class, 'channel_subcategory', 'channel_id', 'subcategory_id')
->withTimestamps();
}
}
class Channel extends Model
{
public function subcategories()
{
return $this->belongsToMany(Subcategory::class, 'channel_subcategory', 'channel_id', 'subcategory_id')->withTimestamps();
}
}
I have 4 tables:
categories
subcategories
channel_subcategory
channels
I want to query all channels inside a category, organized in subcategories. Something like:
Category A
Subcategory A-1
Channel A-1-1
Channel A-1-2
Subcategory A-2
Channel A-2-1
Channel A-2-2
Given the Category.slug I have made the following Query in the Category Controller:
public function show($slug)
{
$category = Category::with('subcategories')->where('categories.slug', '=', $slug)->first();
$channels = $category->channels;
return view('categories.show')->withCategory($category)->withChannels($channels);
}
And I get an error stating:
PDOException in Connection.php line 319:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'channels.subcategory_id' in 'on clause'
(SQL: select `channels`.*, `subcategories`.`category_id`
from `channels`
inner join `subcategories` on `subcategories`.`id` = `channels`.`subcategory_id` where `subcategories`.`category_id` = 1)
Eloquent assumes that there is a one-to-many between channels and subcategories and ignores the pivot table
My questions are:
Is there a solution using Eloquent? Many-throught relations with a pivot table in the middle?
Using plain MySQL, how just got the list of channels ALL togethere, but I have not idea how to separate them in groups of subcategories. Any help?
EDIT TO CLARIFY:
I am trying to query through FOUR tables here:
CATEGORIES, SUBCATEGORIES, CHANNEL_SUBCATEGORY AND CHANNELS
I want to find the channels in a Category grouped by subcategories. (channels and subcategories has a many-to-many relation)
I think that your channels table does'nt have a column name subcategory_id but category_id ?
So that you have to tell laravel how to handle your relationship :
In your Category Model:
public function channels()
{
return $this->hasManyThrough(
'App\Channel','App\Subcategory',
'category_id', 'subcategory_id', 'id'
);
}
Edit after reply :
Ok then the relationship is not a hasManyThrough, but a belongs to :
public function channels()
{
return $this->belongsToMany('App\Channel', 'channel_subcategory', 'channel_id','subcategory_id');
}
By default, laravel will search for a category_id foreign_key, but as you rename it subcategory_id you have to specify all the relationship
Related
For a user with many orders (hasMany relationship), I can use hasOne with ofMany(), e.g.
public function orders()
{
return $this->hasMany(Order::class);
}
public function largestOrder()
{
return $this->hasOne(Order::class)->ofMany('price', 'max');
}
How can I achieve the same for a many to many relationship?
E.g. a product can have many categories (and each category has a weight),
and I want to get the products and their preferred category (=max weight).
// Product
public function categories() {
return $this->belongsToMany(Category::class);
}
// Category
public function products() {
return $this->belongsToMany(Product::class);
}
The following does not work:
// Product
public function preferredCategory() {
return $this->hasOne(Category::class)->ofMany('weight','max');
}
// Product::where('id',1)->with('preferredCategory')
// Column not found: 1054 Unknown column 'categories.product_id'
Suggestions? Using Laravel 9.
I am trying to get product name from pivot table but unfortunately i have no idea how can i get product name from pivot table please help me thanks.
Product Model
public function category()
{
return $this->belongsToMany('App\ProductCategory', 'product_category', 'product_id', 'mf_product_category_id');
}
productcategory model
public function products()
{
return $this->belongsToMany('App\Product', 'product_category', 'mf_product_category_id', 'product_id');
}
controller
public function getproduct(Request $request)
{
// getting category Id
$categoryId = $request->category;
// getting product Id
$name = trim($request->product);
$productId = Product::where('name', $name)->pluck('id');
$getProductcategory = ProductCategoryCount::whereIn('mf_product_category_id', $categoryId)->whereIn('product_id', $productId)->get();
return $getProductcategory;
// return response()->json($getproduct);
}
As stated in laravel doc, you can retrieve pivot table extra data using withPivot on the relationship.
Define category in the Product Model
public function category()
{
return $this->belongsToMany('App\ProductCategory', 'product_category', 'product_id', 'mf_product_category_id')
->withPivot('Your_extra_data_on_pivot_table');
}
And by querying products, you can get category (which I suggest to be categories). You can retrieve it like this $products->first()->pivot->{name of_your_extra_data_on_pivot_table}
It is more optimized if you eager load this relationship.
I have three Models Record, Category and Subcategory. The Record table stores category_id and subcategory_id as foreign keys. There is a pivot table "category_subcategory".
I would like to retrieve all Records that have an "invalid Category Subcategory relation" in an elegant and performant way via a custom function in the Model.
What do I mean by "invalid Category Subcategory relations":
a) Record has as Categroy and Subcategory. But the Subcategory doesn't belong to the Category (No entry in pivot table)
b) Record has a Category but no Subcategory (subcategory_id = NULL). Because the Category itself has Subcategories, the subcategory_id of the Record should be NULL
c) Record has a Category and a Subcategory, but the Cateory itself has no Subcategories, therefore the Record should have the subcategory_id = NULL
With this custom function in the Model I would like to be able to do sth like this in a Controller:
Records::withInvalidCategorySubcategoryRelation()->get(); //or similar
rather than going through endless foreach loops in the Controller like
$records = Record::all();
foreach($records as record){ ...
Any suggestions are much appreciated!
Here are my Model Classes:
class Record extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
public function subcategory()
{
return $this->belongsTo(Subcategory::class);
}
}
class Category extends Model
{
public function subcategories()
{
return $this->belongsToMany(Subcategory::class);
}
}
class Subcategory extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class);
}
}
Laravel offers whereNotExists that can be helpful here.
Add the following function to Record model:
// Adjust the func name as per your test ;)
public function scopeInvalidRecords($query)
{
return $query->whereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
AND
category_subcategory.subcategory_id = records.subcategory_id
');
});
}
In Controller:
Record::invalidRecords()->get();
// It'll give you all the invalid records(invalid as per your definition)
Hope it is helpful!
Thanks to #Zeshan Khattak I was able to get what I needed. Now I get the "invalid" Records where a) b) c) is met
public function scopeInvalidRecords($query)
{
return $query->whereExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
');
})->whereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
AND
category_subcategory.subcategory_id = records.subcategory_id
');
})->orWhereNotExists(function($q) {
$q->select(\DB::raw(1))
->from('category_subcategory')
->whereRaw('
category_subcategory.category_id = records.category_id
');
})->whereNotNull('subcategory_id');
}
I have three table:
categories table fields id, category_name
subcategories table fields id, category_id, subcategory_name
child_categories table fields id, category_id, subcategory_id, child_category_name
I have three model Category, Subcategory,
1) =>category model code
class Category extends model {
public function subcategory(){
return $this->hasMany(Subcategory::class);
}
public function Child_category(){
return $this->hasMany(Child_category::class);
}
}
2) =>Subcategory model code
class Subcategory extends model {
public function Category(){
return $this->belongsTo(Category::class);
}
}
3) =>Child_category model code
class Child_category extends model {
public function Category(){
return $this->belongsTo(Category::class);
}
}
how to make Eloquent relationship to find all data from child_categories table with related category & subcategory name?
Once the relationships are defined you can get them by simply calling the property that has the same name as the relation you need.
$category = Category::first();
$subcategory = $category->subcategory;
If you wanted to get all the categories with all subcategories and child categories in one line you can use the with() method to eagerload them efficiently.
$categories = Category::with(['subcategory', 'Child_category'])->get();
This will fetch all the categories, then fetch all the related subcategories and child categories and associate them appropriately.
I have a table called invoiceDetails that has item_id as foreign key from another table called items which has category_id as foreign key from table called categories.
I want to do that following using eloquent:
$result = InvoiceDetail::groupBy('item_id')
->selectRaw('sum(qty) as qty, item_id')->with('item', 'category')->get();
but I am getting error:
Call to undefined relationship [category] on model [App\InvoiceDetail].
Here's my relation inside Category model:
public function invoiceDetail() {
return $this->hasManyThrough('App\InvoiceDetail', 'App\Item', 'category_id', 'item_id');
}
Any suggestions?
Not sure you would even need a hasManyThrough relation here, unless you want to fetch all InvoiceDatail objects belonging to all items which in turn belong to the Category. That part is not clear from your question.
But in your example you are fetching items with their category from distinct item_id.
The reason this is not working is because you are trying to fetch the category relation from the InvoiceDetail object, which does not exist.
->with('item', 'category')
You want to load the Category based on the item relation, not based on the InvoiceDetail, try the dot notation (given that you did define the other relations)
->with('item.category')
Relations should be like this:
class InvoiceDetail extends Model
{
public function item()
{
return $this->belongsTo(\App\Item::class);
}
}
class Item extends Model
{
public function invoiceDetails()
{
return $this->hasMany(\App\InvoiceDetail::class);
}
public function category()
{
return $this->belongsTo(\App\Category::class);
}
}
class Category extends Model
{
public function items()
{
return $this->hasMany(\App\Item::class);
}
public function invoiceDetails()
{
return $this->hasManyThrough(\App\InvoiceDetail::class, \App\Item::class, 'category_id', 'item_id');
}
}
You would want to use the hasManyThrough if, for example, you have a Category and you want to load all the InvoiceDetails directly.
dd($category->invoiceDetails);