lexik / lexikformfilterbundle Goto Github PK
View Code? Open in Web Editor NEWThis Symfony bundle aim to provide classes to build some form filters and then build a doctrine query from this form filter.
License: MIT License
This Symfony bundle aim to provide classes to build some form filters and then build a doctrine query from this form filter.
License: MIT License
Hello, in the FilterBuilderUpdater class you have:
/**
* Prepare all values needed to apply the filter
*
* @param FormInterface $form
* @return array
*/
protected function prepareFilterValues(FormInterface $form)
{
$config = $form->getConfig();
$values = $this->dataExtractor->extractData($form, $config->getOption('data_extraction_method', 'default'));
//....
}
The name of the form attribute should be 'extractor_data' as in the form extension, otherwise the customs data extractors won't works.
Thanks 👍
Hi
Very good work, just installed in 2.1.1 and worked like a charm.
But I noticed I can do a wildcarded search only by adding '%' with my search term in the input text.
Is there a way to do wildcard search without adding by hand '%', by configuring something for example ?
The latest tag, v2.0.0, is restricted to 2.3
I was forced to specify 2.3.4 in required-dev
because of this, which causes the violation you can see here.
Two solutions :
require-dev
constraint and fix the testsrequire-dev
constraint and fix the require
section to say that this bundle is not compatible with 2.4.*In the code I've found lots of indications that you're busy deprecating all /Filter/ORM/*
classes. Can you provide more information/documentation about this process for all current users of the bundle?
I had to figure out myself where the errors came from after updating the library and that is fine, but a CHANGELOG.md
and/or UPGRADE.md
file would have been usefull.
In my example, the code changed from the following:
'apply_filter' => function (QueryBuilder $filterBuilder, Expr $expr, $field, $values) {
if (!empty($values['value']) && count($values['value']) > 0) {
// Parse array from value entities
$filterValues = array();
foreach($values['value'] as $lang) {
$filterValues[] = $lang->getId();
}
$filterBuilder->leftJoin('e.languages', 'l');
$filterBuilder->andWhere($expr->inEq('l.id', $filterValues));
}
},
To the following in your v2.x tag
'apply_filter' => function (ORMQuery $filterBuilder, $field, $values) {
if (!empty($values['value']) && count($values['value']) > 0) {
// Parse array from value entities
$filterValues = array();
foreach($values['value'] as $lang) {
$filterValues[] = $lang->getId();
}
$filterBuilder->getQueryBuilder()->leftJoin('e.languages', 'l');
$filterBuilder->getQueryBuilder()->andWhere($filterBuilder->getExpr()->in('l.id', $filterValues));
}
},
I pass specific options to my DateTime fields in order to show them as HTML5 date fields. This is possible by adding the following array as the 3th parameter in the formbuilder.
$datetimeAttributes = array(
'date_widget' => 'single_text',
'time_widget' => 'single_text',
);
The date range filter in this bundle does not support those parameters, which results in many separate fields. Any idea on how to make HTML5 fields possible in the library?
http://www.html5tutorial.info/html5-date.php
http://www.w3schools.com/html/html5_form_input_types.asp
Would be interesting if we could define fields for filtering values equal to null or empty.
I Symfony 1.4 the field has "with_empty" option. http://www.symfony-project.org/api/1_4/sfWidgetFormFilterInput
It is seen as follows: http://minus.com/lq0InptLee8Uf
nevermind, issue is not in this bundle
Hi,
I have created 2 FilterTypes VisiteFilterType
and VilleFilterType
and I want to embed VilleFilterType
in VisiteFilterType
. The question is how could I partly embed the filter VilleFilterType
because I don't need all filters in it.
Here is my code:
<?php
namespace BECFrance\BackBundle\Filter;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\QueryBuilder;
use Lexik\Bundle\FormFilterBundle\Filter\FilterBuilderExecuterInterface;
use Doctrine\ORM\Query\Expr;
use Lexik\Bundle\FormFilterBundle\Filter\Extension\Type\FilterTypeSharedableInterface;
class VisiteFilterType extends AbstractType implements FilterTypeSharedableInterface{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('libelle', 'filter_text')
->add('ville', 'filter_entity', array('class'=>'BECFranceBackBundle:Ville', 'property'=>'nom'));
}
public function getName()
{
return 'becfrance_backbundle_filter_visite';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'csrf_protection' => false,
'validation_groups' => array('filtering') // avoid NotBlank() constraint-related message
));
}
public function addShared(FilterBuilderExecuterInterface $qbe)
{
$closure = function(QueryBuilder $filterBuilder, $alias, $joinAlias, Expr $expr) {
// add the join clause to the doctrine query builder
// the where clause for the label and color fields will be added automatically with the right alias later by the Lexik\Filter\QueryBuilderUpdater
$filterBuilder->leftJoin($alias.'.visite', 'visite');
};
// then use the query builder executor to define the join, the join's alias and things to do on the doctrine query builder.
$qbe->addOnce($qbe->getAlias().'.visite', 'visite', $closure);
}
}
<?php
namespace BECFrance\BackBundle\Filter;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\ORM\QueryBuilder;
use Lexik\Bundle\FormFilterBundle\Filter\FilterBuilderExecuterInterface;
use Lexik\Bundle\FormFilterBundle\Filter\Expr;
use Lexik\Bundle\FormFilterBundle\Filter\Extension\Type\FilterTypeSharedableInterface;
class VilleFilterType extends AbstractType implements FilterTypeSharedableInterface{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom', 'filter_text')
->add('codePostal', new CodePostalFilterType())
//->add('codePostal', 'filter_number')
->add('zoneGeographique', new ZoneGeographiqueFilterType())
->add('pays', new PaysFilterType);
}
public function getName()
{
return 'becfrance_backbundle_filter_ville';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'csrf_protection' => false,
'validation_groups' => array('filtering') // avoid NotBlank() constraint-related message
));
}
public function addShared(FilterBuilderExecuterInterface $qbe)
{
$closure = function(QueryBuilder $filterBuilder, $alias, $joinAlias, Expr $expr) {
// add the join clause to the doctrine query builder
// the where clause for the label and color fields will be added automatically with the right alias later by the Lexik\Filter\QueryBuilderUpdater
$filterBuilder->leftJoin($alias.'.ville', 'ville');
};
// then use the query builder executor to define the join, the join's alias and things to do on the doctrine query builder.
$qbe->addOnce($qbe->getAlias().'.ville', 'ville', $closure);
}
}
Actually I use filter_entity
as filter of Ville
, but I want to use the filter nom
in VilleFilterType
. I've tried to use new VilleFilterType
instead, but it returned all the filters in VilleFilterType
that there are some filters I don't need.
Any proposition?
Thanks
Hi, i was testing your bundle, but this erros came to me:
Error: Class 'DMS\Filter\Mapping\Loader\AnnotationLoader' not found ... vendor/dms/dms-filter-bundle/DMS/Bundle/FilterBundle/Service/Filter.php line 30.
Hi, and first of all, thanks for this bundle that I use a lot !
I would like to know if you plan to add the ability to filter with an OR
condition between sub-filters ?
For instance, I have a type named FooFilterType
that build its filter like that:
$builder->add('media', new MediaFilterForm());
$builder->add('contact', new ContactFilterForm());
In these sub-filters, I have some native filters. It works well but in want to encapsulate the both sub-filters into a OR
in the SQL query. I also need to say that these sub-filters are also used as alone filters.
Thanks! :)
Hi,
I've a abstract User inheritance entity.
/**
* @ORM\Mapping\Entity(repositoryClass="UserRepository")
* @ORM\Mapping\Table(name="user")
* @ORM\Mapping\InheritanceType("JOINED")
* @ORM\Mapping\DiscriminatorColumn(name="type", type="string")
* @ORM\Mapping\DiscriminatorMap({
* "Administrator" = "UserAdministrator",
* "SalesDirector" = "UserSalesDirector",
* "SalesManager" = "UserSalesManager",
* "SalesMan" = "UserSalesMan",
* "Prospect" = "UserProspect",
* "Educational" = "UserEducational",
* "ProfesionalDevelopment" = "UserProfesionalDevelopment",
* "Accounts" = "UserAccounts"
* })
*/
abstract class User extends FOSUserBundle\Model\User{}
i've a User listAction where i list all users.
i want add a filter
class UserFilterType extends LIGFormFilterBundle\Form\FormFilterType
{
public function buildForm(Form\FormBuilderInterface $builder, array $options)
{
parent::buildForm($builder, $options);
$typeChoices = array(
"Administrator" => "UserAdministrator",
"SalesDirector" => "UserSalesDirector",
"SalesManager" => "UserSalesManager",
"SalesMan" => "UserSalesMan",
"Prospect" => "UserProspect",
"Educational" => "UserEducational",
"ProfesionalDevelopment" => "UserProfesionalDevelopment",
"Accounts" => "UserAccounts"
);
$builder
->add('type', 'filter_choice', array(
'choices' => $typeChoices,
))
->add('firstname', 'filter_text')
->add('lastname', 'filter_text')
->add('email', 'filter_text')
->add('enabled', 'filter_boolean')
->add('last_login', 'filter_datetime_range')
;
$this->addSubmitAndReset($builder, $options);
}
public function setDefaultOptions(OptionsResolver\OptionsResolverInterface $resolver)
{
parent::setDefaultOptions($resolver);
$resolver->setDefaults(array(
'data_class' => 'LIG\Bundle\UserBundle\Entity\User',
));
}
public function getName()
{
return 'userfiltertype';
}
}
but when i submit the form i got :
FatalErrorException: Error: Cannot instantiate abstract class LIG\Bundle\UserBundle\Entity\User in /vendor/symfony/symfony/src/Symfony/Component/Form/Extension/Core/Type/FormType.php line 138
thx for your time
If we use a choice filter with an array of this type:
array(
0 => "foo",
1 => "Bar"
);
If the user chooses foo, php considers that empty(0) is true. However, this is not the case !
This is the same with text filter and number filter.
A solution is to verify if the value isn't NULL and isn't equal to ' ' in the filters.
/**
* {@inheritdoc}
*/
protected function apply(QueryBuilder $filterBuilder, Expr $expr, $field, array $values)
{
if ('' !== $values['value'] && null !== $values['value']) {
// foo
}
}
Before doing #13 some failing unit tests need to be resolved. Maybe they're even tied to same issue.
I tried tracking first issue myself, but couldn't exactly pinpoint why it's happening. It seems that data transformer is not fired on left_date string or something?
Specifically,
Lexik\Bundle\FormFilterBundle\Tests\Filter\QueryBuilderUpdaterTest::testDateRange
InvalidArgumentException: No value found for key "left_date" in form data.
and
Lexik\Bundle\FormFilterBundle\Tests\Filter\QueryBuilderUpdaterTest::testBuildQuery
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'SELECT i FROM Lexik\Bundle\FormFilterBundle\Tests\Fixtures\Entity i WHERE i.name LIKE '%blabla' AND i.position <= 2 AND i.createdAt = '2013-09-27''
+'SELECT i FROM Lexik\Bundle\FormFilterBundle\Tests\Fixtures\Entity i WHERE i.name LIKE '%blabla' AND i.position <= 2'
Hi,
I'd like to use your bundle with PHPCR-ODM which seems not currently supported. Do you ever thought about it? I could help with the implementation if pointed in the right direction.
After a brief investigation I'v seen that \Lexik\Bundle\FormFilterBundle\Event\Listener\PrepareListener has the responsibility to instantiate a \Lexik\Bundle\FormFilterBundle\Filter\Query\QueryInterface but it can't manage Doctrine\ODM\PHPCR\Query\Builder\QueryBuilder which is the one that is used for querying with PHPCR.
Apart from implementing a custom QueryInterface for PHPCR there is something else needed?
On June 1 a new mayor version of Symfony was released. The way how PropertyAccess
works was changed.
https://github.com/symfony/symfony/blob/master/CHANGELOG-2.5.md
This upgrade for me resulted in errors on filters with a custom apply_filter
. The error occurs when the filterform was submitted.
Neither the property "activePowerup" nor one of the methods
"addActivePowerup()"/"removeActivePowerup()", "setActivePowerup()", "activePowerup()",
"__set()" or "__call()" exist and have public access in class
"Acme\DemoBundle\Entity\User\Editor".
It seems like the bundle still tries to set/get the specific property of that entity, which it didn't do that before. The documentation states the following about ignoring getter/setter:
If you wish the field to be ignored when reading or writing to the object you can set the
property_path option to false, but using property_path for this purpose is deprecated, you
should use the mapped option.
http://symfony.com/doc/current/reference/forms/types/form.html
Setting mapped => false
in my FilterType solved this for me.
Hi!
I am using your bundle in my project and looks great! I have no problems with filter_text or filter_date fields ... however when I try to use filter_entity get this error:
Invalid parameter number: number of bound variables does not match number of tokens
Here is my code:
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('nombre', 'filter_text', array(
'condition_pattern' => FilterOperands::STRING_BOTH,
'label' => 'Nombre'));
$builder->add('categoria', 'filter_entity', array(
'class' => 'ProjectDeportivoBundle:Categoria',
'label' => 'Categoría'));
}
public function getName() {
return 'equipo_filter';
}
public function setDefaultOptions(OptionsResolverInterface $resolver) {
$resolver->setDefaults(array(
'csrf_protection' => false,
'validation_groups' => array('filtering')
));
}
public function indexAction() {
$em = $this->getDoctrine()->getManager();
$form = $this->get('form.factory')->create(new EquipoFilterType());
if ($this->get('request')->query->has('submit-filter')) {
$form->bind($this->get('request'));
$filterBuilder = $this->get('doctrine.orm.entity_manager')
->getRepository('ProjectDeportivoBundle:Equipo')
->createQueryBuilder('e');
$this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $filterBuilder);
$query = $em->createQuery($filterBuilder->getDql());
}
...
}
Can you help? You see something strange?
Thanks!
Please adapt composer.json
Is there any way to customize those labels?
I tried to override whole template file "form_div_layout.html" by copying source file and paste it in app/Resources/LexikFormFilterBundle/views/Form/form_div_layout.html and then add my own theme for filter_date_range_widget but this is not working - bundle is still using source file. Clearing the cache did not help :(
Also I tried adding label in filter type like this:
->add('made', 'filter_date_range', array(
'label' => 'Rok produkcji',
'left_date_options' => array('label' => 'od'),
'right_date_options' => array('label' => 'do'),
))
but this is not working.
Adding the 'multiple' => true option a filter_entity or filter_choice field it causes it to fail when the number of selections is not exactly 1.
If no selections are made I would expect it to ignore the filter, and if more than one is selected I would expect it to include results for both options.
I want to use datepicker instead of default filter_date_range, can anybody help me?
I'm still in the process of looking into the issue myself but it seems that you can't mix inheriting from both Form and PHP classes.
More information here symfony/symfony#4666
fix for the changes in sf 2.1 forms : https://github.com/symfony/symfony/blob/master/UPGRADE-2.1.md
Index: vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/BooleanFilterType.php
===================================================================
--- vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/BooleanFilterType.php (revision )
+++ vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/BooleanFilterType.php (revision )
@@ -49,7 +49,7 @@
/**
* {@inheritdoc}
*/
- public function getDefaultOptions(array $options)
+ public function getDefaultOptions()
{
return array(
'choices' => array(
Index: vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/ChoiceFilterType.php
===================================================================
--- vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/ChoiceFilterType.php (revision )
+++ vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/ChoiceFilterType.php (revision )
@@ -16,7 +16,8 @@
*/
public function getParent(array $options)
{
- return $options['expanded'] ? 'filter' : 'filter_field';
+ return isset($options['expanded']) && $options['expanded'] ? 'filter' : 'filter_field';
}
/**
Index: vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/FieldFilterType.php
===================================================================
--- vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/FieldFilterType.php (revision )
+++ vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/FieldFilterType.php (revision )
@@ -29,9 +29,9 @@
/**
* {@inheritdoc}
*/
- public function getDefaultOptions(array $options)
+ public function getDefaultOptions()
{
- $options = parent::getDefaultOptions($options);
+ $options = parent::getDefaultOptions();
$options['required'] = false;
$options['apply_filter'] = null;
Index: vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/NumberFilterType.php
===================================================================
--- vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/NumberFilterType.php (revision )
+++ vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/NumberFilterType.php (revision )
@@ -50,9 +50,9 @@
/**
* {@inheritdoc}
*/
- public function getDefaultOptions(array $options)
+ public function getDefaultOptions()
{
- $options = parent::getDefaultOptions($options);
+ $options = parent::getDefaultOptions();
$options['condition_operator'] = self::OPERATOR_EQUAL;
return $options;
Index: vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/NumberRangeFilterType.php
===================================================================
--- vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/NumberRangeFilterType.php (revision )
+++ vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/NumberRangeFilterType.php (revision )
@@ -28,7 +28,7 @@
/**
* {@inheritdoc}
*/
- public function getDefaultOptions(array $options)
+ public function getDefaultOptions()
{
return array(
'left_number' => array(),
Index: vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/TextFilterType.php
===================================================================
--- vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/TextFilterType.php (revision )
+++ vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/TextFilterType.php (revision )
@@ -49,9 +49,9 @@
/**
* {@inheritdoc}
*/
- public function getDefaultOptions(array $options)
+ public function getDefaultOptions()
{
- $options = parent::getDefaultOptions($options);
+ $options = parent::getDefaultOptions();
$options['condition_pattern'] = self::PATTERN_EQUALS;
return $options;
Can you create documentation on how to make your own filter type?
In my FilterType Classe i have :
$builder->add('created_at', 'filter_datetime_range', array(
'left_datetime' => array(
'date_widget' => 'single_text',
'time_widget' => 'single_text'
),
'right_datetime' => array(),
));
While debugging the where clause for datetime not show up on the Query.
when i debug the bundle i found on the FilterValueKeysTransformer class line 21
$data = $form->getData();
the $data is empty with (null value).
Otherwise, on the same $form i found the data as ExtratData
You can access to it with
$form->getParent()->getExtraData()
i am not sure if i did something wrong while using this format, let me know what do you think!!
When I use the selector to make users able to choose their own filter type, this results in two fields.
->add('email', 'filter_text', array(
'label' => 'E-mail',
'condition_pattern' => FilterOperands::OPERAND_SELECTOR,
))
Hi,
I am using your Bundle for my application. I am migrating a symfony 1.4 website to a symfony 2.1 and I have the following problem with the translations, since the doctrine extensions for 1.4 and 2.1 have a different approach. I have two entities, ProductOption (ProductOptionTranslation to keep the translation) and ProductOptionValue (ProductOptionValueTranslation to do the same as before). The relationship between ProductOption and ProductOptionValue is 1:N (i.e Chocolate/Strawberry/Vanilla are Flavours)
What I want is to filter in the index view of the ProductOptionValue by ProductOption, but the name of the ProductOption is in ProductOptionTranslation entity. So I created a new choice type with a datatransformer. The filter form show the choice perfectly, but the filter is not applied because in the code above the form does not have any children since I am using a DataTransformer and not a filter_XXXX field.
protected function addFilters(FormInterface $form, $filterBuilder, $alias = null, array &$parts = array(), $expr = null)
{
/** @var $child FormInterface */
foreach ($form->all() as $child) {
Here is the code:
<?php
namespace Ecomm\Bundle\CatalogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* ProductOption
*
* @ORM\Entity(repositoryClass="Ecomm\Bundle\CatalogBundle\Entity\Manager\ProductOptionManager")
* @ORM\Table(name="products_options")
* @ORM\HasLifecycleCallbacks
*/
class ProductOption
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(name="product_option_id", type="integer", nullable=false)
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionTranslation", mappedBy="productOption", cascade={"persist", "remove"})
*
*/
protected $translations;
protected $name;
/**
* @ORM\OneToMany(targetEntity="Ecomm\Bundle\CatalogBundle\Entity\ProductOptionValue", mappedBy="productOption")
**/
protected $productOptionValues;
/**
* Constructor
*/
public function __construct()
{
$this->resetTranslations();
$this->productOptionValues = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add translations
*
* @param \Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionTranslation $translations
* @return ProductOption
*/
public function addTranslation(\Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionTranslation $translation)
{
$this->translations[] = $translation;
$translation->setProductOption($this);
return $this;
}
/**
* Remove translations
*
* @param \Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionTranslation $translations
*/
public function removeTranslation(\Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionTranslation $translations)
{
$this->translations->removeElement($translations);
}
/**
* Get translations
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTranslations()
{
return $this->translations;
}
public function resetTranslations()
{
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getName()
{
return $this->name;
}
/**
*
* @ORM\PostLoad
*
*/
public function onPostLoad()
{
$this->name = array();
if ($this->translations)
{
foreach($this->translations as $translation)
{
$locale = $translation->getLocale();
$this->name[$locale] = $translation->getName();
}
}
}
/**
* Add productOptionValue
*
* @param \Ecomm\Bundle\CatalogBundle\Entity\ProductOptionValue $productOptionValue
* @return ProductOption
*/
public function addProductOptionValue(\Ecomm\Bundle\CatalogBundle\Entity\ProductOptionValue $productOptionValue)
{
$this->productOptionValues[] = $productOptionValue;
return $this;
}
/**
* Remove productOptionValue
*
* @param \Ecomm\Bundle\CatalogBundle\Entity\ProductOptionValue $productOptionValue
*/
public function removeProductOptionValue(\Ecomm\Bundle\CatalogBundle\Entity\ProductOptionValue $productOptionValue)
{
$this->productOptionValues->removeElement($productOptionValue);
}
/**
* Get productOptionValue
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getProductOptionValues()
{
return $this->productOptionValues;
}
}
<?php
namespace Ecomm\Bundle\CatalogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* ProductOptionValue
*
* @ORM\Entity(repositoryClass="Ecomm\Bundle\CatalogBundle\Entity\Manager\ProductOptionValueManager")
* @ORM\Table(name="products_options_values")
* @ORM\HasLifecycleCallbacks
*/
class ProductOptionValue
{
/**
* @var integer
*
* @ORM\Id
* @ORM\Column(name="product_option_value_id", type="integer", nullable=false)
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* @ORM\OneToMany(targetEntity="Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionValueTranslation", mappedBy="productOptionValue", cascade={"persist", "remove"})
*
*/
protected $translations;
/**
* @var \ProductsOptions
*
* @ORM\ManyToOne(targetEntity="Ecomm\Bundle\CatalogBundle\Entity\ProductOption", inversedBy="productOptionValues")
* @ORM\JoinColumn(name="product_option_id", referencedColumnName="product_option_id", onDelete="RESTRICT")
*/
protected $productOption;
protected $name;
/**
* Constructor
*/
public function __construct()
{
$this->resetTranslations();
}
public function __toString()
{
return "" . $this->name;
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Add translations
*
* @param \Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionValueTranslation $translations
* @return ProductOptionValue
*/
public function addTranslation(\Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionValueTranslation $translation)
{
$this->translations[] = $translation;
$translation->setProductOptionValue($this);
return $this;
}
/**
* Remove translations
*
* @param \Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionValueTranslation $translations
*/
public function removeTranslation(\Ecomm\Bundle\CatalogBundle\Entity\Translation\ProductOptionValueTranslation $translations)
{
$this->translations->removeElement($translations);
}
/**
* Get translations
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getTranslations()
{
return $this->translations;
}
public function resetTranslations()
{
$this->translations = new \Doctrine\Common\Collections\ArrayCollection();
}
public function getName()
{
return $this->name;
}
/**
*
* @ORM\PostLoad
*
*/
public function onPostLoad()
{
$this->name = array();
if ($this->translations)
{
foreach($this->translations as $translation)
{
$locale = $translation->getLocale();
$this->name[$locale] = $translation->getName();
}
}
}
/**
* Set productOption
*
* @param \Ecomm\Bundle\CatalogBundle\Entity\ProductOption $productOption
* @return ProductOptionValue
*/
public function setProductOption(\Ecomm\Bundle\CatalogBundle\Entity\ProductOption $productOption)
{
$this->productOption = $productOption;
return $this;
}
/**
* Get productOption
*
* @return \Ecomm\Bundle\CatalogBundle\Entity\ProductOption
*/
public function getProductOption()
{
return $this->productOption;
}
}
<?php
namespace Ecomm\Bundle\CatalogBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormError;
use Lexik\Bundle\FormFilterBundle\Filter\ORM\Expr;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Translation\Translator;
class ProductOptionValueFilterType extends AbstractType
{
/**
* @var ServiceContainer
*/
private $sc;
protected $translator;
public function __construct(ContainerInterface $container = null)
{
$this->sc = $container;
$this->translator = $this->sc->get('translator');
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', 'filter_text', array(
'label' => $this->translator->trans('form.filter.label.name'),
'apply_filter' => function (QueryBuilder $queryBuilder, Expr $expr, $field, array $values)
{
if (!empty($values['value']))
{
// add the join if you need it and it not already added
$alias = $queryBuilder->getRootAliases();
$queryBuilder->innerJoin($alias[0].'.translations', 'at');
$queryBuilder->andWhere('at.name = :name')
->setParameter('name', $values['value']);
}
},
))
/*
->add('productOption', 'filter_entity', array(
'label' => $this->translator->trans('form.filter.label.option'),
'class' => 'EcommCatalogBundle:ProductOption',
'empty_value' => 'Choose a Product Option'
))
*/
->add('productOption', $this->sc->get('ecomm.type.product_option_filter_field'));
;
$listener = function(FormEvent $event)
{
// Is data empty?
foreach ($event->getData() as $data) {
if(is_array($data)) {
foreach ($data as $subData) {
if(!empty($subData)) return;
}
}
else {
if(!empty($data)) return;
}
}
$event->getForm()->addError(new FormError('Filter empty'));
};
$builder->addEventListener(FormEvents::POST_BIND, $listener);
}
public function getName()
{
return 'ecomm_bundle_catalogbundle_productoptionvaluefiltertype';
}
}
<?php
namespace Ecomm\Bundle\CatalogBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Ecomm\Bundle\CatalogBundle\Form\DataTransformer\ProductOptionFieldTransformer;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Doctrine\ORM\QueryBuilder;
use Lexik\Bundle\FormFilterBundle\Filter\FilterBuilderExecuterInterface;
use Lexik\Bundle\FormFilterBundle\Filter\ORM\Expr;
use Lexik\Bundle\FormFilterBundle\Filter\Extension\Type\FilterTypeSharedableInterface;
class ProductOptionFilterFieldType extends AbstractType implements FilterTypeSharedableInterface
{
/**
* @var ServiceContainer
*/
private $sc;
public function __construct(ContainerInterface $container = null)
{
$this->sc = $container;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$transformer = new ProductOptionFieldTransformer($this->sc);
$builder->addModelTransformer($transformer);
}
/**
* This method aim to add all joins you need
*/
public function addShared(FilterBuilderExecuterInterface $qbe)
{
$closure = function(QueryBuilder $filterBuilder, $alias, $joinAlias, Expr $expr)
{
// add the join clause to the doctrine query builder
// the where clause for the label and color fields will be added automatically with the right alias later by the Lexik\Filter\QueryBuilderUpdater
$filterBuilder->innerJoin($alias . '.productOption', 'po');
};
// then use the query builder executor to define the join, the join's alias and things to do on the doctrine query builder.
$qbe->addOnce($qbe->getAlias().'.productOption', 'po', $closure);
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'invalid_message' => 'The selected productOption does not exist',
'empty_value' => 'Choose an option',
'choices' => $this->buildData(),
));
}
private function buildData()
{
$em = $this->sc->get('doctrine')->getManager();
$request = $this->sc->get('request');
$choices = array();
$productOptions = $em
->getRepository('EcommCatalogBundle:ProductOption')
->createQueryBuilder('c')
->select('c, ct')
->innerJoin('c.translations', 'ct')
->andWhere('ct.locale = :locale')
->setParameter('locale', $request->getLocale())
->orderBy('ct.name', 'ASC')
->getQuery()
->getResult();
foreach ($productOptions as $productOption)
{
// I assume key is retrieved by getId
$translation = $productOption->getTranslations();
$choices[$productOption->getId()] = $translation[0]->getName();
}
return $choices;
}
public function getName()
{
return 'product_option_filter_field';
}
public function getParent()
{
return 'choice';
}
}
I get this error message with BooleanFilterType.php in SF2.1, this fixes it :
--- vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/BooleanFilterType.php (revision )
+++ vendor/lexik/form-filter-bundle/Lexik/Bundle/FormFilterBundle/Filter/Extension/Type/BooleanFilterType.php (revision )
@@ -4,6 +4,7 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Translation\TranslatorInterface;
+use Symfony\Component\Translation\Translator;
use Doctrine\ORM\QueryBuilder;
@@ -78,7 +79,7 @@
public function getTransformerId()
{
- return 'default';
+ return 'lexik_filter.transformer.default';
}
/**
\ No newline at end of file
Hi, one question.
I'm using the filter_date_range
and I saw in the method convertToSqlDate
in class ExpressionBuilder
that converts the left date to 1970-01-01 00:00:00 and the right date to 2014-06-01 23:59:59, that is cool form me, but after the date formats to 'Y-m-d'.
I know that is the filter_date_range
not the filter_datetime_range
but I thing it will be nice to use the sql date time format 'Y-m-d H:i:s' in this tipe of filter.
Thanks
I get this error when filtering with 'entity_filter' type:
Invalid parameter format, : given, but : or ? expected.
I've solved replacing this two lines:
$fieldAlias = substr($field, strpos($field, '.') + 1);
$filterBuilder->andWhere($expr->eq($field, ':' . $fieldAlias))->setParameter($fieldAlias, $values['value']->getId());
For this:
$filterBuilder->andWhere($expr->eq($field, $values['value']->getId()));
Use case example:
Entity Student
has o2m relationship to Entity Test
, which has a property called result
.
A form should filter out all Student
entities that have result
value of Test
in a given number_range
filter.
Assuming a TestFilterType
class is added as a field of StudentFilterType
, there needs to be a way to join & query by embedded (child) form.
If my explanation is hard to understand, I can PR a unit test case.
I have an error that occurs in a filter from a relaccionados one to many relationship between the entity Employee and Position. But while Position has a one to many relationship with Area.
The error is this
Symfony\Component\HttpKernel\DataCollector\RequestDataCollector::serialize() must return a string or NULL
my filters for entities
->add('cargo', 'filter_entity', array(
'class' => 'PersonalBundle:Cargo',
'property' => 'nombre',
'query_builder' => function (\Doctrine\ORM\EntityRepository $repository) {
return $repository->createQueryBuilder('c')
->join('c.empleados', 'p')
->add('groupBy', 'c.nombre');
}
))
->add('area', 'filter_entity', array(
'class' => 'PersonalBundle:Area',
'property' => 'nombre',
'query_builder' => function (\Doctrine\ORM\EntityRepository $repository) {
return $repository->createQueryBuilder('a')
->join('a.empleados', 'p')
->add('groupBy', 'a.nombre');
}
))
I could not help how to fix.
Hi,
I need help for adding elements dinamically to formfilter based on input data.
I'm trying to face this with EventSubscriber but using form events with the bundle doesn't allow me to access to data class
in the pre_set_data.
This is what I do:
Controller Method
public function fleetManagerAction()
{
$fm=new FilterModel();
$fm->setUser($this->getUser());
$form = $this->get('form.factory')->create(new ContractFilterType(),$fm);
if ($this->get('request')->query->has('submit-filter')) {
// bind values from the request
$form->bindRequest($this->get('request'));
$em = $this->getDoctrine()->getManager('em_fol');
// initialize a query builder
$filterBuilder = $em->getRepository('AldFocusOnLineBundle:WebContract')->createQueryBuilder('c');
// build the query from the given form object
$this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $filterBuilder);
}
}
FormType
class ContractFilterType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$choices = array(
'1' =>'Hierarchy Level 1',
'2' =>'Hierarchy Level 2',
'3' =>'Hierarchy Level 3',
);
$builder->addEventSubscriber(new HierarchySelectorSubscriber());
$builder
->add('codPlate', 'filter_text',array('condition_pattern'=> FilterOperands::STRING_BOTH))
->add('dateBegin','filter_datetime_range')
// NO MAPPED DB FIELD: ONLY FOR DATA TRANSPORT
->add('hierarchy_level', 'filter_choice', array(
'choices' => $choices,
'multiple' => false,
'expanded' => true,
'apply_filter' => function(QueryInterface $filterQuery, $field, $values)
{ // Do nothing
return $filterQuery;
}))
;
}
/**
* {@inheritdoc}
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ald\CustomerArea\SecurityBundle\Model\FilterModel',
'csrf_protection' => false,
'validation_groups' => array('filter'),
));
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'web_contract_filter';
}
EventSubscriber
class HierarchySelectorSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
// Tells the dispatcher that you want to listen on the form.pre_set_data
// event and that the preSetData method should be called.
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
// Always NULL !!!!
var_dump($data->getHierarchyLevel());
switch ($data->getHierarchyLevel())
{
case '1':
$form->add('level1', 'filter_text',array('condition_pattern'=> FilterOperands::STRING_BOTH));
break;
case '2':
$form->add('level2', 'filter_text',array('condition_pattern'=> FilterOperands::STRING_BOTH));
break;
case '3':
$form->add('level3', 'filter_text',array('condition_pattern'=> FilterOperands::STRING_BOTH));
break;
default:
}
}
Data Class
class FilterModel extends WebContract
{
protected $hierarchy;
protected $hierarchy_level;
protected $user;
public function setUser($user)
{
$this->user = $user;
}
public function getUser()
{
return $this->user;
}
public function setHierarchy($hierarchy)
{
$this->hierarchy = $hierarchy;
}
public function getHierarchy()
{
return $this->hierarchy;
}
public function setHierarchyLevel($hierarchy_level)
{
$this->hierarchy_level = $hierarchy_level;
}
public function getHierarchyLevel()
{
return $this->hierarchy_level;
}
Using this solution, making the text filter case insensitive for all RDBMS' would be great.
How I can filter form a custom dql provided in my entityrepository.
Can you create a new tag, so I'm able to update my composer dependencies
I've got a many-to-many relationship between User and Group and I want to filter on the group in the user list. The 'filter_entity' works great, because it shows a list of all available groups:
->add('groups', 'filter_entity', array(
'class' => 'CompanyAppBundle:User\Group',
))
But, submitting the filterform results in a semantical error:
[Semantical Error] line 0, col 59 near 'groups = 2': Error: Invalid PathExpression.
StateFieldPathExpression or SingleValuedAssociationField expected.
Is it possible to add a check in the library to test on the type of relationship?
I have objects with NULL values of some field. How can I filter this objects with filter_entity field type?
I've tried this solution:
$builder
->add('lesson', 'filter_entity', array(
'class' => 'Salem\EntityBundle\Entity\Lesson',
'property' => 'name',
'empty_value' => 'Выберите билет',
'empty_data' => null,
'query_builder' => function(EntityRepository $repository) {
$qb = $repository->createQueryBuilder('l');
return $qb;
}
));
but no luck. I want to add one more field to choice widget with a NULL value.
After upgrade I get:
Catchable Fatal Error: Argument 1 passed to XYZ::applyAbcFilter() must be an instance of Doctrine\ORM\QueryBuilder, instance of Lexik\Bundle\FormFilterBundle\Filter\Doctrine\ORMQuery given in ...
I tried following the guide about embedding filters for a one to many relation named "identifiers", and when I submit my form, symfony tries to map the data to the form, and tries to insert an identifier
with the setIdentifiers()
method (when it should use addIdentifier()
). This causes an error, of course, here is the stack trace :
( ! ) Catchable fatal error: Argument 1 passed to Uc\LegalBundle\Entity\Film::setIdentifiers() must be an instance of Doctrine\Common\Collections\ArrayCollection, instance of Uc\LegalBundle\Entity\FilmIdentifier given, called in /home/users/gparis/workspace/legal-backend/vendor/symfony/symfony/src/Symfony/Component/PropertyAccess/PropertyAccessor.php on line 360 and defined in /home/users/gparis/workspace/legal-backend/src/Uc/LegalBundle/Entity/Film.php on line 198
Call Stack
# Time Memory Function Location
1 0.0002 241312 {main}( ) ../app_dev.php:0
2 0.0017 614568 Symfony\Component\HttpKernel\Kernel->handle( object(Symfony\Component\HttpFoundation\Request)[3], ???, ??? ) ../app_dev.php:28
3 0.0160 1804744 Symfony\Component\HttpKernel\DependencyInjection\ContainerAwareHttpKernel->handle( object(Symfony\Component\HttpFoundation\Request)[3], long, bool ) ../bootstrap.php.cache:2303
4 0.0161 1806384 Symfony\Component\HttpKernel\HttpKernel->handle( object(Symfony\Component\HttpFoundation\Request)[3], long, bool ) ../bootstrap.php.cache:3022
5 0.0161 1806880 Symfony\Component\HttpKernel\HttpKernel->handleRaw( object(Symfony\Component\HttpFoundation\Request)[3], long ) ../bootstrap.php.cache:2883
6 0.3213 2683032 call_user_func_array ( array(2), array(0) ) ../bootstrap.php.cache:2911
7 0.3213 2683600 Uc\LegalBundle\Controller\FilmController->indexAction( ) ../bootstrap.php.cache:2911
8 0.3318 3585000 Symfony\Component\Form\Form->bind( object(Symfony\Component\HttpFoundation\Request)[3] ) ../FilmController.php:40
9 0.3318 3586168 Symfony\Component\Form\Form->submit( object(Symfony\Component\HttpFoundation\Request)[3], ??? ) ../Form.php:667
10 0.3333 3657864 Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper->mapFormsToData( object(RecursiveIteratorIterator)[121], object(Uc\LegalBundle\Entity\Film)[95] ) ../Form.php:616
11 0.3336 3659472 Symfony\Component\PropertyAccess\PropertyAccessor->setValue( object(Uc\LegalBundle\Entity\Film)[95], object(Symfony\Component\PropertyAccess\PropertyPath)[134], object(Uc\LegalBundle\Entity\FilmIdentifier)[81] ) ../PropertyPathMapper.php:94
12 0.3336 3660360 Symfony\Component\PropertyAccess\PropertyAccessor->writeProperty( object(Uc\LegalBundle\Entity\Film)[95], string(11), null, object(Uc\LegalBundle\Entity\FilmIdentifier)[81] ) ../PropertyAccessor.php:99
13 0.3337 3661352 Uc\LegalBundle\Entity\Film->setIdentifiers( object(Uc\LegalBundle\Entity\FilmIdentifier)[81] ) ../PropertyAccessor.php:360
An easy workaround for me was to set the identifiers
field mapped
option to false
.
I tried it the same way like in the documentation, only changed it to my Bundle / Entity and i changed FormBuilder to FormBuilderInterface so it works with Symfony 2.1
An exception has been thrown during the rendering of a template ("Unable to render the form as none of the following blocks
exist: "_test_controllingbundle_contractsearchtype_contract_label", "filter_text_label", "filter_label".") in
TestControllingBundle:Default:index.html.twig at line 10.
Is there a problem because I forgot something relevant for Symfony 2.1?
Hi,
I'm using form with field type "entity". Code looks like:
<?php
namespace SportTV\ManageBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Lexik\Bundle\FormFilterBundle\Filter\Extension\Type\TextFilterType;
class TeamFilter extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add(
'name',
'filter_text',
array(
'widget_controls' => false,
'widget_control_group' => false,
'attr' => array('placeholder' => 'Name'),
'condition_pattern' => TextFilterType::PATTERN_CONTAINS
)
)
->add('competition', 'filter_entity', array(
'class' => 'CommonBundle:Competition',
'property' => 'name'
))
;
}
public function getName()
{
return 'team_filter';
}
}
Object construct in controller:
$form = $this->createForm(new TeamFilter());
$form->bindRequest($this->get('request'));
$filterBuilder = $em->getRepository('CommonBundle:Team')->getPagingQuery($sport);
$this->get('lexik_form_filter.query_builder_updater')->addFilterConditions($form, $filterBuilder);
If I select not first & last, exampe second value in select - results are filtered correctly but I get error: "This value should not be blank."
Adding image for more complete explanation:
Symfony version: 2.1.3
Am I doing something wrong or is it a bug?
I override your parameter to use my own PrepareListener.
But that new feature isn't in the 2.0.2
Can you create a new tag 2.0.3 ?
Thanks !
Hey!
I can't find an easy way to filter associated data.
(except 'filter_entity', which provides a dropdown).
I would like to be able to define that this particular 'filter_text' has a particular alias for DB table
Is this functionality feasible?
Are there any limitations to support Sf 2.2?
I just found and installed your bundle. When I post the form with a value in a filter_text field type, I get no results.
For example, I have an entity with a property containing '2L Fr'. If I try to find that entity using the form filter, entering 2 in that property's form filter field, I get no results. If I enter 2% the entity is found. var_dumping the Dql (as in your docs) confirms that the % wildcard character is not in the query (unless I enter it into the form field myself, which is not ideal).
How can I fix this? (unless I am the cause of the problem somehow, it must surely be affecting all users of the bundle?)
When the select fields contain many entities, it is difficult to find the right option. It would be great if optionally the select2 bundle can be implemented. This great jQuery selectbox replacement makes it possible to type in the select-box.
http://ivaynberg.github.io/select2/
I've already implemented it myself now, but via a workaround instead of directly into the filter bundle. It can be an parameter in the constructor of the filter field.
Please tag a version that supports Symfony 2.3.
Hi,
I'm trying to use this bundle but seems to have some trouble with this kind of relations, can we have a good example on how to manage that so that we can create multiple filters depending on the data?
Thank you so much.
Hi,
How can I customize the name of the field which will be used in the query, without rewriting the "apply_filter" ?
In my case, I have a query which joins the users and her groups (each user is linked to zero, one or more groups).
In the form, I use the "filter_entity" type, with the Group entity, but I can't use "user.group" as field name.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.