Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 orderables table (a orders items)

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.

like image 561
FooBar Avatar asked Jul 26 '15 14:07

FooBar


1 Answers

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);
like image 178
Martin Bean Avatar answered Sep 25 '22 05:09

Martin Bean