Skip to content

Commit f01a2ac

Browse files
committed
Merge pull request doctrine#260 from doctrine/filter-params
Support filter parameters in configuration
2 parents a612b90 + b60ab63 commit f01a2ac

File tree

10 files changed

+260
-39
lines changed

10 files changed

+260
-39
lines changed

DependencyInjection/Configuration.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ private function addDocumentManagersSection(ArrayNodeDefinition $rootNode)
100100
->arrayNode('filters')
101101
->useAttributeAsKey('name')
102102
->prototype('array')
103+
->fixXmlConfig('parameter')
103104
->beforeNormalization()
104105
->ifString()
105106
->then(function($v) { return array('class' => $v); })
@@ -117,6 +118,18 @@ private function addDocumentManagersSection(ArrayNodeDefinition $rootNode)
117118
->children()
118119
->scalarNode('class')->isRequired()->end()
119120
->booleanNode('enabled')->defaultFalse()->end()
121+
->arrayNode('parameters')
122+
->treatNullLike(array())
123+
->useAttributeAsKey('name')
124+
->prototype('variable')
125+
->beforeNormalization()
126+
// Detect JSON object and array syntax (for XML)
127+
->ifTrue(function($v) { return is_string($v) && (preg_match('/\[.*\]/', $v) || preg_match('/\{.*\}/', $v)); })
128+
// Decode objects to associative arrays for consistency with YAML
129+
->then(function($v) { return json_decode($v, true); })
130+
->end()
131+
->end()
132+
->end()
120133
->end()
121134
->end()
122135
->end()

