Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SLR - no timer in TC checkout if not ASC tickets in cart #3106

Merged
merged 2 commits into from
Jul 15, 2024
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
113 changes: 113 additions & 0 deletions src/Tickets/Seating/Commerce/Controller.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php
/**
* Handles the integration with the Tickets Commerce module.
*
* @since TBD
*
* @package TEC\Tickets\Seating\Commerce;
*/

namespace TEC\Tickets\Seating\Commerce;

use TEC\Common\Contracts\Provider\Controller as Controller_Contract;
use TEC\Common\StellarWP\DB\DB;
use TEC\Tickets\Commerce\Cart;
use TEC\Tickets\Commerce\Module;

/**
* Class Controller.
*
* @since TBD
*
* @package TEC\Tickets\Seating\Commerce;
*/
class Controller extends Controller_Contract {
/**
* Subscribes to the WordPress hooks and actions required by the controller.
*
* @since TBD
*
* @return void
*/
protected function do_register(): void {
add_filter(
'tec_tickets_seating_timer_token_object_id_entries',
[ $this, 'filter_timer_token_object_id_entries' ],
);
}

/**
* Unregisters the controller by unsubscribing from WordPress hooks.
*
* @since TBD
*
* @return void
*/
public function unregister(): void {
remove_filter(
'tec_tickets_seating_timer_token_object_id_entries',
[ $this, 'filter_timer_token_object_id_entries' ],
);
}

/**
* Filters the handler used to get the token and object ID from the cookie.
*
* @since TBD
*
* @param array<string,string> $session_entries The entries from the cookie. A map from object ID to token.
*
* @return array<string,string> The entries from the cookie. A map from object ID to token.
*/
public function filter_timer_token_object_id_entries( $session_entries ): array {
$tickets_commerce = tribe( Module::class );

if ( empty( $session_entries ) || ! $tickets_commerce->is_checkout_page() ) {
// Not a Tickets Commerce checkout page: return the original entries.
return $session_entries;
}

// Get the post IDs in the cart.
global $wpdb;
/** @var Cart $cart */
$cart = tribe( Cart::class );
$cart_items = array_keys( $cart->get_items_in_cart() );

if ( empty( $cart_items ) ) {
return [];
}

$ticket_ids_interval = DB::prepare(
implode( ',', array_fill( 0, count( $cart_items ), '%d' ) ),
...$cart_items
);
$cart_post_ids = DB::get_col(
DB::prepare(
"SELECT DISTINCT( meta_value ) FROM %i WHERE post_id IN ({$ticket_ids_interval}) AND meta_key = %s ",
$wpdb->postmeta,
Module::ATTENDEE_EVENT_KEY
)
);

// Get the post IDs in the session.
$session_post_ids = array_keys( $session_entries );

// Find out the post IDs part of both the cart and the seat selection session.
$cart_and_session_ids = array_intersect( $cart_post_ids, $session_post_ids );

if ( empty( $cart_and_session_ids ) ) {
// There are no Tickets for posts using Seat Assignment in the cart.
return [];
}

return array_combine(
$cart_and_session_ids,
array_map(
static function ( $item ) use ( $session_entries ) {
return $session_entries[ $item ];
},
$cart_and_session_ids
)
);
}
}
4 changes: 4 additions & 0 deletions src/Tickets/Seating/Controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,10 @@ function () {
} else {
$this->container->register( Frontend::class );
}

if ( tec_tickets_commerce_is_enabled() ) {
$this->container->register( Commerce\Controller::class );
}
}

