PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / repositories / ContentKeywordsRepository.php

ContentKeywordsRepository.php in 404 Solution trunk, at includes/repositories/ContentKeywordsRepository.php

113 lines 3.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Reads and updates permalink-cache content keyword payloads.
9 */
10 class ABJ_404_Solution_ContentKeywordsRepository {
11
12 /**
13 * Hard upper bound on rows returned per batch. Caller-supplied limits
14 * above this are clamped; this is the safety backstop that keeps a
15 * misconfigured caller from issuing an effectively unbounded query
16 * over wp_posts on large sites.
17 */
18 const MAX_LIMIT = 5000;
19
20 /** @var ABJ_404_Solution_DatabaseCore */
21 private $dbCore;
22 /** @var ABJ_404_Solution_Functions */
23 private $functions;
24 /** @var ABJ_404_Solution_Logging */
25 private $logger;
26
27 /**
28 * @param ABJ_404_Solution_DatabaseCore $dbCore
29 * @param ABJ_404_Solution_Functions $functions
30 * @param ABJ_404_Solution_Logging $logging
31 */
32 public function __construct(
33 ABJ_404_Solution_DatabaseCore $dbCore,
34 $functions,
35 $logging
36 ) {
37 $this->dbCore = $dbCore;
38 $this->functions = $functions;
39 $this->logger = $logging;
40 }
41
42 /**
43 * @param int $limit
44 * @return array<int, object>
45 */
46 public function getPostsNeedingContentKeywords(int $limit = 500): array {
47 $clampedLimit = min(self::MAX_LIMIT, max(1, absint($limit)));
48
49 $query = ABJ_404_Solution_FileSystemService::readFileContents(__DIR__ . "/../sql/getPostsNeedingContentKeywords.sql");
50 $query = $this->functions->str_replace('{limit-results}', (string) $clampedLimit, $query);
51
52 $result = $this->dbCore->queryAndGetResults($query, array(
53 'result_type' => OBJECT,
54 'log_errors' => false,
55 ));
56
57 $lastError = isset($result['last_error']) && is_string($result['last_error']) ? $result['last_error'] : '';
58 if ($lastError !== '') {
59 if (stripos($lastError, 'unknown column') !== false) {
60 $this->logger->warn("content_keywords column not yet available (DB migration pending): " . $lastError);
61 } else if (!$this->dbCore->errorClassifier()->classifyAndHandleInfrastructureError($lastError)) {
62 $this->logger->errorMessage("Error fetching posts for content keywords: " . $lastError);
63 }
64 return array();
65 }
66
67 $rawRows = isset($result['rows']) && is_array($result['rows']) ? $result['rows'] : array();
68 $rows = array();
69 foreach ($rawRows as $row) {
70 if (is_object($row)) {
71 $rows[] = $row;
72 }
73 }
74 return $rows;
75 }
76
77 /** @param array<int, string> $idToKeywords @return void */
78 public function bulkUpdateContentKeywords(array $idToKeywords): void {
79 if (empty($idToKeywords)) {
80 return;
81 }
82
83 $table = $this->dbCore->doTableNameReplacements('{wp_abj404_permalink_cache}');
84
85 $whenClauses = array();
86 $params = array();
87 $ids = array();
88 foreach ($idToKeywords as $id => $keywords) {
89 $intId = (int) $id;
90 $whenClauses[] = 'WHEN %d THEN %s';
91 $params[] = $intId;
92 $params[] = $keywords;
93 $ids[] = $intId;
94 }
95
96 $idPlaceholders = implode(',', array_fill(0, count($ids), '%d'));
97
98 $sql = "UPDATE `{$table}` SET content_keywords = CASE id\n "
99 . implode("\n ", $whenClauses)
100 . "\n END\n WHERE id IN ({$idPlaceholders})";
101
102 $allParams = array_merge($params, $ids);
103
104 $result = $this->dbCore->queryAndGetResults($sql, array('query_params' => $allParams));
105
106 $lastErrorRaw = $result['last_error'] ?? '';
107 $lastError = is_string($lastErrorRaw) ? $lastErrorRaw : '';
108 if ($lastError !== '' && stripos($lastError, 'unknown column') !== false) {
109 $this->logger->warn("content_keywords column not yet available (DB migration pending): " . $lastError);
110 }
111 }
112 }
113