DependencyInjection/DoctrineMongoDBExtension.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,8 @@ protected function loadDocumentManager(array $documentManager, $defaultDM, $defa
203203

204204
$enabledFilters = array();
205205
foreach ($documentManager['filters'] as $name => $filter) {
206-
$odmConfigDef->addMethodCall('addFilter', array($name, $filter['class']));
206+
$parameters = isset($filter['parameters']) ? $filter['parameters'] : array();
207+
$odmConfigDef->addMethodCall('addFilter', array($name, $filter['class'], $parameters));
207208
if ($filter['enabled']) {
208209
$enabledFilters[] = $name;
209210
}

Resources/config/schema/mongodb-1.0.xsd

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,11 +123,22 @@
123123
</xsd:complexType>
124124

125125
<xsd:complexType name="filter">
126+
<xsd:sequence>
127+
<xsd:element name="parameter" type="filter-parameter" minOccurs="0" maxOccurs="unbounded" />
128+
</xsd:sequence>
126129
<xsd:attribute name="name" type="xsd:string" use="required" />
127130
<xsd:attribute name="class" type="xsd:string" use="required" />
128131
<xsd:attribute name="enabled" type="xsd:boolean" />
129132
</xsd:complexType>
130133

134+
<xsd:complexType name="filter-parameter">
135+
<xsd:simpleContent>
136+
<xsd:extension base="xsd:string">
137+
<xsd:attribute name="name" type="xsd:string" use="required" />
138+
</xsd:extension>
139+
</xsd:simpleContent>
140+
</xsd:complexType>
141+
131142
<xsd:complexType name="mapping">
132143
<xsd:attribute name="name" type="xsd:string" use="required" />
133144
<xsd:attribute name="alias" type="xsd:string" />

Resources/doc/config.rst

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -164,23 +164,65 @@ The following configuration shows a bunch of mapping examples:
164164
Filters
165165
~~~~~~~
166166

167-
You can easily add filters to a document manager by using the
168-
following syntax:
167+
Filter classes may be used in order to add criteria to ODM queries, regardless
168+
of where those queries are created within your application. Typically, filters
169+
will limit themselves to operating on a particular class or interface. Filters
170+
may also take parameters, which can be used to customize the injected query
171+
criteria.
169172

170-
.. code-block:: yaml
173+
Filters may be registered with a document manager by using the following syntax:
171174

172-
doctrine_mongodb:
173-
document_managers:
174-
default:
175-
filters:
176-
filter-one:
177-
class: Class\ExampleOne\Filter\ODM\ExampleFilter
178-
enabled: true
179-
filter-two:
180-
class: Class\ExampleTwo\Filter\ODM\ExampleFilter
181-
enabled: false
175+
.. configuration-block::
176+
177+
.. code-block:: yaml
178+
179+
doctrine_mongodb:
180+
document_managers:
181+
default:
182+
filters:
183+
basic_filter:
184+
class: Vendor\Filter\BasicFilter
185+
enabled: true
186+
complex_filter:
187+
class: Vendor\Filter\ComplexFilter
188+
enabled: false
189+
parameters:
190+
author: bob
191+
comments: { $gte: 10 }
192+
tags: { $in: [ 'foo', 'bar' ] }
193+
194+
.. code-block:: xml
195+
196+
<?xml version="1.0" ?>
197+
198+
<container xmlns="http://symfony.com/schema/dic/services"
199+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
200+
xmlns:doctrine="http://symfony.com/schema/dic/doctrine/odm/mongodb"
201+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
202+
http://symfony.com/schema/dic/doctrine/odm/mongodb http://symfony.com/schema/dic/doctrine/odm/mongodb/mongodb-1.0.xsd">
203+
204+
<doctrine:mongodb>
205+
<doctrine:connection id="default" server="mongodb://localhost:27017" />
206+
207+
<doctrine:document-manager id="default" connection="default">
208+
<doctrine:filter name="basic_filter" enabled="true" class="Vendor\Filter\BasicFilter" />
209+
<doctrine:filter name="complex_filter" enabled="true" class="Vendor\Filter\ComplexFilter">
210+
<doctrine:parameter name="author">bob</doctrine:parameter>
211+
<doctrine:parameter name="comments">{ "$gte": 10 }</doctrine:parameter>
212+
<doctrine:parameter name="tags">{ "$in": [ "foo", "bar" ] }</doctrine:parameter>
213+
</doctrine:filter>
214+
</doctrine:document-manager>
215+
</doctrine:mongodb>
216+
</container>
217+
218+
.. note::
182219

183-
Filters are used to append conditions to the queryBuilder regardless of where the query is generated.
220+
Unlike ORM, query parameters in MongoDB ODM may be non-scalar values. Since
221+
such values are difficult to express in XML, the bundle allows JSON strings
222+
to be used in ``parameter`` tags. While processing the configuration, the
223+
bundle will run the tag contents through ``json_decode()`` if the string is
224+
wrapped in square brackets or curly braces for arrays and objects,
225+
respectively.
184226

185227
Multiple Connections
186228
~~~~~~~~~~~~~~~~~~~~

Tests/DependencyInjection/AbstractMongoDBExtensionTest.php

Lines changed: 93 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@
1818
use Doctrine\Bundle\MongoDBBundle\DependencyInjection\DoctrineMongoDBExtension;
1919
use Doctrine\Bundle\MongoDBBundle\Tests\TestCase;
2020
use Symfony\Component\DependencyInjection\ContainerBuilder;
21+
use Symfony\Component\DependencyInjection\Definition;
2122
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBag;
23+
use PHPUnit_Framework_AssertionFailedError;
24+
use PHPUnit_Framework_Constraint;
2225

2326
abstract class AbstractMongoDBExtensionTest extends TestCase
2427
{
@@ -384,7 +387,7 @@ public function testDependencyInjectionImportsOverrideDefaults()
384387

385388
public function testResolveTargetDocument()
386389
{
387-
$container = $this->getContainer('YamlBundle');
390+
$container = $this->getContainer();
388391
$loader = new DoctrineMongoDBExtension();
389392
$container->registerExtension($loader);
390393

@@ -395,35 +398,107 @@ public function testResolveTargetDocument()
395398
$container->compile();
396399

397400
$definition = $container->getDefinition('doctrine_mongodb.odm.listeners.resolve_target_document');
398-
$this->assertDICDefinitionMethodCallOnce($definition, 'addResolveTargetDocument', array('Symfony\Component\Security\Core\User\UserInterface', 'MyUserClass', array()));
401+
$this->assertDefinitionMethodCallOnce($definition, 'addResolveTargetDocument', array('Symfony\Component\Security\Core\User\UserInterface', 'MyUserClass', array()));
399402
$this->assertEquals(array('doctrine_mongodb.odm.event_listener' => array(array('event' => 'loadClassMetadata'))), $definition->getTags());
400403
}
401404

405+
public function testFilters()
406+
{
407+
$container = $this->getContainer();
408+
$loader = new DoctrineMongoDBExtension();
409+
$container->registerExtension($loader);
410+
411+
$this->loadFromFile($container, 'odm_filters');
412+
413+
$container->getCompilerPassConfig()->setOptimizationPasses(array());
414+
$container->getCompilerPassConfig()->setRemovingPasses(array());
415+
$container->compile();
416+
417+
$complexParameters = array(
418+
'integer' => 1,
419+
'string' => 'foo',
420+
'object' => array('key' => 'value'),
421+
'array' => array(1, 2, 3),
422+
);
423+
424+
$definition = $container->getDefinition('doctrine_mongodb.odm.default_configuration');
425+
$this->assertDefinitionMethodCallAny($definition, 'addFilter', array('disabled_filter', 'Vendor\Filter\DisabledFilter', array()));
426+
$this->assertDefinitionMethodCallAny($definition, 'addFilter', array('basic_filter', 'Vendor\Filter\BasicFilter', array()));
427+
$this->assertDefinitionMethodCallAny($definition, 'addFilter', array('complex_filter', 'Vendor\Filter\ComplexFilter', $complexParameters));
428+
429+
$enabledFilters = array('basic_filter', 'complex_filter');
430+
431+
$definition = $container->getDefinition('doctrine_mongodb.odm.default_manager_configurator');
432+
$this->assertEquals($enabledFilters, $definition->getArgument(0), 'Only enabled filters are passed to the ManagerConfigurator.');
433+
}
434+
435+
/**
436+
* Asserts that the given definition contains a call to the method that uses
437+
* the specified parameters.
438+
*
439+
* @param Definition $definition
440+
* @param string $methodName
441+
* @param array $params
442+
*/
443+
private function assertDefinitionMethodCallAny(Definition $definition, $methodName, array $params)
444+
{
445+
$calls = $definition->getMethodCalls();
446+
$called = false;
447+
$lastError = null;
448+
449+
foreach ($calls as $call) {
450+
if ($call[0] !== $methodName) {
451+
continue;
452+
}
453+
454+
$called = true;
455+
456+
try {
457+
$this->assertSame($params, $call[1], "Expected parameters to method '" . $methodName . "' did not match the actual parameters.");
458+
return;
459+
} catch (PHPUnit_Framework_AssertionFailedError $e) {
460+
$lastError = $e;
461+
}
462+
}
463+
464+
if ( ! $called) {
465+
$this->fail("Method '" . $methodName . "' is expected to be called, but it was never called.");
466+
}
467+
468+
if ($lastError) {
469+
throw $lastError;
470+
}
471+
}
472+
402473
/**
403-
* Assertion for the DI Container, check if the given definition contains a method call with the given parameters.
474+
* Asserts that the given definition contains exactly one call to the method
475+
* and that it uses the specified parameters.
404476
*
405-
* @param \Symfony\Component\DependencyInjection\Definition $definition
406-
* @param string $methodName
407-
* @param array $params
477+
* @param Definition $definition
478+
* @param string $methodName
479+
* @param array $params
408480
*/
409-
protected function assertDICDefinitionMethodCallOnce($definition, $methodName, array $params = null)
481+
private function assertDefinitionMethodCallOnce(Definition $definition, $methodName, array $params)
410482
{
411483
$calls = $definition->getMethodCalls();
412484
$called = false;
485+
413486
foreach ($calls as $call) {
414-
if ($call[0] == $methodName) {
415-
if ($called) {
416-
$this->fail("Method '" . $methodName . "' is expected to be called only once, a second call was registered though.");
417-
} else {
418-
$called = true;
419-
if ($params !== null) {
420-
$this->assertEquals($params, $call[1], "Expected parameters to methods '" . $methodName . "' do not match the actual parameters.");
421-
}
422-
}
487+
if ($call[0] !== $methodName) {
488+
continue;
423489
}
490+
491+
if ($called) {
492+
$this->fail("Method '" . $methodName . "' is expected to be called only once, but it was called multiple times.");
493+
}
494+
495+
$called = true;
496+
497+
$this->assertEquals($params, $call[1], "Expected parameters to method '" . $methodName . "' did not match the actual parameters.");
424498
}
425-
if (!$called) {
426-
$this->fail("Method '" . $methodName . "' is expected to be called once, definition does not contain a call though.");
499+
500+
if ( ! $called) {
501+
$this->fail("Method '" . $methodName . "' is expected to be called once, but it was never called.");
427502
}
428503
}
429504

Tests/DependencyInjection/ConfigurationTest.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,25 @@ public function testFullConfiguration($config)
106106
'logging' => '%kernel.debug%',
107107
'auto_mapping' => false,
108108
'filters' => array(
109-
'test_filter' => array(
110-
'class' => 'TestClass',
109+
'disabled_filter' => array(
110+
'class' => 'Vendor\Filter\DisabledFilter',
111+
'enabled' => false,
112+
'parameters' => array(),
113+
),
114+
'basic_filter' => array(
115+
'class' => 'Vendor\Filter\BasicFilter',
116+
'enabled' => true,
117+
'parameters' => array(),
118+
),
119+
'complex_filter' => array(
120+
'class' => 'Vendor\Filter\ComplexFilter',
111121
'enabled' => true,
122+
'parameters' => array(
123+
'integer' => 1,
124+
'string' => 'foo',
125+
'object' => array('key' => 'value'),
126+
'array' => array(1, 2, 3),
127+
),
112128
),
113129
),
114130
'metadata_cache_driver' => array(

Tests/DependencyInjection/Fixtures/config/xml/full.xml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,14 @@
6666
<doctrine:instance-class>instance_val</doctrine:instance-class>
6767
</doctrine:metadata-cache-driver>
6868
<doctrine:profiler enabled="true" pretty="false" />
69-
<doctrine:filter name="test_filter" enabled="true" class="TestClass" />
69+
<doctrine:filter name="disabled_filter" enabled="false" class="Vendor\Filter\DisabledFilter" />
70+
<doctrine:filter name="basic_filter" enabled="true" class="Vendor\Filter\BasicFilter" />
71+
<doctrine:filter name="complex_filter" enabled="true" class="Vendor\Filter\ComplexFilter">
72+
<doctrine:parameter name="integer">1</doctrine:parameter>
73+
<doctrine:parameter name="string">foo</doctrine:parameter>
74+
<doctrine:parameter name="object">{"key":"value"}</doctrine:parameter>
75+
<doctrine:parameter name="array">[1,2,3]</doctrine:parameter>
76+
</doctrine:filter>
7077
</doctrine:document-manager>
7178

7279
<doctrine:document-manager
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xmlns:doctrine="http://symfony.com/schema/dic/doctrine/odm/mongodb"
6+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd
7+
http://symfony.com/schema/dic/doctrine/odm/mongodb http://symfony.com/schema/dic/doctrine/odm/mongodb/mongodb-1.0.xsd">
8+
9+
<doctrine:mongodb>
10+
<doctrine:connection id="default" server="mongodb://localhost:27017" />
11+
12+
<doctrine:document-manager id="default" connection="default">
13+
<doctrine:filter name="disabled_filter" enabled="false" class="Vendor\Filter\DisabledFilter" />
14+
<doctrine:filter name="basic_filter" enabled="true" class="Vendor\Filter\BasicFilter" />
15+
<doctrine:filter name="complex_filter" enabled="true" class="Vendor\Filter\ComplexFilter">
16+
<doctrine:parameter name="integer">1</doctrine:parameter>
17+
<doctrine:parameter name="string">foo</doctrine:parameter>
18+
<doctrine:parameter name="object">{"key":"value"}</doctrine:parameter>
19+
<doctrine:parameter name="array">[1,2,3]</doctrine:parameter>
20+
</doctrine:filter>
21+
</doctrine:document-manager>
22+
23+
</doctrine:mongodb>
24+
</container>

Tests/DependencyInjection/Fixtures/config/yml/full.yml

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,20 @@ doctrine_mongodb:
5959
enabled: true
6060
pretty: false
6161
filters:
62-
test-filter:
63-
class: TestClass
64-
enabled: true
62+
disabled_filter:
63+
class: Vendor\Filter\DisabledFilter
64+
enabled: false
65+
basic_filter:
66+
class: Vendor\Filter\BasicFilter
67+
enabled: true
68+
complex_filter:
69+
class: Vendor\Filter\ComplexFilter
70+
enabled: true
71+
parameters:
72+
integer: 1
73+
string: foo
74+
object: { key: value }
75+
array: [ 1, 2, 3 ]
6576
dm2:
6677
connection: dm2_connection
6778
database: db1
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
doctrine_mongodb:
2+
connections:
3+
default:
4+
server: mongodb://localhost:27017
5+
document_managers:
6+
default:
7+
filters:
8+
disabled_filter:
9+
class: Vendor\Filter\DisabledFilter
10+
enabled: false
11+
basic_filter:
12+
class: Vendor\Filter\BasicFilter
13+
enabled: true
14+
complex_filter:
15+
class: Vendor\Filter\ComplexFilter
16+
enabled: true
17+
parameters:
18+
integer: 1
19+
string: foo
20+
object: { key: value }
21+
array: [ 1, 2, 3 ]

0 commit comments

Comments
 (0)