Automattic\WooCommerce\Internal\OrderReviews

SubmissionHandler::maybe_mark_order_completeprivateWC 1.0

Set the completed-at meta when every eligible item has a review by this customer (approved or pending moderation), whether posted in this submission or an earlier one. Spam/trash comments are excluded.

Method of the class: SubmissionHandler{}

Returns

null. Nothing (null).

Usage

// private - for code of main (parent) class only
$result = $this->maybe_mark_order_complete( $order ): void;
$order(WC_Order) (required)
Order being reviewed.

SubmissionHandler::maybe_mark_order_complete() code WC 10.9.4

private function maybe_mark_order_complete( WC_Order $order ): void {
	// Recording the moment the order first became fully reviewed; never overwrite.
	if ( $order->get_meta( self::COMPLETED_META_KEY ) ) {
		return;
	}

	$customer_email = $order->get_billing_email();
	if ( '' === $customer_email ) {
		return;
	}

	// Build the same eligible-row set the page uses, then collect the
	// distinct (parent product, variation) slots that need a review.
	// Counting by slot rather than per-line-item means a double-submit of
	// the same variation can't satisfy a sibling variation's quota, and
	// the same simple product appearing on multiple rows still only
	// needs one review (the page collapses those rows anyway).
	// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment -- documented at the page-template invocation site.
	$eligible_items = (array) apply_filters( 'woocommerce_review_order_eligible_items', $order->get_items(), $order );

	$required_slots = array();
	$product_ids    = array();
	foreach ( $eligible_items as $item ) {
		if ( ! $item instanceof \WC_Order_Item_Product ) {
			continue;
		}
		$product_id   = (int) $item->get_product_id();
		$variation_id = (int) $item->get_variation_id();
		if ( $product_id > 0 ) {
			$required_slots[ $product_id . '|' . $variation_id ] = true;
			$product_ids[ $product_id ]                          = $product_id;
		}
	}

	if ( empty( $required_slots ) ) {
		return;
	}

	// Single grouped lookup, fetching the comment objects directly so we
	// can read comment_post_ID without a follow-up query per row. Limit
	// to approved + pending-moderation so spam/trash never count as
	// completion, AND to reviews tagged with this order so an older
	// review of the same parent product from a previous order doesn't
	// satisfy the per-row count for the current one. number=>0 disables
	// the default 20-row cap so this still works for orders with many
	// reviewable items.
	$comments = get_comments(
		array(
			'post__in'     => array_values( $product_ids ),
			'author_email' => $customer_email,
			'type'         => 'review',
			'status'       => array( 'approve', 'hold' ),
			'number'       => 0,
			'meta_query'   => array( // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- bounded by post__in + author_email.
				array(
					'key'   => ItemEligibility::ORDER_META_KEY,
					'value' => (string) $order->get_id(),
				),
			),
		)
	);

	if ( ! is_array( $comments ) || empty( $comments ) ) {
		return;
	}

	// Index reviewed slots by (parent_id, variation_id); duplicate comments
	// for the same slot still count as one toward completion.
	$reviewed_slots = array();
	foreach ( $comments as $comment ) {
		if ( $comment instanceof \WP_Comment ) {
			$slot_key                    = (int) $comment->comment_post_ID . '|' . (int) get_comment_meta( (int) $comment->comment_ID, ItemEligibility::VARIATION_META_KEY, true );
			$reviewed_slots[ $slot_key ] = true;
		}
	}

	foreach ( $required_slots as $slot_key => $_ ) {
		if ( ! isset( $reviewed_slots[ $slot_key ] ) ) {
			return;
		}
	}

	$order->update_meta_data( self::COMPLETED_META_KEY, (string) time() );
	$order->save();
}