/**
Expand Down
21 changes: 16 additions & 5 deletions src/Tickets/Seating/Frontend/Session.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ public function cancel_previous_for_object( int $object_id ): bool {
return true;
}

foreach ( $this->get_entries( $_COOKIE[ self::COOKIE_NAME ] ) as $entry_object_id => $entry_token ) {
foreach ( $this->get_entries() as $entry_object_id => $entry_token ) {
if ( $entry_object_id === $object_id ) {
$reservations = $this->sessions->get_reservations_for_token( $entry_token );

Expand Down Expand Up @@ -262,14 +262,25 @@ public function pick_earliest_expiring_token_object_id( array $cookie_entries ):
* @return array{0: string, 1: int}|null The token and object ID from the cookie, or `null` if not found.
*/
public function get_session_token_object_id(): ?array {
$cookie = sanitize_text_field( $_COOKIE[ self::COOKIE_NAME ] ?? '' );
$entries = $this->get_entries();

/**
* Filters the session entries used to get the token and object ID from the cookie for the purpose of
* tracking the seat selection session.
*
* @since TBD
*
* @param array<string,string> $entries The entries from the cookie. A map from object ID to token.
*/
$entries = apply_filters(
'tec_tickets_seating_timer_token_object_id_entries',
$entries,
);

if ( ! $cookie ) {
if ( empty( $entries ) ) {
return null;
}

$entries = $this->get_entries( $cookie );

/**
* Filters the handler used to get the token and object ID from the cookie.
* The default handler will pick the object ID and token couple with the earliest expiration time.
Expand Down
32 changes: 32 additions & 0 deletions src/Tickets/Seating/Frontend/Timer.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,11 +267,21 @@ public function render( string $token = null, int $post_id = null, bool $sync_on

[ $token, $post_id ] = $cookie_timer_token_post_id;
} else {
if ( ! tec_tickets_seating_enabled( $post_id ) ) {
// The post is not using assigned seating, do not render the timer.
return;
}

// If a cookie and token were passed, store them for later use.
$this->current_token = $token;
$this->current_post_id = $post_id;
}

if ( ! tec_tickets_seating_enabled( $post_id ) ) {
// The post is not using assigned seating, do not render the timer.
return;
}

wp_enqueue_script( 'tec-tickets-seating-session' );
wp_enqueue_style( 'tec-tickets-seating-session-style' );

Expand Down Expand Up @@ -539,4 +549,26 @@ public function ajax_interrupt(): void {
]
);
}

/**
* Returns the current token set from a previous render in the context of this request.
*
* @since TBD
*
* @return string|null The current token, or `null` if not set.
*/
public function get_current_token(): ?string {
return $this->current_token;
}

/**
* Returns the current post ID set from a previous render in the context of this request.
*
* @since TBD
*
* @return int|null The current post ID, or `null` if not set.
*/
public function get_current_post_id(): ?int {
return $this->current_post_id;
}
}
124 changes: 124 additions & 0 deletions tests/slr_integration/TEC/Tickets/Seating/Commerce/Controller_Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<?php

namespace TEC\Tickets\Seating\Commerce;

use Closure;
use TEC\Common\Tests\Provider\Controller_Test_Case;
use TEC\Tickets\Commerce\Cart;
use TEC\Tickets\Commerce\Module;
use TEC\Tickets\Seating\Meta;
use Tribe\Tickets\Test\Commerce\TicketsCommerce\Ticket_Maker;
use Tribe__Tickets__Data_API as Data_API;

class Controller_Test extends Controller_Test_Case {
use Ticket_Maker;

protected string $controller_class = Controller::class;

/**
* @before
*/
public function ensure_ticketable_post_types(): void {
$ticketable = tribe_get_option( 'ticket-enabled-post-types', [] );
$ticketable[] = 'post';
tribe_update_option( 'ticket-enabled-post-types', array_values( array_unique( $ticketable ) ) );
}

/**
* @before
*/
public function ensure_tickets_commerce_active(): void {
// Ensure the Tickets Commerce module is active.
add_filter( 'tec_tickets_commerce_is_enabled', '__return_true' );
add_filter( 'tribe_tickets_get_modules', function ( $modules ) {
$modules[ Module::class ] = tribe( Module::class )->plugin_name;

return $modules;
} );

// Reset Data_API object, so it sees Tribe Commerce.
tribe_singleton( 'tickets.data_api', new Data_API );
}

public function filter_timer_token_object_id_entries_data_provider(): \Generator {
yield 'no entries' => [
function (): array {
return [
[],
[],
];
}
];

yield 'not on checkout page' => [
function (): array {
$post_id = static::factory()->post->create();
$ticket_id = $this->create_tc_ticket( $post_id, 10 );
add_filter( 'tec_tickets_commerce_checkout_is_current_page', '__return_false' );

return [
[ $post_id => 'test-token' ],
[ $post_id => 'test-token' ],
];
}
];

yield 'on checkout page but no ASC post in cart' => [
function (): array {
add_filter( 'tec_tickets_commerce_checkout_is_current_page', '__return_true' );
$no_asc_post_id = static::factory()->post->create();
$ticket_id = $this->create_tc_ticket( $no_asc_post_id, 10 );
/** @var Cart $cart */
$cart = tribe( Cart::class );
$cart->add_ticket( $ticket_id, 1 );
$asc_post_id = static::factory()->post->create();
update_post_meta( $asc_post_id, Meta::META_KEY_LAYOUT_ID, 'some-layout-id' );
$asc_ticket_id = $this->create_tc_ticket( $asc_post_id, 10 );
$this->create_tc_ticket( $asc_post_id, 10 );

return [
[ $asc_post_id => 'test-token' ],
[],
];
}
];

yield 'on checkout page with ASC post in cart' => [
function (): array {
add_filter( 'tec_tickets_commerce_checkout_is_current_page', '__return_true' );
$no_asc_post_id = static::factory()->post->create();
$no_asc_ticket_id = $this->create_tc_ticket( $no_asc_post_id, 10 );
/** @var Cart $cart */
$cart = tribe( Cart::class );
$cart->add_ticket( $no_asc_ticket_id, 1 );
$asc_post_id = static::factory()->post->create();
update_post_meta( $asc_post_id, Meta::META_KEY_LAYOUT_ID, 'some-layout-id' );
$asc_ticket_id = $this->create_tc_ticket( $asc_post_id, 10 );
$cart->add_ticket( $asc_ticket_id, 1 );

return [
[ $asc_post_id => 'test-token' ],
[ $asc_post_id => 'test-token' ],
];
}
];
}

/**
* @dataProvider filter_timer_token_object_id_entries_data_provider
* @return void
*/
public function test_filter_timer_token_object_id_entries( Closure $fixture ): void {
[ $input_entries, $expected_entries ] = $fixture();

$controller = $this->make_controller();
$controller->register();

$filtered_entries = apply_filters( 'tec_tickets_seating_timer_token_object_id_entries', $input_entries );

$this->assertEquals(
$expected_entries,
$filtered_entries,
);
}
}
Loading
Loading