PHP PDO左连接查询购物车产品的结果校验及购物车增改逻辑实现问题
解决PDO查询结果校验与购物车业务逻辑实现问题
我来帮你梳理清楚这个问题——核心是你的LEFT JOIN查询导致空购物车场景下返回带NULL字段的结果,干扰了后续业务逻辑判断。下面分步骤拆解解决方案:
1. 优化查询语句,从根源减少NULL值问题
你的目标是获取购物车里的有效商品,其实不需要用LEFT JOIN关联cart和cart_item表。因为WHERE条件已经限定了customer_id=:customerId AND is_ordered=0,改用INNER JOIN能让查询结果更贴合你的需求:
$statement = $this->connection->prepare(" SELECT cart.cart_id, ROUND(SUM(product.price * cartitem.quantity)) as totalprice, product.productid, product.imagepath, product.name, product.price, cartitem.quantity FROM cart as cart INNER JOIN cart_item as cartitem ON cartitem.cart_id = cart.cart_id INNER JOIN product AS product ON cartitem.product_id=product.productid WHERE customer_id=:customerId AND is_ordered = 0 GROUP BY product.productid, cart.cart_id; ");
为什么选INNER JOIN?
- 购物车不存在:查询返回空数组
- 购物车存在但无商品:查询返回空数组
- 购物车有有效商品:返回完整的非NULL商品数据
如果需要单独确认购物车是否存在(即使是空的),我们可以拆分出独立的校验逻辑,不用在同一条查询里混着处理。
2. 完善查询结果的校验逻辑
修改getCartProducts方法,加入对商品核心字段的非NULL校验,确保只有数据完整时才返回CartModel:
public function getCartProducts($customerId): bool|CartModel { $statement = $this->connection->prepare(" SELECT cart.cart_id, ROUND(SUM(product.price * cartitem.quantity)) as totalprice, product.productid, product.imagepath, product.name, product.price, cartitem.quantity FROM cart as cart INNER JOIN cart_item as cartitem ON cartitem.cart_id = cart.cart_id INNER JOIN product AS product ON cartitem.product_id=product.productid WHERE customer_id=:customerId AND is_ordered = 0 GROUP BY product.productid, cart.cart_id; "); $statement->execute(['customerId' => $customerId]); // 用FETCH_ASSOC只返回关联数组,避免索引重复的冗余数据 $rows = $statement->fetchAll(PDO::FETCH_ASSOC); if (empty($rows)) { return false; } $cartModel = new CartModel(); $totalPriceSum = 0; foreach ($rows as $row) { // 校验商品核心字段是否全部非NULL if (is_null($row['productid']) || is_null($row['name']) || is_null($row['price']) || is_null($row['quantity'])) { return false; } $cartItemModel = new CartItemModel(); $productModel = new ProductModel(); $productModel->setName($row['name']); $productModel->setImagepath($row['imagepath']); $productModel->setProductid($row['productid']); $productModel->setPrice($row['price']); $cartItemModel->setProduct($productModel); $cartItemModel->setQuantity($row['quantity']); $totalPriceSum += $row['totalprice']; $cartModel->setCartItem($cartItemModel); } $cartModel->setTotalprice($totalPriceSum); // 把cart_id存入CartModel,后续添加商品时直接使用 $cartModel->setCartId($rows[0]['cart_id']); return $cartModel; }
3. 实现「添加商品到购物车」的完整业务逻辑
现在我们需要一个单独的方法来处理核心业务,区分「购物车不存在」「购物车存在(空/有商品)」三种场景:
public function addProductToCart($customerId, $productId, $quantity) { // 先尝试获取购物车中的商品 $cartModel = $this->getCartProducts($customerId); if ($cartModel instanceof CartModel) { // 购物车存在且有商品,直接添加新商品项 $this->insertOrUpdateCartItem($cartModel->getCartId(), $productId, $quantity); return true; } // 到这里说明购物车不存在,或者购物车存在但为空 $existingCartId = $this->getExistingEmptyCartId($customerId); if ($existingCartId) { // 购物车存在但为空,直接添加商品 $this->insertOrUpdateCartItem($existingCartId, $productId, $quantity); } else { // 购物车不存在,先创建新购物车再添加商品 $newCartId = $this->createNewCart($customerId); $this->insertOrUpdateCartItem($newCartId, $productId, $quantity); } return true; } // 辅助方法:获取用户已存在的未结算空购物车ID private function getExistingEmptyCartId($customerId): ?int { $statement = $this->connection->prepare(" SELECT cart_id FROM cart WHERE customer_id=:customerId AND is_ordered=0 LIMIT 1; "); $statement->execute(['customerId' => $customerId]); $result = $statement->fetchColumn(); return $result ? (int)$result : null; } // 辅助方法:创建新购物车 private function createNewCart($customerId): int { $statement = $this->connection->prepare(" INSERT INTO cart (customer_id, is_ordered, created_at) VALUES (:customerId, 0, NOW()); "); $statement->execute(['customerId' => $customerId]); return $this->connection->lastInsertId(); } // 辅助方法:添加/更新购物车商品项(已存在则增加数量) private function insertOrUpdateCartItem($cartId, $productId, $quantity) { $statement = $this->connection->prepare(" INSERT INTO cart_item (cart_id, product_id, quantity) VALUES (:cartId, :productId, :quantity) ON DUPLICATE KEY UPDATE quantity = quantity + :quantity; "); $statement->execute([ 'cartId' => $cartId, 'productId' => $productId, 'quantity' => $quantity ]); }
关键说明
- 拆分查询后逻辑更清晰:先查商品,再单独校验购物车状态,避免了LEFT JOIN带来的NULL值判断混乱。
insertOrUpdateCartItem方法用ON DUPLICATE KEY UPDATE实现了「商品已在购物车则增加数量」的常见需求,你可以根据业务调整为覆盖数量或其他逻辑。
内容的提问来源于stack exchange,提问作者anticodingelement
相关产品推荐
相关产品推荐

