简体   繁体   中英

Laravel Eloquent Many to Many get newest date and default value if no record in the pivot

This is the simplified version of table in my project.

Table items

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

Table pricing_groups

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

Table 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 and PricingGroups are many to many relationship and the pivot table is price_lists .

Models: Item , PricingGroup , PriceList

So I want to have an eloquent relation called prices in model Item to get the price for each PricingGroups.

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

The output I want is like this:

"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
      }
    ]
  }
]

Note:

  • Show active pricing groups with price=0 if there is no record of the item in price_lists . See Orange-VIP and Mango case.
  • Get the newest price. See Apple-Reguler case.

Is this possible?

You should have a look into the mutators for your object properties.

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

This way you can add an attribute named "Prices" and return them dynamical.

Get the newest price. See Apple-Reguler case.

You can customize your relationship query So you achieve data you want and also it's still a relationship (not like accessors):

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

Output will be

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

Show active pricing groups with price=0 if there is no record of the item in price_lists. See Orange-VIP and Mango case.

I don't think it's even possible using eagerLoading.

You can do it this way:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM