0

我在 laravel API 中有非常复杂的功能(从移动应用程序调用)。示例函数如下所示。如果有人可以帮助改进代码结构。我想知道在单个 API 中处理多个事务的最佳实践。因为如果我将每个表的条目移动到单独的函数 - 我如何处理每个表的错误?例如:当 $request 模型中没有设置数量时

摘要: 有人可以将其分解为更小的功能吗? .... 因为我不知道如何/在哪里将它分成更小的部分。


    public function placeOrder(Request $request)
    {
        $this->validate($request, [
            'addressId' => 'required'
        ]);
        $userId = Auth::user()->id;
        $itemList = array();
        $deliveryCharge = Store::first()->delivery_charge;
        // $deliveryCharge = Store::findOrFail($request->header('StoreId'))->delivery_charge;
        $total = $deliveryCharge;
        $cod = $request->cod;
        $useBalanceFirst = $request->useBalanceFirst;
        $addressId = $request->addressId;
        $list = $request->list;
        $cost = 0;
        // fetch all items using id in request
        foreach ($list as $orderItemFromRequest) {


            if (!isset($orderItemFromRequest['id']))
                return $this->error("Invalid item selected");

            $itemFromDB = Item::find($orderItemFromRequest['id']);

            if (!isset($orderItemFromRequest['quantity']))
                return $this->error("Please enter quantity for {$itemFromDB->name}");

            $qty = $orderItemFromRequest['quantity'];
            if ($qty > 0.0) {

                if ($itemFromDB == null || $itemFromDB->available == false)
                    return $this->error("Invalid item selected");

                $itemFromDB->quantity = $qty;
                $total += ($itemFromDB->sell_rate * $itemFromDB->quantity) / $itemFromDB->rate_unit_multiple;
                $cost += ($qty * $itemFromDB->purchase_rate) / $itemFromDB->rate_unit_multiple;
                array_push($itemList, $itemFromDB);
            }
        }
        if (count($itemList) == 0) {
            return $this->error("At least 1 item is needed to place the order");
        }
        // check user balance 
        $user = User::findOrFail($userId);
        // if balance < total -> throw error    
        if ($cod != true &&  $user->balance < $total) return $this->error("Insufficient Balance. Please recharge your wallet or use C.O.D.", 403);



        $order = new Order();
        $order->user_id = $user->id;
        $order->total = $total;
        $order->delivery_charge = $deliveryCharge;
        $order->purchase_rate = $cost;

        // because in-case user changes his/her name in future - the order should have the historical name & number
        $order->customer_name = $user->name;
        $order->customer_contact = $user->contact_no;
        $order->address_id = $addressId;

        if ($cod == true) {
            if ($useBalanceFirst == true) {
                if ($user->balance < $order->total)
                    $order->amount_due = $order->total - $user->balance;
                else
                    $order->amount_due = 0;
            } else {
                $order->amount_due = $order->total;
            }
        } else
            $order->amount_due = 0;

        $ordertransaction = new OrderTimeLine();
        $ordertransaction->created_by = Auth::user()->id;



        DB::transaction(function () use ($order, $itemList, $user, $ordertransaction) {
            // insert 1 entry into orders
            $order->save();
            $order->refresh();
            $newOrderItemList = array();
            // insert N entries for Items 
            foreach ($itemList as $requestItem) {
                $item   = new OrderItem();
                $item->order_id = $order->id;
                $item->item_id = $requestItem->id;
                $item->item_name = $requestItem->name;
                $item->sell_rate = $requestItem->sell_rate;
                $item->purchase_rate = $requestItem->purchase_rate;
                $item->quantity = $requestItem->quantity;
                $item->quantity_unit = $requestItem->unit;
                $item->rate_unit_multiple = $requestItem->rate_unit_multiple;

                if ($requestItem->remaining_stocks <= 0) {
                    $item->purchase_rate_qty = 0;
                } else if ($requestItem->remaining_stocks < $requestItem->quantity) {
                    $item->purchase_rate_qty = $requestItem->remaining_stocks;
                } else
                    $item->purchase_rate_qty = $requestItem->quantity;

                $item->save();
                $item->refresh();

                $requestItem->remaining_stocks = $requestItem->remaining_stocks - $requestItem->quantity;
                // update remaining QTY and cost price
                unset($requestItem->quantity);
                if ($requestItem->remaining_stocks <= $requestItem->alert_stocks) {
                    // send notification
                    NotificationController::notifyAdminAboutStocks($requestItem);
                }

                $requestItem->save();
                array_push($newOrderItemList, $item);
            }
            $order->items = $newOrderItemList;
            // deduct balance from user table
            $user->balance = $user->balance - $order->total + $order->amount_due;
            $user->save();
            $balanceAffected = 0 - $order->total + $order->amount_due;
            // enter transaction in wallet_transaction table   
            if ($balanceAffected != 0) {
                $transaction =  new WalletTransaction();
                $transaction->user_id = $user->id;
                $transaction->amount = $balanceAffected;
                $transaction->type = WALLET_ORDER_PLACED;
                $transaction->order_id = $order->id;
                $transaction->save();
            }

            $ordertransaction->status = $order->status;
            $ordertransaction->order_id = $order->id;
            $ordertransaction->save();
        });
        NotificationController::notifyAdminAboutOrder($user, $order);
        return $this->success(["balance" => $user->balance, "order" => $order]);
    }

