Laravel eloquent query using two relations - php

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.

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.

How to use hasMany which has belongsTo in the child

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

Laravel Eloquent 5 Tables

I have 5 tables.
Users
Categories
Products
Product_categories
Order Details
A user purchases an an item and in my order details table I store the quantities etc.
I wanted to return all items that are of the main category = 'Testing' via the user.
$user = Auth::user();
return $user->items();
I have the following relationship on my user model.
public function items()
{
return $this->hasMany('App\OrderDetail','user_id')->selectRaw('item_description,count(quantity) as count')->where('item_description','<>','Carriage')->groupBy('item_id')->get();
}
I know I've not associated the the categories table here but I'm wondering how I would pull all the users order details where item category is "testing". The item can be related to many categories hence the product_categories table.
I'm not after someone writing the answer I'd like to know where I start to look at linking these via the model?
Would I be right in saying I have to do a function within my model relation?
According to your requirements & structure, your table should be structured like this:
users
id
name
...
categories
id
name
...
products
id
name
cost
...
category_product
id
category_id
product_id
order_details
id
user_id
cost
...
product_order_detail
id
product_id
order_detail_id
Your models should be structured like this:
class User extends Model
{
public function orderDetails()
{
return $this->hasMany(OrderDetail::class);
}
}
class Product extends Model
{
public function categories()
{
return $this->belongsToMany(Category::class, 'category_product');
}
public function orderDetails()
{
return $this->belongsToMany(Order::class, 'product_order_detail');
}
}
class Category extends Model
{
public function product()
{
return $this->belongsToMany(Product::class, 'category_product');
}
}
class OrderDetail extends Model
{
public function user()
{
return $this->belongsTo(User::class);
}
public function products()
{
return $this->belongsToMany(Product::class, 'product_order_detail');
}
}
and to fetch all the items / products who belongs to the category named Testing and belongs to the user, who've ordered it:
$items = Product::whereHas('categories', function($q) {
$q->where('name', '=', 'Testing');
})->whereHas('orderDetails', function($q) use($user) {
$q->whereHas('user', function($q) use($user) {
$q->where('id', $user->id);
});
})->get();
Hope this helps!

Retrieve all morphedBy relations

