Laravel: One to Many to Many, retrieve distinct() values - laravel

Laravel 4 Project, using Eloquent ORM.
I have three tables: customers, orders and products (+ 1 pivot table order_product). Customers are linked one-to-many to Orders. Orders are linked many-to-many to Products.
Customers 1-->N Orders N<-->N Products
I would like to have a method on Customer model that retrieves a list of products that customer is buying.
To better understand this, assume products are consumable.
For example Customer #1 can place:
Order #1 for Products A, B and C;
Order #2 for Products A, C and D;
Order #3 for Products C and E;
...and the result I want to retrieve is a Collection with Products A, B, C, D and E.
Models are (pseudo-coded on the fly):
class Product extends Eloquent {
public function orders()
{
return $this->belongsToMany('Order');
}
}
class Orders extends Eloquent {
public function customer()
{
return $this->belongsTo('Customer', 'customer_id');
}
public function products()
{
return $this->belongsToMany('Product');
}
}
class Customers extends Eloquent {
public function orders()
{
return $this->hasMany('Orders', 'customer_id');
}
public function products()
{
// What to put here ???
}
}

Thanks to #deczo's answer, I was able to put up a single query method to retrieve items:
public function items()
{
$query = DB::table('items')->select('items.*')
->join('item_order', 'item_order.component_id', '=', 'items.id')
->leftJoin('orders', 'item_order.order_id', '=', 'orders.id')
->leftJoin('customers', 'customers.id' , '=', 'orders.customer_id')
->where('customers.id', $this->id)
->distinct()
->orderBy('items.id');
$eloquent = new Illuminate\Database\Eloquent\Builder( $query );
$eloquent->setModel( new Item );
return $eloquent->get();
}

This is a Many-to-Many relationship, but with the Orders table as the pivot table.
class Customers extends Eloquent {
public function orders()
{
return $this->hasMany('Orders', 'customer_id');
}
public function products()
{
return $this->belongsToMany('Products', 'orders', 'customer_id', 'product_id');
}
}
I've included the last two parameters, but if you follow the singular_id pattern they can be left out.
It's possible to receive distinct Product models like this:
public function products()
{
return $this->belongsToMany('Products', 'orders', 'customer_id', 'product_id')
->distinct();
}

#deczo's answer probably works fine, and is probably a lot more performant as all the data reduction is done in the database itself, but here's a 'pure Laravel' way that's undoubtedly more readable:
use Illuminate\Database\Eloquent\Collection;
class Customer extends Eloquent
{
...
public function products()
{
$products = new Collection;
foreach ($this->orders as $order) {
$products = $products->merge($order->products);
}
return $products;
}
}
Note that this method will not act like normal relationship methods - to get the resulting collection you call the method (i.e. $products = $customer->products();) and you can't access it as a property like you can with relationships (i.e. you can't do $products = $customer->products;).
Also, I'm kinda going on my understanding of the Illuminate\Database\Eloquent\Collection#merge() method here that it automatically does a DISTINCT-like thing. If not, you'll have to do a $collection->unique() kinda thing.

I can't think of easy relation method for this one, but here's a workaround:
$productsIds = DB::table('customers')
->leftJoin('orders', 'orders.customer_id', '=', 'customers.id')
->join('order_item', 'order_item.order_id', '=', 'orders.id')
->leftJoin('items', 'order_item.item_id' , '=', 'items.id')
->distinct()
->get(['items.id']);
$productsIds = array_fetch($productsIds, 'id');
$productsCollection = Product::whereIn('id', $productsIds);

Related

Restricting hasMany relationship where child.field != parent.field without joins

I'm looking for a way to qualify a hasMany relationship to exclude/include children where the value a specific child field does/not match that of a specific parent field without using joins.
The issue with joins is that the ->select() filters out many descendant relationships (unless they are each added to the join, which is too much to manage). The descendant relationships for example would be order_item.options which is a belongsToMany of OrderItem.
Case in point:
class Order extends Model
{
public function items()
{
return $this->hasMany(OrderItem::class, 'order_id', 'id');
}
public function items_removed_by_store()
{
return $this->hasMany(OrderItem::class, 'order_id', 'id')
//->join('order', 'order_item.order_id', 'order.id')
//->where('order_item.deleted_by', '!=', 'order.customer_id')
//->select('order_item.*', 'order.customer_id')
->onlyTrashed();
}
public function items_removed_by_customer()
{
return $this->hasMany(OrderItem::class, 'order_id', 'id')
//->join('order', 'order_item.order_id', 'order.id')
//->where('order_item.deleted_by', '=', 'order.customer_id')
//->select('order_item.*', 'order.customer_id')
->onlyTrashed();
}
}
and I'm looking to query it like:
Location::has('orders.items_removed_by_customer')->get()
Get me locations where customers have removed items from their orders.
It seems like you are currently joining the relationship on its parent. Note you already have the parent data
Maybe try something like this
class Order extends Model
{
public function items()
{
// Note: it is not required to pass 'order_id', 'id' to this method as thats the default value
return $this->hasMany(OrderItem::class);
}
public function items_removed_by_store()
{
return $this->items()
->where('order_items.deleted_by', '!=', $this->customer_id)
->onlyTrashed();
}
public function items_removed_by_customer()
{
return $this->items()
->where('order_item.deleted_by', '=', $this->customer_id)
->onlyTrashed();
}
}

Trying to display eloquent data base from ID's

