How to use hasMany which has belongsTo in the child - php

I have database relation like below
I want to get shop data with their products which each product has their category. If we define it using Eloquent ORM in Laravel, shop hasMany products belongsTo productCategory.
I can get the data of shop with their products using hasMany, but I can't get the productCategory of each products. Does anyone know how to get the productCategory of each product?
Shop model:
class Shop extends Model
{
public function products() {
return $this->hasMany('App\Product');
}
}
Procuct model:
class Product extends Model
{
public function shop() {
return $this->belongsTo('App\Shop');
}
public function category() {
return $this->belongsTo('App\ProductCategory');
}
}
Product category model:
class ProductCategory extends Model
{
public function products() {
return $this->hasMany('App\Product');
}
}
Shop controller to get the data:
class ShopController extends Controller
{
public function show(Shop $shop)
{
$products = $shop->products()->get();
return view('pages.shop-detail.index')->with('shop', $shop)->with('products', $products);
}
}

On One To Many (Inverse) Relationships:
Eloquent determines the default foreign key name by examining the name of the relationship method and suffixing the method name with a _ followed by the name of the primary key column. However, if the foreign key on the Product model is not category_id, you should pass the custom key name as the second argument to the belongsTo method:
class Product extends Model
{
public function category() {
return $this->belongsTo('App\ProductCategory', 'product_category_id');
}
}
Then in the view, loop over shop products an show the name of the product and the name of the product category:
<h3>Shop: {{ $shop->name }}</h3>
#foreach ($shop->products as $product)
<p>Product: {{ $product->name }}</p>
<small>Category: {{ $product->category->name }}</small>
#endforeach
But...
Doing the query in the way you had it in the question you'll have an N + 1 problem when you loop on products in the view.
Avoid the model binding in your controller to eager load the relationships and return just the shop, the products and category will be eager loaded on the Shop object:
class ShopController extends Controller
{
public function show($id)
{
$shop = Shop::with('products', 'products.category')->find($id);
return view('pages.shop-detail.index')->with('shop', $shop);
}
}

Related

Unable to fetch results from hasManyJson Using staudenmeir / eloquent-json-relations

I have been working on two tables Category & Product.
In Category Model I have a relationship like
class Category extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
public function products(){
return $this->hasManyJson(Product::class,'category_ids[]->id');
}
}
In Products Model I have a relationship like
class Product extends Model
{
use \Staudenmeir\EloquentJsonRelations\HasJsonRelationships;
protected $casts = [
'category_ids'=>'json',
];
public function products(){
return $this->belongsToJson(Category::class,'category_ids[]->id');
}
}
Now in my controller when I'm doing trying to get count of each categories product, it is giving me Empty results, below is my controller code.
public function two_category()
{
$list = Category::where('home_status', true)->get();
foreach($list as $ls){
echo $ls->name.' '.count($ls->products).'<br>';
}
dd('ended');
}
This is giving -
Category1 0
Category2 0
And finally this is how my column in product table looks like.

Laravel eloquent query using two relations

