Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 79 additions & 16 deletions includes/class-query-integration.php
Original file line number Diff line number Diff line change
Expand Up @@ -341,26 +341,16 @@ private function get_sticky_query( $query ) {
* @return string listing ids
*/
private function get_sticky_listing_ids( $query ) {
global $wpdb;

$order_by = $query->get( 'orderby' );
$order = $query->get( 'order' );
$join_sort = in_array( $order_by, array( 'title', 'date', 'modified', 'author' ), true );

$query = "SELECT listing_id FROM {$wpdb->prefix}wpbdp_listings";
if ( $join_sort ) {
$query .= " JOIN {$wpdb->posts} p ON p.ID = {$wpdb->prefix}wpbdp_listings.listing_id";
}
$query .= ' WHERE is_sticky=1';
if ( $join_sort ) {
$query .= " ORDER BY p.post_{$order_by} {$order}";
}
$order_by = (string) $query->get( 'orderby' );
$order = strtoupper( (string) $query->get( 'order' ) );
$order = in_array( $order, array( 'ASC', 'DESC' ), true ) ? $order : 'ASC';
$sql_query = $this->get_sticky_listing_ids_query( $order_by, $order );

$results = WPBDP_Utils::check_cache(
array(
'cache_key' => 'sticky_listing_idss',
'cache_key' => $this->get_sticky_listing_ids_cache_key( $order_by, $order ),
'group' => 'wpbdp_listings',
'query' => $query,
'query' => $sql_query,
'type' => 'get_col',
)
);
Expand All @@ -376,6 +366,79 @@ private function get_sticky_listing_ids( $query ) {
return implode( ',', $results );
}

/**
* Get the SQL query for sticky listing ids.
*
* @since x.x
*
* @param string $order_by The orderby value from the current query.
* @param string $order The normalized order direction.
*
* @return string
*/
private function get_sticky_listing_ids_query( $order_by, $order ) {
global $wpdb;

$query = "SELECT l.listing_id FROM {$wpdb->prefix}wpbdp_listings l";

switch ( $order_by ) {
case 'title':
case 'date':
case 'modified':
case 'author':
$query .= " JOIN {$wpdb->posts} p ON p.ID = l.listing_id";
$query .= ' WHERE l.is_sticky=1';
$query .= " ORDER BY p.post_{$order_by} {$order}";

break;
case 'paid':
case 'paid-title':
$next_order = 'paid' === $order_by ? 'post_date DESC' : 'post_title ASC';

$query .= " JOIN {$wpdb->posts} p ON p.ID = l.listing_id";
$query .= ' WHERE l.is_sticky=1';
$query .= " ORDER BY l.fee_price {$order}, p.{$next_order}";

break;
case 'plan-order-date':
case 'plan-order-title':
$plan_order = wpbdp_get_option( 'fee-order' );
if ( is_array( $plan_order ) && isset( $plan_order['method'] ) && 'custom' === $plan_order['method'] ) {
$next_order = 'plan-order-date' === $order_by ? 'post_date' : 'post_title';

$query .= " JOIN {$wpdb->posts} p ON p.ID = l.listing_id";
$query .= " LEFT JOIN {$wpdb->prefix}wpbdp_plans po ON po.id = l.fee_id";
$query .= ' WHERE l.is_sticky=1';
$query .= " ORDER BY po.weight DESC, p.{$next_order} {$order}";
break;
}

$query .= ' WHERE l.is_sticky=1';

break;
default:
$query .= ' WHERE l.is_sticky=1';

break;
}

return $query;
}

/**
* Get the cache key for sticky listing ids.
*
* @since x.x
*
* @param string $order_by The orderby value from the current query.
* @param string $order The normalized order direction.
*
* @return string
*/
private function get_sticky_listing_ids_cache_key( $order_by, $order ) {
return 'sticky_listing_ids_' . md5( $order_by . '|' . $order . '|' . maybe_serialize( wpbdp_get_option( 'fee-order' ) ) );
}

// {{ Sort bar.
public function sortbar_sort_options( $options ) {
$sortbar_fields = wpbdp_sortbar_get_field_options();
Expand Down
190 changes: 190 additions & 0 deletions tests/wpunit/Listing/ListingQuerySortTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<?php
/**
* Includes tests for listing query sorting.
*/

namespace Listing;

use WPBDP\Tests\WPUnitTestCase;
use WPBDP__Fee_Plan;
use WPBDP_Utils;
use WP_Query;

/**
* Tests for listing query sorting.
*/
class ListingQuerySortTest extends WPUnitTestCase {

/**
* @var \WpunitTester
*/
protected $tester;

/**
* @var int[]
*/
private $created_listing_ids = array();

/**
* @var int[]
*/
private $created_plan_ids = array();

/**
* @since x.x
*/
protected function after_setup() {
wpbdp_set_option(
'fee-order',
array(
'method' => 'custom',
'order' => 'asc',
)
);
wpbdp_set_option( 'prevent-sticky-on-directory-view', array() );
WPBDP_Utils::cache_delete_group( 'wpbdp_listings' );
}

/**
* @since x.x
*/
public function tearDown() : void {
foreach ( $this->created_listing_ids as $listing_id ) {
wp_delete_post( $listing_id, true );
}

foreach ( $this->created_plan_ids as $plan_id ) {
$plan = wpbdp_get_fee_plan( $plan_id );
if ( $plan ) {
$plan->delete();
}
}

WPBDP_Utils::cache_delete_group( 'wpbdp_listings' );

parent::tearDown();
}

/**
* @since x.x
*/
public function testStickyListingsUsePlanOrderTitleSort() {
$plan = $this->create_fee_plan( 'sticky_plan_order_title', 10 );

$zebra = $this->create_listing_on_plan( 'Zebra Corp', $plan, '2026-01-01 00:00:00' );
$alpha = $this->create_listing_on_plan( 'Alpha Inc', $plan, '2026-01-02 00:00:00' );
$mango = $this->create_listing_on_plan( 'Mango Ltd', $plan, '2026-01-03 00:00:00' );

$this->assertSame(
array( $alpha, $mango, $zebra ),
$this->get_display_listing_ids_from_sticky_query( 'plan-order-title', 'ASC' )
);

$this->assertSame(
array( $zebra, $mango, $alpha ),
$this->get_display_listing_ids_from_sticky_query( 'plan-order-title', 'DESC' )
);
}

/**
* @since x.x
*/
public function testStickyListingsUsePlanOrderDateSort() {
$plan = $this->create_fee_plan( 'sticky_plan_order_date', 10 );

$newest = $this->create_listing_on_plan( 'Newest Listing', $plan, '2026-01-03 00:00:00' );
$oldest = $this->create_listing_on_plan( 'Oldest Listing', $plan, '2026-01-01 00:00:00' );
$middle = $this->create_listing_on_plan( 'Middle Listing', $plan, '2026-01-02 00:00:00' );

$this->assertSame(
array( $oldest, $middle, $newest ),
$this->get_display_listing_ids_from_sticky_query( 'plan-order-date', 'ASC' )
);
}

/**
* @since x.x
*
* @param string $tag Fee plan tag.
* @param int $weight Fee plan weight.
*
* @return WPBDP__Fee_Plan
*/
private function create_fee_plan( $tag, $weight ) {
$fee = new WPBDP__Fee_Plan(
array(
'label' => 'Sticky Plan ' . $weight,
'amount' => 100,
'days' => 365,
'sticky' => 1,
'recurring' => 0,
'enabled' => 1,
'images' => 0,
'pricing_model' => 'flat',
'supported_categories' => 'all',
'tag' => $tag,
'weight' => $weight,
)
);

$result = $fee->save();
if ( is_wp_error( $result ) ) {
$this->fail( 'Fee creation failed: ' . $result->get_error_message() );
}

$this->assertTrue( is_int( $fee->id ) );
$this->created_plan_ids[] = $fee->id;

return $fee;
}

/**
* @since x.x
*
* @param string $title Post title.
* @param WPBDP__Fee_Plan $plan Fee plan.
* @param string $date Post date.
*
* @return int
*/
private function create_listing_on_plan( $title, $plan, $date ) {
$listing_id = wp_insert_post(
array(
'post_author' => 1,
'post_type' => WPBDP_POST_TYPE,
'post_status' => 'publish',
'post_title' => $title,
'post_date' => $date,
'post_date_gmt' => get_gmt_from_date( $date ),
)
);

$this->assertTrue( is_int( $listing_id ) && 0 < $listing_id );
$this->assertNotFalse( wpbdp_get_listing( $listing_id )->set_fee_plan( $plan ) );
$this->created_listing_ids[] = $listing_id;

return $listing_id;
}

/**
* @since x.x
*
* @param string $order_by Order by value.
* @param string $order Order direction.
*
* @return int[]
*/
private function get_display_listing_ids_from_sticky_query( $order_by, $order ) {
$query = new WP_Query();
$query->set( 'orderby', $order_by );
$query->set( 'order', $order );

$reflection = new \ReflectionClass( 'WPBDP__Query_Integration' );
$method = $reflection->getMethod( 'get_sticky_listing_ids' );
$method->setAccessible( true );

$sticky_ids = $method->invoke( $reflection->newInstanceWithoutConstructor(), $query );

return array_reverse( array_map( 'intval', explode( ',', $sticky_ids ) ) );
}
}
Loading