예제 #1
0
 /**
  * @dataProvider convertToPHPValueProvider
  *
  * @param array  $ref     reference as from mongo
  * @param string $routeId name of route that should get loaded
  * @param string $url     url we expect to result from the conversion
  *
  * @return void
  */
 public function testConvertToPHPValue($ref, $routeId, $url)
 {
     $this->doubles['router']->expects($this->once())->method('generate')->with($this->equalTo($routeId), $this->equalTo(array('id' => $ref['$id'])))->will($this->returnValue($url));
     $sut = Type::getType('extref');
     $sut->setRouter($this->doubles['router']);
     $this->assertEquals($url, $sut->convertToPHPValue($ref));
 }
 /**
  * inject services into custom type
  *
  * @return void
  */
 public function boot()
 {
     /* @var $router Router */
     $router = $this->container->get('router');
     /* @var $type \Graviton\DocumentBundle\Types\ExtReference */
     $type = Type::getType('extref');
     $type->setRouter($router);
     $type->setMapping($this->container->getParameter('graviton.document.type.extref.mapping'));
 }
예제 #3
0
 /**
  * @dataProvider provideDatabaseToPHPValues
  */
 public function testClosureToPHP($input, $output)
 {
     $type = Type::getType(Type::DATE);
     $return = null;
     call_user_func(function ($value) use($type, &$return) {
         eval($type->closureToPHP());
     }, $input);
     $this->assertInstanceOf('DateTime', $return);
     $this->assertTimestampEquals($output, $return);
 }
예제 #4
0
 /**
  * @param ClassMetadata $classMetadata
  *
  * @throws MappingException
  */
 protected function checkEnumType(ClassMetadata $classMetadata)
 {
     foreach ($classMetadata->fieldMappings as $fieldName => $mapping) {
         if (isset($mapping['cubiche:enum'])) {
             $enumMapping = $mapping['cubiche:enum'];
             $type = str_replace('\\', '.', $enumMapping['type']);
             if (!Type::hasType($type)) {
                 Type::registerType($type, DynamicEnumType::class);
                 Type::getType($type)->setTargetClass($enumMapping['type']);
             }
             $classMetadata->fieldMappings[$fieldName]['type'] = $type;
         }
     }
 }
예제 #5
0
 public function convertToPHPValue($value)
 {
     if ($value === null) {
         return null;
     }
     if (!is_array($value)) {
         throw new CustomTypeException('Array expected.');
     }
     $converter = Type::getType('date');
     $value = array_map(function ($date) use($converter) {
         return $converter->convertToPHPValue($date);
     }, array_values($value));
     return $value;
 }
예제 #6
0
 /**
  * {@inheritdoc}
  */
 public function convertToPHPValue($value)
 {
     if ($value === null) {
         return new ArrayList();
     }
     if (is_array($value) || $value instanceof \Traversable) {
         $items = array();
         $type = Type::getType($this->innerType);
         foreach ($value as $item) {
             $items[] = $type->convertToPHPValue($item);
         }
         return new ArrayList($items);
     }
     return parent::convertToPHPValue($value);
 }
 public function testCreation()
 {
     $logger = $this->getMockForAbstractClass('DoctrineMongoODMModule\\Logging\\Logger');
     $metadataCache = $this->getMockForAbstractClass('Doctrine\\Common\\Cache\\Cache');
     $mappingDriver = $this->getMockForAbstractClass('Doctrine\\Common\\Persistence\\Mapping\\Driver\\MappingDriver');
     $serviceLocator = $this->getMockForAbstractClass('Zend\\ServiceManager\\ServiceLocatorInterface');
     $serviceLocator->expects($this->exactly(4))->method('get')->withConsecutive(array('Configuration'), array('stubbed_logger'), array('doctrine.cache.stubbed_metadatacache'), array('doctrine.driver.stubbed_driver'))->willReturnOnConsecutiveCalls(array('doctrine' => array('configuration' => array('odm_test' => array('logger' => 'stubbed_logger', 'metadata_cache' => 'stubbed_metadatacache', 'driver' => 'stubbed_driver', 'generate_proxies' => true, 'proxy_dir' => 'data/DoctrineMongoODMModule/Proxy', 'proxy_namespace' => 'DoctrineMongoODMModule\\Proxy', 'generate_hydrators' => true, 'hydrator_dir' => 'data/DoctrineMongoODMModule/Hydrator', 'hydrator_namespace' => 'DoctrineMongoODMModule\\Hydrator', 'default_db' => 'default_db', 'filters' => array(), 'types' => array('CustomType' => 'DoctrineMongoODMModuleTest\\Assets\\CustomType'), 'classMetadataFactoryName' => 'stdClass')))), $logger, $metadataCache, $mappingDriver);
     $factory = new ConfigurationFactory('odm_test');
     $config = $factory->createService($serviceLocator);
     $this->assertInstanceOf('Doctrine\\ODM\\MongoDB\\Configuration', $config);
     $this->assertNotNull($config->getLoggerCallable());
     $this->assertSame($metadataCache, $config->getMetadataCacheImpl());
     $this->assertSame($mappingDriver, $config->getMetadataDriverImpl());
     $this->assertInstanceOf('DoctrineMongoODMModuleTest\\Assets\\CustomType', \Doctrine\ODM\MongoDB\Types\Type::getType('CustomType'));
 }
