Automattic\WooCommerce\Checkout\Helpers

ReserveStock::reserve_stock_for_productprivateWC 1.0

Reserve stock for a product by inserting rows into the DB.

Method of the class: ReserveStock{}

No Hooks.

Returns

null. Nothing (null).

Usage

// private - for code of main (parent) class only
$result = $this->reserve_stock_for_product( $product_id, $stock_quantity, $order, $minutes );
$product_id(int) (required)
Product ID which is having stock reserved.
$stock_quantity(int) (required)
Stock amount to reserve.
$order(WC_Order) (required)
Order object which contains the product.
$minutes(int) (required)
How long to reserve stock in minutes.

ReserveStock::reserve_stock_for_product() code WC 10.9.4

private function reserve_stock_for_product( $product_id, $stock_quantity, $order, $minutes ) {
	global $wpdb;

	$query_for_stock          = \WC_Data_Store::load( 'product' )->get_query_for_stock( $product_id );
	$query_for_reserved_stock = $this->get_query_for_reserved_stock( $product_id, $order->get_id() );

	// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
	$sql = $wpdb->prepare(
		"
		INSERT INTO {$wpdb->wc_reserved_stock} ( `order_id`, `product_id`, `stock_quantity`, `timestamp`, `expires` )
		SELECT %d, %d, %d, NOW(), ( NOW() + INTERVAL %d MINUTE ) FROM DUAL
		WHERE ( $query_for_stock FOR UPDATE ) - ( $query_for_reserved_stock FOR UPDATE ) >= %d
		ON DUPLICATE KEY UPDATE `expires` = VALUES( `expires` ), `stock_quantity` = VALUES( `stock_quantity` )
		",
		$order->get_id(),
		$product_id,
		$stock_quantity,
		$minutes,
		$stock_quantity
	);
	// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared

	// Reliability: high concurrency on the same product reservation can trigger deadlocks (error codes 1213 and 1205).
	// We currently do not have a reliable method to identify lock errors. The $wpdb interface does not consistently provide
	// error codes, and error messages can vary by database locale. Previously, we matched messages to 'try restarting transaction'.
	for ( $attempt = 0; $attempt < 3; ++$attempt ) {
		$result = $wpdb->query( $sql ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
		if ( false !== $result ) {
			break;
		}
	}

	if ( ! $result ) {
		$product = wc_get_product( $product_id );
		throw new ReserveStockException(
			'woocommerce_product_not_enough_stock',
			sprintf(
				/* translators: %s: product name */
				__( 'Not enough units of %s are available in stock to fulfil this order.', 'woocommerce' ),
				$product ? $product->get_name() : '#' . $product_id
			),
			403
		);
	}
}