ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } } ndexable = $this->find_for_system_page( '404' ); break; } if ( $indexable === false ) { return $this->query()->create( [ 'object_type' => 'unknown', 'post_status' => 'unindexed', 'version' => 1, ] ); } return $indexable; } /** * Retrieves an indexable by its permalink. * * @param string $permalink The indexable permalink. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_by_permalink( $permalink ) { $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink ); // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query. return $this->query() ->where( 'permalink_hash', $permalink_hash ) ->where( 'permalink', $permalink ) ->find_one(); } /** * Retrieves all the indexable instances of a certain object type. * * @param string $object_type The object type. * * @return Indexable[] The array with all the indexable instances of a certain object type. */ public function find_all_with_type( $object_type ) { /** * The array with all the indexable instances of a certain object type. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves all the indexable instances of a certain object subtype. * * @param string $object_type The object type. * @param string $object_sub_type The object subtype. * * @return Indexable[] The array with all the indexable instances of a certain object subtype. */ public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) { /** * The array with all the indexable instances of a certain object type and subtype. * * @var Indexable[] $indexables */ $indexables = $this ->query() ->where( 'object_type', $object_type ) ->where( 'object_sub_type', $object_sub_type ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Retrieves the homepage indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_home_page( $auto_create = true ) { $indexable = \wp_cache_get( 'home-page', 'yoast-seo-indexables' ); if ( ! $indexable ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_home_page(); } $indexable = $this->upgrade_indexable( $indexable ); \wp_cache_set( 'home-page', $indexable, 'yoast-seo-indexables', ( 5 * \MINUTE_IN_SECONDS ) ); } return $indexable; } /** * Retrieves the date archive indexable. * * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_date_archive( $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_date_archive(); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable for a post type archive. * * @param string $post_type The post type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable The indexable, false if none could be found. */ public function find_for_post_type_archive( $post_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'post-type-archive' ) ->where( 'object_sub_type', $post_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_post_type_archive( $post_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves the indexable for a system page. * * @param string $object_sub_type The type of system page. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_for_system_page( $object_sub_type, $auto_create = true ) { /** * Indexable instance. * * @var Indexable $indexable */ $indexable = $this->query() ->where( 'object_type', 'system-page' ) ->where( 'object_sub_type', $object_sub_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_system_page( $object_sub_type ); } return $this->upgrade_indexable( $indexable ); } /** * Retrieves an indexable by its ID and type. * * @param int $object_id The indexable object ID. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return bool|Indexable Instance of indexable. */ public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) { $indexable = $this->query() ->where( 'object_id', $object_id ) ->where( 'object_type', $object_type ) ->find_one(); if ( $auto_create && ! $indexable ) { $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type ); } else { $indexable = $this->upgrade_indexable( $indexable ); } return $indexable; } /** * Retrieves multiple indexables at once by their id's and type. * * @param int[] $object_ids The array of indexable object id's. * @param string $object_type The indexable object type. * @param bool $auto_create Optional. Create the indexable if it does not exist. * * @return Indexable[] An array of indexables. */ public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) { if ( empty( $object_ids ) ) { return []; } /** * Represents an array of indexable objects. * * @var Indexable[] $indexables */ $indexables = $this->query() ->where_in( 'object_id', $object_ids ) ->where( 'object_type', $object_type ) ->find_many(); if ( $auto_create ) { $indexables_available = []; foreach ( $indexables as $indexable ) { $indexables_available[] = $indexable->object_id; } $indexables_to_create = \array_diff( $object_ids, $indexables_available ); foreach ( $indexables_to_create as $indexable_to_create ) { $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type ); } } return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Finds the indexables by id's. * * @param array $indexable_ids The indexable id's. * * @return Indexable[] The found indexables. */ public function find_by_ids( array $indexable_ids ) { if ( empty( $indexable_ids ) ) { return []; } $indexables = $this ->query() ->where_in( 'id', $indexable_ids ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all ancestors of a given indexable. * * @param Indexable $indexable The indexable to find the ancestors of. * * @return Indexable[] All ancestors of the given indexable. */ public function get_ancestors( Indexable $indexable ) { // If we've already set ancestors on the indexable no need to get them again. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable ); // If we've set ancestors on the indexable because we had to build them to find them. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) { return \array_map( [ $this, 'upgrade_indexable' ], $indexable->ancestors ); } if ( empty( $indexable_ids ) ) { return []; } if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) { return []; } $indexables = $this->query() ->where_in( 'id', $indexable_ids ) ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' ) ->find_many(); return \array_map( [ $this, 'upgrade_indexable' ], $indexables ); } /** * Returns all subpages with a given post_parent. * * @param int $post_parent The post parent. * @param array $exclude_ids The id's to exclude. * * @return Indexable[] array of indexables. */ public function get_subpages_by_post_parent( $post_parent, $exclude_ids = [] ) { $query = $this->query() ->where( 'post_parent', $post_parent ) ->where( 'object_type', 'post' ) ->where( 'post_status', 'publish' ); if ( ! empty( $exclude_ids ) ) { $query->where_not_in( 'object_id', $exclude_ids ); } return $query->find_many(); } /** * Updates the incoming link count for an indexable without first fetching it. * * @param int $indexable_id The indexable id. * @param int $count The incoming link count. * * @return bool Whether or not the update was succeful. */ public function update_incoming_link_count( $indexable_id, $count ) { return (bool) $this->query() ->set( 'incoming_link_count', $count ) ->where( 'id', $indexable_id ) ->update_many(); } /** * Ensures that the given indexable has a permalink. * * Will be deprecated in 17.3 - Use upgrade_indexable instead. * * @codeCoverageIgnore * * @param Indexable $indexable The indexable. * * @return bool|Indexable The indexable. */ public function ensure_permalink( $indexable ) { // @phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- self::class is safe. // @phpcs:ignore Squiz.PHP.CommentedOutCode.Found // _deprecated_function( __METHOD__, 'Yoast SEO 17.3', self::class . '::upgrade_indexable' ); return $this->upgrade_indexable( $indexable ); } /** * Checks if an Indexable is outdated, and rebuilds it when necessary. * * @param Indexable $indexable The indexable. * * @return Indexable The indexable. */ public function upgrade_indexable( $indexable ) { if ( $this->version_manager->indexable_needs_upgrade( $indexable ) ) { $indexable = $this->builder->build( $indexable ); } return $indexable; } /** * Resets the permalinks of the passed object type and subtype. * * @param string|null $type The type of the indexable. Can be null. * @param string|null $subtype The subtype. Can be null. * * @return int|bool The number of permalinks changed if the query was succesful. False otherwise. */ public function reset_permalink( $type = null, $subtype = null ) { $query = $this->query()->set( [ 'permalink' => null, 'permalink_hash' => null, 'version' => 0, ] ); if ( $type !== null ) { $query->where( 'object_type', $type ); } if ( $type !== null && $subtype !== null ) { $query->where( 'object_sub_type', $subtype ); } return $query->update_many(); } /** * Gets the total number of stored indexables. * * @return int The total number of stored indexables. */ public function get_total_number_of_indexables() { return $this->query()->count(); } }
Fatal error: Uncaught Error: Class "Automattic\WooCommerce\Blocks\BlockTypes\ProductBestSellers" not found in /htdocs/wp-content/plugins/woocommerce/packages/woocommerce-blocks/src/BlockTypesController.php:65 Stack trace: #0 /htdocs/wp-includes/class-wp-hook.php(310): Automattic\WooCommerce\Blocks\BlockTypesController->register_blocks('') #1 /htdocs/wp-includes/class-wp-hook.php(334): WP_Hook->apply_filters(NULL, Array) #2 /htdocs/wp-includes/plugin.php(517): WP_Hook->do_action(Array) #3 /htdocs/wp-settings.php(632): do_action('init') #4 /htdocs/wp-config.php(96): require_once('/htdocs/wp-sett...') #5 /htdocs/wp-load.php(50): require_once('/htdocs/wp-conf...') #6 /htdocs/wp-blog-header.php(13): require_once('/htdocs/wp-load...') #7 /htdocs/index.php(17): require('/htdocs/wp-blog...') #8 {main} thrown in /htdocs/wp-content/plugins/woocommerce/packages/woocommerce-blocks/src/BlockTypesController.php on line 65