first commit
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
/**
|
||||
* Base class of indexing actions.
|
||||
*/
|
||||
abstract class Abstract_Indexing_Action implements Indexation_Action_Interface, Limited_Indexing_Action_Interface {
|
||||
|
||||
/**
|
||||
* The transient name.
|
||||
*
|
||||
* This is a trick to force derived classes to define a transient themselves.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_COUNT_TRANSIENT = null;
|
||||
|
||||
/**
|
||||
* The transient cache key for limited counts.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';
|
||||
|
||||
/**
|
||||
* Builds a query for selecting the ID's of unindexed posts.
|
||||
*
|
||||
* @param bool $limit The maximum number of post IDs to return.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
abstract protected function get_select_query( $limit );
|
||||
|
||||
/**
|
||||
* Builds a query for counting the number of unindexed posts.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
abstract protected function get_count_query();
|
||||
|
||||
/**
|
||||
* Returns a limited number of unindexed posts.
|
||||
*
|
||||
* @param int $limit Limit the maximum number of unindexed posts that are counted.
|
||||
*
|
||||
* @return int The limited number of unindexed posts. 0 if the query fails.
|
||||
*/
|
||||
public function get_limited_unindexed_count( $limit ) {
|
||||
$transient = \get_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
|
||||
if ( $transient !== false ) {
|
||||
return (int) $transient;
|
||||
}
|
||||
|
||||
\set_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT, 0, ( \MINUTE_IN_SECONDS * 15 ) );
|
||||
|
||||
$query = $this->get_select_query( $limit );
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_count_query returns a prepared query.
|
||||
$unindexed_object_ids = $this->wpdb->get_col( $query );
|
||||
$count = (int) \count( $unindexed_object_ids );
|
||||
|
||||
\set_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT, $count, ( \MINUTE_IN_SECONDS * 15 ) );
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of unindexed posts.
|
||||
*
|
||||
* @return int|false The total number of unindexed posts. False if the query fails.
|
||||
*/
|
||||
public function get_total_unindexed() {
|
||||
$transient = \get_transient( static::UNINDEXED_COUNT_TRANSIENT );
|
||||
if ( $transient !== false ) {
|
||||
return (int) $transient;
|
||||
}
|
||||
|
||||
// Store transient before doing the query so multiple requests won't make multiple queries.
|
||||
// Only store this for 15 minutes to ensure that if the query doesn't complete a wrong count is not kept too long.
|
||||
\set_transient( static::UNINDEXED_COUNT_TRANSIENT, 0, ( \MINUTE_IN_SECONDS * 15 ) );
|
||||
|
||||
$query = $this->get_count_query();
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_count_query returns a prepared query.
|
||||
$count = $this->wpdb->get_var( $query );
|
||||
|
||||
if ( \is_null( $count ) ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
\set_transient( static::UNINDEXED_COUNT_TRANSIENT, $count, \DAY_IN_SECONDS );
|
||||
/**
|
||||
* Action: 'wpseo_indexables_unindexed_calculated' - sets an option to timestamp when there are no unindexed indexables left.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
\do_action( 'wpseo_indexables_unindexed_calculated', static::UNINDEXED_COUNT_TRANSIENT, $count );
|
||||
|
||||
return (int) $count;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use wpdb;
|
||||
use Yoast\WP\SEO\Builders\Indexable_Link_Builder;
|
||||
use Yoast\WP\SEO\Models\SEO_Links;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
|
||||
/**
|
||||
* Reindexing action for link indexables.
|
||||
*/
|
||||
abstract class Abstract_Link_Indexing_Action extends Abstract_Indexing_Action {
|
||||
|
||||
/**
|
||||
* The link builder.
|
||||
*
|
||||
* @var Indexable_Link_Builder
|
||||
*/
|
||||
protected $link_builder;
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* The WordPress database instance.
|
||||
*
|
||||
* @var wpdb
|
||||
*/
|
||||
protected $wpdb;
|
||||
|
||||
/**
|
||||
* Indexable_Post_Indexing_Action constructor
|
||||
*
|
||||
* @param Indexable_Link_Builder $link_builder The indexable link builder.
|
||||
* @param Indexable_Repository $repository The indexable repository.
|
||||
* @param wpdb $wpdb The WordPress database instance.
|
||||
*/
|
||||
public function __construct(
|
||||
Indexable_Link_Builder $link_builder,
|
||||
Indexable_Repository $repository,
|
||||
wpdb $wpdb
|
||||
) {
|
||||
$this->link_builder = $link_builder;
|
||||
$this->repository = $repository;
|
||||
$this->wpdb = $wpdb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds links for indexables which haven't had their links indexed yet.
|
||||
*
|
||||
* @return SEO_Links[] The created SEO links.
|
||||
*/
|
||||
public function index() {
|
||||
$objects = $this->get_objects();
|
||||
|
||||
$indexables = [];
|
||||
foreach ( $objects as $object ) {
|
||||
$indexable = $this->repository->find_by_id_and_type( $object->id, $object->type );
|
||||
if ( $indexable ) {
|
||||
$this->link_builder->build( $indexable, $object->content );
|
||||
$indexable->save();
|
||||
|
||||
$indexables[] = $indexable;
|
||||
}
|
||||
}
|
||||
|
||||
if ( \count( $indexables ) > 0 ) {
|
||||
\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
|
||||
\delete_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
|
||||
}
|
||||
|
||||
return $indexables;
|
||||
}
|
||||
|
||||
/**
|
||||
* In the case of term-links and post-links we want to use the total unindexed count, because using
|
||||
* the limited unindexed count actually leads to worse performance.
|
||||
*
|
||||
* @param int|bool $limit Unused.
|
||||
*
|
||||
* @return int The total number of unindexed links.
|
||||
*/
|
||||
public function get_limited_unindexed_count( $limit = false ) {
|
||||
return $this->get_total_unindexed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of texts that will be indexed in a single link indexing pass.
|
||||
*
|
||||
* @return int The limit.
|
||||
*/
|
||||
public function get_limit() {
|
||||
/**
|
||||
* Filter 'wpseo_link_indexing_limit' - Allow filtering the number of texts indexed during each link indexing pass.
|
||||
*
|
||||
* @api int The maximum number of texts indexed.
|
||||
*/
|
||||
return \apply_filters( 'wpseo_link_indexing_limit', 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects to be indexed.
|
||||
*
|
||||
* @return array Objects to be indexed, should be an array of objects with object_id, object_type and content.
|
||||
*/
|
||||
abstract protected function get_objects();
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use Yoast\WP\SEO\Models\Indexable;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
|
||||
/**
|
||||
* General reindexing action for indexables.
|
||||
*/
|
||||
class Indexable_General_Indexation_Action implements Indexation_Action_Interface, Limited_Indexing_Action_Interface {
|
||||
|
||||
/**
|
||||
* The transient cache key.
|
||||
*/
|
||||
const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_general_items';
|
||||
|
||||
/**
|
||||
* Represents the indexables repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
protected $indexable_repository;
|
||||
|
||||
/**
|
||||
* Indexable_General_Indexation_Action constructor.
|
||||
*
|
||||
* @param Indexable_Repository $indexable_repository The indexables repository.
|
||||
*/
|
||||
public function __construct( Indexable_Repository $indexable_repository ) {
|
||||
$this->indexable_repository = $indexable_repository;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of unindexed objects.
|
||||
*
|
||||
* @return int The total number of unindexed objects.
|
||||
*/
|
||||
public function get_total_unindexed() {
|
||||
$transient = \get_transient( static::UNINDEXED_COUNT_TRANSIENT );
|
||||
if ( $transient !== false ) {
|
||||
return (int) $transient;
|
||||
}
|
||||
|
||||
$indexables_to_create = $this->query();
|
||||
|
||||
$result = \count( $indexables_to_create );
|
||||
|
||||
\set_transient( static::UNINDEXED_COUNT_TRANSIENT, $result, \DAY_IN_SECONDS );
|
||||
|
||||
/**
|
||||
* Action: 'wpseo_indexables_unindexed_calculated' - sets an option to timestamp when there are no unindexed indexables left.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
\do_action( 'wpseo_indexables_unindexed_calculated', static::UNINDEXED_COUNT_TRANSIENT, $result );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a limited number of unindexed posts.
|
||||
*
|
||||
* @param int $limit Limit the maximum number of unindexed posts that are counted.
|
||||
*
|
||||
* @return int|false The limited number of unindexed posts. False if the query fails.
|
||||
*/
|
||||
public function get_limited_unindexed_count( $limit ) {
|
||||
return $this->get_total_unindexed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates indexables for unindexed system pages, the date archive, and the homepage.
|
||||
*
|
||||
* @return Indexable[] The created indexables.
|
||||
*/
|
||||
public function index() {
|
||||
$indexables = [];
|
||||
$indexables_to_create = $this->query();
|
||||
|
||||
if ( isset( $indexables_to_create['404'] ) ) {
|
||||
$indexables[] = $this->indexable_repository->find_for_system_page( '404' );
|
||||
}
|
||||
|
||||
if ( isset( $indexables_to_create['search'] ) ) {
|
||||
$indexables[] = $this->indexable_repository->find_for_system_page( 'search-result' );
|
||||
}
|
||||
|
||||
if ( isset( $indexables_to_create['date_archive'] ) ) {
|
||||
$indexables[] = $this->indexable_repository->find_for_date_archive();
|
||||
}
|
||||
if ( isset( $indexables_to_create['home_page'] ) ) {
|
||||
$indexables[] = $this->indexable_repository->find_for_home_page();
|
||||
}
|
||||
|
||||
\set_transient( static::UNINDEXED_COUNT_TRANSIENT, 0, \DAY_IN_SECONDS );
|
||||
|
||||
return $indexables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of objects that will be indexed in a single indexing pass.
|
||||
*
|
||||
* @return int The limit.
|
||||
*/
|
||||
public function get_limit() {
|
||||
// This matches the maximum number of indexables created by this action.
|
||||
return 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check which indexables already exist and return the values of the ones to create.
|
||||
*
|
||||
* @return array The indexable types to create.
|
||||
*/
|
||||
private function query() {
|
||||
$indexables_to_create = [];
|
||||
if ( ! $this->indexable_repository->find_for_system_page( '404', false ) ) {
|
||||
$indexables_to_create['404'] = true;
|
||||
}
|
||||
|
||||
if ( ! $this->indexable_repository->find_for_system_page( 'search-result', false ) ) {
|
||||
$indexables_to_create['search'] = true;
|
||||
}
|
||||
|
||||
if ( ! $this->indexable_repository->find_for_date_archive( false ) ) {
|
||||
$indexables_to_create['date_archive'] = true;
|
||||
}
|
||||
|
||||
$need_home_page_indexable = ( (int) \get_option( 'page_on_front' ) === 0 && \get_option( 'show_on_front' ) === 'posts' );
|
||||
|
||||
if ( $need_home_page_indexable && ! $this->indexable_repository->find_for_home_page( false ) ) {
|
||||
$indexables_to_create['home_page'] = true;
|
||||
}
|
||||
|
||||
return $indexables_to_create;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use Yoast\WP\SEO\Helpers\Indexable_Helper;
|
||||
|
||||
/**
|
||||
* Indexing action to call when the indexable indexing process is completed.
|
||||
*/
|
||||
class Indexable_Indexing_Complete_Action {
|
||||
|
||||
/**
|
||||
* The options helper.
|
||||
*
|
||||
* @var Indexable_Helper
|
||||
*/
|
||||
protected $indexable_helper;
|
||||
|
||||
/**
|
||||
* Indexable_Indexing_Complete_Action constructor.
|
||||
*
|
||||
* @param Indexable_Helper $indexable_helper The indexable helper.
|
||||
*/
|
||||
public function __construct( Indexable_Helper $indexable_helper ) {
|
||||
$this->indexable_helper = $indexable_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps up the indexing process.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function complete() {
|
||||
$this->indexable_helper->finish_indexing();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,207 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use wpdb;
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\Helpers\Post_Helper;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Models\Indexable;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;
|
||||
|
||||
/**
|
||||
* Reindexing action for post indexables.
|
||||
*/
|
||||
class Indexable_Post_Indexation_Action extends Abstract_Indexing_Action {
|
||||
|
||||
/**
|
||||
* The transient cache key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_posts';
|
||||
|
||||
/**
|
||||
* The transient cache key for limited counts.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
protected $post_type_helper;
|
||||
|
||||
/**
|
||||
* The post helper.
|
||||
*
|
||||
* @var Post_Helper
|
||||
*/
|
||||
protected $post_helper;
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* The WordPress database instance.
|
||||
*
|
||||
* @var wpdb
|
||||
*/
|
||||
protected $wpdb;
|
||||
|
||||
/**
|
||||
* The latest version of Post Indexables.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* Indexable_Post_Indexing_Action constructor
|
||||
*
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
* @param Indexable_Repository $repository The indexable repository.
|
||||
* @param wpdb $wpdb The WordPress database instance.
|
||||
* @param Indexable_Builder_Versions $builder_versions The latest versions for each Indexable type.
|
||||
* @param Post_Helper $post_helper The post helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Post_Type_Helper $post_type_helper,
|
||||
Indexable_Repository $repository,
|
||||
wpdb $wpdb,
|
||||
Indexable_Builder_Versions $builder_versions,
|
||||
Post_Helper $post_helper
|
||||
) {
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
$this->repository = $repository;
|
||||
$this->wpdb = $wpdb;
|
||||
$this->version = $builder_versions->get_latest_version_for_type( 'post' );
|
||||
$this->post_helper = $post_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates indexables for unindexed posts.
|
||||
*
|
||||
* @return Indexable[] The created indexables.
|
||||
*/
|
||||
public function index() {
|
||||
$query = $this->get_select_query( $this->get_limit() );
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
|
||||
$post_ids = $this->wpdb->get_col( $query );
|
||||
|
||||
$indexables = [];
|
||||
foreach ( $post_ids as $post_id ) {
|
||||
$indexables[] = $this->repository->find_by_id_and_type( (int) $post_id, 'post' );
|
||||
}
|
||||
|
||||
if ( \count( $indexables ) > 0 ) {
|
||||
\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
|
||||
\delete_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
|
||||
}
|
||||
|
||||
return $indexables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of posts that will be indexed in a single indexing pass.
|
||||
*
|
||||
* @return int The limit.
|
||||
*/
|
||||
public function get_limit() {
|
||||
/**
|
||||
* Filter 'wpseo_post_indexation_limit' - Allow filtering the amount of posts indexed during each indexing pass.
|
||||
*
|
||||
* @api int The maximum number of posts indexed.
|
||||
*/
|
||||
$limit = \apply_filters( 'wpseo_post_indexation_limit', 25 );
|
||||
|
||||
if ( ! \is_int( $limit ) || $limit < 1 ) {
|
||||
$limit = 25;
|
||||
}
|
||||
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for counting the number of unindexed posts.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_count_query() {
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
|
||||
$post_types = $this->post_type_helper->get_indexable_post_types();
|
||||
$excluded_post_statuses = $this->post_helper->get_excluded_post_statuses();
|
||||
$replacements = \array_merge(
|
||||
$post_types,
|
||||
$excluded_post_statuses
|
||||
);
|
||||
|
||||
$replacements[] = $this->version;
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_select_query as well.
|
||||
// @phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||
return $this->wpdb->prepare(
|
||||
"
|
||||
SELECT COUNT(P.ID)
|
||||
FROM {$this->wpdb->posts} AS P
|
||||
WHERE P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $post_types ), '%s' ) ) . ')
|
||||
AND P.post_status NOT IN (' . \implode( ', ', \array_fill( 0, \count( $excluded_post_statuses ), '%s' ) ) . ")
|
||||
AND P.ID not in (
|
||||
SELECT I.object_id from $indexable_table as I
|
||||
WHERE I.object_type = 'post'
|
||||
AND I.version = %d )",
|
||||
$replacements
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for selecting the ID's of unindexed posts.
|
||||
*
|
||||
* @param bool $limit The maximum number of post IDs to return.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_select_query( $limit = false ) {
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
|
||||
$post_types = $this->post_type_helper->get_indexable_post_types();
|
||||
$excluded_post_statuses = $this->post_helper->get_excluded_post_statuses();
|
||||
$replacements = \array_merge(
|
||||
$post_types,
|
||||
$excluded_post_statuses
|
||||
);
|
||||
$replacements[] = $this->version;
|
||||
|
||||
$limit_query = '';
|
||||
if ( $limit ) {
|
||||
$limit_query = 'LIMIT %d';
|
||||
$replacements[] = $limit;
|
||||
}
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
|
||||
// @phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber
|
||||
return $this->wpdb->prepare(
|
||||
"
|
||||
SELECT P.ID
|
||||
FROM {$this->wpdb->posts} AS P
|
||||
WHERE P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $post_types ), '%s' ) ) . ')
|
||||
AND P.post_status NOT IN (' . \implode( ', ', \array_fill( 0, \count( $excluded_post_statuses ), '%s' ) ) . ")
|
||||
AND P.ID not in (
|
||||
SELECT I.object_id from $indexable_table as I
|
||||
WHERE I.object_type = 'post'
|
||||
AND I.version = %d )
|
||||
$limit_query",
|
||||
$replacements
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use Yoast\WP\SEO\Builders\Indexable_Builder;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
use Yoast\WP\SEO\Models\Indexable;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;
|
||||
|
||||
/**
|
||||
* Reindexing action for post type archive indexables.
|
||||
*
|
||||
* @phpcs:disable Yoast.NamingConventions.ObjectNameDepth.MaxExceeded
|
||||
*/
|
||||
class Indexable_Post_Type_Archive_Indexation_Action implements Indexation_Action_Interface, Limited_Indexing_Action_Interface {
|
||||
|
||||
|
||||
/**
|
||||
* The transient cache key.
|
||||
*/
|
||||
const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_post_type_archives';
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
protected $post_type;
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* The indexable builder.
|
||||
*
|
||||
* @var Indexable_Builder
|
||||
*/
|
||||
protected $builder;
|
||||
|
||||
/**
|
||||
* The current version of the post type archive indexable builder.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* Indexation_Post_Type_Archive_Action constructor.
|
||||
*
|
||||
* @param Indexable_Repository $repository The indexable repository.
|
||||
* @param Indexable_Builder $builder The indexable builder.
|
||||
* @param Post_Type_Helper $post_type The post type helper.
|
||||
* @param Indexable_Builder_Versions $versions The current versions of all indexable builders.
|
||||
*/
|
||||
public function __construct(
|
||||
Indexable_Repository $repository,
|
||||
Indexable_Builder $builder,
|
||||
Post_Type_Helper $post_type,
|
||||
Indexable_Builder_Versions $versions
|
||||
) {
|
||||
$this->repository = $repository;
|
||||
$this->builder = $builder;
|
||||
$this->post_type = $post_type;
|
||||
$this->version = $versions->get_latest_version_for_type( 'post-type-archive' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of unindexed post type archives.
|
||||
*
|
||||
* @param int $limit Limit the number of counted objects.
|
||||
*
|
||||
* @return int The total number of unindexed post type archives.
|
||||
*/
|
||||
public function get_total_unindexed( $limit = false ) {
|
||||
$transient = \get_transient( static::UNINDEXED_COUNT_TRANSIENT );
|
||||
if ( $transient !== false ) {
|
||||
return (int) $transient;
|
||||
}
|
||||
|
||||
\set_transient( static::UNINDEXED_COUNT_TRANSIENT, 0, \DAY_IN_SECONDS );
|
||||
|
||||
$result = \count( $this->get_unindexed_post_type_archives( $limit ) );
|
||||
|
||||
\set_transient( static::UNINDEXED_COUNT_TRANSIENT, $result, \DAY_IN_SECONDS );
|
||||
|
||||
/**
|
||||
* Action: 'wpseo_indexables_unindexed_calculated' - sets an option to timestamp when there are no unindexed indexables left.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
\do_action( 'wpseo_indexables_unindexed_calculated', static::UNINDEXED_COUNT_TRANSIENT, $result );
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates indexables for post type archives.
|
||||
*
|
||||
* @return Indexable[] The created indexables.
|
||||
*/
|
||||
public function index() {
|
||||
$unindexed_post_type_archives = $this->get_unindexed_post_type_archives( $this->get_limit() );
|
||||
|
||||
$indexables = [];
|
||||
foreach ( $unindexed_post_type_archives as $post_type_archive ) {
|
||||
$indexables[] = $this->builder->build_for_post_type_archive( $post_type_archive );
|
||||
}
|
||||
|
||||
if ( \count( $indexables ) > 0 ) {
|
||||
\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
|
||||
}
|
||||
|
||||
return $indexables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of post type archives that will be indexed in a single indexing pass.
|
||||
*
|
||||
* @return int The limit.
|
||||
*/
|
||||
public function get_limit() {
|
||||
/**
|
||||
* Filter 'wpseo_post_type_archive_indexation_limit' - Allow filtering the number of posts indexed during each indexing pass.
|
||||
*
|
||||
* @api int The maximum number of posts indexed.
|
||||
*/
|
||||
$limit = \apply_filters( 'wpseo_post_type_archive_indexation_limit', 25 );
|
||||
|
||||
if ( ! \is_int( $limit ) || $limit < 1 ) {
|
||||
$limit = 25;
|
||||
}
|
||||
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of post types for which no indexable for its archive page has been made yet.
|
||||
*
|
||||
* @param int|false $limit Limit the number of retrieved indexables to this number.
|
||||
*
|
||||
* @return array The list of post types for which no indexable for its archive page has been made yet.
|
||||
*/
|
||||
protected function get_unindexed_post_type_archives( $limit = false ) {
|
||||
$post_types_with_archive_pages = $this->get_post_types_with_archive_pages();
|
||||
$indexed_post_types = $this->get_indexed_post_type_archives();
|
||||
|
||||
$unindexed_post_types = \array_diff( $post_types_with_archive_pages, $indexed_post_types );
|
||||
|
||||
if ( $limit ) {
|
||||
return \array_slice( $unindexed_post_types, 0, $limit );
|
||||
}
|
||||
|
||||
return $unindexed_post_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the names of all the post types that have archive pages.
|
||||
*
|
||||
* @return array The list of names of all post types that have archive pages.
|
||||
*/
|
||||
protected function get_post_types_with_archive_pages() {
|
||||
// We only want to index archive pages of public post types that have them.
|
||||
$post_types_with_archive = $this->post_type->get_indexable_post_archives();
|
||||
|
||||
// We only need the post type names, not the objects.
|
||||
$post_types = [];
|
||||
foreach ( $post_types_with_archive as $post_type_with_archive ) {
|
||||
$post_types[] = $post_type_with_archive->name;
|
||||
}
|
||||
|
||||
return $post_types;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of post type names for which an archive indexable exists.
|
||||
*
|
||||
* @return array The list of names of post types with unindexed archive pages.
|
||||
*/
|
||||
protected function get_indexed_post_type_archives() {
|
||||
$results = $this->repository->query()
|
||||
->select( 'object_sub_type' )
|
||||
->where( 'object_type', 'post-type-archive' )
|
||||
->where_equal( 'version', $this->version )
|
||||
->find_array();
|
||||
|
||||
if ( $results === false ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$callback = static function( $result ) {
|
||||
return $result['object_sub_type'];
|
||||
};
|
||||
|
||||
return \array_map( $callback, $results );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a limited number of unindexed posts.
|
||||
*
|
||||
* @param int $limit Limit the maximum number of unindexed posts that are counted.
|
||||
*
|
||||
* @return int|false The limited number of unindexed posts. False if the query fails.
|
||||
*/
|
||||
public function get_limited_unindexed_count( $limit ) {
|
||||
return $this->get_total_unindexed( $limit );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use wpdb;
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
|
||||
use Yoast\WP\SEO\Models\Indexable;
|
||||
use Yoast\WP\SEO\Repositories\Indexable_Repository;
|
||||
use Yoast\WP\SEO\Values\Indexables\Indexable_Builder_Versions;
|
||||
|
||||
/**
|
||||
* Reindexing action for term indexables.
|
||||
*/
|
||||
class Indexable_Term_Indexation_Action extends Abstract_Indexing_Action {
|
||||
|
||||
/**
|
||||
* The transient cache key.
|
||||
*/
|
||||
const UNINDEXED_COUNT_TRANSIENT = 'wpseo_total_unindexed_terms';
|
||||
|
||||
/**
|
||||
* The transient cache key for limited counts.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Taxonomy_Helper
|
||||
*/
|
||||
protected $taxonomy;
|
||||
|
||||
/**
|
||||
* The indexable repository.
|
||||
*
|
||||
* @var Indexable_Repository
|
||||
*/
|
||||
protected $repository;
|
||||
|
||||
/**
|
||||
* The WordPress database instance.
|
||||
*
|
||||
* @var wpdb
|
||||
*/
|
||||
protected $wpdb;
|
||||
|
||||
/**
|
||||
* The latest version of the Indexable term builder
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* Indexable_Term_Indexation_Action constructor
|
||||
*
|
||||
* @param Taxonomy_Helper $taxonomy The taxonomy helper.
|
||||
* @param Indexable_Repository $repository The indexable repository.
|
||||
* @param wpdb $wpdb The WordPress database instance.
|
||||
* @param Indexable_Builder_Versions $builder_versions The latest versions of all indexable builders.
|
||||
*/
|
||||
public function __construct(
|
||||
Taxonomy_Helper $taxonomy,
|
||||
Indexable_Repository $repository,
|
||||
wpdb $wpdb,
|
||||
Indexable_Builder_Versions $builder_versions
|
||||
) {
|
||||
$this->taxonomy = $taxonomy;
|
||||
$this->repository = $repository;
|
||||
$this->wpdb = $wpdb;
|
||||
$this->version = $builder_versions->get_latest_version_for_type( 'term' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates indexables for unindexed terms.
|
||||
*
|
||||
* @return Indexable[] The created indexables.
|
||||
*/
|
||||
public function index() {
|
||||
$query = $this->get_select_query( $this->get_limit() );
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
|
||||
$term_ids = $this->wpdb->get_col( $query );
|
||||
|
||||
$indexables = [];
|
||||
foreach ( $term_ids as $term_id ) {
|
||||
$indexables[] = $this->repository->find_by_id_and_type( (int) $term_id, 'term' );
|
||||
}
|
||||
|
||||
if ( \count( $indexables ) > 0 ) {
|
||||
\delete_transient( static::UNINDEXED_COUNT_TRANSIENT );
|
||||
\delete_transient( static::UNINDEXED_LIMITED_COUNT_TRANSIENT );
|
||||
}
|
||||
|
||||
return $indexables;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of terms that will be indexed in a single indexing pass.
|
||||
*
|
||||
* @return int The limit.
|
||||
*/
|
||||
public function get_limit() {
|
||||
/**
|
||||
* Filter 'wpseo_term_indexation_limit' - Allow filtering the number of terms indexed during each indexing pass.
|
||||
*
|
||||
* @api int The maximum number of terms indexed.
|
||||
*/
|
||||
$limit = \apply_filters( 'wpseo_term_indexation_limit', 25 );
|
||||
|
||||
if ( ! \is_int( $limit ) || $limit < 1 ) {
|
||||
$limit = 25;
|
||||
}
|
||||
|
||||
return $limit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for counting the number of unindexed terms.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_count_query() {
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
$taxonomy_table = $this->wpdb->term_taxonomy;
|
||||
$public_taxonomies = $this->taxonomy->get_indexable_taxonomies();
|
||||
|
||||
$taxonomies_placeholders = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );
|
||||
|
||||
$replacements = [ $this->version ];
|
||||
\array_push( $replacements, ...$public_taxonomies );
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
|
||||
return $this->wpdb->prepare(
|
||||
"
|
||||
SELECT COUNT(term_id)
|
||||
FROM {$taxonomy_table} AS T
|
||||
LEFT JOIN $indexable_table AS I
|
||||
ON T.term_id = I.object_id
|
||||
AND I.object_type = 'term'
|
||||
AND I.version = %d
|
||||
WHERE I.object_id IS NULL
|
||||
AND taxonomy IN ($taxonomies_placeholders)",
|
||||
$replacements
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for selecting the ID's of unindexed terms.
|
||||
*
|
||||
* @param bool $limit The maximum number of term IDs to return.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_select_query( $limit = false ) {
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
$taxonomy_table = $this->wpdb->term_taxonomy;
|
||||
$public_taxonomies = $this->taxonomy->get_indexable_taxonomies();
|
||||
$placeholders = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );
|
||||
|
||||
$replacements = [ $this->version ];
|
||||
\array_push( $replacements, ...$public_taxonomies );
|
||||
|
||||
$limit_query = '';
|
||||
if ( $limit ) {
|
||||
$limit_query = 'LIMIT %d';
|
||||
$replacements[] = $limit;
|
||||
}
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
|
||||
return $this->wpdb->prepare(
|
||||
"
|
||||
SELECT term_id
|
||||
FROM {$taxonomy_table} AS T
|
||||
LEFT JOIN $indexable_table AS I
|
||||
ON T.term_id = I.object_id
|
||||
AND I.object_type = 'term'
|
||||
AND I.version = %d
|
||||
WHERE I.object_id IS NULL
|
||||
AND taxonomy IN ($placeholders)
|
||||
$limit_query",
|
||||
$replacements
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
/**
|
||||
* Interface definition of reindexing action for indexables.
|
||||
*/
|
||||
interface Indexation_Action_Interface {
|
||||
|
||||
/**
|
||||
* Returns the total number of unindexed objects.
|
||||
*
|
||||
* @return int The total number of unindexed objects.
|
||||
*/
|
||||
public function get_total_unindexed();
|
||||
|
||||
/**
|
||||
* Indexes a number of objects.
|
||||
*
|
||||
* NOTE: ALWAYS use limits, this method is intended to be called multiple times over several requests.
|
||||
*
|
||||
* For indexing that requires JavaScript simply return the objects that should be indexed.
|
||||
*
|
||||
* @return array The reindexed objects.
|
||||
*/
|
||||
public function index();
|
||||
|
||||
/**
|
||||
* Returns the number of objects that will be indexed in a single indexing pass.
|
||||
*
|
||||
* @return int The limit.
|
||||
*/
|
||||
public function get_limit();
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use Yoast\WP\SEO\Helpers\Indexing_Helper;
|
||||
|
||||
/**
|
||||
* Indexing action to call when the indexing is completed.
|
||||
*/
|
||||
class Indexing_Complete_Action {
|
||||
|
||||
/**
|
||||
* The indexing helper.
|
||||
*
|
||||
* @var Indexing_Helper
|
||||
*/
|
||||
protected $indexing_helper;
|
||||
|
||||
/**
|
||||
* Indexing_Complete_Action constructor.
|
||||
*
|
||||
* @param Indexing_Helper $indexing_helper The indexing helper.
|
||||
*/
|
||||
public function __construct( Indexing_Helper $indexing_helper ) {
|
||||
$this->indexing_helper = $indexing_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps up the indexing process.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function complete() {
|
||||
$this->indexing_helper->complete();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use Yoast\WP\SEO\Helpers\Indexing_Helper;
|
||||
|
||||
/**
|
||||
* Class Indexing_Prepare_Action.
|
||||
*
|
||||
* Action for preparing the indexing routine.
|
||||
*/
|
||||
class Indexing_Prepare_Action {
|
||||
|
||||
/**
|
||||
* The indexing helper.
|
||||
*
|
||||
* @var Indexing_Helper
|
||||
*/
|
||||
protected $indexing_helper;
|
||||
|
||||
/**
|
||||
* Action for preparing the indexing routine.
|
||||
*
|
||||
* @param Indexing_Helper $indexing_helper The indexing helper.
|
||||
*/
|
||||
public function __construct(
|
||||
Indexing_Helper $indexing_helper
|
||||
) {
|
||||
$this->indexing_helper = $indexing_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the indexing routine.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function prepare() {
|
||||
$this->indexing_helper->prepare();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
/**
|
||||
* Interface definition of a reindexing action for indexables that have a limited unindexed count.
|
||||
*/
|
||||
interface Limited_Indexing_Action_Interface {
|
||||
|
||||
/**
|
||||
* Returns a limited number of unindexed posts.
|
||||
*
|
||||
* @param int $limit Limit the maximum number of unindexed posts that are counted.
|
||||
*
|
||||
* @return int|false The limited number of unindexed posts. False if the query fails.
|
||||
*/
|
||||
public function get_limited_unindexed_count( $limit );
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\Helpers\Post_Type_Helper;
|
||||
|
||||
/**
|
||||
* Reindexing action for post link indexables.
|
||||
*/
|
||||
class Post_Link_Indexing_Action extends Abstract_Link_Indexing_Action {
|
||||
|
||||
/**
|
||||
* The transient name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_COUNT_TRANSIENT = 'wpseo_unindexed_post_link_count';
|
||||
|
||||
/**
|
||||
* The transient cache key for limited counts.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Post_Type_Helper
|
||||
*/
|
||||
protected $post_type_helper;
|
||||
|
||||
/**
|
||||
* Sets the required helper.
|
||||
*
|
||||
* @required
|
||||
*
|
||||
* @param Post_Type_Helper $post_type_helper The post type helper.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_helper( Post_Type_Helper $post_type_helper ) {
|
||||
$this->post_type_helper = $post_type_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects to be indexed.
|
||||
*
|
||||
* @return array Objects to be indexed.
|
||||
*/
|
||||
protected function get_objects() {
|
||||
$query = $this->get_select_query( $this->get_limit() );
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
|
||||
$posts = $this->wpdb->get_results( $query );
|
||||
|
||||
return \array_map(
|
||||
static function ( $post ) {
|
||||
return (object) [
|
||||
'id' => (int) $post->ID,
|
||||
'type' => 'post',
|
||||
'content' => $post->post_content,
|
||||
];
|
||||
},
|
||||
$posts
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for counting the number of unindexed post links.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_count_query() {
|
||||
$public_post_types = $this->post_type_helper->get_indexable_post_types();
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
$links_table = Model::get_table_name( 'SEO_Links' );
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_select_query as well.
|
||||
return $this->wpdb->prepare(
|
||||
"SELECT COUNT(P.ID)
|
||||
FROM {$this->wpdb->posts} AS P
|
||||
LEFT JOIN $indexable_table AS I
|
||||
ON P.ID = I.object_id
|
||||
AND I.link_count IS NOT NULL
|
||||
AND I.object_type = 'post'
|
||||
LEFT JOIN $links_table AS L
|
||||
ON L.post_id = P.ID
|
||||
AND L.target_indexable_id IS NULL
|
||||
AND L.type = 'internal'
|
||||
AND L.target_post_id IS NOT NULL
|
||||
AND L.target_post_id != 0
|
||||
WHERE ( I.object_id IS NULL OR L.post_id IS NOT NULL )
|
||||
AND P.post_status = 'publish'
|
||||
AND P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $public_post_types ), '%s' ) ) . ')',
|
||||
$public_post_types
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for selecting the ID's of unindexed post links.
|
||||
*
|
||||
* @param int|false $limit The maximum number of post link IDs to return.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_select_query( $limit = false ) {
|
||||
$public_post_types = $this->post_type_helper->get_indexable_post_types();
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
$links_table = Model::get_table_name( 'SEO_Links' );
|
||||
$replacements = $public_post_types;
|
||||
|
||||
$limit_query = '';
|
||||
if ( $limit ) {
|
||||
$limit_query = 'LIMIT %d';
|
||||
$replacements[] = $limit;
|
||||
}
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
|
||||
return $this->wpdb->prepare(
|
||||
"
|
||||
SELECT P.ID, P.post_content
|
||||
FROM {$this->wpdb->posts} AS P
|
||||
LEFT JOIN $indexable_table AS I
|
||||
ON P.ID = I.object_id
|
||||
AND I.link_count IS NOT NULL
|
||||
AND I.object_type = 'post'
|
||||
LEFT JOIN $links_table AS L
|
||||
ON L.post_id = P.ID
|
||||
AND L.target_indexable_id IS NULL
|
||||
AND L.type = 'internal'
|
||||
AND L.target_post_id IS NOT NULL
|
||||
AND L.target_post_id != 0
|
||||
WHERE ( I.object_id IS NULL OR L.post_id IS NOT NULL )
|
||||
AND P.post_status = 'publish'
|
||||
AND P.post_type IN (" . \implode( ', ', \array_fill( 0, \count( $public_post_types ), '%s' ) ) . ")
|
||||
$limit_query",
|
||||
$replacements
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace Yoast\WP\SEO\Actions\Indexing;
|
||||
|
||||
use Yoast\WP\Lib\Model;
|
||||
use Yoast\WP\SEO\Helpers\Taxonomy_Helper;
|
||||
|
||||
/**
|
||||
* Reindexing action for term link indexables.
|
||||
*/
|
||||
class Term_Link_Indexing_Action extends Abstract_Link_Indexing_Action {
|
||||
|
||||
/**
|
||||
* The transient name.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_COUNT_TRANSIENT = 'wpseo_unindexed_term_link_count';
|
||||
|
||||
/**
|
||||
* The transient cache key for limited counts.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const UNINDEXED_LIMITED_COUNT_TRANSIENT = self::UNINDEXED_COUNT_TRANSIENT . '_limited';
|
||||
|
||||
/**
|
||||
* The post type helper.
|
||||
*
|
||||
* @var Taxonomy_Helper
|
||||
*/
|
||||
protected $taxonomy_helper;
|
||||
|
||||
/**
|
||||
* Sets the required helper.
|
||||
*
|
||||
* @required
|
||||
*
|
||||
* @param Taxonomy_Helper $taxonomy_helper The taxonomy helper.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function set_helper( Taxonomy_Helper $taxonomy_helper ) {
|
||||
$this->taxonomy_helper = $taxonomy_helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns objects to be indexed.
|
||||
*
|
||||
* @return array Objects to be indexed.
|
||||
*/
|
||||
protected function get_objects() {
|
||||
$query = $this->get_select_query( $this->get_limit() );
|
||||
|
||||
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Function get_select_query returns a prepared query.
|
||||
$terms = $this->wpdb->get_results( $query );
|
||||
|
||||
return \array_map(
|
||||
static function ( $term ) {
|
||||
return (object) [
|
||||
'id' => (int) $term->term_id,
|
||||
'type' => 'term',
|
||||
'content' => $term->description,
|
||||
];
|
||||
},
|
||||
$terms
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for counting the number of unindexed term links.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_count_query() {
|
||||
$public_taxonomies = $this->taxonomy_helper->get_indexable_taxonomies();
|
||||
$placeholders = \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) );
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_select_query as well.
|
||||
return $this->wpdb->prepare(
|
||||
"
|
||||
SELECT COUNT(T.term_id)
|
||||
FROM {$this->wpdb->term_taxonomy} AS T
|
||||
LEFT JOIN $indexable_table AS I
|
||||
ON T.term_id = I.object_id
|
||||
AND I.object_type = 'term'
|
||||
AND I.link_count IS NOT NULL
|
||||
WHERE I.object_id IS NULL
|
||||
AND T.taxonomy IN ($placeholders)",
|
||||
$public_taxonomies
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a query for selecting the ID's of unindexed term links.
|
||||
*
|
||||
* @param int|false $limit The maximum number of term link IDs to return.
|
||||
*
|
||||
* @return string The prepared query string.
|
||||
*/
|
||||
protected function get_select_query( $limit = false ) {
|
||||
$public_taxonomies = $this->taxonomy_helper->get_indexable_taxonomies();
|
||||
|
||||
$indexable_table = Model::get_table_name( 'Indexable' );
|
||||
$replacements = $public_taxonomies;
|
||||
|
||||
$limit_query = '';
|
||||
if ( $limit ) {
|
||||
$limit_query = 'LIMIT %d';
|
||||
$replacements[] = $limit;
|
||||
}
|
||||
|
||||
// Warning: If this query is changed, makes sure to update the query in get_count_query as well.
|
||||
return $this->wpdb->prepare(
|
||||
"
|
||||
SELECT T.term_id, T.description
|
||||
FROM {$this->wpdb->term_taxonomy} AS T
|
||||
LEFT JOIN $indexable_table AS I
|
||||
ON T.term_id = I.object_id
|
||||
AND I.object_type = 'term'
|
||||
AND I.link_count IS NOT NULL
|
||||
WHERE I.object_id IS NULL
|
||||
AND T.taxonomy IN (" . \implode( ', ', \array_fill( 0, \count( $public_taxonomies ), '%s' ) ) . ")
|
||||
$limit_query",
|
||||
$replacements
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user