Provider

Class Provider


Source

File: src/Filter_Bar/Custom_Tables/V1/Provider.php

class Provider extends \tad_DI52_ServiceProvider {

	/**
	 * A flag property indicating whether the Service Provider did register or not.
	 *
	 * @since 5.4.0
	 *
	 * @var bool
	 */
	private $did_register = false;

	/**
	 * Registers the filters and implementations required by the Custom Tables implementation.
	 *
	 * @since 5.4.0
	 *
	 * @return bool Whether the Provider did register or not.
	 */
	public function register() {
		if ( $this->did_register ) {
			// Let's avoid double filtering by making sure we're registering at most once.
			return true;
		}

		if ( ! ( class_exists( TEC_Provider::class ) && TEC_Provider::is_active() && class_exists( State::class ) ) ) {
			return false;
		}

		$state = tribe( State::class );
		if ( ! $state->is_migrated() ) {
			return false;
		}

		try {
			$this->set_up_query_filter_redirection();

			if ( tribe( Tribe__Dependency::class )->is_plugin_active( 'Tribe__Events__Pro__Main' ) ) {
				$this->register_series_filter();
				add_filter( 'tec_events_filter_stack_where_table_field_ids', [ $this, 'redirect_table_field_ids' ] );
			}

			$this->did_register = true;

			return true;
		} catch ( Throwable $t ) {
			// This code will never fire on PHP 5.6, but will do in PHP 7.0+.

			/**
			 * Fires an action when an error or exception happens in the
			 * context of Custom Tables v1 implementation AND the server
			 * runs PHP 7.0+.
			 *
			 * @since 5.4.0
			 *
			 * @param Throwable $t The thrown error.
			 */
			do_action( 'tec_custom_tables_v1_error', $t );
		} catch ( Exception $e ) {
			// PHP 5.6 compatible code.

			/**
			 * Fires an action when an error or exception happens in the
			 * context of Custom Tables v1 implementation AND the server
			 * runs PHP 5.6.
			 *
			 * @since 5.4.0
			 *
			 * @param Exception $e The thrown error.
			 */
			do_action( 'tec_custom_tables_v1_error', $e );
		}
	}

	/**
	 * Sets up the redirection of Filter Bar modification to JOIN and WHERE clauses to the Custom Tables queries.
	 *
	 * @since 5.4.0
	 */
	private function set_up_query_filter_redirection() {
		// The Redirector will need to keep state between its different applications: make it a singleton.
		$this->container->singleton( Redirector::class,
			static function () {
				$collector = new Filtering_Refs_Collector(
					Filter::class,
					[
						'posts_where' => 11,
						'posts_join'  => 11
					] );

				return new Redirector( $collector );
			} );

		/*
		 * Step 1: collect the methods hooked by the currently active Filters.
		 * Do this on `posts_where` as it will be triggered before the `posts_join` filter in the `WP_Query`
		 * code.
		 */
		add_filter( 'posts_where', [ $this, 'collect_clause_updates' ], 10, 2 );

		/**
		 * Step 2: apply the updates to the Custom Tables Query WHERE and JOIN clauses.
		 */
		add_filter( 'posts_where', [ $this, 'apply_where_clauses' ], 20, 2 );
		add_filter( 'posts_join', [ $this, 'apply_join_clauses' ], 20, 2 );
	}

	/**
	 * Hooks and sets up the Series Filter to correctly integrate with Filter Bar and Views v2.
	 *
	 * The method will take care to delay the Series Filter and hooking to the very last moment filters are built
	 * by Filter Bar or the Context Filters manager.
	 *
	 * @since 5.4.0
	 */
	private function register_series_filter() {
		$this->container->singleton( Series_Filter::class, static function () {
			return new Series_Filter(
				_x( 'Series', 'The name of the Series Filter Bar Filter.', 'tribe-events-filter-view' ),
				'series_id'
			);
		} );
		$this->hook_to_context();

		add_filter( 'tribe_events_filter_bar_default_filter_names_map', [ $this, 'filter_default_filter_names_map' ] );
	}