예제 #8
0
 /**
  * Used to do the common work of computeChangeSet and recomputeSingleDocumentChangeSet
  *
  * @param \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $class
  * @param object $document
  * @param boolean $recompute
  */
 private function computeOrRecomputeChangeSet(ClassMetadata $class, $document, $recompute = false)
 {
     $oid = spl_object_hash($document);
     $actualData = $this->getDocumentActualData($document);
     $isNewDocument = !isset($this->originalDocumentData[$oid]);
     if ($isNewDocument) {
         // Document is either NEW or MANAGED but not yet fully persisted (only has an id).
         // These result in an INSERT.
         $this->originalDocumentData[$oid] = $actualData;
         $changeSet = array();
         foreach ($actualData as $propName => $actualValue) {
             $changeSet[$propName] = array(null, $actualValue);
         }
         $this->documentChangeSets[$oid] = $changeSet;
     } else {
         // Document is "fully" MANAGED: it was already fully persisted before
         // and we have a copy of the original data
         $originalData = $this->originalDocumentData[$oid];
         $isChangeTrackingNotify = $class->isChangeTrackingNotify();
         if ($isChangeTrackingNotify && !$recompute) {
             $changeSet = $this->documentChangeSets[$oid];
         } else {
             $changeSet = array();
         }
         foreach ($actualData as $propName => $actualValue) {
             // skip not saved fields
             if (isset($class->fieldMappings[$propName]['notSaved']) && $class->fieldMappings[$propName]['notSaved'] === true) {
                 continue;
             }
             $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
             // skip if value has not changed
             if ($orgValue === $actualValue) {
                 // but consider dirty GridFSFile instances as changed
                 if (!(isset($class->fieldMappings[$propName]['file']) && $actualValue->isDirty())) {
                     continue;
                 }
             }
             // if relationship is a embed-one, schedule orphan removal to trigger cascade remove operations
             if (isset($class->fieldMappings[$propName]['embedded']) && $class->fieldMappings[$propName]['type'] === 'one') {
                 if ($orgValue !== null) {
                     $this->scheduleOrphanRemoval($orgValue);
                 }
                 $changeSet[$propName] = array($orgValue, $actualValue);
                 continue;
             }
             // if owning side of reference-one relationship
             if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'one' && $class->fieldMappings[$propName]['isOwningSide']) {
                 if ($orgValue !== null && $class->fieldMappings[$propName]['orphanRemoval']) {
                     $this->scheduleOrphanRemoval($orgValue);
                 }
                 $changeSet[$propName] = array($orgValue, $actualValue);
                 continue;
             }
             if ($isChangeTrackingNotify) {
                 continue;
             }
             // ignore inverse side of reference-many relationship
             if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'many' && $class->fieldMappings[$propName]['isInverseSide']) {
                 continue;
             }
             // Persistent collection was exchanged with the "originally"
             // created one. This can only mean it was cloned and replaced
             // on another document.
             if ($actualValue instanceof PersistentCollection) {
                 $owner = $actualValue->getOwner();
                 if ($owner === null) {
                     // cloned
                     $actualValue->setOwner($document, $class->fieldMappings[$propName]);
                 } elseif ($owner !== $document) {
                     // no clone, we have to fix
                     if (!$actualValue->isInitialized()) {
                         $actualValue->initialize();
                         // we have to do this otherwise the cols share state
                     }
                     $newValue = clone $actualValue;
                     $newValue->setOwner($document, $class->fieldMappings[$propName]);
                     $class->reflFields[$propName]->setValue($document, $newValue);
                 }
             }
             // if embed-many or reference-many relationship
             if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'many') {
                 $changeSet[$propName] = array($orgValue, $actualValue);
                 if ($orgValue instanceof PersistentCollection) {
                     $this->scheduleCollectionDeletion($orgValue);
                 }
                 continue;
             }
             // skip equivalent date values
             if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'date') {
                 $dateType = Type::getType('date');
                 $dbOrgValue = $dateType->convertToDatabaseValue($orgValue);
                 $dbActualValue = $dateType->convertToDatabaseValue($actualValue);
                 if ($dbOrgValue instanceof \MongoDate && $dbActualValue instanceof \MongoDate && $dbOrgValue == $dbActualValue) {
                     continue;
                 }
             }
             // regular field
             $changeSet[$propName] = array($orgValue, $actualValue);
         }
         if ($changeSet) {
             $this->documentChangeSets[$oid] = $recompute && isset($this->documentChangeSets[$oid]) ? $changeSet + $this->documentChangeSets[$oid] : $changeSet;
             $this->originalDocumentData[$oid] = $actualData;
             $this->documentUpdates[$oid] = $document;
         }
     }
     // Look for changes in associations of the document
     foreach ($class->fieldMappings as $mapping) {
         // skip not saved fields
         if (isset($mapping['notSaved']) && $mapping['notSaved'] === true) {
             continue;
         }
         if (isset($mapping['reference']) || isset($mapping['embedded'])) {
             $value = $class->reflFields[$mapping['fieldName']]->getValue($document);
             if ($value !== null) {
                 $this->computeAssociationChanges($document, $mapping, $value);
                 if (isset($mapping['reference'])) {
                     continue;
                 }
                 $values = $value;
                 if (isset($mapping['type']) && $mapping['type'] === 'one') {
                     $values = array($values);
                 } elseif ($values instanceof PersistentCollection) {
                     $values = $values->unwrap();
                 }
                 foreach ($values as $obj) {
                     $oid2 = spl_object_hash($obj);
                     if (isset($this->documentChangeSets[$oid2])) {
                         $this->documentChangeSets[$oid][$mapping['fieldName']] = array($value, $value);
                         if (!$isNewDocument) {
                             $this->documentUpdates[$oid] = $document;
                         }
                         break;
                     }
                 }
             }
         }
     }
 }
 /**
  * @expectedException \Doctrine\ODM\MongoDB\MongoDBException
  * @expectedExceptionMessage Hash type requires value of type array or null, scalar given
  */
 public function testHashDoesntAcceptScalar()
 {
     $t = Type::getType('hash');
     $t->convertToDatabaseValue(true);
 }
 private function getType($type)
 {
     // due to change in ODM beta 9
     return class_exists('Doctrine\\ODM\\MongoDB\\Types\\Type') ? \Doctrine\ODM\MongoDB\Types\Type::getType($type) : \Doctrine\ODM\MongoDB\Mapping\Types\Type::getType($type);
 }
