Skip to content
Snippets Groups Projects
Commit c2544db5 authored by Kevin Ditscheid's avatar Kevin Ditscheid
Browse files

Merge branch 'feature_AutomaticRelatedArticle' into 'master'

[TASK] Add related news by tag or category

See merge request !41
parents f50be7aa 21b1fb6d
No related branches found
No related tags found
1 merge request!41[TASK] Add related news by tag or category
......@@ -107,9 +107,9 @@ class News extends CategoryAndNews {
*/
public function __construct() {
parent::__construct();
$this->relatedNews = new ObjectStorage();
$this->tags = new ObjectStorage();
$this->newsAuthor = new ObjectStorage();
$this->relatedNews = new ObjectStorage();
}
/**
......
......@@ -26,8 +26,10 @@ namespace SGalinski\SgNews\Domain\Repository;
* This copyright notice MUST APPEAR in all copies of the script!
***************************************************************/
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;
......@@ -505,4 +507,97 @@ class NewsRepository extends AbstractRepository {
$query->matching($query->equals('uid', $uid));
return $query->execute()->getFirst();
}
/**
* This method finds news related by Tag or Category to the given news record
*
* @param News $news The news to find related news to
* @param int $limit Limit the amount of related news
* @return QueryResultInterface
*/
public function findRelated(News $news, int $limit = 0) {
$connection = $this->getConnection();
$qb = $connection->createQueryBuilder();
// We need to build the constraint for the tags/categories
$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());
}
// here we fetch the lastUpdated of the $limit amount of news with newer lastUpdated dates
$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', 'desc')
->execute();
$newest = $result->fetchOne();
// Here we fetch the lastUpdated of the $limit amount of news with older lastUpdated dates
$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', 'asc')
->execute();
$oldest = $result->fetchOne();
$query = $this->createQuery();
$query->getQuerySettings()->setRespectStoragePage(FALSE);
$query->setOrderings([
'crdate' => QueryInterface::ORDER_DESCENDING
]);
$constraints = [];
if ($newest) {
$constraints[] = $query->lessThanOrEqual('lastUpdated', $newest);
}
if ($oldest) {
$constraints[] = $query->greaterThanOrEqual('lastUpdated', $oldest);
}
// Now we fetch the $limit amount of news via extbase query and limit them to the newest and oldest date
// remember, we fetched the oldest and newest date from the $limit amount of news newer and older then
// the given news. If we limit the result of the following query to $limit, we get $limit amount of news
// "around" the given news, where newer news are preferred due to the ordering.
$tags = $news->getTags();
if ($tags->count() > 0) {
foreach ($tags as $tag) {
$constraints[] = $query->contains('tags', $tag);
}
} else {
$constraints[] = $query->equals('pid', $news->getPid());
}
$query->matching(
$constraints
);
if ($limit > 0) {
$query->setLimit($limit);
}
return $query->execute();
}
protected function getConnection(): Connection {
return GeneralUtility::makeInstance(ConnectionPool::class)
->getConnectionForTable('pages');
}
}
<?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;
}
}
......@@ -179,21 +179,40 @@
</div>
<f:if condition="{newsMetaData.news.relatedNews}">
<div class="tx-sgnews-single-related">
<h3>
<f:translate key="frontend.singleview.relatedArticles" />
</h3>
<ul>
<f:for each="{newsMetaData.news.relatedNews}" as="relatedNewsEntry">
<li>
<a href="{f:uri.page(pageUid: '{relatedNewsEntry.uid}')}">
{relatedNewsEntry.subtitleWithFallbackToTitle}
</a>
</li>
</f:for>
</ul>
</div>
<f:then>
<div class="tx-sgnews-single-related">
<h3>
<f:translate key="frontend.singleview.relatedArticles" />
</h3>
<ul>
<f:for each="{newsMetaData.news.relatedNews}" as="relatedNewsEntry">
<li>
<a href="{f:uri.page(pageUid: '{relatedNewsEntry.uid}')}">
{relatedNewsEntry.subtitleWithFallbackToTitle}
</a>
</li>
</f:for>
</ul>
</div>
</f:then>
<f:else>
<div class="tx-sgnews-single-related">
<h3>
<f:translate key="frontend.singleview.relatedArticles" />
</h3>
<ul>
<sg:related news="{newsMetaData.news}" limit="5" as="relatedNewsEntry">
<li>
<a href="{f:uri.page(pageUid: '{relatedNewsEntry.uid}')}">
{relatedNewsEntry.subtitleWithFallbackToTitle}
</a>
</li>
</sg:related>
</ul>
</div>
</f:else>
</f:if>
</div>
</div>
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment