upgraded to symfony 4 #74 (#81)

This commit is contained in:
Kevin Papst
2018-01-12 20:39:07 +01:00
committed by GitHub
parent c011b83b73
commit a87355695e
232 changed files with 5260 additions and 4231 deletions

View File

@@ -0,0 +1,56 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository;
use App\Repository\Query\BaseQuery;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\Query;
use Doctrine\ORM\QueryBuilder;
use Pagerfanta\Adapter\DoctrineORMAdapter;
use Pagerfanta\Pagerfanta;
/**
* Class AbstractRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
abstract class AbstractRepository extends EntityRepository
{
/**
* @param QueryBuilder $qb
* @param BaseQuery $query
* @return QueryBuilder|Pagerfanta
*/
protected function getBaseQueryResult(QueryBuilder $qb, BaseQuery $query)
{
if ($query->getResultType() == BaseQuery::RESULT_TYPE_PAGER) {
return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize());
}
return $qb;
}
/**
* @param Query $query
* @param int $page
* @param int $maxPerPage
* @return Pagerfanta
*/
protected function getPager(Query $query, $page = 1, $maxPerPage = 25)
{
$paginator = new Pagerfanta(new DoctrineORMAdapter($query, false));
$paginator->setMaxPerPage($maxPerPage);
$paginator->setCurrentPage($page);
return $paginator;
}
}

View File

@@ -0,0 +1,184 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository;
use App\Entity\User;
use Doctrine\ORM\Query;
use App\Entity\Activity;
use App\Entity\Timesheet;
use App\Model\ActivityStatistic;
use App\Repository\Query\ActivityQuery;
/**
* Class ActivityRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ActivityRepository extends AbstractRepository
{
/**
* @param $id
* @return null|Activity
*/
public function getById($id)
{
return $this->find($id);
}
/**
* @param User|null $user
* @param \DateTime|null $startFrom
* @return mixed
*/
public function getRecentActivities(User $user = null, \DateTime $startFrom = null)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t', 'a', 'p', 'c')
->from(Timesheet::class, 't')
->join('t.activity', 'a')
->join('a.project', 'p')
->join('p.customer', 'c')
->where($qb->expr()->isNotNull('t.end'))
->andWhere('a.visible = 1')
->andWhere('p.visible = 1')
->andWhere('c.visible = 1')
->groupBy('a.id')
->orderBy('t.end', 'DESC')
->setMaxResults(10)
;
if ($user !== null) {
$qb->andWhere('t.user = :user')
->setParameter('user', $user);
}
if ($startFrom !== null) {
$qb->andWhere($qb->expr()->gt('t.begin', ':begin'))
->setParameter('begin', $startFrom);
}
$results = $qb->getQuery()->getResult();
$activities = [];
/* @var Timesheet $entry */
foreach ($results as $entry) {
$activities[] = $entry->getActivity();
}
return $activities;
}
/**
* Return global statistic data for all user.
*
* @return ActivityStatistic
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function getGlobalStatistics()
{
$countAll = $this->getEntityManager()
->createQuery('SELECT COUNT(a.id) FROM '.Activity::class.' a')
->getSingleScalarResult();
$stats = new ActivityStatistic();
$stats->setCount($countAll);
return $stats;
}
/**
* Retrieves statistics for one activity.
*
* @param Activity $activity
* @return ActivityStatistic
*/
public function getActivityStatistics(Activity $activity)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('COUNT(t.id) as totalRecords', 'SUM(t.duration) as totalDuration')
->from(Timesheet::class, 't')
->where('t.activity = :activity')
;
$result = $qb->getQuery()->execute(['activity' => $activity], Query::HYDRATE_ARRAY);
$stats = new ActivityStatistic();
if (isset($result[0])) {
$dbStats = $result[0];
$stats->setCount(1);
$stats->setRecordAmount($dbStats['totalRecords']);
$stats->setRecordDuration($dbStats['totalDuration']);
}
return $stats;
}
/**
* Returns a query builder that is used for ActivityType and your own 'query_builder' option.
*
* @param Activity|null $entity
* @return \Doctrine\ORM\QueryBuilder
*/
public function builderForEntityType(Activity $entity = null)
{
$query = new ActivityQuery();
$query->setHiddenEntity($entity);
$query->setResultType(ActivityQuery::RESULT_TYPE_QUERYBUILDER);
return $this->findByQuery($query);
}
/**
* @param ActivityQuery $query
* @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta
*/
public function findByQuery(ActivityQuery $query)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('a', 'p', 'c')
->from(Activity::class, 'a')
->join('a.project', 'p')
->join('p.customer', 'c')
->orderBy('a.' . $query->getOrderBy(), $query->getOrder());
if ($query->getVisibility() == ActivityQuery::SHOW_VISIBLE) {
if (!$query->isExclusiveVisibility()) {
$qb->andWhere('c.visible = 1');
$qb->andWhere('p.visible = 1');
}
$qb->andWhere('a.visible = 1');
/** @var Activity $entity */
$entity = $query->getHiddenEntity();
if ($entity !== null) {
$qb->orWhere('a.id = :activity')->setParameter('activity', $entity);
}
} elseif ($query->getVisibility() == ActivityQuery::SHOW_HIDDEN) {
$qb->andWhere('a.visible = 0');
}
if ($query->getProject() !== null) {
$qb->andWhere('a.project = :project')
->setParameter('project', $query->getProject());
} elseif ($query->getCustomer() !== null) {
$qb->andWhere('p.customer = :customer')
->setParameter('customer', $query->getCustomer());
}
return $this->getBaseQueryResult($qb, $query);
}
}

