質問編集履歴

1

Order・OrderDomainService・Coupons・ProductCoupon・CouponInterfaceを追記しました。

2023/05/15 09:06

投稿

rashild
rashild

スコア27

test CHANGED
File without changes
test CHANGED
@@ -21,7 +21,7 @@
21
21
  ・OrderDetailクラスは商品情報を持った注文詳細クラス(各注文商品の金額を持つ)
22
22
  ・CouponクラスはCouponInterfaceを使用することで、様々な仕様のクーポン機能が可能
23
23
 
24
- 現状のOrderクラス
24
+ Orderクラス
25
25
  ```php
26
26
  <?php
27
27
  namespace App\Beer;
@@ -32,7 +32,7 @@
32
32
 
33
33
  private ?int $id;
34
34
 
35
- private int $totalPrice;
35
+ private int $price;
36
36
 
37
37
  private Customer $customer;
38
38
 
@@ -44,38 +44,67 @@
44
44
 
45
45
  public function __construct(
46
46
  ?int $id,
47
- int $totalPrice,
47
+ int $price,
48
48
  Customer $customer,
49
49
  OrderDetails $orderDetails,
50
50
  Coupons $coupons,
51
51
  DeliveryFee $deliveryFee,
52
52
  ) {
53
53
  $this->id = $id;
54
- $this->totalPrice = $totalPrice;
54
+ $this->price = $price;
55
55
  $this->customer = $customer;
56
56
  $this->orderDetails = $orderDetails;
57
57
  $this->coupons = $coupons;
58
58
  $this->deliveryFee = $deliveryFee;
59
59
  }
60
60
 
61
+ public function getId(): ?int {
62
+ return $this->id;
63
+ }
64
+
65
+ public function getPrice():int {
66
+ return $this->price;
67
+ }
68
+
69
+ public function getTotalPrice():int {
70
+ return $this->price + $this->getDeliveryFee()->getValue();
71
+ }
72
+
73
+ public function getCustomer():Customer {
74
+ return $this->customer;
75
+ }
76
+
77
+ public function getOrderDetails(): OrderDetails {
78
+ return $this->orderDetails;
79
+ }
80
+
81
+ public function getCoupons():Coupons {
82
+ return $this->coupons;
83
+ }
84
+
85
+ public function getDeliveryFee():DeliveryFee {
86
+ return $this->deliveryFee;
87
+ }
88
+
89
+ public function addCoupon(CouponInterface $coupon) {
90
+ $this->coupons = $this->coupons->addCoupon($coupon);
91
+ }
92
+
61
93
  public function addOrderDetail(OrderDetail $orderDetail) {
62
94
  $this->orderDetails->addOrderDetail($orderDetail);
63
95
  }
64
96
 
65
- public function getTotalPrice() {
97
+ public function hasProduct(Product $product):bool {
66
- return $this->totalPrice;
98
+ return $this->orderDetails->hasProduct($product);
67
- }
99
+ }
68
-
69
- public function getOrderDetails(): OrderDetails {
70
- return $this->orderDetails;
71
- }
100
+ }
72
- }
73
- ```
101
+ ```
74
- 現状のOrderDomainService
102
+ OrderDomainService
75
103
  ```php
76
104
  <?php
77
105
  namespace App\Beer\DomainService;
78
106
 
107
+ use App\Beer\Coupons;
79
108
  use App\Beer\Order;
80
109
  use App\Beer\Interface\CouponInterface;
81
110
 
@@ -87,14 +116,184 @@
87
116
 
88
117
  }
89
118
 
