Skip to content

Commit

Permalink
1.0.0-beta4, renamed huh.filter.registry to huh.filter.manager and im…
Browse files Browse the repository at this point in the history
…proved performance by using custom filter form submit route and POST/REDIRECT/GET Pattern
  • Loading branch information
Rico Kaltofen committed Mar 21, 2018
1 parent 8f736c0 commit 8872a8e
Show file tree
Hide file tree
Showing 54 changed files with 955 additions and 396 deletions.
2 changes: 1 addition & 1 deletion src/Backend/FilterConfigElement.php
Expand Up @@ -88,7 +88,7 @@ public function prepareChoiceTypes(DataContainer $dc)
return null;
}

if (null === ($filter = System::getContainer()->get('huh.filter.registry')->findById($filterConfigElement->pid))) {
if (null === ($filter = System::getContainer()->get('huh.filter.manager')->findById($filterConfigElement->pid))) {
return null;
}

Expand Down
87 changes: 87 additions & 0 deletions src/Config/FilterConfig.php
Expand Up @@ -9,15 +9,19 @@
namespace HeimrichHannot\FilterBundle\Config;

use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
use Contao\InsertTags;
use Contao\System;
use HeimrichHannot\FilterBundle\Session\FilterSession;
use HeimrichHannot\FilterBundle\Filter\AbstractType;
use HeimrichHannot\FilterBundle\Form\Extension\FormButtonExtension;
use HeimrichHannot\FilterBundle\Form\Extension\FormTypeExtension;
use HeimrichHannot\FilterBundle\Form\FilterType;
use HeimrichHannot\FilterBundle\Model\FilterConfigElementModel;
use HeimrichHannot\FilterBundle\QueryBuilder\FilterQueryBuilder;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Forms;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\PropertyAccess\PropertyAccess;

class FilterConfig
Expand Down Expand Up @@ -164,6 +168,89 @@ public function initQueryBuilder()
}
}

/**
* @param mixed $request The request to handle
* @return RedirectResponse|null
*/
public function handleForm($request = null): ?RedirectResponse
{
if (null === $this->getBuilder()) {
$this->buildForm($this->getData());
}

if (null === $this->getBuilder()) {
return null;
}

try {
$form = $this->getBuilder()->getForm();
} catch (TransformationFailedException $e) {
// for instance field changed from single to multiple value, transform old session data will throw an TransformationFailedException -> clear session and build again with empty data
$this->resetData();
$this->buildForm($this->getData());
$form = $this->getBuilder()->getForm();
}

$form->handleRequest($request);

// redirect back to tl_filter_config.action or given referrer
$url = $this->getUrl() ?: $form->get(FilterType::FILTER_REFERRER_NAME)->getData();

if ($form->isSubmitted() && $form->isValid()) {
if (!$form->has(FilterType::FILTER_ID_NAME)) {
return null;
}

// form id must match
if ((int)$form->get(FilterType::FILTER_ID_NAME)->getData() !== $this->getId()) {
return null;
}

$data = $form->getData();
$url = System::getContainer()->get('huh.utils.url')->removeQueryString([$form->getName()], $url ?: null);

// do not save filter id in session
$this->setData($data);

// allow reset, support different form configuration with same form name
if (null !== $form->getClickedButton() && in_array($form->getClickedButton()->getName(), $this->getResetNames(), true)) {
$this->resetData();
// redirect to referrer page without filter parameters
$url = System::getContainer()->get('huh.utils.url')->removeQueryString([$form->getName()], $form->get(FilterType::FILTER_REFERRER_NAME)->getData() ?: null);
}

return new RedirectResponse($url, 303);
}

return null;
}

/**
* Get the redirect url based on current filter action.
*
* @return string
*/
protected function getUrl()
{
$filter = $this->getFilter();

if (!isset($filter['action'])) {
return '';
}

/**
* @var InsertTags
*/
$insertTagAdapter = $this->framework->createInstance(InsertTags::class);

// while unit testing, the mock object cant be instantiated
if (null === $insertTagAdapter) {
$insertTagAdapter = $this->framework->getAdapter(InsertTags::class);
}

return '/' . urldecode($insertTagAdapter->replace($filter['action']));
}

/**
* @return int|null
*/
Expand Down
34 changes: 30 additions & 4 deletions src/Controller/FrontendController.php
Expand Up @@ -9,16 +9,42 @@
namespace HeimrichHannot\FilterBundle\Controller;


use Symfony\Component\HttpFoundation\Response;
use Contao\FrontendCron;
use HeimrichHannot\FilterBundle\Exception\HandleFormException;
use HeimrichHannot\FilterBundle\Exception\MissingFilterException;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class FrontendController extends \Contao\CoreBundle\Controller\FrontendController
/**
* Handles the filter frontend routes.
*
* @Route(defaults={"_scope" = "frontend", "_token_check" = true})
*/
class FrontendController extends Controller
{

/**
* @return Response
* @Route("/_filter/submit/{id}", name="filter_frontend_submit")
*
* @param int $id Filter id
*
* @throws MissingFilterException
* @throws HandleFormException
* @return RedirectResponse
*/
public function submit()
public function submitAction(int $id): RedirectResponse
{
$this->get('contao.framework')->initialize();

if (null === ($filter = $this->get('huh.filter.manager')->findById($id))) {
throw new MissingFilterException('A filter with id ' . $id . ' does not exist.');
}

if (null === ($response = $filter->handleForm())) {
throw new HandleFormException('Unable to handle form for filter with id ' . $id . '.');
}

return $response;
}
}
13 changes: 13 additions & 0 deletions src/Exception/HandleFormException.php
@@ -0,0 +1,13 @@
<?php