View File

@@ -0,0 +1,136 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository;
use App\Entity\Activity;
use App\Entity\Project;
use App\Entity\Timesheet;
use Doctrine\ORM\Query;
use App\Entity\Customer;
use App\Model\CustomerStatistic;
use App\Repository\Query\CustomerQuery;
/**
* Class CustomerRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class CustomerRepository extends AbstractRepository
{
/**
* @param $id
* @return null|Customer
*/
public function getById($id)
{
return $this->find($id);
}
/**
* Return statistic data for all customer.
*
* @return CustomerStatistic
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function getGlobalStatistics()
{
$countAll = $this->getEntityManager()
->createQuery('SELECT COUNT(c.id) FROM '.Customer::class.' c')
->getSingleScalarResult();
$stats = new CustomerStatistic();
$stats->setCount($countAll);
return $stats;
}
/**
* Retrieves statistics for one customer.
*
* @param Customer $customer
* @return CustomerStatistic
*/
public function getCustomerStatistics(Customer $customer)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('COUNT(t.id) as recordAmount', 'SUM(t.duration) as recordDuration, COUNT(DISTINCT(a.id)) as activityAmount, COUNT(DISTINCT(p.id)) as projectAmount')
->from(Timesheet::class, 't')
->join(Activity::class, 'a')
->join(Project::class, 'p')
->join(Customer::class, 'c')
->andWhere('t.activity = a.id')
->andWhere('a.project = p.id')
->andWhere('p.customer = c.id')
->andWhere('c.id = :customer')
;
// dump($qb->getQuery()->getSQL());exit;
$result = $qb->getQuery()->execute(['customer' => $customer], Query::HYDRATE_ARRAY);
$stats = new CustomerStatistic();
if (isset($result[0])) {
$dbStats = $result[0];
$stats->setCount(1);
$stats->setRecordAmount($dbStats['recordAmount']);
$stats->setRecordDuration($dbStats['recordDuration']);
$stats->setActivityAmount($dbStats['activityAmount']);
$stats->setProjectAmount($dbStats['projectAmount']);
}
return $stats;
}
/**
* Returns a query builder that is used for CustomerType and your own 'query_builder' option.
*
* @param Customer|null $entity
* @return \Doctrine\ORM\QueryBuilder
*/
public function builderForEntityType(Customer $entity = null)
{
$query = new CustomerQuery();
$query->setHiddenEntity($entity);
$query->setResultType(CustomerQuery::RESULT_TYPE_QUERYBUILDER);
return $this->findByQuery($query);
}
/**
* @param CustomerQuery $query
* @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta
*/
public function findByQuery(CustomerQuery $query)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('c')
->from(Customer::class, 'c')
->orderBy('c.' . $query->getOrderBy(), $query->getOrder());
if ($query->getVisibility() == CustomerQuery::SHOW_VISIBLE) {
$qb->andWhere('c.visible = 1');
/** @var Customer $entity */
$entity = $query->getHiddenEntity();
if ($entity!== null) {
$qb->orWhere('c.id = :customer')->setParameter('customer', $entity);
}
} elseif ($query->getVisibility() == CustomerQuery::SHOW_HIDDEN) {
$qb->andWhere('c.visible = 0');
}
return $this->getBaseQueryResult($qb, $query);
}
}