I have next db structure - product can be in many categories, product can be in many markets. Models \App\Product, \App\Market and \App\Category are created with many to many relations - belongsToMany().
class Product extends Model
{
public function categories()
{
return $this->belongsToMany('App\Category');
}
public function markets()
{
return $this->belongsToMany('App\Market');
}
}
class Category extends Model
{
public function products()
{
return $this->belongsToMany('App\Product');
}
}
class Market extends Model
{
public function products()
{
return $this->belongsToMany('App\Product');
}
}
In route.web I get category to display products
Route::get('/catalog/{current_category?}', 'CatalogController#index')->name('catalog.index');
Current market I can get from session (user select market when open website)
$market = $request->session()->get('market'); // or Session::get('market');
// $market->id
// $market->slug
In my MarketController#index I want to get all products for category from route and for current market from session. But how can I do it? I can get category products and market products. But how can I get category and market products at the same time?
public function index(Request $request, Category $current_category = null)
{
if ($current_category) {
$market_id = $request->session()->get('market')->id;
$products = $current_category->products;
// ...
}
}
If you want product based on category , use below query:
$products = $current_category->products()->get();
If you want products based on market, first you need to get market object than you can get products based on it.
$market = Market::find($market_id);
$market_products = $market->products()->get();
If you want products based on market and category you can use below query.
$products = Product::whereHas('categories', function($q) {
$q->where('category_id', $current_category->id);
})
->whereHas('markets', function($q) {
$q->where('market_id', $market_id);
})
->get();
As pointed in comment, you can achieve it with many to many polymorphic relation
tables structure
categories
id - integer
name - string
markets
id - integer
name - string
products
id - integer
name - string
productables
product_id - integer
productable_id - integer
productable_type - string
Category model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
/**
* Get all of the products for the category.
*/
public function products()
{
return $this->morphToMany('App\Product', 'productable');
}
}
Market model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Market extends Model
{
/**
* Get all of the products for the market.
*/
public function products()
{
return $this->morphToMany('App\Product', 'productable');
}
}
Product model
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* Get all of the categories that are assigned this product.
*/
public function categories()
{
return $this->morphedByMany('App\Category', 'productable');
}
/**
* Get all of the markets that are assigned this product.
*/
public function markets()
{
return $this->morphedByMany('App\Market', 'productable');
}
}
Than you can get products belonging to both (certain category and certain market) with
$products = \App\Product::where(['productable_id' => $category->id, 'productable_type' => get_class($category)])->orWhere(['productable_id' => $market->id, 'productable_type' => get_class($market)])->get();
assuming from your question that category and market are already known.
#YasinPatel 's solution should work too, but this way your DB architecture is more flexible. It's up to you now. Study about polymorphic relations solutions, you could find it interesting.

laravel eloquent multiple tables where criteria

I have spent two days trying to solve this issue but no luck.
I have 3 tables, categories, items and related items, every item is under one category and category can have many items, this part working fine, now the issue is in related item, I have in the table realteditems 3 fields, id(just autoincrement), ritemf_id,riteml_id those refers to item_id in items table.
What I want to do is to display item with its details and related items to this item, means if item1 have many realted items,such item2,item3,item4 .. so need to display like this
item_title: item1
related item:
item2
item3
item4
controller
$items = Item::orderBy('category_id', 'asc')->with('category')->get()->groupBy('category_id');
$categories = Category::orderBy('category_id', 'asc')->get();
return view('home',['items' => $items,'ritems' => $ritems,'categories' => $categories]);
items modal
public function category()
{
return $this->belongsTo('App\Category', 'category_id');
}
public function relateditems()
{
return $this->belongsTo('App\Relateditem', 'ritemf_id');
}
relateditems modal:
class Relateditem extends Model
{
protected $table="relateditems";
protected $fillable=['ritemf_id','riteml_id'];
protected $primaryKey='id';
public $timestamps=false;
public function items()
{
return $this->belongsTo('App\Item', 'item_id');
}
}
showing items with its category in blade(working fine)
#if (!empty($categoryItems->first()->category))
{{ $categoryItems->first()->category->category_name }} #else {{$category_id}} #endif
#foreach($categoryItems as $item)
{{$item->item_title}}
${{$item->item_price}}
#endforeach
#endforeach
relateditems()definition looks incorrect to me in Item model, Item may have more than one related items then this should be hasMany/beongsToMany association.
Assume its hasMany then update your item model as
public function relateditems() {
return $this->hasMany('App\Relateditem', 'ritemf_id');
}
And eager load your related items for each item
$items = Item::orderBy('category_id', 'asc')
->with(['category', 'relateditems'])
->get()
->groupBy('category_id');
I assume the groupBy method is used from collection class to group retrieved data not on database side
You need to fix both relations in Item Model and RelatedItem model. relateditems should be hasMany because an item can have many related item.
You also need to define belongsTo relation in RelatedItem to Item using riteml_id key
Item modal
public function category()
{
return $this->belongsTo('App\Category', 'category_id');
}
public function relateditems()
{
return $this->hasMany('App\Relateditem', 'ritemf_id');
}
Relateditem modal:
class Relateditem extends Model
{
protected $table="relateditems";
protected $fillable=['ritemf_id','riteml_id'];
protected $primaryKey='id';
public $timestamps=false;
public function item() //i have change it to item instead items, because belongsTo always return single record
{
return $this->belongsTo('App\Item', 'riteml_id');
}
}
Fetch Data
$items = Item::orderBy('category_id', 'asc')->with('category','relateditems', 'relateditems.item')->get()->groupBy('category_id');
foreach($items as $categoryId => $groupItems){
echo $groupItems->first()->category;
foreach($groupItems as $item) {
echo $item->item_tile;
...
foreach($item->relateditems as $relatedItem){
if ($relatedItem->item){
echo $relatedItem->item->item_tile; //this will show you related item title
}
}
}
}
For Single Item
$item = Item::with('category','relateditems', 'relateditems.item')->find(1);
foreach($item->relateditems as $relatedItem){
if ($relatedItem->item){
echo $relatedItem->item->item_tile; //this will show you related item title
}
}

