Merge branch '2.1.x'

Conflicts:
	Tests/ResetterTest.php
This commit is contained in:
Tim Nagel 2013-11-11 13:30:29 +11:00
commit 3c26f157aa
10 changed files with 263 additions and 9 deletions

View file

@ -179,6 +179,7 @@ class Configuration implements ConfigurationInterface
->children()
->scalarNode('hydrate')->defaultTrue()->end()
->scalarNode('ignore_missing')->defaultFalse()->end()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('service')->end()
->end()
->end()
@ -270,6 +271,7 @@ class Configuration implements ConfigurationInterface
->children()
->scalarNode('hydrate')->defaultTrue()->end()
->scalarNode('ignore_missing')->defaultFalse()->end()
->scalarNode('query_builder_method')->defaultValue('createQueryBuilder')->end()
->scalarNode('service')->end()
->end()
->end()
@ -284,6 +286,7 @@ class Configuration implements ConfigurationInterface
->end()
->append($this->getIdNode())
->append($this->getMappingsNode())
->append($this->getDynamicTemplateNode())
->append($this->getSourceNode())
->append($this->getBoostNode())
->append($this->getRoutingNode())
@ -316,6 +319,37 @@ class Configuration implements ConfigurationInterface
return $node;
}
/**
* Returns the array node used for "dynamic_templates".
*/
public function getDynamicTemplateNode()
{
$builder = new TreeBuilder();
$node = $builder->root('dynamic_templates');
$node
->useAttributeAsKey('name')
->prototype('array')
->children()
->scalarNode('match')->isRequired()->end()
->scalarNode('match_mapping_type')->end()
->arrayNode('mapping')
->isRequired()
->children()
->scalarNode('type')->end()
->scalarNode('index')->end()
->arrayNode('fields')
->children()
->end()
->end()
->end()
->end()
->end()
;
return $node;
}
/**
* @param \Symfony\Component\Config\Definition\Builder\NodeBuilder $node The node to which to attach the field config to
* @param array $nestings the nested mappings for the current field level

View file

@ -232,6 +232,12 @@ class FOSElasticaExtension extends Extension
if (isset($type['index'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['index'] = $type['index'];
}
if (!empty($type['dynamic_templates'])) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['dynamic_templates'] = array();
foreach ($type['dynamic_templates'] as $templateName => $templateData) {
$this->indexConfigs[$indexName]['config']['mappings'][$name]['dynamic_templates'][] = array($templateName => $templateData);
}
}
}
}
@ -304,7 +310,8 @@ class FOSElasticaExtension extends Extension
$serviceDef->replaceArgument($argPos + 1, array(
'hydrate' => $typeConfig['elastica_to_model_transformer']['hydrate'],
'identifier' => $typeConfig['identifier'],
'ignore_missing' => $typeConfig['elastica_to_model_transformer']['ignore_missing']
'ignore_missing' => $typeConfig['elastica_to_model_transformer']['ignore_missing'],
'query_builder_method' => $typeConfig['elastica_to_model_transformer']['query_builder_method']
));
$container->setDefinition($serviceId, $serviceDef);

View file

@ -35,6 +35,7 @@ abstract class AbstractElasticaToModelTransformer implements ElasticaToModelTran
'hydrate' => true,
'identifier' => 'id',
'ignore_missing' => false,
'query_builder_method' => 'createQueryBuilder',
);
/**

View file

@ -22,7 +22,7 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
{
return $this->registry
->getManagerForClass($this->objectClass)
->createQueryBuilder($this->objectClass)
->{$this->options['query_builder_method']}($this->objectClass)
->field($this->options['identifier'])->in($identifierValues)
->hydrate($hydrate)
->getQuery()

View file

@ -12,6 +12,8 @@ use Doctrine\ORM\Query;
*/
class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
{
const ENTITY_ALIAS = 'o';
/**
* Fetch objects for theses identifier values
*
@ -25,14 +27,25 @@ class ElasticaToModelTransformer extends AbstractElasticaToModelTransformer
return array();
}
$hydrationMode = $hydrate ? Query::HYDRATE_OBJECT : Query::HYDRATE_ARRAY;
$qb = $this->registry
->getManagerForClass($this->objectClass)
->getRepository($this->objectClass)
->createQueryBuilder('o');
/* @var $qb \Doctrine\ORM\QueryBuilder */
$qb->where($qb->expr()->in('o.'.$this->options['identifier'], ':values'))
$qb = $this->getEntityQueryBuilder();
$qb->where($qb->expr()->in(static::ENTITY_ALIAS.'.'.$this->options['identifier'], ':values'))
->setParameter('values', $identifierValues);
return $qb->getQuery()->setHydrationMode($hydrationMode)->execute();
}
/**
* Retrieves a query builder to be used for querying by identifiers
*
* @return \Doctrine\ORM\QueryBuilder
*/
protected function getEntityQueryBuilder()
{
$repository = $this->registry
->getManagerForClass($this->objectClass)
->getRepository($this->objectClass);
return $repository->{$this->options['query_builder_method']}(static::ENTITY_ALIAS);
}
}

