Get all relationships morphedBy

An Order

has many ordered elements

And the Order

ordered items can be either User

orProduct

What I'm looking for is a way to get all changed objects in Order

. Instead of $order->users

or $order->products

I would like to do $order->items

.

My progress

My progress so far includes Lots of polymorphic relationships .

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

      


An example of what a table looks like orderables

order table (order items)

This is how I create an order and add user and 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 like ...

foreach($order->items as $item) {
    // $item is either User or Product class
}

      

I found this question, which was the closest I could find to what I am trying to do, but I cannot get it to work for my requires, it is outdated and also seems like a very hackish solution.

Have a different approach?

If you have a different approach than Polymorphic relationships, please let me know.

+3


source to share


2 answers


Personally, my models Order

have a lot OrderItems

, and those OrderItems

that have a polymorphic relationship. This way I can get all the order items regardless of the model type:

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

      

Then, create an interface and trait that I can apply to Eloquent, making them "ordered":

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 copy OrderItem

may be either Product

, EventTicket

or any other model that implements the interface Orderable

. Then you can get all your orders, for example:

$orderItem = Order::find($orderId)->items;

      

And it doesn't matter what type the instances are OrderItem

.


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

      

+3


source


I think your solution is fine. I would 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) {

      

0


source







All Articles