mirror of
https://github.com/Part-DB/Part-DB-server.git
synced 2025-06-21 01:25:55 +02:00
Allow to filter parameters by their (numeric) value
This commit is contained in:
parent
b56a970d5b
commit
dd400ae70c
8 changed files with 288 additions and 23 deletions
|
@ -13,8 +13,14 @@ export default class extends Controller {
|
||||||
*/
|
*/
|
||||||
update()
|
update()
|
||||||
{
|
{
|
||||||
|
const two_element_values = [
|
||||||
|
"BETWEEN",
|
||||||
|
'RANGE_IN_RANGE',
|
||||||
|
'RANGE_INTERSECT_RANGE'
|
||||||
|
];
|
||||||
|
|
||||||
for (const thingToHide of this.thingsToHideTargets) {
|
for (const thingToHide of this.thingsToHideTargets) {
|
||||||
thingToHide.classList.toggle("d-none", this.operatorTarget.value !== "BETWEEN");
|
thingToHide.classList.toggle("d-none", !two_element_values.includes(this.operatorTarget.value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -22,6 +22,9 @@ class ParameterConstraint extends AbstractConstraint
|
||||||
/** @var TextConstraint */
|
/** @var TextConstraint */
|
||||||
protected $value_text;
|
protected $value_text;
|
||||||
|
|
||||||
|
/** @var ParameterValueConstraint */
|
||||||
|
protected $value;
|
||||||
|
|
||||||
/** @var string The alias to use for the subquery */
|
/** @var string The alias to use for the subquery */
|
||||||
protected $alias;
|
protected $alias;
|
||||||
|
|
||||||
|
@ -33,6 +36,7 @@ class ParameterConstraint extends AbstractConstraint
|
||||||
$this->alias = uniqid('param_', false);
|
$this->alias = uniqid('param_', false);
|
||||||
|
|
||||||
$this->value_text = new TextConstraint($this->alias . '.value_text');
|
$this->value_text = new TextConstraint($this->alias . '.value_text');
|
||||||
|
$this->value = new ParameterValueConstraint($this->alias );
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isEnabled(): bool
|
public function isEnabled(): bool
|
||||||
|
@ -71,6 +75,7 @@ class ParameterConstraint extends AbstractConstraint
|
||||||
|
|
||||||
//Apply all subfilters
|
//Apply all subfilters
|
||||||
$this->value_text->apply($subqb);
|
$this->value_text->apply($subqb);
|
||||||
|
$this->value->apply($subqb);
|
||||||
|
|
||||||
//Copy all parameters from the subquery to the main query
|
//Copy all parameters from the subquery to the main query
|
||||||
//We can not use setParameters here, as this would override the exiting paramaters in queryBuilder
|
//We can not use setParameters here, as this would override the exiting paramaters in queryBuilder
|
||||||
|
@ -144,14 +149,12 @@ class ParameterConstraint extends AbstractConstraint
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DO NOT USE THIS SETTER!
|
* @return ParameterValueConstraint
|
||||||
* This is just a workaround for collectionType behavior
|
|
||||||
* @param $value
|
|
||||||
* @return $this
|
|
||||||
*/
|
*/
|
||||||
/*public function setValueText($value): self
|
public function getValue(): ParameterValueConstraint
|
||||||
{
|
{
|
||||||
//Do not really set the value here, as we dont want to override the constraint created in the constructor
|
return $this->value;
|
||||||
return $this;
|
}
|
||||||
}*/
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,130 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\DataTables\Filters\Constraints\Part;
|
||||||
|
|
||||||
|
use App\DataTables\Filters\Constraints\NumberConstraint;
|
||||||
|
use Doctrine\ORM\QueryBuilder;
|
||||||
|
|
||||||
|
class ParameterValueConstraint extends NumberConstraint
|
||||||
|
{
|
||||||
|
protected $alias;
|
||||||
|
|
||||||
|
public const ALLOWED_OPERATOR_VALUES = ['=', '!=', '<', '>', '<=', '>=', 'BETWEEN',
|
||||||
|
//Additional operators
|
||||||
|
'IN_RANGE', 'NOT_IN_RANGE', 'GREATER_THAN_RANGE', 'GREATER_EQUAL_RANGE', 'LESS_THAN_RANGE', 'LESS_EQUAL_RANGE', 'RANGE_IN_RANGE', 'RANGE_INTERSECT_RANGE'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $alias The alias which is used in the sub query of ParameterConstraint
|
||||||
|
*/
|
||||||
|
public function __construct(string $alias) {
|
||||||
|
$this->alias = $alias;
|
||||||
|
|
||||||
|
parent::__construct($alias . '.value_typical');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function apply(QueryBuilder $queryBuilder): void
|
||||||
|
{
|
||||||
|
//Skip if not enabled
|
||||||
|
if(!$this->isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$paramName1 = $this->generateParameterIdentifier('value1');
|
||||||
|
$paramName2 = $this->generateParameterIdentifier('value2');
|
||||||
|
|
||||||
|
if ($this->operator === 'IN_RANGE') {
|
||||||
|
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
"({$this->alias}.value_min <= :{$paramName1} AND {$this->alias}.value_max >= :{$paramName1}) OR
|
||||||
|
({$this->alias}.value_typical = :{$paramName1})"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->operator === 'NOT_IN_RANGE') {
|
||||||
|
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
"({$this->alias}.value_min > :{$paramName1} OR {$this->alias}.value_max < :{$paramName1}) AND
|
||||||
|
({$this->alias}.value_typical IS NULL OR {$this->alias}.value_typical != :{$paramName1})"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->operator === 'GREATER_THAN_RANGE') {
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
"{$this->alias}.value_max < :{$paramName1} OR {$this->alias}.value_typical < :{$paramName1}"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->operator === 'GREATER_EQUAL_RANGE') {
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
"{$this->alias}.value_max <= :{$paramName1} OR {$this->alias}.value_typical <= :{$paramName1}"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->operator === 'LESS_THAN_RANGE') {
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
"{$this->alias}.value_min > :{$paramName1} OR {$this->alias}.value_typical > :{$paramName1}"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->operator === 'LESS_EQUAL_RANGE') {
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
"{$this->alias}.value_min >= :{$paramName1} OR {$this->alias}.value_typical >= :{$paramName1}"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This operator means the constraint range must lie completely within the parameter value range
|
||||||
|
if ($this->operator === 'RANGE_IN_RANGE') {
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
"({$this->alias}.value_min <= :{$paramName1} AND {$this->alias}.value_max >= :{$paramName2}) OR
|
||||||
|
({$this->alias}.value_typical >= :{$paramName1} AND {$this->alias}.value_typical <= :{$paramName2})"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
$queryBuilder->setParameter($paramName2, $this->value2);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->operator === 'RANGE_INTERSECT_RANGE') {
|
||||||
|
$queryBuilder->andWhere(
|
||||||
|
//The ORs are important here!!
|
||||||
|
"({$this->alias}.value_min <= :{$paramName1} OR {$this->alias}.value_min <= :{$paramName2}) OR
|
||||||
|
({$this->alias}.value_max >= :{$paramName1} OR {$this->alias}.value_max >= :{$paramName2}) OR
|
||||||
|
({$this->alias}.value_typical >= :{$paramName1} AND {$this->alias}.value_typical <= :{$paramName2})"
|
||||||
|
);
|
||||||
|
|
||||||
|
$queryBuilder->setParameter($paramName1, $this->value1);
|
||||||
|
$queryBuilder->setParameter($paramName2, $this->value2);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//For all other cases use the default implementation
|
||||||
|
parent::apply($queryBuilder);
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,6 +13,17 @@ use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
class NumberConstraintType extends AbstractType
|
class NumberConstraintType extends AbstractType
|
||||||
{
|
{
|
||||||
|
protected const CHOICES = [
|
||||||
|
'' => '',
|
||||||
|
'=' => '=',
|
||||||
|
'!=' => '!=',
|
||||||
|
'<' => '<',
|
||||||
|
'>' => '>',
|
||||||
|
'<=' => '<=',
|
||||||
|
'>=' => '>=',
|
||||||
|
'filter.number_constraint.value.operator.BETWEEN' => 'BETWEEN',
|
||||||
|
];
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
|
@ -31,17 +42,6 @@ class NumberConstraintType extends AbstractType
|
||||||
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$choices = [
|
|
||||||
'' => '',
|
|
||||||
'=' => '=',
|
|
||||||
'!=' => '!=',
|
|
||||||
'<' => '<',
|
|
||||||
'>' => '>',
|
|
||||||
'<=' => '<=',
|
|
||||||
'>=' => '>=',
|
|
||||||
'filter.number_constraint.value.operator.BETWEEN' => 'BETWEEN',
|
|
||||||
];
|
|
||||||
|
|
||||||
$builder->add('value1', NumberType::class, array_merge_recursive([
|
$builder->add('value1', NumberType::class, array_merge_recursive([
|
||||||
'label' => 'filter.number_constraint.value1',
|
'label' => 'filter.number_constraint.value1',
|
||||||
'attr' => [
|
'attr' => [
|
||||||
|
@ -68,7 +68,7 @@ class NumberConstraintType extends AbstractType
|
||||||
|
|
||||||
$builder->add('operator', ChoiceType::class, [
|
$builder->add('operator', ChoiceType::class, [
|
||||||
'label' => 'filter.number_constraint.operator',
|
'label' => 'filter.number_constraint.operator',
|
||||||
'choices' => $choices,
|
'choices' => static::CHOICES,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,9 @@ class ParameterConstraintType extends AbstractType
|
||||||
//'required' => false,
|
//'required' => false,
|
||||||
] );
|
] );
|
||||||
|
|
||||||
|
$builder->add('value', ParameterValueConstraintType::class, [
|
||||||
|
]);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I am not quite sure why this is needed, but somehow symfony tries to create a new instance of TextConstraint
|
* I am not quite sure why this is needed, but somehow symfony tries to create a new instance of TextConstraint
|
||||||
* instead of using the existing one for the prototype (or the one from empty data). This fails as the constructor of TextConstraint requires
|
* instead of using the existing one for the prototype (or the one from empty data). This fails as the constructor of TextConstraint requires
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Filters\Constraints;
|
||||||
|
|
||||||
|
class ParameterValueConstraintType extends NumberConstraintType
|
||||||
|
{
|
||||||
|
protected const CHOICES = [
|
||||||
|
'' => '',
|
||||||
|
'filter.parameter_value_constraint.operator.=' => '=',
|
||||||
|
'filter.parameter_value_constraint.operator.!=' => '!=',
|
||||||
|
'filter.parameter_value_constraint.operator.<' => '<',
|
||||||
|
'filter.parameter_value_constraint.operator.>' => '>',
|
||||||
|
'filter.parameter_value_constraint.operator.<=' => '<=',
|
||||||
|
'filter.parameter_value_constraint.operator.>=' => '>=',
|
||||||
|
'filter.parameter_value_constraint.operator.BETWEEN' => 'BETWEEN',
|
||||||
|
|
||||||
|
//Extensions by ParameterValueConstraint
|
||||||
|
'filter.parameter_value_constraint.operator.IN_RANGE' => 'IN_RANGE',
|
||||||
|
'filter.parameter_value_constraint.operator.NOT_IN_RANGE' => 'NOT_IN_RANGE',
|
||||||
|
'filter.parameter_value_constraint.operator.GREATER_THAN_RANGE' => 'GREATER_THAN_RANGE',
|
||||||
|
'filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE' => 'GREATER_EQUAL_RANGE',
|
||||||
|
'filter.parameter_value_constraint.operator.LESS_THAN_RANGE' => 'LESS_THAN_RANGE',
|
||||||
|
'filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE' => 'LESS_EQUAL_RANGE',
|
||||||
|
|
||||||
|
'filter.parameter_value_constraint.operator.RANGE_IN_RANGE' => 'RANGE_IN_RANGE',
|
||||||
|
'filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE' => 'RANGE_INTERSECT_RANGE'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function getParent(): string
|
||||||
|
{
|
||||||
|
return NumberConstraintType::class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -43,9 +43,9 @@
|
||||||
{% block parameter_constraint_widget %}
|
{% block parameter_constraint_widget %}
|
||||||
{% import 'components/collection_type.macro.html.twig' as collection %}
|
{% import 'components/collection_type.macro.html.twig' as collection %}
|
||||||
<tr {{ stimulus_controller('pages/parameters_autocomplete', {"url": url('typeahead_parameters', {"query": "__QUERY__", "type": "part"}) }) }} >
|
<tr {{ stimulus_controller('pages/parameters_autocomplete', {"url": url('typeahead_parameters', {"query": "__QUERY__", "type": "part"}) }) }} >
|
||||||
<td>{{ form_widget(form.name, {"attr": {"data-pages--parameters-autocomplete-target": "name"}}) }}</td>
|
<td class="col-sm-2">{{ form_widget(form.name, {"attr": {"data-pages--parameters-autocomplete-target": "name"}}) }}</td>
|
||||||
<td {{ stimulus_controller('pages/latex_preview') }}>{{ form_widget(form.symbol, {"attr": {"data-pages--parameters-autocomplete-target": "symbol", "data-pages--latex-preview-target": "input"}}) }}<span {{ stimulus_target('pages/latex_preview', 'preview') }}></span></td>
|
<td {{ stimulus_controller('pages/latex_preview') }}>{{ form_widget(form.symbol, {"attr": {"data-pages--parameters-autocomplete-target": "symbol", "data-pages--latex-preview-target": "input"}}) }}<span {{ stimulus_target('pages/latex_preview', 'preview') }}></span></td>
|
||||||
<td></td>
|
<td>{{ form_widget(form.value) }}</td>
|
||||||
<td {{ stimulus_controller('pages/latex_preview') }}>{{ form_widget(form.unit, {"attr": {"data-pages--parameters-autocomplete-target": "unit", "data-pages--latex-preview-target": "input"}}) }}<span {{ stimulus_target('pages/latex_preview', 'preview') }}></span></td>
|
<td {{ stimulus_controller('pages/latex_preview') }}>{{ form_widget(form.unit, {"attr": {"data-pages--parameters-autocomplete-target": "unit", "data-pages--latex-preview-target": "input"}}) }}<span {{ stimulus_target('pages/latex_preview', 'preview') }}></span></td>
|
||||||
<td>{{ form_widget(form.value_text) }}</td>
|
<td>{{ form_widget(form.value_text) }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
|
@ -9543,5 +9543,95 @@ Element 3</target>
|
||||||
<target>Filter</target>
|
<target>Filter</target>
|
||||||
</segment>
|
</segment>
|
||||||
</unit>
|
</unit>
|
||||||
|
<unit id="kTizqBM" name="filter.parameter_value_constraint.operator.=">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.=</source>
|
||||||
|
<target>Typ. Value =</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="2Nf2h2a" name="filter.parameter_value_constraint.operator.!=">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.!=</source>
|
||||||
|
<target>Typ. Value !=</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="0fmepxx" name="filter.parameter_value_constraint.operator.<">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.<</source>
|
||||||
|
<target><![CDATA[Typ. Value <]]></target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="ubkcT6U" name="filter.parameter_value_constraint.operator.>">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.></source>
|
||||||
|
<target><![CDATA[Typ. Value >]]></target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="O4x0opb" name="filter.parameter_value_constraint.operator.<=">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.<=</source>
|
||||||
|
<target><![CDATA[Typ. Value <=]]></target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="ituiTs9" name="filter.parameter_value_constraint.operator.>=">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.>=</source>
|
||||||
|
<target><![CDATA[Typ. Value >=]]></target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="Y8B3A9X" name="filter.parameter_value_constraint.operator.BETWEEN">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.BETWEEN</source>
|
||||||
|
<target>Typ. Value is between</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="FqUoIEy" name="filter.parameter_value_constraint.operator.IN_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.IN_RANGE</source>
|
||||||
|
<target>In Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="v_1AWPE" name="filter.parameter_value_constraint.operator.NOT_IN_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.NOT_IN_RANGE</source>
|
||||||
|
<target>Not in Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="X4hI3kL" name="filter.parameter_value_constraint.operator.GREATER_THAN_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.GREATER_THAN_RANGE</source>
|
||||||
|
<target>Greater than Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="yQO92R2" name="filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.GREATER_EQUAL_RANGE</source>
|
||||||
|
<target>Greater equal than Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="h8zJtL5" name="filter.parameter_value_constraint.operator.LESS_THAN_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.LESS_THAN_RANGE</source>
|
||||||
|
<target>Less than Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="sxSjUOg" name="filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.LESS_EQUAL_RANGE</source>
|
||||||
|
<target>Less equal than Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="fqRL5fp" name="filter.parameter_value_constraint.operator.RANGE_IN_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.RANGE_IN_RANGE</source>
|
||||||
|
<target>Range is completly in Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
|
<unit id="PJgHP5E" name="filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE">
|
||||||
|
<segment>
|
||||||
|
<source>filter.parameter_value_constraint.operator.RANGE_INTERSECT_RANGE</source>
|
||||||
|
<target>Range intersects Value range</target>
|
||||||
|
</segment>
|
||||||
|
</unit>
|
||||||
</file>
|
</file>
|
||||||
</xliff>
|
</xliff>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue