‪TYPO3CMS  11.5
BrokenLinkRepository.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
20 use Doctrine\DBAL\Exception\TableNotFoundException;
26 
31 {
32  protected const ‪TABLE = 'tx_linkvalidator_link';
33 
41  public function ‪isLinkTargetBrokenLink(string $linkTarget, string $linkType = ''): bool
42  {
43  try {
44  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
45  ->getQueryBuilderForTable(static::TABLE);
46  $constraints = [
47  $queryBuilder->expr()->eq('url', $queryBuilder->createNamedParameter($linkTarget)),
48  ];
49  if ($linkType !== '') {
50  $constraints[] = $queryBuilder->expr()->eq('link_type', $queryBuilder->createNamedParameter($linkType));
51  }
52 
53  $queryBuilder
54  ->count('uid')
55  ->from(static::TABLE)
56  ->where(...$constraints);
57 
58  return (bool)$queryBuilder
59  ->executeQuery()
60  ->fetchOne();
61  } catch (TableNotFoundException $e) {
62  return false;
63  }
64  }
65 
74  public function ‪getNumberOfBrokenLinksForRecordsOnPages(array $pageIds, array $searchFields): array
75  {
76  $result = [
77  'total' => 0,
78  ];
79 
80  // We need to do the work in chunks, as it may be quite huge and would hit the one
81  // or other limit depending on the used dbms - and we also avoid placeholder usage
82  // as they are hard to calculate beforehand because of some magic handling of dbal.
84  GeneralUtility::makeInstance(ConnectionPool::class)
85  ->getConnectionForTable(static::TABLE)
86  ->getDatabasePlatform()
87  );
88  foreach (array_chunk($pageIds, (int)floor($maxChunk / 3)) as $pageIdsChunk) {
89  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
90  ->getQueryBuilderForTable(static::TABLE);
91  $queryBuilder->getRestrictions()->removeAll();
92  if (!‪$GLOBALS['BE_USER']->isAdmin()) {
93  $queryBuilder->getRestrictions()
94  ->add(GeneralUtility::makeInstance(EditableRestriction::class, $searchFields, $queryBuilder));
95  }
96  $statement = $queryBuilder->select('link_type')
97  ->addSelectLiteral($queryBuilder->expr()->count(static::TABLE . '.uid', 'amount'))
98  ->from(static::TABLE)
99  ->join(
100  static::TABLE,
101  'pages',
102  'pages',
103  $queryBuilder->expr()->eq('record_pid', $queryBuilder->quoteIdentifier('pages.uid'))
104  )
105  ->where(
106  $queryBuilder->expr()->orX(
107  $queryBuilder->expr()->andX(
108  $queryBuilder->expr()->in(
109  'record_uid',
110  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdsChunk)
111  ),
112  $queryBuilder->expr()->eq('table_name', $queryBuilder->quote('pages'))
113  ),
114  $queryBuilder->expr()->andX(
115  $queryBuilder->expr()->in(
116  'record_pid',
117  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdsChunk)
118  ),
119  $queryBuilder->expr()->neq('table_name', $queryBuilder->quote('pages'))
120  )
121  )
122  )
123  ->groupBy('link_type')
124  ->executeQuery();
125 
126  while ($row = $statement->fetchAssociative()) {
127  if (!isset($result[$row['link_type']])) {
128  $result[$row['link_type']] = 0;
129  }
130  $result[$row['link_type']] += $row['amount'];
131  $result['total'] += $row['amount'];
132  }
133  }
134  return $result;
135  }
136 
137  public function ‪setNeedsRecheckForRecord(int $recordUid, string $tableName): void
138  {
139  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
140  ->getQueryBuilderForTable(static::TABLE);
141 
142  $queryBuilder->update(static::TABLE)
143  ->where(
144  $queryBuilder->expr()->eq(
145  'record_uid',
146  $queryBuilder->createNamedParameter($recordUid, ‪Connection::PARAM_INT)
147  ),
148  $queryBuilder->expr()->eq(
149  'table_name',
150  $queryBuilder->createNamedParameter($tableName)
151  )
152  )
153  ->set('needs_recheck', 1)
154  ->executeStatement();
155  }
156 
157  public function ‪removeBrokenLinksForRecord(string $tableName, int $recordUid): void
158  {
159  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
160  ->getQueryBuilderForTable(static::TABLE);
161 
162  $queryBuilder->delete(static::TABLE)
163  ->where(
164  $queryBuilder->expr()->eq(
165  'record_uid',
166  $queryBuilder->createNamedParameter($recordUid, ‪Connection::PARAM_INT)
167  ),
168  $queryBuilder->expr()->eq(
169  'table_name',
170  $queryBuilder->createNamedParameter($tableName)
171  )
172  )
173  ->executeStatement();
174  }
175 
180  public function ‪removeAllBrokenLinksOfRecordsOnPageIds(array $pageIds, array $linkTypes): void
181  {
182  // We need to do the work in chunks, as it may be quite huge and would hit the one
183  // or other limit depending on the used dbms - and we also avoid placeholder usage
184  // as they are hard to calculate beforehand because of some magic handling of dbal.
186  GeneralUtility::makeInstance(ConnectionPool::class)
187  ->getConnectionForTable(static::TABLE)
188  ->getDatabasePlatform()
189  );
190  foreach (array_chunk($pageIds, (int)floor($maxChunk / 3)) as $pageIdsChunk) {
191  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
192  ->getQueryBuilderForTable(static::TABLE);
193 
194  $queryBuilder->delete(static::TABLE)
195  ->where(
196  $queryBuilder->expr()->orX(
197  $queryBuilder->expr()->andX(
198  $queryBuilder->expr()->in(
199  'record_uid',
200  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdsChunk)
201  ),
202  $queryBuilder->expr()->eq('table_name', $queryBuilder->quote('pages'))
203  ),
204  $queryBuilder->expr()->andX(
205  $queryBuilder->expr()->in(
206  'record_pid',
207  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdsChunk)
208  ),
209  $queryBuilder->expr()->neq(
210  'table_name',
211  $queryBuilder->quote('pages')
212  )
213  )
214  ),
215  $queryBuilder->expr()->in(
216  'link_type',
217  $queryBuilder->quoteArrayBasedValueListToStringList($linkTypes)
218  )
219  )
220  ->executeStatement();
221  }
222  }
223 
237  array $pageIds,
238  array $linkTypes,
239  array $searchFields = [],
240  array $languages = []
241  ): array {
242  $results = [];
243 
244  // We need to do the work in chunks, as it may be quite huge and would hit the one
245  // or other limit depending on the used dbms - and we also avoid placeholder usage
246  // as they are hard to calculate beforehand because of some magic handling of dbal.
248  GeneralUtility::makeInstance(ConnectionPool::class)
249  ->getConnectionForTable(static::TABLE)
250  ->getDatabasePlatform()
251  );
252  foreach (array_chunk($pageIds, (int)floor($maxChunk / 2)) as $pageIdsChunk) {
253  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
254  ->getQueryBuilderForTable(self::TABLE);
255  if (!‪$GLOBALS['BE_USER']->isAdmin()) {
256  $queryBuilder->getRestrictions()
257  ->add(GeneralUtility::makeInstance(EditableRestriction::class, $searchFields, $queryBuilder));
258  }
259 
260  $constraints = [
261  $queryBuilder->expr()->orX(
262  $queryBuilder->expr()->andX(
263  $queryBuilder->expr()->in(
264  'record_uid',
265  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdsChunk)
266  ),
267  $queryBuilder->expr()->eq('table_name', $queryBuilder->quote('pages'))
268  ),
269  $queryBuilder->expr()->andX(
270  $queryBuilder->expr()->in(
271  'record_pid',
272  $queryBuilder->quoteArrayBasedValueListToIntegerList($pageIdsChunk)
273  ),
274  $queryBuilder->expr()->neq('table_name', $queryBuilder->quote('pages'))
275  )
276  ),
277  $queryBuilder->expr()->in(
278  'link_type',
279  $queryBuilder->quoteArrayBasedValueListToStringList($linkTypes)
280  ),
281  ];
282 
283  if ($languages !== []) {
284  $constraints[] = $queryBuilder->expr()->in(
285  'language',
286  $queryBuilder->quoteArrayBasedValueListToIntegerList($languages)
287  );
288  }
289 
290  $records = $queryBuilder
291  ->select(self::TABLE . '.*')
292  ->from(self::TABLE)
293  ->join(
294  'tx_linkvalidator_link',
295  'pages',
296  'pages',
297  $queryBuilder->expr()->eq(
298  'tx_linkvalidator_link.record_pid',
299  $queryBuilder->quoteIdentifier('pages.uid')
300  )
301  )
302  ->where(...$constraints)
303  ->orderBy('tx_linkvalidator_link.record_uid')
304  ->addOrderBy('tx_linkvalidator_link.uid')
305  ->executeQuery()
306  ->fetchAllAssociative();
307  foreach ($records as &$record) {
308  $response = json_decode($record['url_response'], true);
309  // Fallback mechanism to still support the old serialized data, could be removed in TYPO3 v12 or later
310  if ($response === null) {
311  $response = unserialize($record['url_response'], ['allowed_classes' => false]);
312  }
313  $record['url_response'] = $response;
314  $results[] = $record;
315  }
316  }
317  return $results;
318  }
319 
330  public function ‪addBrokenLink($record, bool $isValid, array $errorParams = null): void
331  {
332  $response = ['valid' => $isValid];
333  $response['errorParams'] = $errorParams ?? [];
334  $record['url_response'] = json_encode($response);
335  GeneralUtility::makeInstance(ConnectionPool::class)
336  ->getConnectionForTable(self::TABLE)
337  ->insert(self::TABLE, $record);
338  }
339 }
‪TYPO3\CMS\Core\Database\Connection\PARAM_INT
‪const PARAM_INT
Definition: Connection.php:49
‪TYPO3\CMS\Core\Database\Platform\PlatformInformation\getMaxBindParameters
‪static int getMaxBindParameters(AbstractPlatform $platform)
Definition: PlatformInformation.php:125
‪TYPO3\CMS\Linkvalidator\QueryRestrictions\EditableRestriction
Definition: EditableRestriction.php:30
‪TYPO3\CMS\Core\Database\Connection
Definition: Connection.php:38
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Database\Platform\PlatformInformation
Definition: PlatformInformation.php:33
‪TYPO3\CMS\Core\Database\ConnectionPool
Definition: ConnectionPool.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:50
‪TYPO3\CMS\Linkvalidator\Repository
Definition: BrokenLinkRepository.php:18