added table-column ordering (#1086)

This commit is contained in:
Kevin Papst
2019-09-09 23:47:42 +02:00
committed by GitHub
parent d041a3f4f9
commit a651e55dc9
82 changed files with 932 additions and 516 deletions

View File

@@ -81,13 +81,16 @@ class ActivityController extends AbstractController
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->getPagerfantaForQuery($query);
return $this->render('activity/index.html.twig', [
'entries' => $entries,
'query' => $query,
'showFilter' => $query->isDirty(),
'toolbarForm' => $form->createView(),
]);
}

View File

@@ -83,12 +83,15 @@ class CustomerController extends AbstractController
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
$entries = $this->getRepository()->getPagerfantaForQuery($query);
return $this->render('customer/index.html.twig', [
'entries' => $entries,
'query' => $query,
'showFilter' => $query->isDirty(),
'toolbarForm' => $form->createView(),
]);
}

View File

@@ -78,13 +78,16 @@ class ProjectController extends AbstractController
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->getPagerfantaForQuery($query);
return $this->render('project/index.html.twig', [
'entries' => $entries,
'query' => $query,
'showFilter' => $query->isDirty(),
'toolbarForm' => $form->createView(),
]);
}

View File

@@ -44,12 +44,15 @@ class TagController extends AbstractController
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
$tags = $repository->getTagCount($query);
return $this->render('tags/index.html.twig', [
'tags' => $tags,
'query' => $query,
'showFilter' => $query->isDirty(),
'toolbarForm' => $form->createView(),
]);
}

View File

@@ -53,18 +53,20 @@ class TeamController extends AbstractController
{
$query = new TeamQuery();
$query->setPage($page);
$query->setOrderBy('name');
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
$teams = $repository->getPagerfantaForQuery($query);
return $this->render('team/index.html.twig', [
'teams' => $teams,
'query' => $query,
'showFilter' => $query->isDirty(),
'toolbarForm' => $form->createView(),
]);
}

View File

@@ -90,13 +90,15 @@ abstract class TimesheetAbstractController extends AbstractController
$form->setData($query);
$form->submit($request->query->all(), false);
if ($form->isValid()) {
if (null !== $query->getBegin()) {
$query->getBegin()->setTime(0, 0, 0);
}
if (null !== $query->getEnd()) {
$query->getEnd()->setTime(23, 59, 59);
}
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
if (null !== $query->getBegin()) {
$query->getBegin()->setTime(0, 0, 0);
}
if (null !== $query->getEnd()) {
$query->getEnd()->setTime(23, 59, 59);
}
$tags = $query->getTags(true);
@@ -110,8 +112,6 @@ abstract class TimesheetAbstractController extends AbstractController
);
}
$dirtyQuery = $query->isDirty();
$this->prepareQuery($query);
$pager = $this->getRepository()->getPagerfantaForQuery($query);
@@ -120,7 +120,6 @@ abstract class TimesheetAbstractController extends AbstractController
'entries' => $pager,
'page' => $query->getPage(),
'query' => $query,
'showFilter' => $dirtyQuery,
'toolbarForm' => $form->createView(),
'showSummary' => $this->includeSummary(),
'showStartEndTime' => $this->canSeeStartEndTime()

View File

@@ -66,19 +66,21 @@ class UserController extends AbstractController
{
$query = new UserQuery();
$query->setPage($page);
$query->setOrderBy('username');
$form = $this->getToolbarForm($query);
$form->setData($query);
$form->submit($request->query->all(), false);
if (!$form->isValid()) {
$query->resetByFormError($form->getErrors());
}
/* @var $entries Pagerfanta */
$entries = $this->getRepository()->findByQuery($query);
return $this->render('user/index.html.twig', [
'entries' => $entries,
'query' => $query,
'showFilter' => $query->isDirty(),
'toolbarForm' => $form->createView(),
]);
}

View File

@@ -128,6 +128,7 @@ class CustomerFixtures extends Fixture
->setName($faker->catchPhrase . (self::INDICATE_HIDDEN && !$visible ? ' (x)' : ''))
->setComment($faker->text)
->setCustomer($customer)
->setOrderNumber('P-' . $faker->ean8)
->setVisible($visible)
;

View File