4

1 回答 1

1

您在控制器功能中处理了太多工作。首先,我建议充分利用Laravel 验证功能。然后使用Laravel Collection使您的代码更具可读性和紧凑性。我已经按照我通常的处理方式重构了您的功能。

public function placeOrder(Request $request)
{
   $data = $request->validate([
      'addressId' => 'required',
      'cod' => 'required|boolean',
      'useBalanceFirst' => 'required|boolean',
      'list' => 'required|array',
      'list.*.quantity' => 'required|numeric',
      'list.*.id' => ['required', 'numeric', Rule::exists('items')->where(function ($query) {
          $query->where('available', true);
      })],
  ]);
  $user = Auth::user();
  $orderItems = collect($data['list'])->map(function($listItem){
      $item = Item::find($listItem['id']);
      $orderItem = new OrderItem([
          'item_id' => $item->id,
          'item_name' => $item->name,
          'sell_rate' => $item->sell_rate,
          'purchase_rate' => $item->purchase_rate,
          'quantity' => $listItem['quantity'],
          'quantity_unit' => $item->quantity_unit,
          'rate_unit_multiple' => $item->rate_unit_multiple,
      ]);
      if ($item->remaining_stocks <= 0) {
          $orderItem->purchase_rate_qty = 0;
      } else if ($item->remaining_stocks < $orderItem->quantity) {
          $orderItem->purchase_rate_qty = $item->remaining_stocks;
      } else
          $orderItem->purchase_rate_qty = $orderItem->quantity;

      return $orderItem;
  });
  $total =  $orderItems->sum(function($item){
      return ($item->sell_rate * $item->quantity) / $item->rate_unit_multiple;
  });
  $cost =  $orderItems->sum(function($item){
      return ($item->quantity * $item->purchase_rate) / $item->rate_unit_multiple;
  });

$deliveryCharge = Store::first()->delivery_charge;
  $total += $deliveryCharge;

  // if balance < total -> throw error
  if (!$data['cod'] && $user->balance < $total)
  {
      return $this->error("Insufficient Balance. Please recharge your wallet or use C.O.D.", 403);
  }

$order = new Order([
      'user_id' => $user->id,
      'total' => $total,
      'delivery_charge' => $deliveryCharge,
      'purchase_rate' => $cost,
      // because in-case user changes his/her name in future - the order should have the historical name & number
      'customer_name' => $user->name,
      'customer_contact' => $user->contact_no,
      'address_id' => $data['addressId'],
  ]);
  if ($data['cod']) {
      if ($data['useBalanceFirst']) {
          if ($user->balance < $order->total)
              $order->amount_due = $order->total - $user->balance;
          else
              $order->amount_due = 0;
      } else {
          $order->amount_due = $order->total;
      }
  } else
  {
      $order->amount_due = 0;
  }
  DB::transaction(function () use ($order, $orderItems, $user) {
      // insert 1 entry into orders
      $order->save();
      $order->items->saveMany($orderItems);
      // deduct balance from user table
      $user->balance = $user->balance - $order->total + $order->amount_due;
      $user->save();

    $ordertransaction = OrderTimeLine::create([
          'created_by' => $user->id,
          'status' => $order->status,
          'order_id' => $order->id,
      ]);
  });

  return $this->success(["balance" => $user->balance, "order" => $order]);
}

正如您所注意到的,我已经删除了通知和用户钱包交易代码,因为它们应该异步更新,不会影响您的用户体验。我建议你看看Laravel Events

于 2020-12-16T13:36:38.050 回答