*/ public function toArray(Basket $basket): array { return [ 'id' => (string) $basket->id->value, 'items' => array_map( fn (BasketProduct $item) => [ 'id' => (string) $item->id->value, 'name' => $item->name, 'qty' => $item->qty, 'unitPriceHt' => $item->unitPriceHt, 'unitDiscount' => $item->unitDiscount, 'taxRate' => $item->taxRate->value, 'weightGrams' => $item->weightGrams, 'brand' => $item->brand?->value, 'incoId' => $item->incoId?->value, 'orderUnit' => $item->orderUnit->value, 'slug' => $item->slug, 'picture' => $item->picture, 'description' => $item->description, 'weightVariable' => $item->weightVariable, 'discountRateBp' => $item->discountRate->basisPoints(), 'priceKgTtc' => $item->priceKgTtc, 'options' => $item->options, ], array_values($basket->items()) ), ]; } /** @param array $data */ public function fromArray(array $data): Basket { $basketId = Arr::string($data, 'id'); $rawItems = Arr::array($data, 'items', []); $hydratedItems = []; foreach ($rawItems as $itemData) { $itemData = Arr::wrap($itemData); $id = Arr::string($itemData, 'id'); $name = Arr::string($itemData, 'name'); $qty = Arr::integer($itemData, 'qty'); $unitPriceHt = Arr::array($itemData, 'unitPriceHt'); $unitPriceHtCurrency = Arr::string($unitPriceHt, 'currency'); $unitDiscount = Arr::array($itemData, 'unitDiscount'); $unitDiscountCurrency = Arr::string($unitDiscount, 'currency'); $taxRate = Arr::integer($itemData, 'taxRate'); $weightGrams = Arr::integer($itemData, 'weightGrams'); $brand = Arr::get($itemData, 'brand'); // using get because it can be null $incoId = Arr::get($itemData, 'incoId'); $orderUnit = Arr::string($itemData, 'orderUnit'); $slug = Arr::string($itemData, 'slug'); $picture = Arr::get($itemData, 'picture'); $description = Arr::get($itemData, 'description'); $weightVariable = Arr::boolean($itemData, 'weightVariable'); $discountRate = $this->hydrateDiscountRate($itemData); $priceKgTtc = Arr::array($itemData, 'priceKgTtc'); $priceKgTtcCurrency = Arr::string($priceKgTtc, 'currency'); $options = Arr::array($itemData, 'options', []); if (empty($unitPriceHtCurrency)) { throw new \DomainException("Currency is required for unitPriceHt of item with id $id"); } if (empty($unitDiscountCurrency)) { throw new \DomainException("Currency is required for unitDiscount of item with id $id"); } if (empty($priceKgTtcCurrency)) { throw new \DomainException("Currency is required for priceKgTtc of item with id $id"); } $hydratedItems[$id] = new BasketProduct( id: new ProductId(Uuid::fromString($id)), name: $name, qty: $qty, unitPriceHt: new Money( is_scalar($a = Arr::get($unitPriceHt, 'amount', 0)) ? (int) $a : 0, new Currency($unitPriceHtCurrency) ), unitDiscount: new Money( is_scalar($a = Arr::get($unitDiscount, 'amount', 0)) ? (int) $a : 0, new Currency($unitDiscountCurrency) ), taxRate: TaxRate::from($taxRate), weightGrams: $weightGrams, brand: is_string($brand) && $brand !== '' ? ProductBrand::from($brand) : null, incoId: is_scalar($incoId) && $incoId !== 0 ? new IncoId((int) $incoId) : null, orderUnit: OrderUnit::from($orderUnit), slug: $slug, picture: is_string($picture) && $picture !== '' ? $picture : null, description: is_string($description) && $description !== '' ? $description : null, weightVariable: $weightVariable, discountRate: $discountRate, priceKgTtc: new Money( is_scalar($a = Arr::get($priceKgTtc, 'amount', 0)) ? (int) $a : 0, new Currency($priceKgTtcCurrency) ), options: $options ); } return new Basket( id: new BasketId(Uuid::fromString($basketId)), items: $hydratedItems ); } /** * Reads the discount rate in basis points; falls back to the legacy float ratio * (key "discountRate") stored in sessions created before the DiscountRate value object. * * @param array $itemData */ private function hydrateDiscountRate(array $itemData): DiscountRate { $basisPoints = Arr::get($itemData, 'discountRateBp'); if (is_int($basisPoints)) { return DiscountRate::fromBasisPoints($basisPoints); } $legacyRatio = Arr::get($itemData, 'discountRate', 0.0); return is_numeric($legacyRatio) ? DiscountRate::fromRatio(min(max((float) $legacyRatio, 0.0), 1.0)) : DiscountRate::zero(); } }