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 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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With