1<?php 2 3declare(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 18namespace TYPO3\CMS\Redirects\Repository; 19 20use TYPO3\CMS\Core\Database\Connection; 21use TYPO3\CMS\Core\Database\ConnectionPool; 22use TYPO3\CMS\Core\Database\Query\QueryBuilder; 23use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction; 24use TYPO3\CMS\Core\Utility\GeneralUtility; 25 26/** 27 * Class for accessing redirect records from the database 28 * @internal 29 */ 30class RedirectRepository 31{ 32 /** 33 * Used within the backend module, which also includes the hidden records, but never deleted records. 34 */ 35 public function findRedirectsByDemand(Demand $demand): array 36 { 37 return $this->getQueryBuilderForDemand($demand) 38 ->setMaxResults($demand->getLimit()) 39 ->setFirstResult($demand->getOffset()) 40 ->executeQuery() 41 ->fetchAllAssociative(); 42 } 43 44 public function countRedirectsByByDemand(Demand $demand): int 45 { 46 return $this->getQueryBuilderForDemand($demand)->executeQuery()->rowCount(); 47 } 48 49 /** 50 * Prepares the QueryBuilder with Constraints from the Demand 51 */ 52 protected function getQueryBuilderForDemand(Demand $demand): QueryBuilder 53 { 54 $queryBuilder = $this->getQueryBuilder(); 55 $queryBuilder 56 ->select('*') 57 ->from('sys_redirect'); 58 59 $queryBuilder->orderBy( 60 $demand->getOrderField(), 61 $demand->getOrderDirection() 62 ); 63 64 if ($demand->hasSecondaryOrdering()) { 65 $queryBuilder->addOrderBy($demand->getSecondaryOrderField()); 66 } 67 68 $constraints = []; 69 if ($demand->hasSourceHosts()) { 70 $constraints[] =$queryBuilder->expr()->in( 71 'source_host', 72 $queryBuilder->createNamedParameter($demand->getSourceHosts(), Connection::PARAM_STR_ARRAY) 73 ); 74 } 75 76 if ($demand->hasSourcePath()) { 77 $escapedLikeString = '%' . $queryBuilder->escapeLikeWildcards($demand->getSourcePath()) . '%'; 78 $constraints[] = $queryBuilder->expr()->like( 79 'source_path', 80 $queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR) 81 ); 82 } 83 84 if ($demand->hasTarget()) { 85 $escapedLikeString = '%' . $queryBuilder->escapeLikeWildcards($demand->getTarget()) . '%'; 86 $constraints[] = $queryBuilder->expr()->like( 87 'target', 88 $queryBuilder->createNamedParameter($escapedLikeString, \PDO::PARAM_STR) 89 ); 90 } 91 92 if ($demand->hasStatusCodes()) { 93 $constraints[] = $queryBuilder->expr()->in( 94 'target_statuscode', 95 $queryBuilder->createNamedParameter($demand->getStatusCodes(), Connection::PARAM_INT_ARRAY) 96 ); 97 } 98 99 if ($demand->hasMaxHits()) { 100 $constraints[] = $queryBuilder->expr()->lt( 101 'hitcount', 102 $queryBuilder->createNamedParameter($demand->getMaxHits(), \PDO::PARAM_INT) 103 ); 104 // When max hits is set, exclude records which explicitly disabled the hitcount feature 105 $constraints[] = $queryBuilder->expr()->eq( 106 'disable_hitcount', 107 $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT) 108 ); 109 } 110 111 if (!empty($constraints)) { 112 $queryBuilder->where(...$constraints); 113 } 114 return $queryBuilder; 115 } 116 117 /** 118 * Used for the filtering in the backend 119 */ 120 public function findHostsOfRedirects(): array 121 { 122 return $this->getQueryBuilder() 123 ->select('source_host as name') 124 ->from('sys_redirect') 125 ->orderBy('source_host') 126 ->groupBy('source_host') 127 ->executeQuery() 128 ->fetchAllAssociative(); 129 } 130 131 /** 132 * Used for the filtering in the backend 133 */ 134 public function findStatusCodesOfRedirects(): array 135 { 136 return $this->getQueryBuilder() 137 ->select('target_statuscode as code') 138 ->from('sys_redirect') 139 ->orderBy('target_statuscode') 140 ->groupBy('target_statuscode') 141 ->executeQuery() 142 ->fetchAllAssociative(); 143 } 144 145 protected function getQueryBuilder(): QueryBuilder 146 { 147 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_redirect'); 148 $queryBuilder->getRestrictions() 149 ->removeAll() 150 ->add(GeneralUtility::makeInstance(DeletedRestriction::class)); 151 return $queryBuilder; 152 } 153 154 public function removeByDemand(Demand $demand): void 155 { 156 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class) 157 ->getQueryBuilderForTable('sys_redirect'); 158 $queryBuilder 159 ->delete('sys_redirect') 160 ->where( 161 $queryBuilder->expr()->eq('protected', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)) 162 ); 163 164 if ($demand->hasMaxHits()) { 165 $queryBuilder->andWhere( 166 $queryBuilder->expr()->lt('hitcount', $queryBuilder->createNamedParameter($demand->getMaxHits(), \PDO::PARAM_INT)) 167 ); 168 } 169 if ($demand->hasSourceHosts()) { 170 $queryBuilder 171 ->andWhere('source_host IN (:domains)') 172 ->setParameter('domains', $demand->getSourceHosts(), Connection::PARAM_STR_ARRAY); 173 } 174 if ($demand->hasStatusCodes()) { 175 $queryBuilder 176 ->andWhere('target_statuscode IN (:statusCodes)') 177 ->setParameter('statusCodes', $demand->getStatusCodes(), Connection::PARAM_INT_ARRAY); 178 } 179 if ($demand->hasOlderThan()) { 180 $timeStamp = $demand->getOlderThan()->getTimestamp(); 181 $queryBuilder->andWhere( 182 $queryBuilder->expr()->lt('createdon', $queryBuilder->createNamedParameter($timeStamp, \PDO::PARAM_INT)) 183 ); 184 } 185 if ($demand->hasSourcePath()) { 186 $queryBuilder 187 ->andWhere($queryBuilder->expr()->like('source_path', ':path')) 188 ->setParameter('path', $demand->getSourcePath(), \PDO::PARAM_STR); 189 } 190 191 $queryBuilder->executeStatement(); 192 } 193} 194