View File

@@ -0,0 +1,142 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository;
use App\Entity\Activity;
use App\Entity\Timesheet;
use Doctrine\ORM\Query;
use App\Entity\Project;
use App\Model\ProjectStatistic;
use App\Repository\Query\ProjectQuery;
/**
* Class ProjectRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ProjectRepository extends AbstractRepository
{
/**
* @param $id
* @return null|Project
*/
public function getById($id)
{
return $this->find($id);
}
/**
* Return statistic data for all user.
*
* @return ProjectStatistic
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function getGlobalStatistics()
{
$countAll = $this->getEntityManager()
->createQuery('SELECT COUNT(p.id) FROM '.Project::class.' p')
->getSingleScalarResult();
$stats = new ProjectStatistic();
$stats->setCount($countAll);
return $stats;
}
/**
* Retrieves statistics for one activity.
*
* @param Project $project
* @return ProjectStatistic
*/
public function getProjectStatistics(Project $project)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('COUNT(t.id) as recordAmount', 'SUM(t.duration) as recordDuration, COUNT(DISTINCT(a.id)) as activityAmount')
->from(Activity::class, 'a')
->join(Timesheet::class, 't')
->where('a.project = :project')
->andWhere('t.activity = a.id')
;
$result = $qb->getQuery()->execute(['project' => $project], Query::HYDRATE_ARRAY);
$stats = new ProjectStatistic();
if (isset($result[0])) {
$dbStats = $result[0];
$stats->setCount(1);
$stats->setRecordAmount($dbStats['recordAmount']);
$stats->setRecordDuration($dbStats['recordDuration']);
$stats->setActivityAmount($dbStats['activityAmount']);
}
return $stats;
}
/**
* Returns a query builder that is used for ProjectType and your own 'query_builder' option.
*
* @param Project|null $entity
* @return \Doctrine\ORM\QueryBuilder
*/
public function builderForEntityType(Project $entity = null)
{
$query = new ProjectQuery();
$query->setHiddenEntity($entity);
$query->setResultType(ProjectQuery::RESULT_TYPE_QUERYBUILDER);
return $this->findByQuery($query);
}
/**
* @param ProjectQuery $query
* @return \Doctrine\ORM\QueryBuilder|\Pagerfanta\Pagerfanta
*/
public function findByQuery(ProjectQuery $query)
{
$qb = $this->getEntityManager()->createQueryBuilder();
// if we join activities, the maxperpage limit will limit the list
// due to the raised amount of rows by projects * activities
$qb->select('p', 'c')
->from(Project::class, 'p')
->join('p.customer', 'c')
->orderBy('p.' . $query->getOrderBy(), $query->getOrder());
if ($query->getVisibility() == ProjectQuery::SHOW_VISIBLE) {
if (!$query->isExclusiveVisibility()) {
$qb->andWhere('c.visible = 1');
}
$qb->andWhere('p.visible = 1');
/** @var Project $entity */
$entity = $query->getHiddenEntity();
if ($entity !== null) {
$qb->orWhere('p.id = :project')->setParameter('project', $entity);
}
// TODO check for visibility of customer
} elseif ($query->getVisibility() == ProjectQuery::SHOW_HIDDEN) {
$qb->andWhere('p.visible = 0');
// TODO check for visibility of customer
}
if ($query->getCustomer() !== null) {
$qb->andWhere('p.customer = :customer')
->setParameter('customer', $query->getCustomer());
}
return $this->getBaseQueryResult($qb, $query);
}
}

View File

@@ -0,0 +1,71 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
use App\Entity\Customer;
use App\Entity\Project;
/**
* Can be used for advanced queries with the: ActivityRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ActivityQuery extends VisibilityQuery
{
/**
* @var Project
*/
protected $project;
/**
* @var Customer
*/
protected $customer;
/**
* @return Customer
*/
public function getCustomer()
{
return $this->customer;
}
/**
* @param Customer $customer
* @return $this
*/
public function setCustomer(Customer $customer = null)
{
$this->customer = $customer;
return $this;
}
/**
* @return Project
*/
public function getProject()
{
return $this->project;
}
/**
* @param Project $project
* @return $this
*/
public function setProject(Project $project = null)
{
$this->project = $project;
return $this;
}
}

View File

