private function extractWithMetadata($entity, Entity $metadata) : array
 {
     if ($entity instanceof ProxyInterface) {
         $entity = $entity->__getRealEntity();
     }
     Assertion::isInstanceOf($entity, $metadata->getClassName());
     $data = [];
     $reflectionClass = new ReflectionClass($entity);
     foreach ($metadata->getFields() as $fieldMetadata) {
         if ($fieldMetadata->isReadOnly()) {
             continue;
         }
         $fieldName = $fieldMetadata->getFieldName();
         try {
             $type = $fieldMetadata->getType();
             $value = $this->getProperty($reflectionClass, $entity, $fieldMetadata->getPropertyName());
             if (!$fieldMetadata->isRepeatable()) {
                 $data[$fieldName] = $type->toFileMakerValue($value);
                 continue;
             }
             Assertion::isArray($value);
             $index = 0;
             foreach ($value as $individualValue) {
                 $data[sprintf('%s(%d)', $fieldName, ++$index)] = $type->toFileMakerValue($individualValue);
             }
         } catch (Exception $e) {
             throw ExtractionException::fromInvalidField($metadata, $fieldMetadata, $e);
         }
     }
     foreach ($metadata->getEmbeddables() as $embeddableMetadata) {
         $prefix = $embeddableMetadata->getFieldNamePrefix();
         $embeddableData = $this->extractWithMetadata($this->getProperty($reflectionClass, $entity, $embeddableMetadata->getPropertyName()), $embeddableMetadata->getMetadata());
         foreach ($embeddableData as $key => $value) {
             $data[$prefix . $key] = $value;
         }
     }
     $toOne = array_filter($metadata->getManyToOne(), function (ManyToOne $manyToOneMetadata) {
         return !$manyToOneMetadata->isReadOnly();
     }) + array_filter($metadata->getOneToOne(), function (OneToOne $oneToOneMetadata) {
         return $oneToOneMetadata->isOwningSide() && !$oneToOneMetadata->isReadOnly();
     });
     foreach ($toOne as $relationMetadata) {
         $relation = $this->getProperty($reflectionClass, $entity, $relationMetadata->getPropertyName());
         if (null === $relation) {
             $data[$relationMetadata->getFieldName()] = null;
             continue;
         }
         if ($relation instanceof ProxyInterface) {
             Assertion::isInstanceOf($relation->__getRealEntity(), $relationMetadata->getTargetEntity());
             $relationId = $relation->__getRelationId();
         } else {
             Assertion::isInstanceOf($relation, $relationMetadata->getTargetEntity());
             $relationId = $this->getProperty(new ReflectionClass($relation), $relation, $relationMetadata->getTargetPropertyName());
         }
         $data[$relationMetadata->getFieldName()] = $relationId;
     }
     return $data;
 }
 public function testGenericGetters()
 {
     $fields = [new Field('', '', $this->prophesize(TypeInterface::class)->reveal(), false, false)];
     $embeddables = [new Embeddable('', '', new Entity('', '', [], [], [], [], []))];
     $oneToMany = [new OneToMany('', '', '', '')];
     $manyToOne = [new ManyToOne('', '', '', '', '', '', '', false)];
     $oneToOne = [new OneToOne('', '', '', '', '', false, false)];
     $metadata = new Entity('layout', 'className', $fields, $embeddables, $oneToMany, $manyToOne, $oneToOne);
     $this->assertSame('layout', $metadata->getLayout());
     $this->assertSame('className', $metadata->getClassName());
     $this->assertSame($fields, $metadata->getFields());
     $this->assertSame($embeddables, $metadata->getEmbeddables());
     $this->assertSame($oneToMany, $metadata->getOneToMany());
     $this->assertSame($manyToOne, $metadata->getManyToOne());
     $this->assertSame($oneToOne, $metadata->getOneToOne());
 }
 private function hydrateWithMetadata(array $data, $entity, Entity $metadata)
 {
     Assertion::isInstanceOf($entity, $metadata->getClassName());
     $reflectionClass = new ReflectionClass($entity);
     foreach ($metadata->getFields() as $fieldMetadata) {
         try {
             $type = $fieldMetadata->getType();
             $value = $data[$fieldMetadata->getFieldName()];
             if ($fieldMetadata->isRepeatable()) {
                 Assertion::isArray($value);
                 $value = array_map(function ($value) use($type) {
                     return $type->fromFileMakerValue($value);
                 }, $value);
             } else {
                 $value = $type->fromFileMakerValue($value);
             }
             $this->setProperty($reflectionClass, $entity, $fieldMetadata->getPropertyName(), $value);
         } catch (Exception $e) {
             throw HydrationException::fromInvalidField($metadata, $fieldMetadata, $e);
         }
     }
     foreach ($metadata->getEmbeddables() as $embeddableMetadata) {
         $prefix = $embeddableMetadata->getFieldNamePrefix();
         if ('' === $prefix) {
             $embeddableData = $data;
         } else {
             $prefixLength = strlen($prefix);
             foreach ($data as $key => $value) {
                 if (0 !== strpos($key, $prefix)) {
                     continue;
                 }
                 $embeddableData[substr($key, $prefixLength)] = $value;
             }
         }
         $reflectionProperty = $reflectionClass->getProperty($embeddableMetadata->getPropertyName());
         $reflectionProperty->setAccessible(true);
         $embeddable = $reflectionProperty->getValue($entity);
         if (null === $embeddable) {
             $embeddable = (new ReflectionClass($embeddableMetadata->getMetadata()->getClassName()))->newInstanceWithoutConstructor();
         }
         $reflectionProperty->setValue($entity, $this->hydrateWithMetadata($embeddableData, $embeddable, $embeddableMetadata->getMetadata()));
     }
     foreach ($metadata->getOneToMany() as $relationMetadata) {
         $repository = $this->repositoryBuilder->buildRepository($relationMetadata->getTargetEntity());
         if ($relationMetadata->hasEagerHydration()) {
             $items = [];
             foreach ($data[$relationMetadata->getTargetTable()] as $record) {
                 $items[] = $repository->createEntity($record);
             }
             $collection = new ItemCollection($items, count($items));
         } else {
             $collection = new LazyLoadedCollection($repository, $relationMetadata->getTargetFieldName(), $data[$relationMetadata->getTargetTable()]);
         }
         $this->setProperty($reflectionClass, $entity, $relationMetadata->getPropertyName(), $collection);
     }
     $toOne = $metadata->getManyToOne() + $metadata->getOneToOne();
     foreach ($toOne as $relationMetadata) {
         if (empty($data[$relationMetadata->getTargetTable()])) {
             $this->setProperty($reflectionClass, $entity, $relationMetadata->getPropertyName(), null);
             continue;
         }
         $repository = $this->repositoryBuilder->buildRepository($relationMetadata->getTargetEntity());
         if ($relationMetadata->hasEagerHydration()) {
             $this->setProperty($reflectionClass, $entity, $relationMetadata->getPropertyName(), $repository->createEntity($data[$relationMetadata->getTargetTable()][0]));
             continue;
         }
         Assertion::true($metadata->hasInterfaceName(), sprintf('Entity "%s" has no interface name definied', $metadata->getClassName()));
         $fieldName = $relationMetadata->getTargetFieldName();
         $fieldValue = (string) $data[$relationMetadata->getTargetTable()][0][$fieldName];
         $proxy = $this->proxyBuilder->createProxy($relationMetadata->getTargetInterfaceName(), function () use($repository, $fieldName, $fieldValue) {
             return $repository->findOneBy([$fieldName => $repository->quoteString($fieldValue)]);
         }, $fieldValue);
         $this->setProperty($reflectionClass, $entity, $relationMetadata->getPropertyName(), $proxy);
     }
     if ($metadata->hasRecordId()) {
         $recordIdMetadata = $metadata->getRecordId();
         $this->setProperty($reflectionClass, $entity, $recordIdMetadata->getPropertyName(), $data['record-id']);
     }
     return $entity;
 }