@@ -23,6 +23,7 @@ use App\Repository\ActivityRepository;
use App\Repository\CustomerRepository;
use App\Repository\ProjectRepository;
use App\Repository\Query\ActivityFormTypeQuery;
use App\Repository\Query\BaseQuery;
use App\Repository\Query\CustomerFormTypeQuery;
use App\Repository\Query\ProjectFormTypeQuery;
use App\Repository\Query\TimesheetQuery;
@@ -32,6 +33,7 @@ use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Validator\Constraints\Choice;
/**
* Defines the base form used for all toolbars.
@@ -51,9 +53,15 @@ abstract class AbstractToolbarForm extends AbstractType
return '';
}
/**
* Returns whether the Javascript select-picker is allowed.
* TODO can be removed... once all bugs are fixed.
*
* @return bool
*/
protected function getSelectpickerConfig(): bool
{
return false;
return true;
}
protected function addUserChoice(FormBuilderInterface $builder)
@@ -203,6 +211,24 @@ abstract class AbstractToolbarForm extends AbstractType
]);
}
protected function addHiddenOrder(FormBuilderInterface $builder)
{
$builder->add('order', HiddenType::class, [
'constraints' => [
new Choice(['choices' => [BaseQuery::ORDER_ASC, BaseQuery::ORDER_DESC]])
]
]);
}
protected function addHiddenOrderBy(FormBuilderInterface $builder, array $allowedColumns)
{
$builder->add('orderBy', HiddenType::class, [
'constraints' => [
new Choice(['choices' => $allowedColumns])
]
]);
}
protected function addTagInputField(FormBuilderInterface $builder)
{
$builder->add('tags', TagsInputType::class, [

View File

@@ -40,6 +40,8 @@ class ActivityToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, ActivityQuery::ACTIVITY_ORDER_ALLOWED);
}
/**

View File

@@ -27,6 +27,8 @@ class CustomerToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, CustomerQuery::CUSTOMER_ORDER_ALLOWED);
}
/**

View File

@@ -28,6 +28,8 @@ class ProjectToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, ProjectQuery::PROJECT_ORDER_ALLOWED);
}
/**

View File

@@ -23,6 +23,8 @@ class TagToolbarForm extends AbstractToolbarForm
$this->addSearchTermInputField($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, TagQuery::TAG_ORDER_ALLOWED);
}
/**

View File

@@ -23,6 +23,8 @@ class TeamToolbarForm extends AbstractToolbarForm
$this->addSearchTermInputField($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, TeamQuery::TEAM_ORDER_ALLOWED);
}
/**

View File

@@ -35,6 +35,8 @@ class TimesheetToolbarForm extends AbstractToolbarForm
$this->addTimesheetStateChoice($builder);
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, TimesheetQuery::TIMESHEET_ORDER_ALLOWED);
}
/**

View File

@@ -28,6 +28,8 @@ class UserToolbarForm extends AbstractToolbarForm
$this->addVisibilityChoice($builder, 'label.active');
$this->addPageSizeChoice($builder);
$this->addHiddenPagination($builder);
$this->addHiddenOrder($builder);
$this->addHiddenOrderBy($builder, UserQuery::USER_ORDER_ALLOWED);
}
/**

View File

@@ -245,9 +245,23 @@ class ActivityRepository extends EntityRepository
->from(Activity::class, 'a')
->leftJoin('a.project', 'p')
->leftJoin('p.customer', 'c')
->addOrderBy('a.' . $query->getOrderBy(), $query->getOrder())
;
$orderBy = $query->getOrderBy();
switch ($orderBy) {
case 'project':
$orderBy = 'p.name';
break;
case 'customer':
$orderBy = 'c.name';
break;
default:
$orderBy = 'a.' . $orderBy;
break;
}
$qb->addOrderBy($orderBy, $query->getOrder());
$where = $qb->expr()->andX();
if (in_array($query->getVisibility(), [ActivityQuery::SHOW_VISIBLE, ActivityQuery::SHOW_HIDDEN])) {

View File

@@ -212,9 +212,20 @@ class ProjectRepository extends EntityRepository
->select('p')
->from(Project::class, 'p')
->leftJoin('p.customer', 'c')
->addOrderBy('p.' . $query->getOrderBy(), $query->getOrder())
;
$orderBy = $query->getOrderBy();
switch ($orderBy) {
case 'customer':
$orderBy = 'c.name';
break;
default:
$orderBy = 'p.' . $orderBy;
break;
}
$qb->addOrderBy($orderBy, $query->getOrder());
if (in_array($query->getVisibility(), [ProjectQuery::SHOW_VISIBLE, ProjectQuery::SHOW_HIDDEN])) {
$qb
->andWhere($qb->expr()->eq('p.visible', ':visible'))

View File

@@ -16,6 +16,8 @@ use App\Entity\Project;
*/
class ActivityQuery extends ProjectQuery
{
public const ACTIVITY_ORDER_ALLOWED = ['id', 'name', 'comment', 'customer', 'project'];
/**
* @var Project|int|null
*/
@@ -28,7 +30,9 @@ class ActivityQuery extends ProjectQuery
public function __construct()
{
parent::__construct();
$this->setOrderBy('name');
$this->setDefaults([
'orderBy' => 'name',
]);
}
/**
@@ -68,24 +72,4 @@ class ActivityQuery extends ProjectQuery
return $this;
}
/**
* {@inheritdoc}
*/
public function isDirty(): bool
{
if (parent::isDirty()) {
return true;
}
if ($this->project !== null) {
return true;
}
if ($this->globalsOnly !== false) {
return true;
}
return false;
}
}

View File

@@ -12,6 +12,7 @@ namespace App\Repository\Query;
use App\Entity\Team;
use App\Entity\User;
use App\Utils\SearchTerm;
use Symfony\Component\Form\FormErrorIterator;
/**
* Base class for advanced Repository queries.
@@ -28,6 +29,13 @@ class BaseQuery
public const RESULT_TYPE_PAGER = 'PagerFanta';
public const RESULT_TYPE_QUERYBUILDER = 'QueryBuilder';
private $defaults = [
'page' => self::DEFAULT_PAGE,
'pageSize' => self::DEFAULT_PAGESIZE,
'orderBy' => 'id',
'order' => self::ORDER_ASC,
'searchTerm' => null,
];
/**
* @var int
*/
@@ -214,29 +222,43 @@ class BaseQuery
return $this;
}
/**
* Returns whether the query has changed fields, compared to the original state.
*
* @return bool
*/
public function isDirty(): bool
protected function set($name, $value)
{
if ($this->page !== self::DEFAULT_PAGE) {
return true;
$method = 'set' . ucfirst($name);
if (method_exists($this, $method)) {
$this->{$method}($value);
} elseif (property_exists($this, $name)) {
$this->$name = $value;
}
}
/**
* @param array $defaults
* @return self
*/
protected function setDefaults(array $defaults)
{
$this->defaults = array_merge($this->defaults, $defaults);
foreach ($this->defaults as $key => $value) {
$this->set($key, $value);
}
if ($this->pageSize !== self::DEFAULT_PAGESIZE) {
return true;
return $this;
}
/**
* @param FormErrorIterator $errors
* @return self
*/
public function resetByFormError(FormErrorIterator $errors)
{
foreach ($errors as $error) {
$key = $error->getOrigin()->getName();
if (array_key_exists($key, $this->defaults)) {
$this->set($key, $this->defaults[$key]);
}
}
if (!empty($this->teams)) {
return true;
}
if (null !== $this->searchTerm) {
return true;
}
return false;
return $this;
}
}

View File

@@ -14,8 +14,12 @@ namespace App\Repository\Query;
*/
class CustomerQuery extends VisibilityQuery
{
public const CUSTOMER_ORDER_ALLOWED = ['id', 'name', 'comment', 'country', 'number'];
public function __construct()
{
$this->setOrderBy('name');
$this->setDefaults([
'orderBy' => 'name',
]);
}
}

View File

@@ -16,6 +16,8 @@ use App\Entity\Customer;
*/
class ProjectQuery extends CustomerQuery
{
public const PROJECT_ORDER_ALLOWED = ['id', 'name', 'comment', 'customer', 'orderNumber'];
/**
* @var Customer|int|null
*/
@@ -24,7 +26,9 @@ class ProjectQuery extends CustomerQuery
public function __construct()
{
parent::__construct();
$this->setOrderBy('name');
$this->setDefaults([
'orderBy' => 'name',
]);
}
/**
@@ -45,20 +49,4 @@ class ProjectQuery extends CustomerQuery
return $this;
}
/**
* {@inheritdoc}
*/
public function isDirty(): bool
{
if (parent::isDirty()) {
return true;
}
if ($this->customer !== null) {
return true;
}
return false;
}
}

View File

@@ -11,8 +11,12 @@ namespace App\Repository\Query;
class TagQuery extends BaseQuery
{
public const TAG_ORDER_ALLOWED = ['id', 'name', 'amount'];
public function __construct()
{
$this->setOrderBy('name');
$this->setDefaults([
'orderBy' => 'name',
]);
}
}

View File

@@ -11,8 +11,12 @@ namespace App\Repository\Query;
class TeamQuery extends BaseQuery
{
public const TEAM_ORDER_ALLOWED = ['id', 'name', 'teamlead'];
public function __construct()
{
$this->setOrderBy('name');
$this->setDefaults([
'orderBy' => 'name',
]);
}
}

View File

@@ -25,6 +25,8 @@ class TimesheetQuery extends ActivityQuery
public const STATE_EXPORTED = 4;
public const STATE_NOT_EXPORTED = 5;
public const TIMESHEET_ORDER_ALLOWED = ['begin', 'end', 'duration', 'rate', 'customer', 'project', 'activity', 'description'];
/**
* @var User|null
*/
@@ -57,9 +59,11 @@ class TimesheetQuery extends ActivityQuery
public function __construct()
{
parent::__construct();
$this->setOrder(self::ORDER_DESC);
$this->setOrderBy('begin');
$this->dateRange = new DateRange();
$this->setDefaults([
'order' => self::ORDER_DESC,
'orderBy' => 'begin',
'dateRange' => new DateRange()
]);
}
public function addUser(User $user): self
@@ -266,40 +270,4 @@ class TimesheetQuery extends ActivityQuery
return $this;
}
/**
* {@inheritdoc}
*/
public function isDirty(): bool
{
if (parent::isDirty()) {
return true;
}
if ($this->activity !== null) {
return true;
}
if (!empty($this->tags)) {
return true;
}
if ($this->timesheetUser !== null) {
return true;
}
if ($this->state !== self::STATE_ALL) {
return true;
}
if ($this->exported !== self::STATE_ALL) {
return true;
}
if ($this->dateRange->getBegin() !== null || $this->dateRange->getEnd() !== null) {
return true;
}
return false;
}
}