Can't display data in view from model in Laravel

In my project I have orders which have many products and customers who have many orders. I am confused because I want to get all orders that a certain customer has and the products of each order. I messed up something somewhere and I am not sure if I set my relationships correctly. Here is my products table:
Here is my customers table:
And here is my orders table:
Here are my models:
Product:
class Product extends Model
{
public function orders()
{
return $this->belongsToMany('App\Order');
}
}
Order:
class Order extends Model
{
public function products()
{
return $this->hasMany('App\Product', 'id');
}
public function customer()
{
return $this->belongsTo('App\Customer');
}
}
Customer:
class Customer extends Model
{
public function orders()
{
return $this->hasMany('App\Order', 'id');
}
}
I get all customers from my database with App\Customer::all() in my CustomersController and pass the data in my customers.blade.php.
<h1>Customers:</h1>
#foreach($customers as $customer)
<h3>{{$customer->name}}</h3>
#foreach($customer->orders as $order)
<p>Order ID: {{$order->id}}</p>
#foreach($order->products as $product)
<p>Product title: {{$product->title}}</p>
#endforeach
#endforeach
<hr>
#endforeach
Here is the output:
If someone could explain why it doesn't output everything and give some advice if this is the way to go with the relationships, I would be very thankful.
Your products should belong to an order, rather than have a many-to-many relationship.
class Product extends Model
{
public function orders()
{
return $this->belongsTo('App\Order');
}
}
I found a solution by making a pivot table for products and orders called products_orders which holds the product_id and the order_id and making a many to many relationship between Product and Order. That is because an order may have multiple products and products may exist in multiple orders.
My pivot table products_orders:
class Product extends Model
{
public function orders()
{
return $this->belongsToMany('App\Order');
}
}
class Order extends Model
{
public function products()
{
return $this->belongsToMany('App\Product', 'products_orders');
}
public function customer()
{
return $this->belongsTo('App\Customer');
}
}
I made a one to many relationship (customer_id in orders table) for Customer and Order and now everything works fine.
class Customer extends Model
{
public function orders()
{
return $this->hasMany('App\Order');
}
}

Laravel Eloquent for sale-product-customer relationship

So using Laravel 4, I have a Sales table that has a many to many relationship with a Products table, and it also has a one to many relation with a Customers table.
I set up my models as follows:
class Sale extends Eloquent {
...
public function products(){
return $this->belongsToMany('Product');
}
public function customers(){
return $this->belongsTo('Customer');
}
}
class Product extends Eloquent {
...
public function sales(){
return $this->belongsToMany('Sale');
}
}
class Customer extends Eloquent {
...
public function sales(){
return $this->hasMany('Sale');
}
}
What I want to do is return the data of all sales, including the data of each product included in each sale and the data of the customer that bought it.
In my SalesController I'm using eager loading to query my data like this:
public function index()
{
return Sale::with('products', 'customers')->get();
}
It returns an object with the Sale data, the Product data, but the Customer data is null.
How can I achieve this using Eloquent (or a custom query)?
EDIT
This is the object string it returns:
[{"id":1,"customer_id":1,"date":"2013-11-21","status":1,"created_at":"0000-00-00 00:00:00","updated_at":"0000-00-00 00:00:00","products":[{"id":1,"name":"Monitor","price":50,"status":1,"created_at":"0000-00-00 00:00:00","updated_at":"0000-00-00 00:00:00","pivot":{"sale_id":1,"product_id":1,"custom_price":25,"order":1}}],"customers":null}]
Try changing your customers relationship to singular:
class Sale extends Eloquent {
...
public function products(){
return $this->belongsToMany('Product');
}
public function customer(){ // <- here
return $this->belongsTo('Customer');
}
}
(Moved from comments to answer)

Categories