Laravel | Using Eloquent hasManyThrough - laravel

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);

Related

Laravel many to many relationship with pivot

I'm using Laravel Filament.
I got a projects and responsibles tables in a many-to-many relationship. But also another table of responsabilityTypes
projects
id
title
responsibles
id
name
responsabilityTypes
id
name
project_responsible
project_id
responsible_id
responsibilityType_id
And here are my relationships setup:
Responsible.php
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType_id')
->withTimestamps()
->using(AcademicoProyecto::class);
}
Project.php
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType_id','sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
I have set up a class for the pivot table like so:
ProjectResponsible.php
use Illuminate\Database\Eloquent\Relations\Pivot;
class AcademicoProyecto extends Pivot
{
}
ResponsibilityType.php
//Don't know how to set up
My question is, when the user is in a Project Edit page and clicks on the "attach" button, in order to add a Responsible record, a Modal pops up to select a Responsible, but I also implemented a Select list to display the different types of responsibilities.
What am I missing to set up in order to access and display the types of responsibilities in the select list and attach it to the pivot table?
Your question asks about "access and display" but you have no controller or view code. But for the model, it's just a simple relationship between two tables, so define it as such:
class AcademicoProyecto extends Pivot
{
use SoftDeletes;
public function responsibilityType() {
return $this->belongsTo(ResponsibilityType::class);
}
}
class ResponsibilityType extends Model
{
protected $fillable = ["name"];
}
Now you simply update the other models to access the relationship in the withPivot() call.
class Responsible extends Model {
public function projects() {
return $this->belongsToMany(Project::class,'rel_project_responsible','responsible_id','project_id')
->withPivot('responsibilityType')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
class Project extends Model {
public function responsibles() {
return $this->belongsToMany(Responsible::class,'rel_project_responsible','project_id','responsible_id')
->withPivot('responsibilityType', 'sort')
->withTimestamps()
->using(AcademicoProyecto::class);
}
}
Now you should be able to do, for example:
$foo = Responsible::with("projects")->first();
foreach ($foo->projects as $project) {
echo $project->pivot->responsibilityType?->name;
}

Retrieve Models with invalid Category Subcategory relations

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');
}

Laravel how to make Eloquent relationship to find all data from child_category table with related category & subcategory name?

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.

Laravel Polymorphic Many to Many relationship

I am very new to polymorphic relationships and am struggling on my first task. My real life scenario is complicated, so for the purpose of this question, I have simplified it a little.
I have a range of products. Each of these products can be 'tagged' to one or more "Categories", "Brand" and "Consumer". For example:
I figured with this setup, I would need a table for my polymorphic relationships as below:
I have created a new Taggable class which contains the following
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Taggable extends Model
{
public function taggable()
{
return $this->morphTo();
}
}
...and added a method to my Product class:
public function taggedCategories()
{
return $this->morphMany(Taggable::class, 'taggable');
}
Finally, in my ProductController, I am trying to retrieve all products with their relationships as such:
$products = Product::with('taggedCategories')
Whilst this isn't producing an error, there are no categories returned in my results. Looking at the SQL output in LaravelDebugBar, I see the following SQL.
select * from `taggables` where `taggables`.`taggable_id` in (1) and `taggables`.`taggable_type` = 'App\Models\Product'
This clearly is not right, but I cannot for love nor money figure out where I have gone wrong. I feel I am close, but not quite there.
Can anyone explain what is wrong? Also, would I need to do something different for getting the "Brand" as this is a one-to-many relationship, not many-to-many?
Thanks
Your model structure is going to be like:
class Categories extends Model
{
public function products()
{
return $this->morphToMany('App\Tag', 'productable');
}
}
class Brand extends Model
{
public function products()
{
return $this->morphToMany('App\Tag', 'productable');
}
}
// and Consumers, ....
User Model:
class Product extends Model
{
public function categories()
{
return $this->morphedByMany('App\Categories', 'productable');
}
public function brands()
{
return $this->morphedByMany('App\Brunds', 'productable');
}
}
Database schema:
categories
id - integer
...
brands
id - integer
...
consumer
id - integer
...
productable
product_id - integer
productable_id - integer
productable_type - string
Now, you can retrieve the relations:
$categories = App\Categories::find(1);
// retrieve product of a type
foreach ($categories->products as $product) {
//
}
$product = App\Product::find(1);
// retrieve categories of a product
foreach ($product->categories as $categories) {
//
}
Actually, your type product (categories, brands, consumers) are productable.

Cannot access Collection::$items

I've got some troubles with an eloquent query.
Users have many feeds and feeds have many items.
I need to get all the items that belongs to the feeds of the user order by date.
I've got a pivot table:
feed_user
----------
- id
- feed_id
- user_id
and relationships are defined like this in my models:
class UsersController extends BaseController {
public function feeds() {
return $this->hasMany('feed');
}
class Feed extends \Eloquent {
protected $fillable = [];
public function users() {
return $this->belongsToMany('User');
}
public function items() {
return $this->hasMany('Item');
}
class Item extends \Eloquent {
protected $fillable = [];
public function feed() {
return $this->belongsTo('Feed');
}
But when I do this query...
Auth::user()->feeds->items->orderBy('date', 'DESC')->get();
It returns this error:
Cannot access protected property Illuminate\Database\Eloquent\Collection::$items
There are a couple issues here.
First, the relationship on User model is not correct. A hasMany relationship is one half a one-to-many relationship. This would assume that a feed belongs to one user, and that the feed table has the user_id field. A many-to-many relationship is defined by adding a belongsToMany relationship on both models. So, a user belongsToMany feeds, and a feed belongsToMany users.
class User extends \Eloquent {
public function feeds() {
return $this->belongsToMany('feed');
}
}
Next, the error you're seeing is because Auth::user()->feeds returns a Illuminate\Database\Eloquent\Collection object. You're then trying to access the items attribute on the Collection, which is protected and throws the error you're seeing.
Finally, since Laravel does not use joins for relationships, you cannot order a query by a field on a related table without manually doing the join yourself.
Try using eager loading:
Auth::user()->with('feeds.items')->orderBy('date', 'DESC')->get();

Resources