/htdocs/wp-content/plugins/wordpress-seo/src/repositories/indexable-repository.php

https://gitlab.com/VTTE/sitios-vtte · PHP · 432 lines · 203 code · 52 blank · 177 comment · 27 complexity · 49411bcb4f8edf22c99459b57433832e MD5 · raw file

  1. <?php
  2. /**
  3. * Yoast extension of the Model class.
  4. *
  5. * @package Yoast\WP\SEO\Repositories
  6. */
  7. namespace Yoast\WP\SEO\Repositories;
  8. use Psr\Log\LoggerInterface;
  9. use Yoast\WP\Lib\ORM;
  10. use Yoast\WP\SEO\Builders\Indexable_Builder;
  11. use Yoast\WP\SEO\Helpers\Current_Page_Helper;
  12. use Yoast\WP\SEO\Loggers\Logger;
  13. use Yoast\WP\SEO\Models\Indexable;
  14. use Yoast\WP\Lib\Model;
  15. /**
  16. * Class Indexable_Repository
  17. */
  18. class Indexable_Repository {
  19. /**
  20. * The indexable builder.
  21. *
  22. * @var Indexable_Builder
  23. */
  24. private $builder;
  25. /**
  26. * Represents the hierarchy repository.
  27. *
  28. * @var Indexable_Hierarchy_Repository
  29. */
  30. protected $hierarchy_repository;
  31. /**
  32. * The current page helper.
  33. *
  34. * @var Current_Page_Helper
  35. */
  36. protected $current_page;
  37. /**
  38. * The logger object.
  39. *
  40. * @var LoggerInterface
  41. */
  42. protected $logger;
  43. /**
  44. * Returns the instance of this class constructed through the ORM Wrapper.
  45. *
  46. * @param Indexable_Builder $builder The indexable builder.
  47. * @param Current_Page_Helper $current_page The current post helper.
  48. * @param Logger $logger The logger.
  49. * @param Indexable_Hierarchy_Repository $hierarchy_repository The hierarchy repository.
  50. */
  51. public function __construct(
  52. Indexable_Builder $builder,
  53. Current_Page_Helper $current_page,
  54. Logger $logger,
  55. Indexable_Hierarchy_Repository $hierarchy_repository
  56. ) {
  57. $this->builder = $builder;
  58. $this->current_page = $current_page;
  59. $this->logger = $logger;
  60. $this->hierarchy_repository = $hierarchy_repository;
  61. }
  62. /**
  63. * Starts a query for this repository.
  64. *
  65. * @return ORM
  66. */
  67. public function query() {
  68. return Model::of_type( 'Indexable' );
  69. }
  70. /**
  71. * Attempts to find the indexable for the current WordPress page. Returns false if no indexable could be found.
  72. * This may be the result of the indexable not existing or of being unable to determine what type of page the
  73. * current page is.
  74. *
  75. * @return bool|Indexable The indexable, false if none could be found.
  76. */
  77. public function for_current_page() {
  78. $indexable = false;
  79. switch ( true ) {
  80. case $this->current_page->is_simple_page():
  81. $indexable = $this->find_by_id_and_type( $this->current_page->get_simple_page_id(), 'post' );
  82. break;
  83. case $this->current_page->is_home_static_page():
  84. $indexable = $this->find_by_id_and_type( $this->current_page->get_front_page_id(), 'post' );
  85. break;
  86. case $this->current_page->is_home_posts_page():
  87. $indexable = $this->find_for_home_page();
  88. break;
  89. case $this->current_page->is_term_archive():
  90. $indexable = $this->find_by_id_and_type( $this->current_page->get_term_id(), 'term' );
  91. break;
  92. case $this->current_page->is_date_archive():
  93. $indexable = $this->find_for_date_archive();
  94. break;
  95. case $this->current_page->is_search_result():
  96. $indexable = $this->find_for_system_page( 'search-result' );
  97. break;
  98. case $this->current_page->is_post_type_archive():
  99. $indexable = $this->find_for_post_type_archive( $this->current_page->get_queried_post_type() );
  100. break;
  101. case $this->current_page->is_author_archive():
  102. $indexable = $this->find_by_id_and_type( $this->current_page->get_author_id(), 'user' );
  103. break;
  104. case $this->current_page->is_404():
  105. $indexable = $this->find_for_system_page( '404' );
  106. break;
  107. }
  108. if ( $indexable === false ) {
  109. return $this->query()->create(
  110. [
  111. 'object_type' => 'unknown',
  112. 'post_status' => 'unindexed',
  113. ]
  114. );
  115. }
  116. return $indexable;
  117. }
  118. /**
  119. * Retrieves an indexable by its permalink.
  120. *
  121. * @param string $permalink The indexable permalink.
  122. *
  123. * @return bool|Indexable The indexable, false if none could be found.
  124. */
  125. public function find_by_permalink( $permalink ) {
  126. $permalink_hash = \strlen( $permalink ) . ':' . \md5( $permalink );
  127. // Find by both permalink_hash and permalink, permalink_hash is indexed so will be used first by the DB to optimize the query.
  128. return $this->query()
  129. ->where( 'permalink_hash', $permalink_hash )
  130. ->where( 'permalink', $permalink )
  131. ->find_one();
  132. }
  133. /**
  134. * Retrieves all the indexable instances of a certain object type.
  135. *
  136. * @param string $object_type The object type.
  137. *
  138. * @return Indexable[] The array with all the indexable instances of a certain object type.
  139. */
  140. public function find_all_with_type( $object_type ) {
  141. /**
  142. * The array with all the indexable instances of a certain object type.
  143. *
  144. * @var Indexable[] $indexables
  145. */
  146. $indexables = $this
  147. ->query()
  148. ->where( 'object_type', $object_type )
  149. ->find_many();
  150. return \array_map( [ $this, 'ensure_permalink' ], $indexables );
  151. }
  152. /**
  153. * Retrieves all the indexable instances of a certain object subtype.
  154. *
  155. * @param string $object_type The object type.
  156. * @param string $object_sub_type The object subtype.
  157. *
  158. * @return Indexable[] The array with all the indexable instances of a certain object subtype.
  159. */
  160. public function find_all_with_type_and_sub_type( $object_type, $object_sub_type ) {
  161. /**
  162. * The array with all the indexable instances of a certain object type and subtype.
  163. *
  164. * @var Indexable[] $indexables
  165. */
  166. $indexables = $this
  167. ->query()
  168. ->where( 'object_type', $object_type )
  169. ->where( 'object_sub_type', $object_sub_type )
  170. ->find_many();
  171. return \array_map( [ $this, 'ensure_permalink' ], $indexables );
  172. }
  173. /**
  174. * Retrieves the homepage indexable.
  175. *
  176. * @param bool $auto_create Optional. Create the indexable if it does not exist.
  177. *
  178. * @return bool|Indexable Instance of indexable.
  179. */
  180. public function find_for_home_page( $auto_create = true ) {
  181. /**
  182. * Indexable instance.
  183. *
  184. * @var Indexable $indexable
  185. */
  186. $indexable = $this->query()->where( 'object_type', 'home-page' )->find_one();
  187. if ( $auto_create && ! $indexable ) {
  188. $indexable = $this->builder->build_for_home_page();
  189. }
  190. return $this->ensure_permalink( $indexable );
  191. }
  192. /**
  193. * Retrieves the date archive indexable.
  194. *
  195. * @param bool $auto_create Optional. Create the indexable if it does not exist.
  196. *
  197. * @return bool|Indexable Instance of indexable.
  198. */
  199. public function find_for_date_archive( $auto_create = true ) {
  200. /**
  201. * Indexable instance.
  202. *
  203. * @var Indexable $indexable
  204. */
  205. $indexable = $this->query()->where( 'object_type', 'date-archive' )->find_one();
  206. if ( $auto_create && ! $indexable ) {
  207. $indexable = $this->builder->build_for_date_archive();
  208. }
  209. return $this->ensure_permalink( $indexable );
  210. }
  211. /**
  212. * Retrieves an indexable for a post type archive.
  213. *
  214. * @param string $post_type The post type.
  215. * @param bool $auto_create Optional. Create the indexable if it does not exist.
  216. *
  217. * @return bool|Indexable The indexable, false if none could be found.
  218. */
  219. public function find_for_post_type_archive( $post_type, $auto_create = true ) {
  220. /**
  221. * Indexable instance.
  222. *
  223. * @var Indexable $indexable
  224. */
  225. $indexable = $this->query()
  226. ->where( 'object_type', 'post-type-archive' )
  227. ->where( 'object_sub_type', $post_type )
  228. ->find_one();
  229. if ( $auto_create && ! $indexable ) {
  230. $indexable = $this->builder->build_for_post_type_archive( $post_type );
  231. }
  232. return $this->ensure_permalink( $indexable );
  233. }
  234. /**
  235. * Retrieves the indexable for a system page.
  236. *
  237. * @param string $object_sub_type The type of system page.
  238. * @param bool $auto_create Optional. Create the indexable if it does not exist.
  239. *
  240. * @return bool|Indexable Instance of indexable.
  241. */
  242. public function find_for_system_page( $object_sub_type, $auto_create = true ) {
  243. /**
  244. * Indexable instance.
  245. *
  246. * @var Indexable $indexable
  247. */
  248. $indexable = $this->query()
  249. ->where( 'object_type', 'system-page' )
  250. ->where( 'object_sub_type', $object_sub_type )
  251. ->find_one();
  252. if ( $auto_create && ! $indexable ) {
  253. $indexable = $this->builder->build_for_system_page( $object_sub_type );
  254. }
  255. return $this->ensure_permalink( $indexable );
  256. }
  257. /**
  258. * Retrieves an indexable by its ID and type.
  259. *
  260. * @param int $object_id The indexable object ID.
  261. * @param string $object_type The indexable object type.
  262. * @param bool $auto_create Optional. Create the indexable if it does not exist.
  263. *
  264. * @return bool|Indexable Instance of indexable.
  265. */
  266. public function find_by_id_and_type( $object_id, $object_type, $auto_create = true ) {
  267. $indexable = $this->query()
  268. ->where( 'object_id', $object_id )
  269. ->where( 'object_type', $object_type )
  270. ->find_one();
  271. if ( $auto_create && ! $indexable ) {
  272. $indexable = $this->builder->build_for_id_and_type( $object_id, $object_type );
  273. }
  274. return $this->ensure_permalink( $indexable );
  275. }
  276. /**
  277. * Retrieves multiple indexables at once by their IDs and type.
  278. *
  279. * @param int[] $object_ids The array of indexable object IDs.
  280. * @param string $object_type The indexable object type.
  281. * @param bool $auto_create Optional. Create the indexable if it does not exist.
  282. *
  283. * @return Indexable[] An array of indexables.
  284. */
  285. public function find_by_multiple_ids_and_type( $object_ids, $object_type, $auto_create = true ) {
  286. /**
  287. * Represents an array of indexable objects.
  288. *
  289. * @var Indexable[] $indexables
  290. */
  291. $indexables = $this->query()
  292. ->where_in( 'object_id', $object_ids )
  293. ->where( 'object_type', $object_type )
  294. ->find_many();
  295. if ( $auto_create ) {
  296. $indexables_available = [];
  297. foreach ( $indexables as $indexable ) {
  298. $indexables_available[] = $indexable->object_id;
  299. }
  300. $indexables_to_create = \array_diff( $object_ids, $indexables_available );
  301. foreach ( $indexables_to_create as $indexable_to_create ) {
  302. $indexables[] = $this->builder->build_for_id_and_type( $indexable_to_create, $object_type );
  303. }
  304. }
  305. return \array_map( [ $this, 'ensure_permalink' ], $indexables );
  306. }
  307. /**
  308. * Returns all ancestors of a given indexable.
  309. *
  310. * @param Indexable $indexable The indexable to find the ancestors of.
  311. *
  312. * @return Indexable[] All ancestors of the given indexable.
  313. */
  314. public function get_ancestors( Indexable $indexable ) {
  315. // If we've already set ancestors on the indexable no need to get them again.
  316. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) {
  317. return \array_map( [ $this, 'ensure_permalink' ], $indexable->ancestors );
  318. }
  319. $indexable_ids = $this->hierarchy_repository->find_ancestors( $indexable );
  320. // If we've set ancestors on the indexable because we had to build them to find them.
  321. if ( \is_array( $indexable->ancestors ) && ! empty( $indexable->ancestors ) ) {
  322. return \array_map( [ $this, 'ensure_permalink' ], $indexable->ancestors );
  323. }
  324. if ( empty( $indexable_ids ) ) {
  325. return [];
  326. }
  327. if ( $indexable_ids[0] === 0 && \count( $indexable_ids ) === 1 ) {
  328. return [];
  329. }
  330. $indexables = $this->query()
  331. ->where_in( 'id', $indexable_ids )
  332. ->order_by_expr( 'FIELD(id,' . \implode( ',', $indexable_ids ) . ')' )
  333. ->find_many();
  334. return \array_map( [ $this, 'ensure_permalink' ], $indexables );
  335. }
  336. /**
  337. * Ensures that the given indexable has a permalink.
  338. *
  339. * @param Indexable $indexable The indexable.
  340. *
  341. * @return bool|Indexable The indexable.
  342. */
  343. protected function ensure_permalink( $indexable ) {
  344. if ( $indexable && $indexable->permalink === null ) {
  345. $indexable->permalink = $this->get_permalink_for_indexable( $indexable );
  346. $indexable->save();
  347. }
  348. return $indexable;
  349. }
  350. /**
  351. * Retrieves the permalink for an indexable.
  352. *
  353. * @param Indexable $indexable The indexable.
  354. *
  355. * @return string|null The permalink.
  356. */
  357. protected function get_permalink_for_indexable( $indexable ) {
  358. switch ( true ) {
  359. case $indexable->object_type === 'post':
  360. case $indexable->object_type === 'home-page':
  361. if ( $indexable->object_sub_type === 'attachment' ) {
  362. return \wp_get_attachment_url( $indexable->object_id );
  363. }
  364. return \get_permalink( $indexable->object_id );
  365. case $indexable->object_type === 'term':
  366. $term = \get_term( $indexable->object_id );
  367. if ( $term === null || \is_wp_error( $term ) ) {
  368. return null;
  369. }
  370. return \get_term_link( $term, $term->taxonomy );
  371. case $indexable->object_type === 'system-page' && $indexable->object_sub_type === 'search-page':
  372. return \get_search_link();
  373. case $indexable->object_type === 'post-type-archive':
  374. return \get_post_type_archive_link( $indexable->object_sub_type );
  375. case $indexable->object_type === 'user':
  376. return \get_author_posts_url( $indexable->object_id );
  377. }
  378. return null;
  379. }
  380. }