Skip to content

Commit

Permalink
[TASK] Performance improvement for backend module with many download … (
Browse files Browse the repository at this point in the history
#197)

* [TASK] Performance improvement for backend module with many download log entries #151 [TER-187] [TER-188]

* [TASK] Add second parameter for integer values in createNamedParameter #151 [TER-187] [TER-188]
  • Loading branch information
bmgrieger committed Mar 20, 2024
1 parent 38435e0 commit 4c0f7b0
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 50 deletions.
51 changes: 31 additions & 20 deletions Classes/Controller/LogController.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,26 +75,31 @@ public function listAction(?Filter $filter = null): void
{
$filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter());
$filter->setPageId(0);
$logEntries = $this->logRepository->findByFilter($filter);
$currentPage = GeneralUtility::_GP('currentPage') ? (int)GeneralUtility::_GP('currentPage') : 1;
$itemsPerPage = 20;

$logEntries = $this->logRepository->findByFilter($filter, $currentPage, $itemsPerPage);
$totalResultsCount = $this->logRepository->countByFilter($filter);
$totalPages = (int)(ceil($totalResultsCount / 20));
$statistics = new Statistic($logEntries);
$statistics->setTraffic($this->logRepository->getTrafficSumByFilter($filter));

// Store filter data in session of backend user (used for pagination)
$GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize($filter));

$itemsPerPage = 20;
$currentPage = GeneralUtility::_GP('currentPage') ? (int)GeneralUtility::_GP('currentPage') : 1;

$paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage);
$pagination = new SimplePagination($paginator);

$this->view->assignMultiple([
'logs' => $paginator->getPaginatedItems(),
'logs' => $logEntries,
'users' => $this->getUsers(),
'fileTypes' => $this->getFileTypes(),
'filter' => $filter,
'statistic' => new Statistic($logEntries),
'paginator' => $paginator,
'pagination' => $pagination,
'totalResultCount' => count($logEntries),
'statistic' => $statistics,
'pagination' => [
'totalPages' => $totalPages,
'currentPage' => $currentPage,
'previousPage' => ($currentPage - 1) > 0 ? $currentPage - 1 : null,
'nextPage' => $totalPages > $currentPage ? $currentPage + 1 : null,
],
'totalResultCount' => $totalResultsCount,
]);
}

Expand Down Expand Up @@ -145,27 +150,33 @@ public function showAction(?Filter $filter = null): void

$filter = $filter ?? unserialize($GLOBALS['BE_USER']->getSessionData(self::FILTER_SESSION_KEY)) ?? (new Filter());
$filter->setPageId($pageId);
$logEntries = $this->logRepository->findByFilter($filter);

// Store filter data in session of backend user (used for pagination)
$GLOBALS['BE_USER']->setSessionData(self::FILTER_SESSION_KEY, serialize($filter));

$itemsPerPage = 20;
$currentPage = GeneralUtility::_GP('currentPage') ? (int)GeneralUtility::_GP('currentPage') : 1;

$paginator = new ArrayPaginator($logEntries->toArray(), $currentPage, $itemsPerPage);
$pagination = new SimplePagination($paginator);
$logEntries = $this->logRepository->findByFilter($filter, $currentPage, $itemsPerPage);
$totalResultsCount = $this->logRepository->countByFilter($filter);
$totalPages = (int)(ceil($totalResultsCount / 20));
$statistics = new Statistic($logEntries);
$statistics->setTraffic($this->logRepository->getTrafficSumByFilter($filter));

$this->view->assignMultiple([
'logs' => $paginator->getPaginatedItems(),
'logs' => $logEntries,
'page' => BackendUtility::getRecord('pages', $pageId),
'users' => $this->getUsers(),
'fileTypes' => $this->getFileTypes(),
'filter' => $filter,
'statistic' => new Statistic($logEntries),
'paginator' => $paginator,
'pagination' => $pagination,
'totalResultCount' => count($logEntries),
'statistic' => $statistics,
'pagination' => [
'totalPages' => $totalPages,
'currentPage' => $currentPage,
'previousPage' => ($currentPage - 1) > 0 ? $currentPage - 1 : null,
'nextPage' => $totalPages > $currentPage ? $currentPage + 1 : null,
],
'totalResultCount' => $totalResultsCount,
]);
}

