Retrieve Models with invalid Category Subcategory relations - laravel

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

Related

nested query with laravel model

I am writing a nested query with Laravel.
Namely First, the Truck information is drawn, then I list the data of the vehicle with "truck_history", there is no problem until here, but I want to show the information of the invoice belonging to the "invoice_id" in truck_history. but I couldn't understand how to query, I want to do it in the model, is this possible? If possible, how will it be done?
"ID" COLUMN IN INVOICE TABLE AND "invoice_id" in "InvoiceDetail" match.
TruckController
public function getTruck($id)
{
$truck = Truck::with(['truckHistory'])->find($id);
return $truck;
}
Truck Model
protected $appends = ['company_name'];
public function companys()
{
return $this->belongsTo(Contact::class, 'company_id', 'id');
}
public function getCompanyNameAttribute()
{
return $this->companys()->first()->name;
}
public function truckHistory(){
return $this->hasMany(InvoiceDetail::class,'plate_no','plate');
}
So you can add another relationship in the InvoiceDetail::class and add in the truck history.
try something like this:
public function truckHistory(){
return $this->hasMany(InvoiceDetail::class,'plate_no','plate')->with('Invoice');
}
Simply add the following relations (if you don't already have them):
Invoice model :
public function truckHistory()
{
return $this->hasOne(InvoiceDetail::class);
}
InvoiceDetail model :
public function invoice()
{
return $this->belongsTo(Invoice::class);
}
And you can get the relation invoice of the relation truckHistory adding a point as separator :
public function getTruck($id)
{
$truck = Truck::with(['truckHistory.invoice'])->find($id);
return $truck;
}

How to get product name from pivot table using laravel?

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.

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.

BelongsToMany with one or vice versa relationship

I'm using Laravel and Eloquent.
I have a courses table and a Pivot table that holds the related courses.
My pivot table named relatedCourses has 2 columns, course1 & course2.
In the above example, Course ID 5 is related with Course ID 7.
$data["course"] = course::where("isActive",1)->with('related')->find(5);
This works and brings ID 7 as related.
If i try
$data["course"] = course::where("isActive",1)->with('related')->find(7) it should bring ID 5 as related, this is what i'm trying to achieve.
My code is like this in the model :
class course extends Model
{
public function category()
{
return $this->hasOne('App\courseCategory',"categoryId");
}
public function related()
{
return $this->belongsToMany('App\Models\course', 'relatedCourses', 'course1', 'course2');
}
}
You can define two relationships for this query.
class course extends Model
{
public function category()
{
return $this->hasOne('App\courseCategory',"categoryId");
}
public function relatedCourses()
{
return $this->belongsToMany('App\Models\Course', 'relatedCourses', 'course1', 'course2');
}
public function originalCourses()
{
return $this->belongsToMany('App\Models\Course', 'relatedCourses', 'course2', 'course1');
}
}
Then you can query your relations,
$data["course"] = course::where("isActive",1)->with('relatedCourses')->find(5);
and
$data["course"] = course::where("isActive",1)->with('originalCourses')->find(7);
To merge both relations you can do like this
$course = course::where("isActive",1)->with('originalCourses', 'relatedCourses')->get();
Hope this will help you.

Laravel | Using Eloquent hasManyThrough

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

Resources