119
+ public function couponIsApplicable(Order $order, CouponInterface $coupon):bool {
120
+ // TODO:: すでに適用されているクーポンとの状況も確認する必要がある
121
+
122
+ return $coupon->isApplicable($order);
123
+ }
124
+
90
- public function addCoupon(Order $order, CouponInterface $coupon): Order {
125
+ public function applyCoupon(Order $order, CouponInterface $coupon): Order {
126
+ $order->addCoupon($coupon);
127
+
128
+ $appliedOrder = $this->calculateCouponsOrder($order);
129
+
130
+ return $appliedOrder;
131
+ }
132
+
133
+ private function calculateCouponsOrder(Order $order):Order {
134
+ $orders = [];
135
+ $coupons = $order->getCoupons()->asArray();
136
+
137
+ $combinations = $this->permutation($coupons);
138
+ // クーポンの適用順で最も最小金額になる組み合わせを探す
139
+ foreach ($combinations as $coupons) {
140
+ $couponCollection = new Coupons($coupons);
91
- if (!$coupon->isApplicable($order)) {
141
+ $appliedOrder = $couponCollection->apply($order);
92
- throw new \Exception('適用できません');
142
+ $orders[] = $appliedOrder;
93
143
  }
144
+ // 最小注文金額のOrderを返す
145
+ $minTotalPriceOrder = $this->getMinTotalPriceOrder($orders);
146
+
147
+ return $minTotalPriceOrder;
148
+ }
149
+
150
+ private function getMinTotalPriceOrder(Array $orders):Order {
151
+ $totalPrices = [];
152
+ foreach($orders as $order) {
153
+ $totalPrices[] = $order->getTotalPrice();
154
+ }
155
+
156
+ $minTotalPriceIndex = array_keys($totalPrices, min($totalPrices))[0];
157
+
158
+ return $orders[$minTotalPriceIndex];
159
+ }
160
+
161
+ // 本来は渡された配列の順列を返す
162
+ private function permutation(array $arr): array {
163
+ return [
164
+ [$arr[0],$arr[1]],
165
+ [$arr[1],$arr[0]],
166
+ ];
167
+ }
168
+ }
169
+ ```
170
+ Couponsクラス
171
+ ```php
172
+ <?php
173
+ namespace App\Beer;
174
+
175
+ use App\Beer\Interface\CouponInterface;
176
+
177
+ class Coupons {
178
+
179
+ private Array $coupons;
180
+
181
+ public function __construct(
182
+ Array $coupons,
183
+ ) {
184
+ $this->coupons = $coupons;
185
+ }
186
+
187
+ public function asArray():Array {
188
+ return $this->coupons;
189
+ }
190
+
191
+ public function addCoupon(CouponInterface $coupon):self {
192
+ $this->coupons[] = $coupon;
193
+
194
+ return new Coupons($this->coupons);
195
+ }
196
+
197
+ public function apply(Order $order): Order {
198
+ foreach($this->coupons as $coupon) {
94
- $appliedOrder = $coupon->apply($order);
199
+ $order = $coupon->apply($order);
200
+ }
201
+
202
+ return $order;
203
+ }
204
+ }
205
+ ```
206
+
207
+ ProductCouponクラス
208
+ ```php
209
+ <?php
210
+ namespace App\Beer;
211
+
212
+ use App\Beer\Enums\CouponTarget;
213
+ use App\Beer\Enums\DiscountType;
214
+ use App\Beer\Interface\CouponInterface;
215
+
216
+ class ProductCoupon implements CouponInterface {
217
+
218
+ private String $couponCode;
219
+
220
+ private int $specificPrice;
221
+
222
+ private Product $product;
223
+
224
+ public function __construct(
225
+ String $couponCode,
226
+ int $specificPrice,
227
+ Product $product
228
+ ) {
229
+ $this->couponCode = $couponCode;
230
+ $this->specificPrice = $specificPrice;
231
+ $this->product = $product;
232
+ }
233
+
234
+ public function isApplicable(Order $order): bool {
235
+ return $order->hasProduct($this->product);
236
+ }
237
+
238
+ public function apply(Order $order):Order {
239
+ $appliedOrderDetails = [];
240
+ foreach($order->getOrderDetails()->asArray() as $orderDetail) {
241
+ if ($orderDetail->getProduct()->getProductCode() == $this->product->getProductCode()) {
242
+ $appliedOrderDetail = new OrderDetail(
243
+ $orderDetail->getId(),
244
+ $this->specificPrice,
245
+ $this->product,
246
+ );
247
+ $appliedOrderDetails[] = $appliedOrderDetail;
248
+ } else {
249
+ $appliedOrderDetails[] = $orderDetail;
250
+ }
251
+ }
252
+ $orderDetails = new OrderDetails($appliedOrderDetails);
253
+
254
+ $appliedOrder = new Order(
255
+ $order->getId(),
256
+ $orderDetails->getTotalPrice(),
257
+ $order->getCustomer(),
258
+ $orderDetails,
259
+ $order->getCoupons(),
260
+ $order->getDeliveryFee(),
261
+ );
95
262
 
96
263
  return $appliedOrder;
97
264
  }
265
+
266
+ public function getCouponTarget(CouponTarget $couponTarget): CouponTarget
267
+ {
268
+ return CouponTarget::PRODUCT_CODE;
269
+ }
270
+
271
+ public function getDiscountType(DiscountType $discountType): DiscountType
272
+ {
273
+ return DiscountType::SPECIFIC_PRICE;
274
+ }
275
+ }
276
+ ```
277
+
278
+ CouponInterface
279
+ ```php
280
+ <?php
281
+ namespace App\Beer\Interface;
282
+
283
+ use App\Beer\Enums\CouponTarget;
284
+ use App\Beer\Enums\DiscountType;
285
+ use App\Beer\Order;
286
+
287
+ interface CouponInterface {
288
+
289
+ public function isApplicable(Order $order): bool;
290
+
291
+ public function apply(Order $order):Order;
292
+
293
+ public function getCouponTarget(CouponTarget $couponTarget):CouponTarget;
294
+
295
+ public function getDiscountType(DiscountType $discountType):DiscountType;
296
+
98
297
  }
99
298
  ```
100
299