예제 #11
0
 public function closureToPHP()
 {
     return '$process = $value;foreach ($process as $key => $value) { if ($value) { ' . Type::getType('dough_currency_money')->closureToPHP() . '$process[$key] = $return; } } $return = $process;';
 }
예제 #12
0
 /**
  * setup type we want to test
  *
  * @return void
  */
 public function setUp()
 {
     Type::registerType('hasharray', HashArrayType::class);
     $this->type = Type::getType('hasharray');
 }
예제 #13
0
 public function testClosureToPHP()
 {
     $type = Type::getType('dough_money');
     $this->assertSame('$return = new \\Dough\\Money\\Money($value);', $type->closureToPHP());
 }
예제 #14
0
 /**
  * @dataProvider provideInvalidMongoIdConstructorArguments
  */
 public function testConvertToDatabaseValueShouldGenerateMongoIds($value)
 {
     $type = Type::getType('id');
     $this->assertInstanceOf('MongoId', $type->convertToDatabaseValue($value));
 }
 /**
  * Casts the identifier to its database type.
  *
  * @param mixed $id
  * @return mixed $id
  */
 public function getDatabaseIdentifierValue($id)
 {
     $idType = $this->fieldMappings[$this->identifier]['type'];
     return Type::getType($idType)->convertToDatabaseValue($id);
 }
예제 #16
0
 public function provideTypesForIdempotent()
 {
     return array(array(Type::getType(Type::ID), new \MongoId()), array(Type::getType(Type::DATE), new \MongoDate()), array(Type::getType(Type::TIMESTAMP), new \MongoTimestamp()), array(Type::getType(Type::BINDATA), new MongoBinData('foobarbaz', 0)), array(Type::getType(Type::BINDATAFUNC), new MongoBinData('foobarbaz', MongoBinData::FUNC)), array(Type::getType(Type::BINDATABYTEARRAY), new MongoBinData('foobarbaz', MongoBinData::BYTE_ARRAY)), array(Type::getType(Type::BINDATAUUID), new MongoBinData("7f1c6d80-3e0b-11e5-b8ed-0002a5d5c51b", MongoBinData::UUID)), array(Type::getType(Type::BINDATAUUIDRFC4122), new MongoBinData(str_repeat('a', 16), 4)), array(Type::getType(Type::BINDATAMD5), new MongoBinData(md5('ODM'), MongoBinData::MD5)), array(Type::getType(Type::BINDATACUSTOM), new MongoBinData('foobarbaz', MongoBinData::CUSTOM)), array(Type::getType(Type::OBJECTID), new \MongoId()));
 }
예제 #17
0
 /**
  * setup type we want to test
  *
  * @return void
  */
 public function setUp()
 {
     Type::registerType('hash', 'Graviton\\DocumentBundle\\Types\\HashType');
     $this->type = Type::getType('hash');
 }
예제 #18
0
 /**
  * boot bundle function
  *
  * @return void
  */
 public function boot()
 {
     $extRefConverter = $this->container->get('graviton.document.service.extrefconverter');
     $customType = Type::getType('hash');
     $customType->setExtRefConverter($extRefConverter);
 }
예제 #19
0
 /**
  * @param object $document
  *
  * @return array
  * @throws MongoDBException
  */
 private function getShardKeyQuery($document)
 {
     if (!$this->class->isSharded()) {
         return array();
     }
     $shardKey = $this->class->getShardKey();
     $keys = array_keys($shardKey['keys']);
     $data = $this->uow->getDocumentActualData($document);
     $shardKeyQueryPart = array();
     foreach ($keys as $key) {
         $mapping = $this->class->getFieldMappingByDbFieldName($key);
         $this->guardMissingShardKey($document, $key, $data);
         $value = Type::getType($mapping['type'])->convertToDatabaseValue($data[$mapping['fieldName']]);
         $shardKeyQueryPart[$key] = $value;
     }
     return $shardKeyQueryPart;
 }