	/**
	 * Hooks this Filter instance to the additional, Context-related, filters.
	 *
	 * @since 5.4.0
	 */
	private function hook_to_context() {
		if ( ! has_action( 'tribe_events_filters_create_filters', [ $this, 'build_series_filter' ] ) ) {
			add_action( 'tribe_events_filters_create_filters', [ $this, 'build_series_filter' ] );
		}

		if ( ! has_filter( 'tribe_context_locations', [ $this, 'filter_context_locations' ] ) ) {
			add_filter( 'tribe_context_locations', [ $this, 'filter_context_locations' ] );
		}

		if ( ! has_filter( 'tribe_events_filter_bar_context_to_filter_map', [ $this, 'filter_context_to_filter_map' ] ) ) {
			add_filter( 'tribe_events_filter_bar_context_to_filter_map', [ $this, 'filter_context_to_filter_map' ] );
		}

		if ( ! has_filter( 'tribe_events_filter_bar_option_key_map', [ $this, 'filter_option_key_map' ] ) ) {
			add_filter( 'tribe_events_filter_bar_option_key_map', [ $this, 'filter_option_key_map' ] );
		}
	}

	/**
	 * Collects, by "discharging" them, the JOIN and WHERE SQL clause modifications Filter Bar
	 * filters would do the current Query.
	 *
	 * This method is the first part in a "filter transplant" from the original Query Filter Bar
	 * filters would target, to the Custom Tables Query that replaces it.
	 *
	 * @since 5.4.0
	 *
	 * @param string   $sql   The input SQL (WHERE) statement.
	 * @param WP_Query $query A reference to the Query object that is currently being filtered.
	 *
	 * @return string The input SQL statement, untouched at this stage.
	 * @see   Redirector::collect_clause_updates() for the method proxied by this one.
	 *
	 */
	public function collect_clause_updates( $sql, $query ) {
		$redirector = $this->container->make( Redirector::class );

		return $redirector->collect_clause_updates( $sql, $query );
	}

	/**
	 * Filters a Query WHERE SQL clause to add, if required and applicable, the modifications
	 * Filter Bar filters would have, originally, applied to it.
	 *
	 * @param string   $where The input WHERE SQL clause.
	 * @param WP_Query $query A reference to the Query object that is currently being filtered.
	 *
	 * @return string The filtered JOIN SQL clause.
	 *
	 * @see redirector::apply_where_clauses() for the method proxied by this one.
	 */
	public function apply_where_clauses( $where, $query ) {
		$redirector = $this->container->make( Redirector::class );

		return $redirector->apply_where_clauses( $where, $query );
	}

	/**
	 * Filters a Query JOIN SQL clause to add, if required and applicable, the modifications
	 * Filter Bar filters would have, originally, applied to it.
	 *
	 * @since 5.4.0
	 *
	 * @param WP_Query $query A reference to the Query object that is currently being filtered.
	 * @param string   $join  The input JOIN SQL clause.
	 *
	 * @return string The filtered JOIN SQL clause.
	 *
	 * @see   redirector::apply_join_clauses() for the method proxied by this one.
	 */
	public function apply_join_clauses( $join, $query ) {
		$redirector = $this->container->make( Redirector::class );

		return $redirector->apply_join_clauses( $join, $query );
	}

	/**
	 * Builds, thus hooking it to the legacy Filter Bar actions and filters, the instance of the
	 * Series Filter.
	 *
	 * @since 5.4.0
	 */
	public function build_series_filter() {
		$this->container->make( Series_Filter::class );
	}

