问题
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 ???
}
}
回答1:
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();
}
回答2:
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.
回答3:
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);
回答4:
@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.
来源:https://stackoverflow.com/questions/22940091/laravel-one-to-many-to-many-retrieve-distinct-values