예제 #20
0
 public function test64bit1900Date()
 {
     if (PHP_INT_SIZE === 8) {
         $type = Type::getType(Type::DATE);
         $return = $type->convertToDatabaseValue('1900-01-01');
         $this->assertInstanceOf('MongoDate', $return);
         $this->assertEquals(new \MongoDate(strtotime('1900-01-01')), $return);
     } else {
         $this->markTestSkipped("Platform is not 64-bit");
     }
 }
예제 #21
0
 public function testClosureToPHP()
 {
     $type = Type::getType('dough_currency_money_hash');
     $expected = '$process = $value;foreach ($process as $key => $value) { if ($value) { $money = explode(\':\', $value);$return = new \\Dough\\Money\\MultiCurrencyMoney($money[1], $money[0]);$process[$key] = $return; } } $return = $process;';
     $this->assertSame($expected, $type->closureToPHP());
 }
예제 #22
0
 public function testClosureToPHP()
 {
     $type = Type::getType('dough_currency_money');
     $this->assertSame('$money = explode(\':\', $value);$return = new \\Dough\\Money\\MultiCurrencyMoney($money[1], $money[0]);', $type->closureToPHP());
 }
예제 #23
0
 /**
  * Returns the embedded document to be stored in MongoDB.
  *
  * The return value will usually be an associative array with string keys
  * corresponding to field names on the embedded document. An object may be
  * returned if the document is empty, to ensure that a BSON object will be
  * stored in lieu of an array.
  *
  * If $includeNestedCollections is true, nested collections will be included
  * in this prepared value and the option will cascade to all embedded
  * associations. If any nested PersistentCollections (embed or reference)
  * within this value were previously scheduled for deletion or update, they
  * will also be unscheduled.
  *
  * @param array $embeddedMapping
  * @param object $embeddedDocument
  * @param boolean $includeNestedCollections
  * @return array|object
  * @throws \UnexpectedValueException if an unsupported associating mapping is found
  */
 public function prepareEmbeddedDocumentValue(array $embeddedMapping, $embeddedDocument, $includeNestedCollections = false)
 {
     $embeddedDocumentValue = array();
     $class = $this->dm->getClassMetadata(get_class($embeddedDocument));
     foreach ($class->fieldMappings as $mapping) {
         // Skip notSaved fields
         if (!empty($mapping['notSaved'])) {
             continue;
         }
         // Inline ClassMetadataInfo::getFieldValue()
         $rawValue = $class->reflFields[$mapping['fieldName']]->getValue($embeddedDocument);
         $value = null;
         if ($rawValue !== null) {
             switch (isset($mapping['association']) ? $mapping['association'] : null) {
                 // @Field, @String, @Date, etc.
                 case null:
                     $value = Type::getType($mapping['type'])->convertToDatabaseValue($rawValue);
                     break;
                 case ClassMetadata::EMBED_ONE:
                 case ClassMetadata::REFERENCE_ONE:
                     // Nested collections should only be included for embedded relationships
                     $value = $this->prepareAssociatedDocumentValue($mapping, $rawValue, $includeNestedCollections && isset($mapping['embedded']));
                     break;
                 case ClassMetadata::EMBED_MANY:
                 case ClassMetadata::REFERENCE_MANY:
                     // Skip PersistentCollections already scheduled for deletion
                     if (!$includeNestedCollections && $rawValue instanceof PersistentCollection && $this->uow->isCollectionScheduledForDeletion($rawValue)) {
                         break;
                     }
                     // We're handling atomicSet or atomicSetArray collection
                     if ($includeNestedCollections && $rawValue instanceof PersistentCollection) {
                         $this->uow->unscheduleCollectionDeletion($rawValue);
                         $this->uow->unscheduleCollectionUpdate($rawValue);
                     }
                     $pb = $this;
                     $value = $rawValue->map(function ($v) use($pb, $mapping, $includeNestedCollections) {
                         // Nested collections should only be included for embedded relationships
                         return $pb->prepareAssociatedDocumentValue($mapping, $v, $includeNestedCollections && isset($mapping['embedded']));
                     })->toArray();
                     // Numerical reindexing may be necessary to ensure BSON array storage
                     if (in_array($mapping['strategy'], array('atomicSetArray', 'setArray', 'pushAll', 'addToSet'))) {
                         $value = array_values($value);
                     }
                     break;
                 default:
                     throw new \UnexpectedValueException('Unsupported mapping association: ' . $mapping['association']);
             }
         }
         // Omit non-nullable fields that would have a null value
         if ($value === null && $mapping['nullable'] === false) {
             continue;
         }
         $embeddedDocumentValue[$mapping['name']] = $value;
     }
     /* Add a discriminator value if the embedded document is not mapped
      * explicitly to a targetDocument class.
      */
     if (!isset($embeddedMapping['targetDocument'])) {
         $discriminatorField = $embeddedMapping['discriminatorField'];
         $discriminatorValue = isset($embeddedMapping['discriminatorMap']) ? array_search($class->name, $embeddedMapping['discriminatorMap']) : $class->name;
         /* If the discriminator value was not found in the map, use the full
          * class name. In the future, it may be preferable to throw an
          * exception here (perhaps based on some strictness option).
          *
          * @see DocumentManager::createDBRef()
          */
         if ($discriminatorValue === false) {
             $discriminatorValue = $class->name;
         }
         $embeddedDocumentValue[$discriminatorField] = $discriminatorValue;
     }
     /* If the class has a discriminator (field and value), use it. A child
      * class that is not defined in the discriminator map may only have a
      * discriminator field and no value, so default to the full class name.
      */
     if (isset($class->discriminatorField)) {
         $embeddedDocumentValue[$class->discriminatorField] = isset($class->discriminatorValue) ? $class->discriminatorValue : $class->name;
     }
     // Ensure empty embedded documents are stored as BSON objects
     if (empty($embeddedDocumentValue)) {
         return (object) $embeddedDocumentValue;
     }
     /* @todo Consider always casting the return value to an object, or
      * building $embeddedDocumentValue as an object instead of an array, to
      * handle the edge case where all database field names are sequential,
      * numeric keys.
      */
     return $embeddedDocumentValue;
 }