Hi I am trying to create a record base from ID of an order transaction.
I have here the ORDERS, CAMPANIES and CURRENCIES models.
Now I want the ORDER to get the ID from a URL Parameter, and then the companies should grab the company_id from Orders same with currency, I want to grab the currency_id from orders.
Here's what I came up so far:
public function create($order_id)
{
$order = Orders::find($order_id)->where('id', '=', $order_id)->get();
$company = Companies::where('id', '=', $order['company_id'])->get();
$currency = Currencies::where('id', '=', $order['company_id'])->get();
$banks = Banks::all('name','acronym','id')->get();
return view('orderPayments.create', compact('order'))
->with('company', $company)
->with('currency', $currency)
->with('banks', $banks);
}
currencies model
public function orderPayments()
{
return $this->hasMany('App\Orderpayments', 'id','currency_id');
}
Companies Model
public function orderPayments()
{
return $this->hasMany('App\Orderpayments', 'id','company_id');
}
Order Payments Model
public function company()
{
return $this->hasOne('App\Companies', 'id','company_id');
}
public function currency()
{
return $this->hasOne('App\Currencies', 'id', 'currency_id');
}
public function bank()
{
return $this->hasOne('App\Bank', 'id', 'currency_id');
}
How can I achieve it? thank you so much in advance!
UPDATE
I just applied #sandy's answer. I checked if the $order has a content so I echod the $order by doing this, {{ $order }} The return value is ok.
but when I calling the other attributes like $order->grandtotal, or $order->companies->comp_name the error is
If your relationship between your models is a One To Many relationship you should use belongsTo in your relations like this:
OrderPayment Model
public function company()
{
return $this->belongsTo('App\Companies','company_id');
}
public function currency()
{
return $this->belongsTo('App\Currencies','currency_id');
}
public function bank()
{
return $this->belongsTo('App\Bank', 'currency_id');
}
Also, the second parameter on the function should be your foreign key.
Answering your question:
If you want to access the relations of a given Order you could do this:
$order = Order::find($order_id);
$order->company; // this will return the company
$order->currency; // this will return the currency
If you want to display in your view:
{{$order->company->anyCompanyAttribute}}
Note:
You should name your models in SINGULAR like Currency, Company. Not in plural.
try like this
$order = Orders::with(['company','currency'])->where('id', '=', $order_id)->get();

How I can make the same SQL query whith the halp of Eloqument ORM methods and models

I have followng database:
And database diagramm is on next picture. Simply speaking, Users have many shops, Shops have many products etc.
In need to select all products from all shops of particular user. In my Controller it's look like so:
But it's does't work (
Instade of this I try to write direct SQL
I just need to make following request by Eloqument methods
SELECT * FROM tmp.products
WHERE tmp.products.shop_id IN
(SELECT id FROM shops where user_id = 1);
with dynamics parametrs of course.
You could get all products by using whereHas filter like
$products = Product::whereHas('shop.user', function ($query) use($request) {
$query->where('id', '=', $request->user()->id);
})->get();
I assume you have defined proper mappings in your models like
class Product extends Model {
public function shop() {
return $this->belongsTo('Shop', 'shop_id');
}
}
class Shop extends Model {
public function user() {
return $this->belongsTo('User', 'user_id');
}
public function products() {
return $this->hasMany('Product', 'shop_id');
}
}
class User extends Model {
public function shops() {
return $this->hasMany('Shop', 'user_id');
}
}

Merge two hasOne relation in laravel - Eloquent

I have two table one is product and secound is RelatedProduct
where related product is connected with products.id and sometime with product.parent_id
public function relationWithId()
{
return $this->hasOne('App\RelatedProduct', 'product_id', 'id');
}
public function relationWithParent()
{
return $this->hasOne('App\RelatedProduct', 'product_id', 'parent_id');
}
so need a third relation where I can merge above both like this
public function relationProduct()
{
/*****/
}
Product::where('status', '=', 'active')->with('relationProduct')->get();
thanks in advance
Try creating the accessor which merges the two relationship and returns the collections.
public function getRelatedProductAttribute($value){
$withId = $this->relationWithId;
$withParentId = $this->relationWithParent;
return $withId->push($withParentId);
}

Laravel 4 eloquent

Is there any way I can do this with eloquent?
$orders = Customer::with('orders','orders.shop')->where('orders.shop.location','=','Japan')->get()
Customers, orders and shop are tables where 1 customer has many orders and each order has one shop only.
Location is a column in the shop table
I keep getting an error stating orders.shop.location is a column not found.
Anyone can help? Thanks in advance.
You need to defined relationship in your model classes.
Customer model:
public function orders()
{
return $this->hasMany('Order');
}
Order model:
public function customer()
{
return $this->belongsTo('Customer');
}
Then if you want orders of a special customer you just have to do :
$orders = Customer::find($id)->orders;
Or find the user attatched to an order:
$user = Order::find($id)->user;
You can also use the same kind of relation between your Shop and Order model and do something like this:
$orders = Order::with(array('shop' => function($query)
{
$query->where('location', '=', 'japan');
}))->get();
Which should give you all orders for a shop located in japan.
More informations about this type of request:
http://laravel.com/docs/eloquent#eager-loading
in CostumerModel you need set a relationship (One To Many):
public function order()
{
return $this->hasMany('OrderModel', 'foreign_key_in_orderTable');
}
in OrderModel too:
public function costumer()
{
return $this->belongsTo('CostumerModel', 'foreign_key_in_orderTable');
}
then in OrderModel one more relationship with Shop (One To One):
public function shop()
{
return $this->hasOne('ShopModel', 'foreign_key');
}
Now in ShopModel (One To One):
public function order()
{
return $this->belongsTo('OrderModel', 'local_key');
}
query:
$orders = Customer::with('costumer', 'shop')->where('location','=','Japan')->get();

Resources