diff --git a/Classes/Domain/Repository/NewsRepository.php b/Classes/Domain/Repository/NewsRepository.php index 25da9fb7a147ccf182ba84dfc550157f596ac1be..e09d74ed202021388ab892896c9df4e578126b33 100644 --- a/Classes/Domain/Repository/NewsRepository.php +++ b/Classes/Domain/Repository/NewsRepository.php @@ -28,6 +28,9 @@ namespace SGalinski\SgNews\Domain\Repository; use SGalinski\SgNews\Domain\Model\Author; use SGalinski\SgNews\Domain\Model\News; +use TYPO3\CMS\Core\Database\Connection; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Persistence\Generic\QueryResult; use TYPO3\CMS\Extbase\Persistence\QueryInterface; use TYPO3\CMS\Extbase\Persistence\QueryResultInterface; @@ -514,23 +517,71 @@ class NewsRepository extends AbstractRepository { * @return QueryResultInterface */ public function findRelated(News $news, int $limit = 0) { + $connection = $this->getConnection(); + $qb = $connection->createQueryBuilder(); + $constraints = []; + $tags = $news->getTags(); + if ($tags->count() > 0) { + $tagConstraints = []; + foreach ($tags as $tag) { + $qb->leftJoin('pages', 'sys_category_records_mm', 'tags', 'pages.uid=tags.uid_foreign'); + $tagConstraints[] = $qb->expr()->eq('tags.uid_local', $tag->getUid()); + } + $constraints[] = $qb->expr()->eq('tags.tablenames', $qb->createNamedParameter('pages')); + $constraints[] = $qb->expr()->eq('tags.fieldname', $qb->createNamedParameter('tx_sgnews_tags')); + $constraints[] = $qb->expr()->orX($tagConstraints); + } else { + $constraints[] = $qb->expr()->eq('pid', $news->getPid()); + } + + $result = $qb->select('lastUpdated') + ->from('pages') + ->where( + $qb->expr()->eq('doktype', $qb->createNamedParameter(News::DOK_TYPE_NEWS, Connection::PARAM_INT)), + $qb->expr()->gt('lastUpdated', $news->getLastUpdated()->getTimestamp()), + $qb->expr()->andX(...$constraints) + ) + ->setMaxResults($limit) + ->orderBy('lastUpdated', 'asc') + ->execute(); + $max = $result->fetchOne(); + + $result = $qb->select('lastUpdated') + ->from('pages') + ->where( + $qb->expr()->eq('doktype', $qb->createNamedParameter(News::DOK_TYPE_NEWS, Connection::PARAM_INT)), + $qb->expr()->lt('lastUpdated', $news->getLastUpdated()->getTimestamp()), + $qb->expr()->andX(...$constraints) + ) + ->setMaxResults($limit) + ->orderBy('lastUpdated', 'desc') + ->execute(); + $min = $result->fetchOne(); + $query = $this->createQuery(); $query->getQuerySettings()->setRespectStoragePage(FALSE); $query->setOrderings([ 'crdate' => QueryInterface::ORDER_DESCENDING ]); - $contains = []; + $constraints = []; + if ($max) { + $constraints[] = $query->lessThan('lastUpdated', $max); + } + if ($min) { + $constraints[] = $query->greaterThan('lastUpdated', $min); + } + $tags = $news->getTags(); if ($tags->count() > 0) { foreach ($tags as $tag) { - $contains[] = $query->contains('tags', $tag); + $constraints[] = $query->contains('tags', $tag); } } else { - $contains[] = $query->equals('pid', $news->getPid()); + $constraints[] = $query->equals('pid', $news->getPid()); } $query->matching( - $contains + $constraints ); if ($limit > 0) { $query->setLimit($limit); @@ -538,4 +589,9 @@ class NewsRepository extends AbstractRepository { return $query->execute(); } + + protected function getConnection(): Connection { + return GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable('pages'); + } } diff --git a/Classes/ViewHelpers/RelatedViewHelper.php b/Classes/ViewHelpers/RelatedViewHelper.php new file mode 100644 index 0000000000000000000000000000000000000000..f49aeecbb0ba4c8109d328bceabf4588c9232f23 --- /dev/null +++ b/Classes/ViewHelpers/RelatedViewHelper.php @@ -0,0 +1,64 @@ +<?php + +namespace SGalinski\SgNews\ViewHelpers; + +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper; + +class RelatedViewHelper extends AbstractViewHelper { + /** + * Initialize the view helper arguments + */ + public function initializeArguments() { + $this->registerArgument( + 'news', + 'SGalinski\SgNews\Domain\Model\News', + 'The news record from which to find related news', + TRUE + ); + $this->registerArgument('limit', 'int', 'Limit the amount of related news to display', FALSE, 5); + $this->registerArgument('as', 'string', 'The name of the iteration variable', TRUE); + $this->registerArgument('iteration', 'string', 'The name of the variable to store iteration information (index, cycle, isFirst, isLast, isEven, isOdd)'); + } + + /** + * Render the ViewHelper + * It works like the for-ViewHelper by running through the child content and adding the related news records to it + * + * @return string + */ + public function render() { + $news = $this->arguments['news']; + $newsRepository = GeneralUtility::makeInstance(NewsRepository::class); + $related = $newsRepository->findRelated($news); + $templateVariableContainer = $this->renderingContext->getVariableProvider(); + $output = ''; + if (isset($this->arguments['iteration'])) { + $iterationData = [ + 'index' => 0, + 'cycle' => 1, + 'total' => count($related) + ]; + } + foreach ($related as $relatedNews) { + $templateVariableContainer->add($this->arguments['as'], $relatedNews); + if (isset($this->arguments['iteration'])) { + $iterationData['isFirst'] = $iterationData['cycle'] === 1; + $iterationData['isLast'] = $iterationData['cycle'] === $iterationData['total']; + $iterationData['isEven'] = $iterationData['cycle'] % 2 === 0; + $iterationData['isOdd'] = !$iterationData['isEven']; + $templateVariableContainer->add($this->arguments['iteration'], $iterationData); + $iterationData['index']++; + $iterationData['cycle']++; + } + + $output .= $this->renderChildren(); + $templateVariableContainer->remove($this->arguments['as']); + if (isset($this->arguments['iteration'])) { + $templateVariableContainer->remove($this->arguments['iteration']); + } + } + + return $output; + } +}