Expand Down
94 changes: 85 additions & 9 deletions Classes/Domain/Repository/LogRepository.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Leuchtfeuer\SecureDownloads\Domain\Transfer\Filter;
use Leuchtfeuer\SecureDownloads\Domain\Transfer\Token\AbstractToken;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\QueryBuilder;
use TYPO3\CMS\Core\Resource\ResourceFactory;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Persistence\Exception\InvalidQueryException;
Expand Down Expand Up @@ -54,7 +55,7 @@ public function createQuery(): QueryInterface
*
* @return QueryResultInterface The query result.
*/
public function findByFilter(?Filter $filter): QueryResultInterface
public function findByFilter(?Filter $filter, int $currentPage = 1, int $itemsPerPage = 20): QueryResultInterface
{
$query = $this->createQuery();

Expand All @@ -66,9 +67,53 @@ public function findByFilter(?Filter $filter): QueryResultInterface
}
}

$query->setLimit($itemsPerPage);
$query->setOffset($itemsPerPage * ($currentPage - 1));

return $query->execute();
}

public function countByFilter(?Filter $filter): int
{
$query = $this->createQuery();

if ($filter instanceof Filter) {
try {
$this->applyFilter($query, $filter);
} catch (InvalidQueryException $exception) {
// Do nothing for now.
}
}

return $query->count();
}

public function getTrafficSumByFilter(?Filter $filter): float
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('tx_securedownloads_domain_model_log');
$constraints = [];

// FileType
$this->applyFileTypePropertyToFilterQB($filter->getFileType(), $queryBuilder, $constraints);

// User Type
$this->applyUserTypePropertyToFilterQB($filter, $queryBuilder, $constraints);

// Period
$this->applyPeriodPropertyToFilterQB($filter, $queryBuilder, $constraints);

// User and Page
$this->applyEqualPropertyToFilterQB((int)$filter->getFeUserId(), 'user', $queryBuilder, $constraints);
$this->applyEqualPropertyToFilterQB((int)$filter->getPageId(), 'page', $queryBuilder, $constraints);

return (float)$queryBuilder
->selectLiteral('SUM(file_size) AS sum')
->from('tx_securedownloads_domain_model_log')
->where(...$constraints)
->executeQuery()
->fetchOne() ?? 0.0;
}

/**
* Applies the filter to a query object.
*
Expand Down Expand Up @@ -112,6 +157,13 @@ protected function applyFileTypePropertyToFilter($fileType, QueryInterface $quer
}
}

protected function applyFileTypePropertyToFilterQB(string $fileType, QueryBuilder $queryBuilder, array &$constraints): void
{
if ($fileType !== '' && $fileType !== '0') {
$constraints[] = $queryBuilder->expr()->eq('media_type', $queryBuilder->createNamedParameter($fileType));
}
}

/**
* Applies the user type property of the filter to the query object.
*
Expand All @@ -121,15 +173,21 @@ protected function applyFileTypePropertyToFilter($fileType, QueryInterface $quer
*/
protected function applyUserTypePropertyToFilter(Filter $filter, QueryInterface $query, array &$constraints): void
{
if ($filter->getUserType() != 0) {
$userQuery = $query->equals('user', null);
if ($filter->getUserType() === Filter::USER_TYPE_LOGGED_ON) {
$constraints[] = $query->greaterThan('user', 0);
}
if ($filter->getUserType() === Filter::USER_TYPE_LOGGED_OFF) {
$constraints[] = $query->equals('user', 0);
}
}

if ($filter->getUserType() === Filter::USER_TYPE_LOGGED_ON) {
$constraints[] = $query->logicalNot($userQuery);
}
if ($filter->getUserType() === Filter::USER_TYPE_LOGGED_OFF) {
$constraints[] = $userQuery;
}
protected function applyUserTypePropertyToFilterQB(Filter $filter, QueryBuilder $queryBuilder, array &$constraints): void
{
if ($filter->getUserType() === Filter::USER_TYPE_LOGGED_ON) {
$constraints[] = $queryBuilder->expr()->gt('user', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT));
}
if ($filter->getUserType() === Filter::USER_TYPE_LOGGED_OFF) {
$constraints[] = $queryBuilder->expr()->eq('user', $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT));
}
}

Expand All @@ -152,6 +210,17 @@ protected function applyPeriodPropertyToFilter(Filter $filter, QueryInterface $q
}
}

protected function applyPeriodPropertyToFilterQB(Filter $filter, QueryBuilder $queryBuilder, array &$constraints): void
{
if ((int)$filter->getFrom() !== 0) {
$constraints[] = $queryBuilder->expr()->gte('tstamp', $queryBuilder->createNamedParameter($filter->getFrom(), \PDO::PARAM_INT));
}

if ((int)$filter->getTill() !== 0) {
$constraints[] = $queryBuilder->expr()->lte('tstamp', $queryBuilder->createNamedParameter($filter->getTill(), \PDO::PARAM_INT));
}
}