@@ -0,0 +1,172 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
/**
* Base class for advanced Repository queries.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class BaseQuery
{
const ORDER_ASC = 'ASC';
const ORDER_DESC = 'DESC';
const DEFAULT_PAGESIZE = 25;
const DEFAULT_PAGE = 1;
const RESULT_TYPE_PAGER = 'PagerFanta';
const RESULT_TYPE_QUERYBUILDER = 'QueryBuilder';
/**
* @var \stdClass
*/
protected $hiddenEntity;
/**
* @var int
*/
protected $page = self::DEFAULT_PAGE;
/**
* @var int
*/
protected $pageSize = self::DEFAULT_PAGESIZE;
/**
* @var string
*/
protected $orderBy = 'id';
/**
* @var string
*/
protected $order = 'ASC';
/**
* @var string
*/
protected $resultType = self::RESULT_TYPE_PAGER;
/**
* @return int
*/
public function getPage()
{
return $this->page;
}
/**
* @param int $page
* @return $this
*/
public function setPage($page)
{
$this->page = (int)$page;
return $this;
}
/**
* @return int
*/
public function getPageSize()
{
return $this->pageSize;
}
/**
* @param int $pageSize
* @return $this
*/
public function setPageSize($pageSize)
{
if (!empty($pageSize) && (int)$pageSize > 0) {
$this->pageSize = (int)$pageSize;
}
return $this;
}
/**
* @return string
*/
public function getOrderBy()
{
return $this->orderBy;
}
/**
* You need to validate carefully if this value is used from a user-input.
*
* @param string $orderBy
* @return $this
*/
public function setOrderBy($orderBy)
{
$this->orderBy = $orderBy;
return $this;
}
/**
* @return string
*/
public function getOrder()
{
return $this->order;
}
/**
* @param string $order
* @return $this
*/
public function setOrder($order)
{
if (in_array($order, [self::ORDER_ASC, self::ORDER_DESC])) {
$this->order = $order;
}
return $this;
}
/**
* @return string
*/
public function getResultType()
{
return $this->resultType;
}
/**
* @param string $resultType
* @return $this
*/
public function setResultType($resultType)
{
if (in_array($resultType, [self::RESULT_TYPE_PAGER, self::RESULT_TYPE_QUERYBUILDER])) {
$this->resultType = $resultType;
}
return $this;
}
/**
* @return \stdClass
*/
public function getHiddenEntity()
{
return $this->hiddenEntity;
}
/**
* @param \stdClass $hiddenEntity
* @return BaseQuery
*/
public function setHiddenEntity($hiddenEntity)
{
$this->hiddenEntity = $hiddenEntity;
return $this;
}
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
/**
* Can be used for advanced queries with the: CustomerRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class CustomerQuery extends VisibilityQuery
{
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
use App\Entity\Customer;
/**
* Can be used for advanced queries with the: ProjectRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class ProjectQuery extends VisibilityQuery
{
/**
* @var Customer
*/
protected $customer;
/**
* @return Customer
*/
public function getCustomer()
{
return $this->customer;
}
/**
* @param Customer $customer
* @return $this
*/
public function setCustomer(Customer $customer = null)
{
$this->customer = $customer;
return $this;
}
}

View File

@@ -0,0 +1,161 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
use App\Entity\User;
use App\Repository\Query\BaseQuery;
use App\Entity\Activity;
use App\Entity\Customer;
use App\Entity\Project;
/**
* Can be used for advanced timesheet repository queries.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class TimesheetQuery extends BaseQuery
{
const STATE_ALL = 0;
const STATE_RUNNING = 1;
const STATE_STOPPED = 2;
/**
* Overwritten for different default order
* @var string
*/
protected $order = self::ORDER_DESC;
/**
* Overwritten for different default order
* @var string
*/
protected $orderBy = 'begin';
/**
* @var User
*/
protected $user;
/**
* @var Activity
*/
protected $activity;
/**
* @var Project
*/
protected $project;
/**
* @var Customer
*/
protected $customer;
/**
* @var int
*/
protected $state = self::STATE_ALL;
/**
* @return User
*/
public function getUser()
{
return $this->user;
}
/**
* @param User $user
* @return TimesheetQuery
*/
public function setUser(User $user = null)
{
$this->user = $user;
return $this;
}
/**
* Activity overwrites: setProject() and setCustomer()
*
* @return Activity
*/
public function getActivity()
{
return $this->activity;
}
/**
* @param Activity $activity
* @return TimesheetQuery
*/
public function setActivity(Activity $activity = null)
{
$this->activity = $activity;
return $this;
}
/**
* @return Project
*/
public function getProject()
{
return $this->project;
}
/**
* Project overwrites: setCustomer()
* Is overwritten by: setActivity()
*
* @param Project $project
* @return TimesheetQuery
*/
public function setProject(Project $project = null)
{
$this->project = $project;
return $this;
}
/**
* @return Customer
*/
public function getCustomer()
{
return $this->customer;
}
/**
* Project overwrites: none
* Is overwritten by: setActivity() and setProject()
*
* @param Customer $customer
* @return TimesheetQuery
*/
public function setCustomer(Customer $customer = null)
{
$this->customer = $customer;
return $this;
}
/**
* @return int
*/
public function getState()
{
return $this->state;
}
/**
* @param int $state
* @return TimesheetQuery
*/
public function setState($state)
{
if (in_array($state, [self::STATE_ALL, self::STATE_RUNNING, self::STATE_STOPPED], true)) {
$this->state = $state;
}
return $this;
}
}