예제 #24
0
 /**
  * @param ClassMetadata $classMetadata
  *
  * @throws MappingException
  */
 protected function checkArrayCollectionType(ClassMetadata $classMetadata)
 {
     foreach ($classMetadata->fieldMappings as $fieldName => $mapping) {
         if (isset($mapping['embedded']) || isset($mapping['reference'])) {
             continue;
         }
         if (isset($mapping['cubiche:collection'])) {
             $collectionMapping = $mapping['cubiche:collection'];
             if ($collectionMapping['of'] === null) {
                 throw MappingException::inField('The "of" option in ' . $collectionMapping['type'] . ' type is missing', $classMetadata->name, $fieldName);
             }
             $type = $collectionMapping['of'] . $collectionMapping['type'];
             if (!Type::hasType($type)) {
                 Type::addType($type, $collectionMapping['typeClassName']);
                 Type::getType($type)->setInnerType($collectionMapping['of']);
             }
             $classMetadata->fieldMappings[$fieldName]['type'] = $type;
         }
     }
 }
예제 #25
0
 /**
  * setup type we want to test
  *
  * @return void
  */
 public function setUp()
 {
     Type::registerType('extref', ExtReferenceType::class);
     $this->type = Type::getType('extref');
 }
예제 #26
0
 /**
  * setup type we want to test
  *
  * @return void
  */
 public function setUp()
 {
     Type::registerType('datearray', DateArrayType::class);
     $this->type = Type::getType('datearray');
 }
