customize html and pdf exports with twig templates (#1582)
This commit is contained in:
@@ -49,6 +49,9 @@ class AppExtension extends Extension
|
||||
$config['invoice']['documents'] = array_merge($config['invoice']['documents'], $config['invoice']['defaults']);
|
||||
unset($config['invoice']['defaults']);
|
||||
|
||||
$config['export']['documents'] = array_merge($config['export']['documents'], $config['export']['defaults']);
|
||||
unset($config['export']['defaults']);
|
||||
|
||||
// safe alternatives to %kernel.project_dir%
|
||||
$container->setParameter('kimai.data_dir', $config['data_dir']);
|
||||
$container->setParameter('kimai.plugin_dir', $config['plugin_dir']);
|
||||
@@ -60,6 +63,7 @@ class AppExtension extends Extension
|
||||
$container->setParameter('kimai.dashboard', $config['dashboard']);
|
||||
$container->setParameter('kimai.widgets', $config['widgets']);
|
||||
$container->setParameter('kimai.invoice.documents', $config['invoice']['documents']);
|
||||
$container->setParameter('kimai.export.documents', $config['export']['documents']);
|
||||
$container->setParameter('kimai.defaults', $config['defaults']);
|
||||
|
||||
$this->createPermissionParameter($config['permissions'], $container);
|
||||
|
||||
@@ -9,10 +9,14 @@
|
||||
|
||||
namespace App\DependencyInjection\Compiler;
|
||||
|
||||
use App\Export\Renderer\HtmlRenderer;
|
||||
use App\Export\Renderer\HtmlRendererFactory;
|
||||
use App\Export\Renderer\PdfRendererFactory;
|
||||
use App\Export\ServiceExport;
|
||||
use App\Kernel;
|
||||
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
|
||||
use Symfony\Component\DependencyInjection\ContainerBuilder;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\DependencyInjection\Reference;
|
||||
|
||||
/**
|
||||
@@ -37,5 +41,40 @@ class ExportServiceCompilerPass implements CompilerPassInterface
|
||||
foreach ($taggedExporter as $id => $tags) {
|
||||
$definition->addMethodCall('addTimesheetExporter', [new Reference($id)]);
|
||||
}
|
||||
|
||||
$path = dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR;
|
||||
foreach ($container->getParameter('kimai.export.documents') as $exportPath) {
|
||||
if (!is_dir($path . $exportPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach (glob($path . $exportPath . '/*.html.twig') as $htmlTpl) {
|
||||
$tplName = basename($htmlTpl);
|
||||
|
||||
$serviceId = 'exporter_renderer.' . str_replace('.', '_', $tplName);
|
||||
|
||||
$factoryDefinition = new Definition(HtmlRenderer::class);
|
||||
$factoryDefinition->addArgument($tplName);
|
||||
$factoryDefinition->addArgument($tplName);
|
||||
$factoryDefinition->setFactory([new Reference(HtmlRendererFactory::class), 'create']);
|
||||
|
||||
$container->setDefinition($serviceId, $factoryDefinition);
|
||||
$definition->addMethodCall('addRenderer', [new Reference($serviceId)]);
|
||||
}
|
||||
|
||||
foreach (glob($path . $exportPath . '/*.pdf.twig') as $pdfHtml) {
|
||||
$tplName = basename($pdfHtml);
|
||||
|
||||
$serviceId = 'exporter_renderer.' . str_replace('.', '_', $tplName);
|
||||
|
||||
$factoryDefinition = new Definition(HtmlRenderer::class);
|
||||
$factoryDefinition->addArgument($tplName);
|
||||
$factoryDefinition->addArgument($tplName);
|
||||
$factoryDefinition->setFactory([new Reference(PdfRendererFactory::class), 'create']);
|
||||
|
||||
$container->setDefinition($serviceId, $factoryDefinition);
|
||||
$definition->addMethodCall('addRenderer', [new Reference($serviceId)]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,16 +32,21 @@ class TwigContextCompilerPass implements CompilerPassInterface
|
||||
$saml = $container->getParameter('kimai.saml');
|
||||
$twig->addMethodCall('addGlobal', ['saml', $saml]);
|
||||
|
||||
if ($container->hasDefinition('twig.loader.native_filesystem')) {
|
||||
$definition = $container->getDefinition('twig.loader.native_filesystem');
|
||||
$definition = $container->getDefinition('twig.loader.native_filesystem');
|
||||
|
||||
$path = dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR;
|
||||
foreach ($container->getParameter('kimai.invoice.documents') as $invoicePath) {
|
||||
if (!is_dir($path . $invoicePath)) {
|
||||
continue;
|
||||
}
|
||||
$definition->addMethodCall('addPath', [$path . $invoicePath, 'invoice']);
|
||||
$path = dirname(dirname(dirname(__DIR__))) . DIRECTORY_SEPARATOR;
|
||||
foreach ($container->getParameter('kimai.invoice.documents') as $invoicePath) {
|
||||
if (!is_dir($path . $invoicePath)) {
|
||||
continue;
|
||||
}
|
||||
$definition->addMethodCall('addPath', [$path . $invoicePath, 'invoice']);
|
||||
}
|
||||
|
||||
foreach ($container->getParameter('kimai.export.documents') as $exportPath) {
|
||||
if (!is_dir($path . $exportPath)) {
|
||||
continue;
|
||||
}
|
||||
$definition->addMethodCall('addPath', [$path . $exportPath, 'export']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ class Configuration implements ConfigurationInterface
|
||||
->append($this->getUserNode())
|
||||
->append($this->getTimesheetNode())
|
||||
->append($this->getInvoiceNode())
|
||||
->append($this->getExportNode())
|
||||
->append($this->getLanguagesNode())
|
||||
->append($this->getCalendarNode())
|
||||
->append($this->getThemeNode())
|
||||
@@ -236,6 +237,33 @@ class Configuration implements ConfigurationInterface
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function getExportNode()
|
||||
{
|
||||
$builder = new TreeBuilder('export');
|
||||
/** @var ArrayNodeDefinition $node */
|
||||
$node = $builder->getRootNode();
|
||||
|
||||
$node
|
||||
->addDefaultsIfNotSet()
|
||||
->children()
|
||||
->arrayNode('defaults')
|
||||
->scalarPrototype()->end()
|
||||
->defaultValue([
|
||||
'var/export/',
|
||||
'templates/export/renderer/'
|
||||
])
|
||||
->end()
|
||||
->arrayNode('documents')
|
||||
->requiresAtLeastOneElement()
|
||||
->scalarPrototype()->end()
|
||||
->defaultValue([])
|
||||
->end()
|
||||
->end()
|
||||
;
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function getLanguagesNode()
|
||||
{
|
||||
$builder = new TreeBuilder('languages');
|
||||
|
||||
@@ -17,6 +17,7 @@ use App\Event\ProjectMetaDisplayEvent;
|
||||
use App\Event\TimesheetMetaDisplayEvent;
|
||||
use App\Event\UserPreferenceDisplayEvent;
|
||||
use App\Export\ExportItemInterface;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\CustomerQuery;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
@@ -35,11 +36,24 @@ class HtmlRenderer
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
protected $dispatcher;
|
||||
/**
|
||||
* @var ProjectRepository
|
||||
*/
|
||||
private $projectRepository;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id = 'html';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $template = 'default.html.twig';
|
||||
|
||||
public function __construct(Environment $twig, EventDispatcherInterface $dispatcher)
|
||||
public function __construct(Environment $twig, EventDispatcherInterface $dispatcher, ProjectRepository $projectRepository)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->projectRepository = $projectRepository;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,10 +101,13 @@ class HtmlRenderer
|
||||
$this->dispatcher->dispatch($event);
|
||||
$userPreferences = $event->getPreferences();
|
||||
|
||||
$content = $this->twig->render('export/renderer/default.html.twig', array_merge([
|
||||
$summary = $this->calculateSummary($timesheets);
|
||||
|
||||
$content = $this->twig->render($this->getTemplate(), array_merge([
|
||||
'entries' => $timesheets,
|
||||
'query' => $query,
|
||||
'summaries' => $this->calculateSummary($timesheets),
|
||||
'summaries' => $summary,
|
||||
'budgets' => $this->calculateProjectBudget($timesheets, $query, $this->projectRepository),
|
||||
'timesheetMetaFields' => $timesheetMetaFields,
|
||||
'customerMetaFields' => $customerMetaFields,
|
||||
'projectMetaFields' => $projectMetaFields,
|
||||
@@ -104,8 +121,27 @@ class HtmlRenderer
|
||||
return $response;
|
||||
}
|
||||
|
||||
protected function getTemplate(): string
|
||||
{
|
||||
return '@export/' . $this->template;
|
||||
}
|
||||
|
||||
public function setTemplate(string $filename): HtmlRenderer
|
||||
{
|
||||
$this->template = $filename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setId(string $id): HtmlRenderer
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'html';
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
namespace App\Export\Base;
|
||||
|
||||
use App\Export\ExportItemInterface;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Utils\HtmlToPdfConverter;
|
||||
@@ -33,17 +34,30 @@ class PDFRenderer
|
||||
* @var HtmlToPdfConverter
|
||||
*/
|
||||
private $converter;
|
||||
/**
|
||||
* @var ProjectRepository
|
||||
*/
|
||||
private $projectRepository;
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $id = 'pdf';
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $template = 'default.pdf.twig';
|
||||
|
||||
public function __construct(Environment $twig, UserDateTimeFactory $dateTime, HtmlToPdfConverter $converter)
|
||||
public function __construct(Environment $twig, UserDateTimeFactory $dateTime, HtmlToPdfConverter $converter, ProjectRepository $projectRepository)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
$this->dateTime = $dateTime;
|
||||
$this->converter = $converter;
|
||||
$this->projectRepository = $projectRepository;
|
||||
}
|
||||
|
||||
protected function getTemplate(): string
|
||||
{
|
||||
return 'export/renderer/pdf.html.twig';
|
||||
return '@export/' . $this->template;
|
||||
}
|
||||
|
||||
protected function getOptions(TimesheetQuery $query): array
|
||||
@@ -68,11 +82,13 @@ class PDFRenderer
|
||||
*/
|
||||
public function render(array $timesheets, TimesheetQuery $query): Response
|
||||
{
|
||||
$summary = $this->calculateSummary($timesheets);
|
||||
$content = $this->twig->render($this->getTemplate(), array_merge([
|
||||
'entries' => $timesheets,
|
||||
'query' => $query,
|
||||
'now' => $this->dateTime->createDateTime(),
|
||||
'summaries' => $this->calculateSummary($timesheets),
|
||||
'summaries' => $summary,
|
||||
'budgets' => $this->calculateProjectBudget($timesheets, $query, $this->projectRepository),
|
||||
'decimal' => false,
|
||||
], $this->getOptions($query)));
|
||||
|
||||
@@ -88,8 +104,22 @@ class PDFRenderer
|
||||
return $response;
|
||||
}
|
||||
|
||||
public function setTemplate(string $filename): PDFRenderer
|
||||
{
|
||||
$this->template = $filename;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setId(string $id): PDFRenderer
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getId(): string
|
||||
{
|
||||
return 'pdf';
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
namespace App\Export\Base;
|
||||
|
||||
use App\Export\ExportItemInterface;
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
|
||||
trait RendererTrait
|
||||
{
|
||||
@@ -80,4 +82,52 @@ trait RendererTrait
|
||||
|
||||
return $summary;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ExportItemInterface[] $exportItems
|
||||
* @param TimesheetQuery $query
|
||||
* @param ProjectRepository $projectRepository
|
||||
* @return array
|
||||
*/
|
||||
protected function calculateProjectBudget(array $exportItems, TimesheetQuery $query, ProjectRepository $projectRepository)
|
||||
{
|
||||
$summary = [];
|
||||
|
||||
foreach ($exportItems as $exportItem) {
|
||||
$customer = null;
|
||||
$customerId = 'none';
|
||||
$project = null;
|
||||
$projectId = 'none';
|
||||
|
||||
if (null !== ($project = $exportItem->getProject())) {
|
||||
$customer = $project->getCustomer();
|
||||
$customerId = $customer->getId();
|
||||
$projectId = $project->getId();
|
||||
}
|
||||
|
||||
$id = $customerId . '_' . $projectId;
|
||||
|
||||
if (!isset($summary[$id])) {
|
||||
$summary[$id] = [
|
||||
'time' => $project->getTimeBudget(),
|
||||
'money' => $project->getBudget(),
|
||||
'time_left' => null,
|
||||
'money_left' => null,
|
||||
];
|
||||
|
||||
if (null !== $project && ($project->getTimeBudget() > 0 || $project->getBudget() > 0)) {
|
||||
$projectStats = $projectRepository->getProjectStatistics($project, null, $query->getEnd());
|
||||
|
||||
if ($project->getTimeBudget() > 0) {
|
||||
$summary[$id]['time_left'] = $project->getTimeBudget() - $projectStats->getRecordDuration();
|
||||
}
|
||||
if ($project->getBudget() > 0) {
|
||||
$summary[$id]['money_left'] = $project->getBudget() - $projectStats->getRecordRate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $summary;
|
||||
}
|
||||
}
|
||||
|
||||
38
src/Export/ExportRendererInterface.php
Normal file
38
src/Export/ExportRendererInterface.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Export;
|
||||
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
interface ExportRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param ExportItemInterface[] $exportItems
|
||||
* @param TimesheetQuery $query
|
||||
* @return Response
|
||||
*/
|
||||
public function render(array $exportItems, TimesheetQuery $query): Response;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIcon(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string;
|
||||
}
|
||||
@@ -10,9 +10,9 @@
|
||||
namespace App\Export\Renderer;
|
||||
|
||||
use App\Export\Base\HtmlRenderer as BaseHtmlRenderer;
|
||||
use App\Export\RendererInterface;
|
||||
use App\Export\ExportRendererInterface;
|
||||
|
||||
final class HtmlRenderer extends BaseHtmlRenderer implements RendererInterface
|
||||
final class HtmlRenderer extends BaseHtmlRenderer implements ExportRendererInterface
|
||||
{
|
||||
public function getIcon(): string
|
||||
{
|
||||
|
||||
46
src/Export/Renderer/HtmlRendererFactory.php
Normal file
46
src/Export/Renderer/HtmlRendererFactory.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Export\Renderer;
|
||||
|
||||
use App\Repository\ProjectRepository;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Twig\Environment;
|
||||
|
||||
final class HtmlRendererFactory
|
||||
{
|
||||
/**
|
||||
* @var Environment
|
||||
*/
|
||||
private $twig;
|
||||
/**
|
||||
* @var EventDispatcherInterface
|
||||
*/
|
||||
private $dispatcher;
|
||||
/**
|
||||
* @var ProjectRepository
|
||||
*/
|
||||
private $projectRepository;
|
||||
|
||||
public function __construct(Environment $twig, EventDispatcherInterface $dispatcher, ProjectRepository $projectRepository)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
$this->dispatcher = $dispatcher;
|
||||
$this->projectRepository = $projectRepository;
|
||||
}
|
||||
|
||||
public function create(string $id, string $template): HtmlRenderer
|
||||
{
|
||||
$renderer = new HtmlRenderer($this->twig, $this->dispatcher, $this->projectRepository);
|
||||
$renderer->setId($id);
|
||||
$renderer->setTemplate($template);
|
||||
|
||||
return $renderer;
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,9 @@
|
||||
namespace App\Export\Renderer;
|
||||
|
||||
use App\Export\Base\PDFRenderer as BasePDFRenderer;
|
||||
use App\Export\RendererInterface;
|
||||
use App\Export\ExportRendererInterface;
|
||||
|
||||
final class PDFRenderer extends BasePDFRenderer implements RendererInterface
|
||||
final class PDFRenderer extends BasePDFRenderer implements ExportRendererInterface
|
||||
{
|
||||
public function getIcon(): string
|
||||
{
|
||||
|
||||
52
src/Export/Renderer/PdfRendererFactory.php
Normal file
52
src/Export/Renderer/PdfRendererFactory.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Kimai time-tracking app.
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace App\Export\Renderer;
|
||||
|
||||
use App\Repository\ProjectRepository;
|
||||
use App\Timesheet\UserDateTimeFactory;
|
||||
use App\Utils\HtmlToPdfConverter;
|
||||
use Twig\Environment;
|
||||
|
||||
final class PdfRendererFactory
|
||||
{
|
||||
/**
|
||||
* @var Environment
|
||||
*/
|
||||
private $twig;
|
||||
/**
|
||||
* @var UserDateTimeFactory
|
||||
*/
|
||||
private $dateTime;
|
||||
/**
|
||||
* @var HtmlToPdfConverter
|
||||
*/
|
||||
private $converter;
|
||||
/**
|
||||
* @var ProjectRepository
|
||||
*/
|
||||
private $projectRepository;
|
||||
|
||||
public function __construct(Environment $twig, UserDateTimeFactory $dateTime, HtmlToPdfConverter $converter, ProjectRepository $projectRepository)
|
||||
{
|
||||
$this->twig = $twig;
|
||||
$this->dateTime = $dateTime;
|
||||
$this->converter = $converter;
|
||||
$this->projectRepository = $projectRepository;
|
||||
}
|
||||
|
||||
public function create(string $id, string $template): PDFRenderer
|
||||
{
|
||||
$renderer = new PDFRenderer($this->twig, $this->dateTime, $this->converter, $this->projectRepository);
|
||||
$renderer->setId($id);
|
||||
$renderer->setTemplate($template);
|
||||
|
||||
return $renderer;
|
||||
}
|
||||
}
|
||||
@@ -9,30 +9,6 @@
|
||||
|
||||
namespace App\Export;
|
||||
|
||||
use App\Repository\Query\TimesheetQuery;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
interface RendererInterface
|
||||
interface RendererInterface extends ExportRendererInterface
|
||||
{
|
||||
/**
|
||||
* @param ExportItemInterface[] $exportItems
|
||||
* @param TimesheetQuery $query
|
||||
* @return Response
|
||||
*/
|
||||
public function render(array $exportItems, TimesheetQuery $query): Response;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getId(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getIcon(): string;
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle(): string;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace App\Export;
|
||||
final class ServiceExport
|
||||
{
|
||||
/**
|
||||
* @var RendererInterface[]
|
||||
* @var ExportRendererInterface[]
|
||||
*/
|
||||
private $renderer = [];
|
||||
|
||||
@@ -21,7 +21,7 @@ final class ServiceExport
|
||||
*/
|
||||
private $exporter = [];
|
||||
|
||||
public function addRenderer(RendererInterface $renderer): ServiceExport
|
||||
public function addRenderer(ExportRendererInterface $renderer): ServiceExport
|
||||
{
|
||||
$this->renderer[] = $renderer;
|
||||
|
||||
@@ -29,14 +29,14 @@ final class ServiceExport
|
||||
}
|
||||
|
||||
/**
|
||||
* @return RendererInterface[]
|
||||
* @return ExportRendererInterface[]
|
||||
*/
|
||||
public function getRenderer(): array
|
||||
{
|
||||
return $this->renderer;
|
||||
}
|
||||
|
||||
public function getRendererById(string $id): ?RendererInterface
|
||||
public function getRendererById(string $id): ?ExportRendererInterface
|
||||
{
|
||||
foreach ($this->renderer as $renderer) {
|
||||
if ($renderer->getId() === $id) {
|
||||
|
||||
@@ -73,20 +73,31 @@ class ProjectRepository extends EntityRepository
|
||||
return $this->count([]);
|
||||
}
|
||||
|
||||
public function getProjectStatistics(Project $project): ProjectStatistic
|
||||
public function getProjectStatistics(Project $project, ?\DateTime $begin = null, ?\DateTime $end = null): ProjectStatistic
|
||||
{
|
||||
$stats = new ProjectStatistic($project);
|
||||
|
||||
$qb = $this->getEntityManager()->createQueryBuilder();
|
||||
|
||||
$qb
|
||||
->from(Timesheet::class, 't')
|
||||
->addSelect('COUNT(t.id) as recordAmount')
|
||||
->addSelect('SUM(t.duration) as recordDuration')
|
||||
->addSelect('SUM(t.rate) as recordRate')
|
||||
->from(Timesheet::class, 't')
|
||||
->andWhere('t.project = :project')
|
||||
->setParameter('project', $project)
|
||||
;
|
||||
$timesheetResult = $qb->getQuery()->execute(['project' => $project], Query::HYDRATE_ARRAY);
|
||||
|
||||
if (null !== $begin) {
|
||||
$qb->andWhere($qb->expr()->gte('t.begin', ':begin'))
|
||||
->setParameter('begin', $begin);
|
||||
}
|
||||
if (null !== $end) {
|
||||
$qb->andWhere($qb->expr()->lte('t.end', ':end'))
|
||||
->setParameter('end', $end);
|
||||
}
|
||||
|
||||
$timesheetResult = $qb->getQuery()->getArrayResult();
|
||||
|
||||
if (isset($timesheetResult[0])) {
|
||||
$stats->setRecordAmount($timesheetResult[0]['recordAmount']);
|
||||
|
||||
Reference in New Issue
Block a user