View File

@@ -0,0 +1,46 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
/**
* Can be used for advanced queries with the: UserRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class UserQuery extends VisibilityQuery
{
/**
* @var string
*/
protected $role;
/**
* @return string
*/
public function getRole()
{
return $this->role;
}
/**
* @param string $role
* @return UserQuery
*/
public function setRole($role)
{
if (strpos($role, 'ROLE_') !== false || $role === null) {
$this->role = $role;
}
return $this;
}
}

View File

@@ -0,0 +1,80 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository\Query;
/**
* Query class for Repositories with a visibility field.
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class VisibilityQuery extends BaseQuery
{
const SHOW_VISIBLE = 1;
const SHOW_HIDDEN = 2;
const SHOW_BOTH = 3;
/**
* @var integer
*/
protected $visibility = self::SHOW_VISIBLE;
/**
* @var bool
*/
protected $exclusiveVisibility = false;
/**
* @return int
*/
public function getVisibility()
{
return $this->visibility;
}
/**
* @param int $visibility
* @return $this
*/
public function setVisibility($visibility)
{
if (!is_int($visibility) && $visibility != (int) $visibility) {
return $this;
}
$visibility = (int) $visibility;
if (in_array($visibility, [self::SHOW_BOTH, self::SHOW_VISIBLE, self::SHOW_HIDDEN], true)) {
$this->visibility = $visibility;
}
return $this;
}
/**
* @return bool
*/
public function isExclusiveVisibility()
{
return $this->exclusiveVisibility;
}
/**
* If set to true, this will ONLY filter the visibility on the main queried object.
*
* @param bool $exclusiveVisibility
* @return $this
*/
public function setExclusiveVisibility($exclusiveVisibility)
{
$this->exclusiveVisibility = (bool) $exclusiveVisibility;
return $this;
}
}

View File