An Order have many ordered items
An Order's ordered items can either be a User or Product
What I am looking for is a way to retrieve all morphed objects to an Order. Instead of $order->users or $order->products I would like to do $order->items.
My progress
My progress so far involves a Many To Many Polymorphic Relationship.
My tables:
orders
id - integer
orderables (the order items)
order_id - integer
orderable_id - integer
orderable_type - string
quantity - integer
price - double
-----------
users
id - integer
name - string
products
id - integer
name - string
Example on how orderables table look
This is how I create an order and add a user and a product:
/**
* Order
* #var Order
*/
$order = new App\Order;
$order->save();
/**
* Add user to order
* #var [type]
*/
$user = \App\User::find(1);
$order->users()->sync([
$user->id => [
'quantity' => 1,
'price' => $user->price()
]
]);
/**
* Add product to order
* #var [type]
*/
$product = \App\product::find(1);
$order->products()->sync([
$product->id => [
'quantity' => 1,
'price' => $product->price()
]
]);
Order.php
/**
* Ordered users
* #return [type] [description]
*/
public function users() {
return $this->morphedByMany('Athliit\User', 'orderable');
}
/**
* Ordered products
*/
public function products() {
return $this->morphedByMany('Athliit\Product', 'orderable');
}
Currently I can do
foreach($order->users as $user) {
echo $user->id;
}
Or..
foreach($order->products as $product) {
echo $product->id;
}
But I would like to be able to do something along the lines of...
foreach($order->items as $item) {
// $item is either User or Product class
}
I have found this question, which was the closest I could find to what I am trying to do, but I can't make it work in regards to my needs, it is outdated, and also seems like a very hacky solution.
Have a different approach?
If you have a different approach than Polymorphic relationships, please let me know.
Personally, my Order models have many OrderItems, and it is the OrderItems that have the polymorphic relation. That way, I can fetch all items of an order, no matter what type of model they are:
class Order extends Model
{
public function items()
{
return $this->hasMany(OrderItem::class);
}
public function addItem(Orderable $item, $quantity)
{
if (!is_int($quantity)) {
throw new InvalidArgumentException('Quantity must be an integer');
}
$item = OrderItem::createFromOrderable($item);
$item->quantity = $quantity;
$this->items()->save($item);
}
}
class OrderItem extends Model
{
public static function createFromOrderable(Orderable $item)
{
$this->orderable()->associate($item);
}
public function order()
{
return $this->belongsTo(Order::class);
}
public function orderable()
{
return $this->morphTo('orderable');
}
}
I’ll then create an interface and trait that I can apply to Eloquent models that makes them “orderable”:
interface Orderable
{
public function getPrice();
}
trait Orderable
{
public function orderable()
{
return $this->morphMany(OrderItem::class, 'orderable');
}
}
use App\Contracts\Orderable as OrderableContract; // interface
use App\Orderable; // trait
class Product extends Model implements OrderableContract
{
use Orderable;
}
class EventTicket extends Model implements OrderableContract
{
use Orderable;
}
As you can see, my OrderItem instance could be either a Product, EventTicket, or any other model that implements the Orderable interface. You can then fetch all of your order’s items like this:
$orderItem = Order::find($orderId)->items;
And it doesn’t matter what type the OrderItem instances are morphed to.
EDIT: To add items to your orders:
// Create an order instance
$order = new Order;
// Add an item to the order
$order->addItem(User::find($userId), $quantity);
I think your solution is fine. I'd just add this helper method:
Order.php
public function items() {
return collect($this->products)->merge($this->users);
}
Then you can loop through the items with:
foreach ($order->items() as $item) {

Laravel 4 eager loading and categories, subcategories, articles

Hi i thought i can handle this myself, but actually i don't know how to bite it.
I am trying to categorise my programs. There will be only 2 levels of categories:
1 CATEGORY
2 |-Subcategory
I want it to be as simple as possible.
- program can belong to only one subcategory,
- categories can have many subcategories,
- subcategories can have many programs,
Of course i would like to list all programs from subcategories, when someone choose a main category.
I am also not sure about my current database tables structure and relationship in models.
Tables in database:
programs: id, title, description, program_subcategory_id
programs_categories: id, name
programs_subcategories: id, name, program_category_id
Models:
Program.php
class Program extends Eloquent {
protected $table = 'programs';
public function user()
{
return $this->belongsTo('User');
}
public function subcategory()
{
return $this->belongsTo('ProgramSubcategory', 'program_subcategory_id');
}
}
ProgramCategory.php
class ProgramCategory extends Eloquent {
protected $table = 'programs_categories';
public function subcategories()
{
return $this->hasMany('ProgramSubcategory');
}
}
ProgramSubcategory.php
class ProgramSubcategory extends Eloquent {
protected $table = 'programs_subcategories';
public function programs()
{
return $this->hasMany('Program');
}
public function category()
{
return $this->belongsTo('ProgramCategory');
}
}
Actual controllers:
ProgramsController.php
class ProgramsController extends BaseController {
public function index()
{
$programs = Program::with('subcategory')->orderBy('programs.id', 'desc')->paginate(5);
$acategories = ArticleCategory::All();
$pcategories = ProgramCategory::All();
return View::make('programs.index', compact('programs', 'acategories', 'pcategories'));
}
}
ProgramsSubcatecories.php
class ProgramsSubcategories extends BaseController {
public function index($cname)
{
$programs = ProgramSubcategory::whereAlias($cname)->first()->programs()->orderBy('id', 'DESC')->paginate(10);
$pcategories = ProgramCategory::All();
$scategories = ProgramSubcategory::All();
$acategories = ArticleCategory::All();
return View::make('programs.index', compact('programs', 'pcategories', 'scategories ', 'acategories'));
}
public function show($cname, $id)
{
$category = ProgramSubcategory::whereAlias($cname)->first();
$program = $category->programs()->findOrFail($id);
$pcategories = ProgramCategory::All();
$acategories = ArticleCategory::All();
return View::make('programs.show', compact('program', 'category', 'pcategories', 'scategories ', 'acategories'));
}
}
It is not a problem for me to list all items from one category with eager loading. But i have problem how to do it with 2-levels categories.
Please advise how to start it.
You are not looking for eager loading, you need to solve how to manage hierarchical data in your database.
Nested sets model serves this purpose very well. You should read some theory on Wiki: http://en.wikipedia.org/wiki/Nested_set_model
Fortunately, there are Eloquent implementations already.
To mention some:
- Baum (the best free, imho), https://github.com/etrepat/baum
- Laravel Nested Set, https://github.com/atrauzzi/laravel-nested-set
- Laravel4-nestedset, https://github.com/lazychaser/laravel4-nestedset
and the paid one (surely highest quality as well)
from Cartalyst company - https://cartalyst.com/manual/nested-sets

Categories