Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/6.1-dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
saimaz committed Dec 30, 2019
2 parents a2eabad + c9f626c commit 1220084
Show file tree
Hide file tree
Showing 20 changed files with 421 additions and 174 deletions.
14 changes: 10 additions & 4 deletions DependencyInjection/Compiler/MappingPass.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private function handleDirectoryMapping(ContainerBuilder $container, string $dir
/** @var DocumentParser $parser */
$parser = $container->get(DocumentParser::class);
$indexesOverride = $container->getParameter(Configuration::ONGR_INDEXES_OVERRIDE);
$converterDefinition = $container->getDefinition(Converter::class);

foreach ($this->getNamespaces($dir) as $namespace) {
$class = new \ReflectionClass($namespace);
Expand All @@ -71,10 +72,9 @@ private function handleDirectoryMapping(ContainerBuilder $container, string $dir

/** @var Index $document */
$document = $parser->getIndexAnnotation($class);
$indexMapping = $parser->getIndexMetadata($class);
$indexMetadata = $parser->getIndexMetadata($class);

if (!empty($indexMapping)) {
if (!empty($indexMetadata)) {
$indexMetadata['settings'] = array_filter(array_merge_recursive(
$indexMetadata['settings'] ?? [],
[
Expand All @@ -99,14 +99,20 @@ private function handleDirectoryMapping(ContainerBuilder $container, string $dir

$indexServiceDefinition = new Definition(IndexService::class, [
$namespace,
$container->getDefinition(Converter::class),
$converterDefinition,
$container->getDefinition('event_dispatcher'),
$container->getDefinition('serializer'),
$indexSettings,
$container->getParameter(Configuration::ONGR_PROFILER_CONFIG)
? $container->getDefinition('ongr.esb.tracer') : null
]);
$indexServiceDefinition->setPublic(true);
$converterDefinition->addMethodCall(
'addClassMetadata',
[
$namespace,
$parser->getPropertyMetadata($class)
]
);

$container->setDefinition($namespace, $indexServiceDefinition);
$this->indexes[$indexAlias] = $namespace;
Expand Down
1 change: 1 addition & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ public function getConfigTreeBuilder()

->arrayNode('indexes')
->defaultValue([])
->useAttributeAsKey('namespace')
->info(
'In case you want to override index settings defined in the annotation.' .
' e.g. use env variables instead.'
Expand Down
101 changes: 87 additions & 14 deletions Mapping/Converter.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,34 +11,107 @@

namespace ONGR\ElasticsearchBundle\Mapping;

use ONGR\ElasticsearchBundle\Annotation\NestedType;
use ONGR\ElasticsearchBundle\Annotation\ObjectType;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerAwareTrait;
use Symfony\Component\Serializer\SerializerInterface;
use ONGR\ElasticsearchBundle\Result\ObjectIterator;

/**
* This class converts array to document object.
*/
class Converter
{
use SerializerAwareTrait;
private $propertyMetadata = [];

private $documentParser;
public function addClassMetadata(string $class, array $metadata): void
{
$this->propertyMetadata[$class] = $metadata;
}

public function convertArrayToDocument(string $namespace, array $raw)
{
if (!isset($this->propertyMetadata[$namespace])) {
throw new \Exception("Cannot convert array to object of class `$class`.");
}

public function __construct(DocumentParser $documentParser)
return $this->denormalize($raw, $namespace);
}

public function convertDocumentToArray($document): array
{
$this->documentParser = $documentParser;
$class = get_class($document);

if (!isset($this->propertyMetadata[$class])) {
throw new \Exception("Cannot convert object of class `$class` to array.");
}

return $this->normalize($document);
}

public function convertArrayToDocument(string $namespace, array $raw, Serializer $serializer)
protected function normalize($document, $metadata = null)
{
return $serializer->denormalize($raw, $namespace);
$metadata = $metadata ?? $this->propertyMetadata[get_class($document)];
$result = [];

foreach ($metadata as $field => $fieldMeta) {
$getter = $fieldMeta['getter'];
$value = $fieldMeta['public'] ? $document->{$fieldMeta['name']} : $document->$getter();

if ($fieldMeta['embeded']) {
if (is_iterable($value)) {
foreach ($value as $item) {
$result[$field][] = $this->normalize($item, $fieldMeta['sub_properties']);
}
} else {
$result[$field] = $this->normalize($value, $fieldMeta['sub_properties']);
}
} else {
if ($value instanceof \DateTime) {
$value = $value->format(\DateTime::ISO8601);
}
$result[$field] = $value;
}
}

return $result;
}

public function convertDocumentToArray($document, Serializer $serializer): array
protected function denormalize(array $raw, string $namespace)
{
return $serializer->normalize($document, 'array');
$metadata = $this->propertyMetadata[$namespace];
$object = new $namespace();

foreach ($raw as $field => $value) {
$fieldMeta = $metadata[$field];
$setter = $fieldMeta['setter'];

if ($fieldMeta['embeded']) {
$this->addClassMetadata($fieldMeta['class'], $fieldMeta['sub_properties']);
$iterator = new ObjectIterator($fieldMeta['class'], $value, $this);

if ($fieldMeta['public']) {
$object->{$fieldMeta['name']} = $iterator;
} else {
$object->$setter($iterator);
}
} else {
if ($fieldMeta['type'] == 'date') {
$value = \DateTime::createFromFormat(\DateTime::ISO8601, $value) ?: null;
}
if ($fieldMeta['public']) {
$object->{$fieldMeta['name']} = $value;
} else {
if ($fieldMeta['identifier']) {
$setter = function ($field, $value) {
$this->$field = $value;
};

$setter = \Closure::bind($setter, $object, $object);
$setter($fieldMeta['name'], $value);
} else {
$object->$setter($value);
}
}
}
}

return $object;
}
}
122 changes: 121 additions & 1 deletion Mapping/DocumentParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use Doctrine\Common\Cache\Cache;
use ONGR\ElasticsearchBundle\Annotation\AbstractAnnotation;
use ONGR\ElasticsearchBundle\Annotation\Embedded;
use ONGR\ElasticsearchBundle\Annotation\Id;
use ONGR\ElasticsearchBundle\Annotation\Index;
use ONGR\ElasticsearchBundle\Annotation\NestedType;
use ONGR\ElasticsearchBundle\Annotation\ObjectType;
Expand Down Expand Up @@ -150,6 +151,9 @@ private function getClassMetadata(\ReflectionClass $class): array

if ($annotation instanceof Property) {
$fieldMapping['type'] = $annotation->type;
if ($annotation->fields) {
$fieldMapping['fields'] = $annotation->fields;
}
$fieldMapping['analyzer'] = $annotation->analyzer;
$fieldMapping['search_analyzer'] = $annotation->searchAnalyzer;
$fieldMapping['search_quote_analyzer'] = $annotation->searchQuoteAnalyzer;
Expand Down Expand Up @@ -186,6 +190,70 @@ private function getClassMetadata(\ReflectionClass $class): array
return $mapping;
}

public function getPropertyMetadata(\ReflectionClass $class, bool $subClass = false): array
{
if ($class->isTrait() || (!$this->reader->getClassAnnotation($class, Index::class) && !$subClass)) {
return [];
}

$metadata = [];

/** @var \ReflectionProperty $property */
foreach ($this->getDocumentPropertiesReflection($class) as $name => $property) {
/** @var AbstractAnnotation $annotation */
foreach ($this->reader->getPropertyAnnotations($property) as $annotation) {
if (!$annotation instanceof PropertiesAwareInterface) {
continue;
}

$propertyMetadata = [
'identifier' => false,
'class' => null,
'embeded' => false,
'type' => null,
'public' => $property->isPublic(),
'getter' => null,
'setter' => null,
'sub_properties' => []
];

$name = $property->getName();
$propertyMetadata['name'] = $name;

if (!$propertyMetadata['public']) {
$propertyMetadata['getter'] = $this->guessGetter($class, $name);
}

if ($annotation instanceof Id) {
$propertyMetadata['identifier'] = true;
} else {
if (!$propertyMetadata['public']) {
$propertyMetadata['setter'] = $this->guessSetter($class, $name);
}
}

if ($annotation instanceof Property) {
// we need the type (and possibly settings?) in Converter::denormalize()
$propertyMetadata['type'] = $annotation->type;
$propertyMetadata['settings'] = $annotation->settings;
}

if ($annotation instanceof Embedded) {
$propertyMetadata['embeded'] = true;
$propertyMetadata['class'] = $annotation->class;
$propertyMetadata['sub_properties'] = $this->getPropertyMetadata(
new \ReflectionClass($annotation->class),
true
);
}

$metadata[$annotation->getName() ?? Caser::snake($name)] = $propertyMetadata;
}
}

return $metadata;
}

public function getAnalysisConfig(\ReflectionClass $class): array
{
$config = [];
Expand All @@ -202,7 +270,14 @@ public function getAnalysisConfig(\ReflectionClass $class): array
}
}

foreach (['tokenizer', 'filter', 'normalizer', 'char_filter'] as $type) {
$normalizers = $this->getListFromArrayByKey('normalizer', $mapping);
foreach ($normalizers as $normalizer) {
if (isset($this->analysisConfig['normalizer'][$normalizer])) {
$config['normalizer'][$normalizer] = $this->analysisConfig['normalizer'][$normalizer];
}
}

foreach (['tokenizer', 'filter', 'char_filter'] as $type) {
$list = $this->getListFromArrayByKey($type, $config);

foreach ($list as $listItem) {
Expand All @@ -215,6 +290,51 @@ public function getAnalysisConfig(\ReflectionClass $class): array
return $config;
}

protected function guessGetter(\ReflectionClass $class, $name): string
{
if ($class->hasMethod($name)) {
return $name;
}

if ($class->hasMethod('get' . ucfirst($name))) {
return 'get' . ucfirst($name);
}

if ($class->hasMethod('is' . ucfirst($name))) {
return 'is' . ucfirst($name);
}

// if there are underscores in the name convert them to CamelCase
if (strpos($name, '_')) {
$name = Caser::camel($name);
if ($class->hasMethod('get' . ucfirst($name))) {
return 'get' . $name;
}
if ($class->hasMethod('is' . ucfirst($name))) {
return 'is' . $name;
}
}

throw new \Exception("Could not determine a getter for `$name` of class `{$class->getNamespaceName()}`");
}

protected function guessSetter(\ReflectionClass $class, $name): string
{
if ($class->hasMethod('set' . ucfirst($name))) {
return 'set' . ucfirst($name);
}

// if there are underscores in the name convert them to CamelCase
if (strpos($name, '_')) {
$name = Caser::camel($name);
if ($class->hasMethod('set' . ucfirst($name))) {
return 'set' . $name;
}
}

throw new \Exception("Could not determine a setter for `$name` of class `{$class->getNamespaceName()}`");
}

private function getListFromArrayByKey(string $searchKey, array $array): array
{
$list = [];
Expand Down
47 changes: 0 additions & 47 deletions Mapping/NameConverter.php

This file was deleted.

Loading

0 comments on commit 1220084

Please sign in to comment.