예제 #27
0
 /**
  * Used to do the common work of computeChangeSet and recomputeSingleDocumentChangeSet
  *
  * @param \Doctrine\ODM\MongoDB\Mapping\ClassMetadata $class
  * @param object $document
  * @param boolean $recompute
  */
 private function computeOrRecomputeChangeSet(ClassMetadata $class, $document, $recompute = false)
 {
     $oid = spl_object_hash($document);
     $actualData = $this->getDocumentActualData($document);
     $isNewDocument = !isset($this->originalDocumentData[$oid]);
     if ($isNewDocument) {
         // Document is either NEW or MANAGED but not yet fully persisted (only has an id).
         // These result in an INSERT.
         $this->originalDocumentData[$oid] = $actualData;
         $changeSet = array();
         foreach ($actualData as $propName => $actualValue) {
             /* At this PersistentCollection shouldn't be here, probably it
              * was cloned and its ownership must be fixed
              */
             if ($actualValue instanceof PersistentCollection && $actualValue->getOwner() !== $document) {
                 $actualData[$propName] = $this->fixPersistentCollectionOwnership($actualValue, $document, $class, $propName);
                 $actualValue = $actualData[$propName];
             }
             $changeSet[$propName] = array(null, $actualValue);
         }
         $this->documentChangeSets[$oid] = $changeSet;
     } else {
         // Document is "fully" MANAGED: it was already fully persisted before
         // and we have a copy of the original data
         $originalData = $this->originalDocumentData[$oid];
         $isChangeTrackingNotify = $class->isChangeTrackingNotify();
         if ($isChangeTrackingNotify && !$recompute) {
             $changeSet = $this->documentChangeSets[$oid];
         } else {
             $changeSet = array();
         }
         foreach ($actualData as $propName => $actualValue) {
             // skip not saved fields
             if (isset($class->fieldMappings[$propName]['notSaved']) && $class->fieldMappings[$propName]['notSaved'] === true) {
                 continue;
             }
             $orgValue = isset($originalData[$propName]) ? $originalData[$propName] : null;
             // skip if value has not changed
             if ($orgValue === $actualValue) {
                 // but consider dirty GridFSFile instances as changed
                 if (!(isset($class->fieldMappings[$propName]['file']) && $actualValue->isDirty())) {
                     continue;
                 }
             }
             // if relationship is a embed-one, schedule orphan removal to trigger cascade remove operations
             if (isset($class->fieldMappings[$propName]['embedded']) && $class->fieldMappings[$propName]['type'] === 'one') {
                 if ($orgValue !== null) {
                     $this->scheduleOrphanRemoval($orgValue);
                 }
                 $changeSet[$propName] = array($orgValue, $actualValue);
                 continue;
             }
             // if owning side of reference-one relationship
             if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'one' && $class->fieldMappings[$propName]['isOwningSide']) {
                 if ($orgValue !== null && $class->fieldMappings[$propName]['orphanRemoval']) {
                     $this->scheduleOrphanRemoval($orgValue);
                 }
                 $changeSet[$propName] = array($orgValue, $actualValue);
                 continue;
             }
             if ($isChangeTrackingNotify) {
                 continue;
             }
             // ignore inverse side of reference-many relationship
             if (isset($class->fieldMappings[$propName]['reference']) && $class->fieldMappings[$propName]['type'] === 'many' && $class->fieldMappings[$propName]['isInverseSide']) {
                 continue;
             }
             // Persistent collection was exchanged with the "originally"
             // created one. This can only mean it was cloned and replaced
             // on another document.
             if ($actualValue instanceof PersistentCollection && $actualValue->getOwner() !== $document) {
                 $this->fixPersistentCollectionOwnership($actualValue, $document, $class, $propName);
             }
             // if embed-many or reference-many relationship
             if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'many') {
                 $changeSet[$propName] = array($orgValue, $actualValue);
                 /* If original collection was exchanged with a non-empty value
                  * and $set will be issued, there is no need to $unset it first
                  */
                 if ($actualValue && $actualValue->isDirty() && CollectionHelper::usesSet($class->fieldMappings[$propName]['strategy'])) {
                     continue;
                 }
                 if ($orgValue instanceof PersistentCollection) {
                     $this->scheduleCollectionDeletion($orgValue);
                 }
                 continue;
             }
             // skip equivalent date values
             if (isset($class->fieldMappings[$propName]['type']) && $class->fieldMappings[$propName]['type'] === 'date') {
                 $dateType = Type::getType('date');
                 $dbOrgValue = $dateType->convertToDatabaseValue($orgValue);
                 $dbActualValue = $dateType->convertToDatabaseValue($actualValue);
                 if ($dbOrgValue instanceof \MongoDate && $dbActualValue instanceof \MongoDate && $dbOrgValue == $dbActualValue) {
                     continue;
                 }
             }
             // regular field
             $changeSet[$propName] = array($orgValue, $actualValue);
         }
         if ($changeSet) {
             $this->documentChangeSets[$oid] = $recompute && isset($this->documentChangeSets[$oid]) ? $changeSet + $this->documentChangeSets[$oid] : $changeSet;
             $this->originalDocumentData[$oid] = $actualData;
             $this->scheduleForUpdate($document);
         }
     }
     // Look for changes in associations of the document
     $associationMappings = array_filter($class->associationMappings, function ($assoc) {
         return empty($assoc['notSaved']);
     });
     foreach ($associationMappings as $mapping) {
         $value = $class->reflFields[$mapping['fieldName']]->getValue($document);
         if ($value === null) {
             continue;
         }
         $this->computeAssociationChanges($document, $mapping, $value);
         if (isset($mapping['reference'])) {
             continue;
         }
         $values = $mapping['type'] === ClassMetadata::ONE ? array($value) : $value->unwrap();
         foreach ($values as $obj) {
             $oid2 = spl_object_hash($obj);
             if (isset($this->documentChangeSets[$oid2])) {
                 $this->documentChangeSets[$oid][$mapping['fieldName']] = array($value, $value);
                 if (!$isNewDocument) {
                     $this->scheduleForUpdate($document);
                 }
                 break;
             }
         }
     }
 }
예제 #28
0
 /**
  * @param $document
  * @return array
  * @throws MongoDBException
  */
 public function getShardKeyQuery($document)
 {
     $shardKeysQueryPart = array();
     $dcs = $this->uow->getDocumentChangeSet($document);
     $data = $this->uow->getDocumentActualData($document);
     $md = $this->dm->getMetadataFactory()->getMetadataFor(get_class($document));
     $keys = $md->shardKeys;
     $fieldMappings = $this->dm->getClassMetadata(get_class($document))->fieldMappings;
     foreach ($keys as $key) {
         if ($key !== 'id') {
             $queryKey = $fieldMappings[$key]['name'];
         } else {
             $queryKey = '_id';
         }
         //If the document is new, we can ignore shard key value, otherwise throw exception
         $isUpdate = $this->uow->isScheduledForUpdate($document);
         if ($isUpdate && isset($dcs[$key]) && $dcs[$key][0] !== null && $dcs[$key][0] != $dcs[$key][1] && (!isset($options['upsert']) || isset($options['upsert']) && $options['upsert'] === true)) {
             throw MongoDBException::shardKeyChange($key);
         }
         if (!isset($data[$key])) {
             throw MongoDBException::shardKeyMissing($key);
         }
         $new = $data[$key];
         $mapping = $fieldMappings[$key];
         // @Field, @String, @Date, etc.
         if (!isset($mapping['association'])) {
             if (isset($new) || $mapping['nullable'] === true) {
                 $shardKeysQueryPart[$queryKey] = is_null($new) ? null : Type::getType($mapping['type'])->convertToDatabaseValue($new);
             }
             // @ReferenceOne
         } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) {
             if (isset($new) || $mapping['nullable'] === true) {
                 $shardKeysQueryPart[$queryKey] = is_null($new) ? null : $this->pb->prepareReferencedDocumentValue($mapping, $new);
             }
             // @ReferenceMany
         } elseif (isset($mapping['association']) && $mapping['association'] === ClassMetadata::REFERENCE_MANY) {
             // Do nothing right now
         }
     }
     return $shardKeysQueryPart;
 }