@@ -0,0 +1,307 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository;
use App\Entity\User;
use App\Entity\Activity;
use App\Entity\Timesheet;
use Doctrine\DBAL\Types\Type;
use Pagerfanta\Pagerfanta;
use App\Model\Statistic\Month;
use App\Model\Statistic\Year;
use App\Model\TimesheetGlobalStatistic;
use App\Model\TimesheetStatistic;
use DateTime;
use App\Repository\Query\TimesheetQuery;
/**
* Class TimesheetRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class TimesheetRepository extends AbstractRepository
{
/**
* @param Timesheet $entry
* @return bool
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function stopRecording(Timesheet $entry)
{
$entry->setEnd(new DateTime());
$entityManager = $this->getEntityManager();
$entityManager->persist($entry);
$entityManager->flush();
return true;
}
/**
* @param User $user
* @param Activity $activity
* @return bool
* @throws \Doctrine\ORM\ORMException
* @throws \Doctrine\ORM\OptimisticLockException
*/
public function startRecording(User $user, Activity $activity)
{
$entry = new Timesheet();
$entry
->setBegin(new DateTime())
->setUser($user)
->setActivity($activity);
$entityManager = $this->getEntityManager();
$entityManager->persist($entry);
$entityManager->flush();
return true;
}
/**
* @param $select
* @param User|null $user
* @return \Doctrine\ORM\QueryBuilder
*/
protected function queryThisMonth($select, User $user = null)
{
$end = new DateTime('last day of this month');
$end->setTime(23, 59, 59);
$begin = new DateTime('first day of this month');
$begin->setTime(0, 0, 0);
return $this->queryTimeRange($select, $begin, $end, $user);
}
/**
* @param $select
* @param DateTime $begin
* @param DateTime $end
* @param User|null $user
* @return \Doctrine\ORM\QueryBuilder
*/
protected function queryTimeRange($select, DateTime $begin, DateTime $end, User $user = null)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select($select)
->from(Timesheet::class, 't')
->where($qb->expr()->gt('t.begin', ':from'))
->andWhere($qb->expr()->lt('t.end', ':to'))
->setParameter('from', $begin, Type::DATETIME)
->setParameter('to', $end, Type::DATETIME);
if (null !== $user) {
$qb->andWhere('t.user = :user')
->setParameter('user', $user);
}
return $qb;
}
/**
* Fetch statistic data for one user.
*
* @param User $user
* @return TimesheetStatistic
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function getUserStatistics(User $user)
{
$durationTotal = $this->getEntityManager()
->createQuery('SELECT SUM(t.duration) FROM '.Timesheet::class.' t WHERE t.user = :user')
->setParameter('user', $user)
->getSingleScalarResult();
$rateTotal = $this->getEntityManager()
->createQuery('SELECT SUM(t.rate) FROM '.Timesheet::class.' t WHERE t.user = :user')
->setParameter('user', $user)
->getSingleScalarResult();
$amountMonth = $this->queryThisMonth('SUM(t.rate)', $user)
->getQuery()
->getSingleScalarResult();
$durationMonth = $this->queryThisMonth('SUM(t.duration)', $user)
->getQuery()
->getSingleScalarResult();
$firstEntry = $this->getEntityManager()
->createQuery('SELECT MIN(t.begin) FROM '.Timesheet::class.' t WHERE t.user = :user')
->setParameter('user', $user)
->getSingleScalarResult();
$stats = new TimesheetStatistic();
$stats->setAmountTotal($rateTotal);
$stats->setDurationTotal($durationTotal);
$stats->setAmountThisMonth($amountMonth);
$stats->setDurationThisMonth($durationMonth);
$stats->setFirstEntry(new DateTime($firstEntry));
return $stats;
}
/**
* Returns an array of Year statistics.
*
* @param User|null $user
* @return Year[]
*/
public function getMonthlyStats(User $user = null)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('SUM(t.rate) as rate, SUM(t.duration) as duration, MONTH(t.begin) as month, YEAR(t.begin) as year')
->from(Timesheet::class, 't')
->where($qb->expr()->gt('t.begin', '0'))
->andWhere($qb->expr()->isNotNull('t.end'))
->orderBy('year', 'DESC')
->addOrderBy('month', 'ASC')
->groupBy('year')
->addGroupBy('month');
if (null !== $user) {
$qb->where('t.user = :user')
->setParameter('user', $user);
}
$years = [];
foreach ($qb->getQuery()->execute() as $statRow) {
$curYear = $statRow['year'];
if (!isset($years[$curYear])) {
$year = new Year($curYear);
for ($i = 1; $i < 13; $i++) {
$month = $i < 10 ? '0' . $i : (string)$i;
$year->setMonth(new Month($month));
}
$years[$curYear] = $year;
}
$month = new Month($statRow['month']);
$month->setTotalDuration($statRow['duration'])
->setTotalRate($statRow['rate']);
$years[$curYear]->setMonth($month);
}
return $years;
}
/**
* Fetch statistic data for all user.
*
* @return TimesheetGlobalStatistic
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function getGlobalStatistics()
{
$durationTotal = $this->getEntityManager()
->createQuery('SELECT SUM(t.duration) FROM '.Timesheet::class.' t')
->getSingleScalarResult();
$rateTotal = $this->getEntityManager()
->createQuery('SELECT SUM(t.rate) FROM '.Timesheet::class.' t')
->getSingleScalarResult();
$userTotal = $this->getEntityManager()
->createQuery('SELECT COUNT(DISTINCT(t.user)) FROM '.Timesheet::class.' t')
->getSingleScalarResult();
$activeNow = $this->getActiveEntries();
$amountMonth = $this->queryThisMonth('SUM(t.rate)')
->getQuery()
->getSingleScalarResult();
$durationMonth = $this->queryThisMonth('SUM(t.duration)')
->getQuery()
->getSingleScalarResult();
$activeMonth = $this->queryThisMonth('COUNT(DISTINCT(t.user))')
->getQuery()
->getSingleScalarResult();
$stats = new TimesheetGlobalStatistic();
$stats->setAmountTotal($rateTotal);
$stats->setDurationTotal($durationTotal);
$stats->setActiveTotal($userTotal);
$stats->setActiveCurrently(count($activeNow));
$stats->setActiveThisMonth($activeMonth);
$stats->setAmountThisMonth($amountMonth);
$stats->setDurationThisMonth($durationMonth);
return $stats;
}
/**
* TODO replace me by a findByQuery() call
*
* @param User $user
* @return Timesheet[]|null
*/
public function getActiveEntries(User $user = null)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t', 'a', 'p', 'c')
->from(Timesheet::class, 't')
->join('t.activity', 'a')
->join('a.project', 'p')
->join('p.customer', 'c')
->where($qb->expr()->gt('t.begin', '0'))
->andWhere($qb->expr()->isNull('t.end'))
->orderBy('t.begin', 'DESC');
$params = [];
if (null !== $user) {
$qb->andWhere('t.user = :user');
$params['user'] = $user;
}
return $qb->getQuery()->execute($params);
}
/**
* @param TimesheetQuery $query
* @return Pagerfanta
*/
public function findByQuery(TimesheetQuery $query)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('t', 'a', 'p', 'c', 'u')
->from(Timesheet::class, 't')
->join('t.activity', 'a')
->join('t.user', 'u')
->join('a.project', 'p')
->join('p.customer', 'c')
->orderBy('t.' . $query->getOrderBy(), $query->getOrder());
if ($query->getUser() !== null) {
$qb->andWhere('t.user = :user')
->setParameter('user', $query->getUser());
}
if ($query->getState() == TimesheetQuery::STATE_RUNNING) {
$qb->andWhere($qb->expr()->isNull('t.end'));
} elseif ($query->getState() == TimesheetQuery::STATE_STOPPED) {
$qb->andWhere($qb->expr()->isNotNull('t.end'));
}
if ($query->getActivity() !== null) {
$qb->andWhere('t.activity = :activity')
->setParameter('activity', $query->getActivity());
} elseif ($query->getProject() !== null) {
$qb->andWhere('a.project = :project')
->setParameter('project', $query->getProject());
} elseif ($query->getCustomer() !== null) {
$qb->andWhere('p.customer = :customer')
->setParameter('customer', $query->getCustomer());
}
return $this->getBaseQueryResult($qb, $query);
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* This file is part of the Kimai package.
*
* (c) Kevin Papst <kevin@kevinpapst.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Repository;
use App\Entity\User;
use App\Model\UserStatistic;
use App\Repository\Query\UserQuery;
/**
* Class UserRepository
*
* @author Kevin Papst <kevin@kevinpapst.de>
*/
class UserRepository extends AbstractRepository
{
/**
* Return statistic data for all user.
*
* @return UserStatistic
* @throws \Doctrine\ORM\NonUniqueResultException
*/
public function getGlobalStatistics()
{
$countAll = $this->getEntityManager()
->createQuery('SELECT COUNT(u.id) FROM '.User::class.' u')
->getSingleScalarResult();
$stats = new UserStatistic();
$stats->setTotalAmount($countAll);
return $stats;
}
/**
* Fetch a user by his username.
*
* @param $username
* @return null|User
*/
public function findByUsername($username)
{
return $this->findOneBy(['username' => $username]);
}
/**
* @param UserQuery $query
* @return \Pagerfanta\Pagerfanta
*/
public function findByQuery(UserQuery $query)
{
$qb = $this->getEntityManager()->createQueryBuilder();
// if we join activities, the maxperpage limit will limit the list to the amount or projects + activties
$qb->select('u')
->from(User::class, 'u')
->orderBy('u.' . $query->getOrderBy(), $query->getOrder());
if ($query->getVisibility() == UserQuery::SHOW_VISIBLE) {
$qb->andWhere('u.active = 1');
} elseif ($query->getVisibility() == UserQuery::SHOW_HIDDEN) {
$qb->andWhere('u.active = 0');
}
if ($query->getRole() !== null) {
$qb->andWhere('u.roles LIKE :role')->setParameter('role', '%' . $query->getRole() . '%');
}
return $this->getPager($qb->getQuery(), $query->getPage(), $query->getPageSize());
}
}