【问题标题】:Laravel Eloquent Many to Many get newest date and default value if no record in the pivot如果数据透视表中没有记录,Laravel Eloquent 多对多获取最新日期和默认值
【发布时间】:2019-02-03 09:48:43
【问题描述】:

这是我项目中表格的简化版。

表格项目

| id   | name   |
| ---- | ------ |
| 1    | Apple  |
| 2    | Orange |
| 3    | Mango  |

表定价组

| id   | name    | deleted |
| ---- | ------- | ------- |
| 1    | Reguler | 0       |
| 2    | VIP     | 0       |
| 3    | VVIP    | 1       |

表 price_lists

| id   | item_id | group_id | price | date       |
| ---- | ------- | -------- | ----- | ---------- |
| 1    | 1       | 1        | 100   | 2019-01-20 |
| 2    | 1       | 2        | 120   | 2019-01-20 |
| 3    | 1       | 1        | 110   | 2019-02-01 |
| 4    | 2       | 1        | 80    | 2019-01-31 |
| 5    | 2       | 3        | 120   | 2019-01-10 |

Items 和 PricingGroups 是多对多关系,数据透视表是 price_lists

型号:ItemPricingGroupPriceList

所以我想在模型 Item 中有一个名为 prices 的雄辩关系来获取每个 PricingGroups 的价格。

class Item extends Model {
    public function prices() {
        // what should I put here??
    }
}

我想要的输出是这样的:

"items": [
  {
    "id": 1,
    "name": "Apple",
    "prices": [
      {
        "id": 1,
        "name": "Reguler",
        "price": 110
      },
      {
        "id": 2,
        "name": "VIP",
        "price": 120
      }
    ]
  },
  {
    "id": 2,
    "name": "Orange",
    "prices": [
      {
        "id": 1,
        "name": "Reguler",
        "price": 80
      },
      {
        "id": 2,
        "name": "VIP",
        "price": 0
      }
    ]
  },
  {
    "id": 3,
    "name": "Mango",
    "prices": [
      {
        "id": 1,
        "name": "Reguler",
        "price": 0
      },
      {
        "id": 2,
        "name": "VIP",
        "price": 0
      }
    ]
  }
]

注意:

  • 如果没有记录,则使用price=0 显示有效定价组 price_lists 中的项目。请参阅 Orange-VIP 和 Mango 案例。
  • 获取最新价格。请参阅 Apple-Reguler 案例。

这可能吗?

【问题讨论】:

  • 那么在您的想象场景中,一个项目是否有可能在单个定价组中有两个不同的价格?
  • @MojtabaHn 是的,就像苹果调节器一样。但是输出需要显示最新的价格。

标签: php laravel eloquent


【解决方案1】:

你可以这样做:

/*define the relationship like this*/
  public function price_groups(){
    return $this->belongsToMany(PricingGroup::class,'price_lists','item_id','group_id')->withPivot(['price','date']);
  }

public static function prices($date = null){

    /*get all available pricing groups*/
    $availablePricingGroups = PricingGroup::where('deleted',false)
      ->select('id','name')
      ->get()
      ->toArray();

    /*eager load*/
    $itemPriceGroups = self::with('price_groups')
      ->get()
      ->map(function($item) use ($date,$availablePricingGroups){

      /*sort by date, latest date on top*/
      $priceGroups = $item->price_groups->sortByDESC(
        function($priceGroup){
          return $priceGroup->pivot->date;
        })
        ->filter(function($priceGroup) use ($date){
          /*filter out deleted and greater than $date price group*/
          return $priceGroup->deleted == false && $priceGroup->pivot->date <= ($date??date('Y-m-d'));
        })->groupBy('id')
        ->map(function($priceGroup){
          /*latest price group is still on the top*/
          /*group them together then select the first price group*/
          return $priceGroup->first();
      })->toArray();

      /*iterate through $availablePricingGroups and set its price*/
      foreach($availablePricingGroups as $availablePricingGroup){
        $price = 0;
        foreach($priceGroups as $priceGroup){
          if($priceGroup['id'] == $availablePricingGroup['id']){
            $price = $priceGroup['pivot']['price'];
            break;
          }
        }
        $endResultPriceGroups[] = $availablePricingGroup + ['price' => $price];
      }

      return [
        'id' => $item->id,
        'name' => $item->name,
        'prices' => $endResultPriceGroups,
      ];
    });

    return $itemPriceGroups;
  }

【讨论】:

  • 为什么是静态的?我怎么称呼它?
  • @ChristhoferNatalius 您可以像这样运行该函数:Item::prices('2019-01-01')。我将其设为静态的原因是为了方便访问该函数,并且它不需要调用实例。
  • 抱歉回复慢,这两天身体不舒服。您的代码完全按照我想要的方式返回 json。谢谢。
【解决方案2】:

您应该查看对象属性的 mutators。

https://laravel.com/docs/5.7/eloquent-mutators

这样您可以添加一个名为“价格”的属性并动态返回它们。

【讨论】:

  • 我认为不是mutators,而是accessor。我尝试过,在其他情况下,我为访问器添加了受保护的 $appends。但是当我执行 Item::all() 时,它正在对每个项目进行查询。你知道如何预先加载访问器吗?
  • 我想用雄辩的关系来做这件事,因为我们可以使用 Item::with('prices')->get(); 进行预加载;
  • 是的,对不起,我的意思是访问者。当您处理大数据时,您可能只在需要时才加载它们是正确的。但是您也可以为您的商品创建一个自定义集合,并在其中插入价格计算功能。通过这种方式,您可以一次计算所有价格,而无需对每一个项目都进行计算。这应该可以为您节省大量查询。
【解决方案3】:

获取最新价格。请参阅 Apple-Reguler 案例。

您可以自定义您的关系查询,这样您就可以获得您想要的数据,而且它仍然是一个关系(不像访问器):

//Item.php
    public function prices()
    {
        // normal belongsToMany
        return $this->belongsToMany(PricingGroup::class, 'price_lists', 'item_id', 'group_id')
        // customize select statement
            ->selectRaw('pricing_groups.*, price_lists.price')
        // order by item id
            ->orderByRaw('price_lists.price');
    }

输出将是

select pricing_group.*, price_lists.price from "pricing_groups" inner join "price_lists" on "pricing_groups"."id" = "pric
e_lists"."group_id" where "price_lists"."item_id" = ? order by price_lists.price

如果 price_lists 中没有商品记录,则显示 price=0 的有效定价组。请参阅 Orange-VIP 和 Mango 案例。

我认为使用 eagerLoading 是不可能的。

【讨论】:

  • 是的,我试过belongsToMany,但如果price_lists表为空,价格将有空数组,而不是总是包含2个价格为0的定价组。这就是这个问题的重点(默认值如果枢轴中没有记录)。基本上是为了自定义 eloquent 使用右连接而不是内连接。所以我想这毕竟是不可能的。
猜你喜欢
  • 1970-01-01
  • 2023-02-05
  • 2014-12-13
  • 2015-02-04
  • 2020-07-28
  • 2015-12-09
  • 1970-01-01
  • 2019-10-04
  • 1970-01-01
相关资源
最近更新 更多