/*
* Copyright (c) 2018 Heimrich & Hannot GmbH
*
* @license LGPL-3.0-or-later
*/

namespace HeimrichHannot\FilterBundle\Exception;

class HandleFormException extends \InvalidArgumentException
{
}
13 changes: 13 additions & 0 deletions src/Exception/MissingFilterException.php
@@ -0,0 +1,13 @@
<?php

/*
* Copyright (c) 2018 Heimrich & Hannot GmbH
*
* @license LGPL-3.0-or-later
*/

namespace HeimrichHannot\FilterBundle\Exception;

class MissingFilterException extends \InvalidArgumentException
{
}
4 changes: 2 additions & 2 deletions src/Filter/Type/DateRangeType.php
Expand Up @@ -56,7 +56,7 @@ public function buildQuery(FilterQueryBuilder $builder, FilterConfigElementModel
$startName = $this->startElement->getFormName($this->config);
$stopName = $this->stopElement->getFormName($this->config);

/** @var $startDate \DateTime|null */
/** @var \DateTime|null $startDate */
$startDate = $startDate = $data[$name][$startName] ?? 0;

if ($this->startElement->isInitial) {
Expand All @@ -66,7 +66,7 @@ public function buildQuery(FilterQueryBuilder $builder, FilterConfigElementModel
$startDate = System::getContainer()->get('huh.utils.date')->getTimeStamp($startDate, true);
}

/** @var $stopDate \DateTime|null */
/** @var \DateTime|null $stopDate */
$stopDate = $data[$name][$stopName] ?? 9999999999999;

if ($this->stopElement->isInitial) {
Expand Down
2 changes: 1 addition & 1 deletion src/Filter/Type/DateTimeType.php
Expand Up @@ -44,7 +44,7 @@ public function buildQuery(FilterQueryBuilder $builder, FilterConfigElementModel
$value = System::getContainer()->get('huh.utils.date')->getTimeStamp($value, true);
}

/** @var $startDate \DateTime|null */
/** @var \DateTime|null $startDate */
$value = System::getContainer()->get('huh.utils.date')->getTimeStamp($value, false);

$minDate = $this->getMinDate($element);
Expand Down
2 changes: 1 addition & 1 deletion src/Filter/Type/DateType.php
Expand Up @@ -48,7 +48,7 @@ public function buildQuery(FilterQueryBuilder $builder, FilterConfigElementModel
$value = System::getContainer()->get('huh.utils.date')->getTimeStamp($value, true);
}

/** @var $startDate \DateTime|null */
/** @var \DateTime|null $startDate */
$value = System::getContainer()->get('huh.utils.date')->getTimeStamp($value, false);

$minDate = $this->getMinDate($element);
Expand Down
2 changes: 1 addition & 1 deletion src/Filter/Type/TimeType.php
Expand Up @@ -44,7 +44,7 @@ public function buildQuery(FilterQueryBuilder $builder, FilterConfigElementModel
$value = System::getContainer()->get('huh.utils.date')->getTimeStamp($value, true);
}

/** @var $startDate \DateTime|null */
/** @var \DateTime|null $startDate */
$value = System::getContainer()->get('huh.utils.date')->getTimeStamp($value, false);

$minDate = $this->getMinDate($element);
Expand Down
32 changes: 15 additions & 17 deletions src/Form/FilterType.php
Expand Up @@ -10,6 +10,7 @@

use Contao\CoreBundle\Framework\ContaoFrameworkInterface;
use Contao\InsertTags;
use Contao\System;
use HeimrichHannot\FilterBundle\Config\FilterConfig;
use HeimrichHannot\FilterBundle\Exception\MissingFilterConfigException;
use Symfony\Component\Form\AbstractType;
Expand All @@ -21,7 +22,8 @@

class FilterType extends AbstractType
{
const FILTER_ID_NAME = 'f_id';
const FILTER_ID_NAME = 'f_id';
const FILTER_REFERRER_NAME = 'f_ref';

/**
* @var FilterConfig|null
Expand Down Expand Up @@ -52,9 +54,14 @@ public function buildForm(FormBuilderInterface $builder, array $options)
$builder->setMethod($filter['method']);
}

$request = System::getContainer()->get('request_stack')->getCurrentRequest();

// always add a hidden field with the filter id
$builder->add(static::FILTER_ID_NAME, HiddenType::class, ['attr' => ['value' => $this->config->getId()]]);

// always add a hidden field with the referrer url (required by reset for example to redirect back to user action page)
$builder->add(static::FILTER_REFERRER_NAME, HiddenType::class, ['attr' => ['value' => $request->getUri()]]);

$this->buildElements($builder, $options);
}

Expand Down Expand Up @@ -186,28 +193,19 @@ protected function buildWrapperElements(array $wrappers, FormBuilderInterface $b
}

/**
* Get the action based on current filter action.
*
* @return string
* Get the form action url to internal filter_frontend_submit action
*/
protected function getAction()
{
$filter = $this->config->getFilter();
$router = System::getContainer()->get('router');

if (!isset($filter['action'])) {
return '';
}

/**
* @var InsertTags
*/
$insertTagAdapter = $this->framework->createInstance(InsertTags::class);
$filter = $this->config->getFilter();

// while unit testing, the mock object cant be instantiated
if (null === $insertTagAdapter) {
$insertTagAdapter = $this->framework->getAdapter(InsertTags::class);
if(!isset($filter['id']))
{
return null;
}

return urldecode($insertTagAdapter->replace($filter['action']));
return $router->generate('filter_frontend_submit', ['id' => $filter['id']]);
}
}

0 comments on commit 8872a8e

Please sign in to comment.