View file

@ -822,3 +822,33 @@ fos_elastica:
lastlogin: { type: date, format: basic_date_time }
birthday: { type: date, format: "yyyy-MM-dd" }
```
#### Dynamic templates
Dynamic templates allow to define mapping templates that will be
applied when dynamic introduction of fields / objects happens.
[Documentation](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-root-object-type.html#_dynamic_templates)
```yaml
fos_elastica:
clients:
default: { host: localhost, port: 9200 }
indexes:
site:
types:
user:
dynamic_templates:
my_template_1:
match: apples_*
mapping:
type: float
my_template_2:
match: *
match_mapping_type: string
mapping:
type: string
index: not_analyzed
mappings:
username: { type: string }
```

View file

@ -78,6 +78,10 @@ class Resetter
$mapping->setParam('_parent', array('type' => $indexConfig['_parent']['type']));
}
if (isset($indexConfig['dynamic_templates'])) {
$mapping->setParam('dynamic_templates', $indexConfig['dynamic_templates']);
}
return $mapping;
}

View file

@ -31,4 +31,45 @@ class ConfigurationTest extends \PHPUnit_Framework_TestCase
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mappings['format']);
$this->assertNull($mappings['format']->getDefaultValue());
}
public function testDynamicTemplateNodes()
{
$tree = $this->configuration->getConfigTree();
$children = $tree->getChildren();
$children = $children['indexes']->getPrototype()->getChildren();
$typeNodes = $children['types']->getPrototype()->getChildren();
$dynamicTemplates = $typeNodes['dynamic_templates']->getPrototype()->getChildren();
$this->assertArrayHasKey('match', $dynamicTemplates);
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['match']);
$this->assertNull($dynamicTemplates['match']->getDefaultValue());
$this->assertArrayHasKey('match_mapping_type', $dynamicTemplates);
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $dynamicTemplates['match_mapping_type']);
$this->assertNull($dynamicTemplates['match_mapping_type']->getDefaultValue());
$this->assertArrayHasKey('mapping', $dynamicTemplates);
$this->assertInstanceOf('Symfony\Component\Config\Definition\ArrayNode', $dynamicTemplates['mapping']);
}
public function testDynamicTemplateMappingNodes()
{
$tree = $this->configuration->getConfigTree();
$children = $tree->getChildren();
$children = $children['indexes']->getPrototype()->getChildren();
$typeNodes = $children['types']->getPrototype()->getChildren();
$dynamicTemplates = $typeNodes['dynamic_templates']->getPrototype()->getChildren();
$mapping = $dynamicTemplates['mapping']->getChildren();
$this->assertArrayHasKey('type', $mapping);
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mapping['type']);
$this->assertNull($mapping['type']->getDefaultValue());
$this->assertArrayHasKey('index', $mapping);
$this->assertInstanceOf('Symfony\Component\Config\Definition\ScalarNode', $mapping['index']);
$this->assertNull($mapping['index']->getDefaultValue());
$this->assertArrayHasKey('fields', $mapping);
$this->assertInstanceOf('Symfony\Component\Config\Definition\ArrayNode', $mapping['fields']);
}
}

View file

@ -0,0 +1,120 @@
<?php
namespace FOS\ElasticaBundle\Tests\Doctrine\ORM;
use FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer;
class ElasticaToModelTransformerTest extends \PHPUnit_Framework_TestCase
{
/**
* @var \Doctrine\Common\Persistence\ManagerRegistry|\PHPUnit_Framework_MockObject_MockObject
*/
protected $registry;
/**
* @var \Doctrine\ORM\EntityManager|\PHPUnit_Framework_MockObject_MockObject
*/
protected $manager;
/**
* @var \Doctrine\Common\Persistence\ObjectRepository|\PHPUnit_Framework_MockObject_MockObject
*/
protected $repository;
/**
* @var string
*/
protected $objectClass = 'stdClass';
/**
* Tests that the Transformer uses the query_builder_method configuration option
* allowing configuration of createQueryBuilder call.
*/
public function testTransformUsesQueryBuilderMethodConfiguration()
{
$qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder')
->disableOriginalConstructor()
->getMock();
$this->repository->expects($this->once())
->method('customQueryBuilderCreator')
->with($this->equalTo(ElasticaToModelTransformer::ENTITY_ALIAS))
->will($this->returnValue($qb));
$this->repository->expects($this->never())
->method('createQueryBuilder');
$transformer = new ElasticaToModelTransformer($this->registry, $this->objectClass, array(
'query_builder_method' => 'customQueryBuilderCreator',
));
$class = new \ReflectionClass('FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer');
$method = $class->getMethod('getEntityQueryBuilder');
$method->setAccessible(true);
$method->invokeArgs($transformer, array());
}
/**
* Tests that the Transformer uses the query_builder_method configuration option
* allowing configuration of createQueryBuilder call.
*/
public function testTransformUsesDefaultQueryBuilderMethodConfiguration()
{
$qb = $this->getMockBuilder('Doctrine\ORM\QueryBuilder')
->disableOriginalConstructor()
->getMock();
$this->repository->expects($this->never())
->method('customQueryBuilderCreator');
$this->repository->expects($this->once())
->method('createQueryBuilder')
->with($this->equalTo(ElasticaToModelTransformer::ENTITY_ALIAS))
->will($this->returnValue($qb));
$transformer = new ElasticaToModelTransformer($this->registry, $this->objectClass);
$class = new \ReflectionClass('FOS\ElasticaBundle\Doctrine\ORM\ElasticaToModelTransformer');
$method = $class->getMethod('getEntityQueryBuilder');
$method->setAccessible(true);
$method->invokeArgs($transformer, array());
}
protected function setUp()
{
if (!interface_exists('Doctrine\Common\Persistence\ManagerRegistry')) {
$this->markTestSkipped('Doctrine Common is not present');
}
if (!class_exists('Doctrine\ORM\EntityManager')) {
$this->markTestSkipped('Doctrine Common is not present');
}
$this->registry = $this->getMockBuilder('Doctrine\Common\Persistence\ManagerRegistry')
->disableOriginalConstructor()
->getMock();
$this->manager = $this->getMockBuilder('Doctrine\ORM\EntityManager')
->disableOriginalConstructor()
->getMock();
$this->registry->expects($this->any())
->method('getManagerForClass')
->with($this->objectClass)
->will($this->returnValue($this->manager));
$this->repository = $this->getMock('Doctrine\Common\Persistence\ObjectRepository', array(
'customQueryBuilderCreator',
'createQueryBuilder',
'find',
'findAll',
'findBy',
'findOneBy',
'getClassName'
));
$this->manager->expects($this->any())
->method('getRepository')
->with($this->objectClass)
->will($this->returnValue($this->repository));
}
}

View file

@ -16,7 +16,10 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
'index' => $this->getMockElasticaIndex(),
'config' => array(
'mappings' => array(
'a' => array('properties' => array()),
'a' => array(
'dynamic_templates' => array(),
'properties' => array(),
),
'b' => array('properties' => array()),
),
),
@ -100,6 +103,7 @@ class ResetterTest extends \PHPUnit_Framework_TestCase
->method('delete');
$mapping = Mapping::create($this->indexConfigsByName['foo']['config']['mappings']['a']['properties']);
$mapping->setParam('dynamic_templates', $this->indexConfigsByName['foo']['config']['mappings']['a']['dynamic_templates']);
$type->expects($this->once())
->method('setMapping')
->with($mapping);