View File

@@ -14,11 +14,20 @@ namespace App\Repository\Query;
*/
class UserQuery extends VisibilityQuery
{
public const USER_ORDER_ALLOWED = ['id', 'alias', 'username', 'title', 'email'];
/**
* @var string|null
*/
protected $role;
public function __construct()
{
$this->setDefaults([
'orderBy' => 'username',
]);
}
/**
* @return string|null
*/

View File

@@ -54,20 +54,4 @@ class VisibilityQuery extends BaseQuery
return $this;
}
/**
* {@inheritdoc}
*/
public function isDirty(): bool
{
if (parent::isDirty()) {
return true;
}
if ($this->visibility !== self::SHOW_VISIBLE) {
return true;
}
return false;
}
}

View File

@@ -105,9 +105,20 @@ class TagRepository extends EntityRepository
->leftJoin('tag.timesheets', 'timesheets')
->addGroupBy('tag.id')
->addGroupBy('tag.name')
->addOrderBy('tag.' . $query->getOrderBy(), $query->getOrder())
;
$orderBy = $query->getOrderBy();
switch ($orderBy) {
case 'amount':
$orderBy = 'amount';
break;
default:
$orderBy = 'tag.' . $orderBy;
break;
}
$qb->addOrderBy($orderBy, $query->getOrder());
if ($query->hasSearchTerm()) {
$searchTerm = $query->getSearchTerm();
$searchAnd = $qb->expr()->andX();

View File

@@ -104,9 +104,21 @@ class TeamRepository extends EntityRepository
$qb
->select('t')
->from(Team::class, 't')
->addOrderBy('t.' . $query->getOrderBy(), $query->getOrder())
;
$orderBy = $query->getOrderBy();
switch ($orderBy) {
case 'teamlead':
$qb->leftJoin('t.teamlead', 'lead');
$orderBy = 'lead.username';
break;
default:
$orderBy = 't.' . $orderBy;
break;
}
$qb->addOrderBy($orderBy, $query->getOrder());
if (!empty($query->getSearchTerm())) {
$qb->andWhere(
$qb->expr()->orX(

View File

@@ -493,7 +493,6 @@ class TimesheetRepository extends EntityRepository
}
$qb
->leftJoin('p.customer', 'c')
->leftJoin('p.teams', 'teams')
->leftJoin('c.teams', 'c_teams');
@@ -551,9 +550,28 @@ class TimesheetRepository extends EntityRepository
->select('t')
->from(Timesheet::class, 't')
->leftJoin('t.project', 'p')
->addOrderBy('t.' . $query->getOrderBy(), $query->getOrder())
->leftJoin('p.customer', 'c')
;
$orderBy = $query->getOrderBy();
switch ($orderBy) {
case 'project':
$orderBy = 'p.name';
break;
case 'customer':
$orderBy = 'c.name';
break;
case 'activity':
$qb->leftJoin('t.activity', 'a');
$orderBy = 'a.name';
break;
default:
$orderBy = 't.' . $orderBy;
break;
}
$qb->addOrderBy($orderBy, $query->getOrder());
$user = [];
if (null !== $query->getUser()) {
$user[] = $query->getUser();

View File

@@ -86,9 +86,11 @@ class UserRepository extends EntityRepository implements UserLoaderInterface
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->select('u')
$qb
->select('u')
->from(User::class, 'u')
->orderBy('u.' . $query->getOrderBy(), $query->getOrder());
->orderBy('u.' . $query->getOrderBy(), $query->getOrder())
;
if (UserQuery::SHOW_VISIBLE == $query->getVisibility()) {
$qb->andWhere($qb->expr()->eq('u.enabled', ':enabled'));