/**
* Applies given property of the filter to the query object.
*
Expand All @@ -167,6 +236,13 @@ protected function applyEqualPropertyToFilter(int $property, string $propertyNam
}
}

protected function applyEqualPropertyToFilterQB(int $property, string $propertyName, QueryBuilder $queryBuilder, array &$constraints): void
{
if ($property !== 0) {
$constraints[] = $queryBuilder->expr()->eq($propertyName, $queryBuilder->createNamedParameter($property, \PDO::PARAM_INT));
}
}

/**
* Creates a log entry in the database.
*
Expand Down
5 changes: 5 additions & 0 deletions Classes/Domain/Transfer/Statistic.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ public function getTraffic(): float
return $this->traffic;
}

public function setTraffic(float $traffic): void
{
$this->traffic = $traffic;
}

public function getFrom(): \DateTime
{
return $this->from;
Expand Down
2 changes: 1 addition & 1 deletion Resources/Private/Layouts/Default.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<f:render partial="Backend/Filter" arguments="{_all}"/>

<div class="com-sm-12">
<f:variable name="traffic">{statistic.traffic -> f:format.bytes()}</f:variable>
<f:variable name="traffic">{statistic.traffic -> f:format.bytes(decimals: '2')}</f:variable>
<f:variable name="from">{statistic.from -> f:format.date(format: '{f:translate(key: \'dateformat\')}')}</f:variable>
<f:variable name="till">{statistic.till -> f:format.date(format: '{f:translate(key: \'dateformat\')}')}</f:variable>

Expand Down
39 changes: 19 additions & 20 deletions Resources/Private/Partials/Backend/View.html
Original file line number Diff line number Diff line change
Expand Up @@ -34,69 +34,68 @@
</f:for>
</tbody>
</table>

<f:if condition="{pagination.firstPageNumber} != {pagination.lastPageNumber}">
<f:if condition="{pagination.totalPages} > 1">
<ul class="pagination">
<f:if condition="{paginator.currentPageNumber} != 1">
<f:if condition="{pagination.previousPage}">
<li class="page-item d-none d-sm-block">
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.previousPageNumber}" addQueryString="1">
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.previousPage}" addQueryString="1">
Previous
</f:link.action>
</li>
</f:if>
<f:if condition="{paginator.currentPageNumber} > 2">
<f:if condition="{pagination.currentPage} > 2">
<li class="page-item">
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: 1}" addQueryString="1">
1
</f:link.action>
</li>
</f:if>
<f:if condition="{paginator.currentPageNumber} > 3">
<f:if condition="{pagination.currentPage} > 3">
<li class="page-item disabled">
<span class="page-link">
...
</span>
</li>
</f:if>
<f:if condition="{paginator.currentPageNumber} != 1">
<f:if condition="{pagination.previousPage}">
<li class="page-item">
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.previousPageNumber}" addQueryString="1">
{pagination.previousPageNumber}
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.previousPage}" addQueryString="1">
{pagination.previousPage}
</f:link.action>
</li>
</f:if>
<li class="page-item active">
<span class="page-link">
{paginator.currentPageNumber}
{pagination.currentPage}
</span>
</li>
<f:if condition="{paginator.currentPageNumber} != {pagination.lastPageNumber}">
<f:if condition="{pagination.nextPage} && {pagination.nextPage} != {pagination.totalPages}">
<li class="page-item">
<f:link.action class="page-link" arguments="{foo: 'bar'}" additionalParams="{currentPage: pagination.nextPageNumber}" addQueryString="1">
{pagination.nextPageNumber}
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.nextPage}" addQueryString="1">
{pagination.nextPage}
</f:link.action>
</li>
</f:if>
<f:if condition="{paginator.currentPageNumber} < {pagination.lastPageNumber - 2}">
<f:if condition="{pagination.currentPage} < {pagination.totalPages - 2}">
<li class="page-item disabled">
<span class="page-link">
...
</span>
</li>
</f:if>
<f:if condition="{paginator.currentPageNumber} < {pagination.lastPageNumber - 1}">
<f:if condition="{pagination.currentPage} < {pagination.totalPages}">
<li class="page-item">
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.lastPageNumber}" addQueryString="1">
{pagination.lastPageNumber}
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.totalPages}" addQueryString="1">
{pagination.totalPages}
</f:link.action>
</li>
</f:if>
<f:if condition="{paginator.currentPageNumber} != {pagination.lastPageNumber}">
<f:if condition="{pagination.nextPage}">
<li class="page-item d-none d-sm-block">
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.nextPageNumber}" addQueryString="1">
<f:link.action class="page-link" arguments="{filter: filter}" additionalParams="{currentPage: pagination.nextPage}" addQueryString="1">
Next
</f:link.action>
</li>
</f:if>
</ul>
</f:if>
</f:if>

0 comments on commit 4c0f7b0

Please sign in to comment.