예제 #29
0
    /**
     * @param ClassMetadata $class
     * @param string $hydratorClassName
     * @param string $fileName
     */
    private function generateHydratorClass(ClassMetadata $class, $hydratorClassName, $fileName)
    {
        $code = '';
        foreach ($class->fieldMappings as $fieldName => $mapping) {
            if (isset($mapping['alsoLoadFields'])) {
                foreach ($mapping['alsoLoadFields'] as $name) {
                    $code .= sprintf(<<<EOF

        /** @AlsoLoad("{$name}") */
        if (!array_key_exists('%1\$s', \$data) && array_key_exists('{$name}', \$data)) {
            \$data['%1\$s'] = \$data['{$name}'];
        }

EOF
, $mapping['name']);
                }
            }
            if ($mapping['type'] === 'date') {
                $code .= sprintf(<<<EOF

        /** @Field(type="date") */
        if (isset(\$data['%1\$s'])) {
            \$value = \$data['%1\$s'];
            %3\$s
            \$this->class->reflFields['%2\$s']->setValue(\$document, clone \$return);
            \$hydratedData['%2\$s'] = \$return;
        }

EOF
, $mapping['name'], $mapping['fieldName'], Type::getType($mapping['type'])->closureToPHP());
            } elseif (!isset($mapping['association'])) {
                $code .= sprintf(<<<EOF

        /** @Field(type="{$mapping['type']}") */
        if (isset(\$data['%1\$s'])) {
            \$value = \$data['%1\$s'];
            %3\$s
            \$this->class->reflFields['%2\$s']->setValue(\$document, \$return);
            \$hydratedData['%2\$s'] = \$return;
        }

EOF
, $mapping['name'], $mapping['fieldName'], Type::getType($mapping['type'])->closureToPHP());
            } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isOwningSide']) {
                $code .= sprintf(<<<EOF

        /** @ReferenceOne */
        if (isset(\$data['%1\$s'])) {
            \$reference = \$data['%1\$s'];
            if (isset(\$this->class->fieldMappings['%2\$s']['simple']) && \$this->class->fieldMappings['%2\$s']['simple']) {
                \$className = \$this->class->fieldMappings['%2\$s']['targetDocument'];
                \$mongoId = \$reference;
            } else {
                \$className = \$this->unitOfWork->getClassNameForAssociation(\$this->class->fieldMappings['%2\$s'], \$reference);
                \$mongoId = \$reference['\$id'];
            }
            \$targetMetadata = \$this->dm->getClassMetadata(\$className);
            \$id = \$targetMetadata->getPHPIdentifierValue(\$mongoId);
            \$return = \$this->dm->getReference(\$className, \$id);
            \$this->class->reflFields['%2\$s']->setValue(\$document, \$return);
            \$hydratedData['%2\$s'] = \$return;
        }

EOF
, $mapping['name'], $mapping['fieldName']);
            } elseif ($mapping['association'] === ClassMetadata::REFERENCE_ONE && $mapping['isInverseSide']) {
                if (isset($mapping['repositoryMethod']) && $mapping['repositoryMethod']) {
                    $code .= sprintf(<<<EOF

        \$className = \$this->class->fieldMappings['%2\$s']['targetDocument'];
        \$return = \$this->dm->getRepository(\$className)->%3\$s(\$document);
        \$this->class->reflFields['%2\$s']->setValue(\$document, \$return);
        \$hydratedData['%2\$s'] = \$return;

EOF
, $mapping['name'], $mapping['fieldName'], $mapping['repositoryMethod']);
                } else {
                    $code .= sprintf(<<<EOF

        \$mapping = \$this->class->fieldMappings['%2\$s'];
        \$className = \$mapping['targetDocument'];
        \$targetClass = \$this->dm->getClassMetadata(\$mapping['targetDocument']);
        \$mappedByMapping = \$targetClass->fieldMappings[\$mapping['mappedBy']];
        \$mappedByFieldName = isset(\$mappedByMapping['simple']) && \$mappedByMapping['simple'] ? \$mapping['mappedBy'] : \$mapping['mappedBy'].'.\$id';
        \$criteria = array_merge(
            array(\$mappedByFieldName => \$data['_id']),
            isset(\$this->class->fieldMappings['%2\$s']['criteria']) ? \$this->class->fieldMappings['%2\$s']['criteria'] : array()
        );
        \$sort = isset(\$this->class->fieldMappings['%2\$s']['sort']) ? \$this->class->fieldMappings['%2\$s']['sort'] : array();
        \$return = \$this->unitOfWork->getDocumentPersister(\$className)->load(\$criteria, null, array(), 0, \$sort);
        \$this->class->reflFields['%2\$s']->setValue(\$document, \$return);
        \$hydratedData['%2\$s'] = \$return;

EOF
, $mapping['name'], $mapping['fieldName']);
                }
            } elseif ($mapping['association'] === ClassMetadata::REFERENCE_MANY || $mapping['association'] === ClassMetadata::EMBED_MANY) {
                $code .= sprintf(<<<EOF

        /** @Many */
        \$mongoData = isset(\$data['%1\$s']) ? \$data['%1\$s'] : null;
        \$return = new \\Doctrine\\ODM\\MongoDB\\PersistentCollection(new \\Doctrine\\Common\\Collections\\ArrayCollection(), \$this->dm, \$this->unitOfWork);
        \$return->setHints(\$hints);
        \$return->setOwner(\$document, \$this->class->fieldMappings['%2\$s']);
        \$return->setInitialized(false);
        if (\$mongoData) {
            \$return->setMongoData(\$mongoData);
        }
        \$this->class->reflFields['%2\$s']->setValue(\$document, \$return);
        \$hydratedData['%2\$s'] = \$return;

EOF
, $mapping['name'], $mapping['fieldName']);
            } elseif ($mapping['association'] === ClassMetadata::EMBED_ONE) {
                $code .= sprintf(<<<EOF

        /** @EmbedOne */
        if (isset(\$data['%1\$s'])) {
            \$embeddedDocument = \$data['%1\$s'];
            \$className = \$this->unitOfWork->getClassNameForAssociation(\$this->class->fieldMappings['%2\$s'], \$embeddedDocument);
            \$embeddedMetadata = \$this->dm->getClassMetadata(\$className);
            \$return = \$embeddedMetadata->newInstance();

            \$this->unitOfWork->setParentAssociation(\$return, \$this->class->fieldMappings['%2\$s'], \$document, '%1\$s');

            \$embeddedData = \$this->dm->getHydratorFactory()->hydrate(\$return, \$embeddedDocument, \$hints);
            \$embeddedId = \$embeddedMetadata->identifier && isset(\$embeddedData[\$embeddedMetadata->identifier]) ? \$embeddedData[\$embeddedMetadata->identifier] : null;

            \$this->unitOfWork->registerManaged(\$return, \$embeddedId, \$embeddedData);

            \$this->class->reflFields['%2\$s']->setValue(\$document, \$return);
            \$hydratedData['%2\$s'] = \$return;
        }

EOF
, $mapping['name'], $mapping['fieldName']);
            }
        }
        $namespace = $this->hydratorNamespace;
        $code = sprintf(<<<EOF
<?php

namespace {$namespace};

use Doctrine\\ODM\\MongoDB\\DocumentManager;
use Doctrine\\ODM\\MongoDB\\Mapping\\ClassMetadata;
use Doctrine\\ODM\\MongoDB\\Hydrator\\HydratorInterface;
use Doctrine\\ODM\\MongoDB\\UnitOfWork;

/**
 * THIS CLASS WAS GENERATED BY THE DOCTRINE ODM. DO NOT EDIT THIS FILE.
 */
class {$hydratorClassName} implements HydratorInterface
{
    private \$dm;
    private \$unitOfWork;
    private \$class;

    public function __construct(DocumentManager \$dm, UnitOfWork \$uow, ClassMetadata \$class)
    {
        \$this->dm = \$dm;
        \$this->unitOfWork = \$uow;
        \$this->class = \$class;
    }

    public function hydrate(\$document, \$data, array \$hints = array())
    {
        \$hydratedData = array();
%s        return \$hydratedData;
    }
}
EOF
, $code);
        if ($fileName === false) {
            if (!class_exists($namespace . '\\' . $hydratorClassName)) {
                eval(substr($code, 5));
            }
        } else {
            $parentDirectory = dirname($fileName);
            if (!is_dir($parentDirectory) && false === @mkdir($parentDirectory, 0775, true)) {
                throw HydratorException::hydratorDirectoryNotWritable();
            }
            if (!is_writable($parentDirectory)) {
                throw HydratorException::hydratorDirectoryNotWritable();
            }
            $tmpFileName = $fileName . '.' . uniqid('', true);
            file_put_contents($tmpFileName, $code);
            rename($tmpFileName, $fileName);
            chmod($fileName, 0664);
        }
    }
예제 #30
0
 public function testClosureToPHP()
 {
     $type = Type::getType('dough_money_hash');
     $expected = '$process = $value;foreach ($process as $key => $value) { if ($value) { $return = new \\Dough\\Money\\Money($value);$process[$key] = $return; } } $return = $process;';
     $this->assertSame($expected, $type->closureToPHP());
 }