/** * @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')); }
/** * @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); }
/** * @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; } } }
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; }
/** * {@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')); }
/** * 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); }
public function closureToPHP() { return '$process = $value;foreach ($process as $key => $value) { if ($value) { ' . Type::getType('dough_currency_money')->closureToPHP() . '$process[$key] = $return; } } $return = $process;'; }
/** * setup type we want to test * * @return void */ public function setUp() { Type::registerType('hasharray', HashArrayType::class); $this->type = Type::getType('hasharray'); }
public function testClosureToPHP() { $type = Type::getType('dough_money'); $this->assertSame('$return = new \\Dough\\Money\\Money($value);', $type->closureToPHP()); }
/** * @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); }
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())); }
/** * setup type we want to test * * @return void */ public function setUp() { Type::registerType('hash', 'Graviton\\DocumentBundle\\Types\\HashType'); $this->type = Type::getType('hash'); }
/** * boot bundle function * * @return void */ public function boot() { $extRefConverter = $this->container->get('graviton.document.service.extrefconverter'); $customType = Type::getType('hash'); $customType->setExtRefConverter($extRefConverter); }
/** * @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; }
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"); } }
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()); }
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()); }
/** * 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; }
/** * @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; } } }
/** * setup type we want to test * * @return void */ public function setUp() { Type::registerType('extref', ExtReferenceType::class); $this->type = Type::getType('extref'); }
/** * setup type we want to test * * @return void */ public function setUp() { Type::registerType('datearray', DateArrayType::class); $this->type = Type::getType('datearray'); }
/** * 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; } } } }
/** * @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; }
/** * @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); } }
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()); }