	/**
	 * Filters the Context locations to add the one that will allow looking up the current
	 * Series Filter values.
	 *
	 * @since 5.4.0
	 *
	 * @param array<string,array> $locations A map of the current Context locations.
	 *
	 * @return array<string,array> A filtered map of Context locations.
	 */
	public function filter_context_locations( array $locations = [] ) {
		$parse_data_from_url = static function ( $view_data ) {
			if ( ! isset( $view_data['url'] ) ) {
				return Context::NOT_FOUND;
			}

			wp_parse_str(
				(string) wp_parse_url( $view_data['url'], PHP_URL_QUERY ),
				$parsed
			);

			return isset( $parsed['tribe_filterbar_series_id'] ) ?
				Arr::list_to_array( $parsed['tribe_filterbar_series_id'] )
				: Context::NOT_FOUND;
		};

		$locations = array_merge( $locations,
			[
				'filterbar_series_id' => [
					'read' => [
						Context::QUERY_VAR     => [ 'tribe_series_id' ],
						Context::REQUEST_VAR   => [ 'tribe_series_id' ],
						Context::LOCATION_FUNC => [ 'view_data', $parse_data_from_url ],
					],
				]
			]
		);

		return $locations;
	}

	/**
	 * Filters the map from Context keys to the corresponding Filter class to add the Series
	 * filter entry.
	 *
	 * @since 5.4.0
	 *
	 * @param array<string,string> $map The unfiltered Context key to Filter class map.
	 *
	 * @return array<string,string> The filtered Context key to Filter class map.
	 */
	public function filter_context_to_filter_map( array $map ) {
		$map['filterbar_series_id'] = Series_Filter::class;

		return $map;
	}

	/**
	 * Filters the reverse map that allows going from a Filter option key to the Filter class to
	 * instantiate.
	 *
	 * @since 5.4.0
	 *
	 * @param array<string,string> $map The input map.
	 *
	 * @return array<string,string> The filtered map, the Series Filter entry added to it.
	 */
	public function filter_option_key_map( array $map = [] ) {
		$map [ Series_Filter::class ] = 'series_id';

		return $map;
	}

	/**
	 * Filters the map that will set the default filter names to add the Series one.
	 *
	 * @since 5.4.0
	 *
	 * @param array<class-string,string> $map A map from the Filter class to their
	 *                                        default names.
	 *
	 * @return array<class-string,string> A map from the Filter class to their
	 *                                    default names updated to add the Series one.
	 */
	public function filter_default_filter_names_map( array $map ) {
		$map[ Series_Filter::class ] = _x( 'Series', 'The default Filter Bar Series filter title.', 'tribe-events-filter-view' );

		return $map;
	}

	/**
	 * Redirects the table, field and IDs set from the posts table to the Occurrences table, if required.
	 *
	 * @since 5.4.0
	 *
	 * @param array{table: string, field: string, ids: array<int>} $table_field_ids The table, field and IDs
	 *                                                                              set from the posts table.
	 *
	 * @return array{table: string, field: string, ids: array<int>} The filtered table, field and IDs set,
	 *                                                              redirected to the Occurrences table.
	 */
	public function redirect_table_field_ids( array $table_field_ids ): array {
		return $this->container->make( Redirector::class )->redirect_table_field_ids( $table_field_ids );
	}
}

Top ↑

Changelog

Changelog
Version Description
5.4.0 Introduced.

Top ↑

Methods

  • apply_join_clauses — Filters a Query JOIN SQL clause to add, if required and applicable, the modifications Filter Bar filters would have, originally, applied to it.
  • apply_where_clauses — Filters a Query WHERE SQL clause to add, if required and applicable, the modifications Filter Bar filters would have, originally, applied to it.
  • build_series_filter — Builds, thus hooking it to the legacy Filter Bar actions and filters, the instance of the Series Filter.
  • collect_clause_updates — Collects, by "discharging" them, the JOIN and WHERE SQL clause modifications Filter Bar filters would do the current Query.
  • filter_context_locations — Filters the Context locations to add the one that will allow looking up the current Series Filter values.
  • filter_context_to_filter_map — Filters the map from Context keys to the corresponding Filter class to add the Series filter entry.
  • filter_default_filter_names_map — Filters the map that will set the default filter names to add the Series one.
  • filter_option_key_map — Filters the reverse map that allows going from a Filter option key to the Filter class to instantiate.
  • redirect_table_field_ids — Redirects the table, field and IDs set from the posts table to the Occurrences table, if required.
  • register — Registers the filters and implementations required by the Custom Tables implementation.