【问题标题】:Retrieve all morphedBy relations检索所有 morphedBy 关系
【发布时间】:2015-07-26 14:38:48
【问题描述】:

一个Order 有许多订购的物品

Order已订购商品 可以是 UserProduct

我正在寻找一种将所有变形对象检索到Order 的方法。而不是$order->users$order->products 我想做$order->items

我的进步

到目前为止,我的进展涉及Many To Many Polymorphic Relationship

我的桌子:

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

orderables 表格外观示例

这就是我创建订单并添加用户和产品的方式:

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

目前可以做

foreach($order->users as $user) {
    echo $user->id;
}

或者..

foreach($order->products as $product) {
    echo $product->id;
}

但我希望能够做一些类似的事情......

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

我找到了this 问题,这是我能找到的最接近我正在尝试做的事情,但我无法让它满足我的需求,它已经过时了,而且似乎就像一个非常hacky的解决方案。

有不同的方法吗?

如果您有与多态关系不同的方法,请告诉我。

【问题讨论】:

    标签: php laravel laravel-5 eloquent


    【解决方案1】:

    就我个人而言,我的Order 模型有很多OrderItems,而具有多态关系的是OrderItems。这样,我可以获取订单的所有商品,无论它们是什么类型的模型:

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

    然后我将创建一个接口和特征,我可以将其应用于 Eloquent 模型,使它们“可排序”:

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

    如您所见,我的OrderItem 实例可以是ProductEventTicket 或任何其他实现Orderable 接口的模型。然后,您可以像这样获取您订单的所有商品:

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

    OrderItem 实例变形为什么类型并不重要。


    编辑:要将商品添加到您的订单中:

    // Create an order instance
    $order = new Order;
    
    // Add an item to the order
    $order->addItem(User::find($userId), $quantity);
    

    【讨论】:

    • 非常好的方法。您能否也展示一下您是如何创建订单并附加订单商品的?
    • 尝试过类似:$order->items()->save(User::find(1));
    • 这可行,但不是很漂亮:$order->items()->create(['orderable_id' => '1', 'orderable_type' => 'App\User']);
    • @Mattias 出于这个原因,我有一个方法可以包装该逻辑,该逻辑采用Orderable 实例和数量:$order->addItem(Orderable $item, int $quantity);。您还可以创建一个命名构造函数:OrderItem::createFromOrderable(Orderable $item);
    • @Mattias 用新的模型方法和使用示例更新了答案。
    【解决方案2】:

    我认为您的解决方案很好。我只是添加这个辅助方法:

    订单.php

    public function items() {
        return collect($this->products)->merge($this->users);
    }
    

    然后你可以循环遍历这些项目:

    foreach ($order->items() as $item) {
    

    【讨论】:

    • 不错的解决方法,但它不支持以下内容:$order->items()->whereNull('some-field');$order->items()->count()$order->with('items')->first(); 等。
    • 实际上是这样——Laravel 集合可以使用->where('blah', '=', null)->count()->first(),你可以使用->with('projects', 'users') 来预先加载项目。我同意这不是“一阶”解决方案,但它提供了几乎相同的功能。不过,我确实更喜欢 Martin 的解决方案。
    猜你喜欢
    • 2020-10-10
    • 2019-11-14
    • 2020-11-14
    • 1970-01-01
    • 2015-04-16
    • 2012-07-25
    • 2020-12-20
    • 2010-11-11
    • 2022-10-07
    相关资源
    最近更新 更多