/**
  * @dataProvider lockDataProvider
  */
 public function testLock($isVersioned, $expectsException)
 {
     $object = new VersionedEntity();
     $em = $this->getMockBuilder('Doctrine\\ORM\\EntityManager')->disableOriginalConstructor()->setMethods(array('lock'))->getMock();
     $modelManager = $this->getMockBuilder('Sonata\\DoctrineORMAdminBundle\\Model\\ModelManager')->disableOriginalConstructor()->setMethods(array('getMetadata', 'getEntityManager'))->getMock();
     $modelManager->expects($this->any())->method('getEntityManager')->will($this->returnValue($em));
     $metadata = $this->getMetadata(get_class($object), $isVersioned);
     $modelManager->expects($this->any())->method('getMetadata')->will($this->returnValue($metadata));
     if ($expectsException) {
         $em->expects($this->once())->method('lock')->will($this->throwException(OptimisticLockException::lockFailed($object)));
         $this->setExpectedException('Sonata\\AdminBundle\\Exception\\LockException');
     }
     $modelManager->lock($object, 123);
 }
 /**
  * {@inheritDoc}
  */
 public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
 {
     // Check identity map first
     if ($entity = $this->_em->getUnitOfWork()->tryGetById($id, $this->_class->rootEntityName)) {
         if (!$entity instanceof $this->_class->name) {
             return null;
         }
         if ($lockMode != LockMode::NONE) {
             $this->_em->lock($entity, $lockMode, $lockVersion);
         }
         return $entity;
         // Hit!
     }
     if (!is_array($id) || count($id) <= 1) {
         // @todo FIXME: Not correct. Relies on specific order.
         $value = is_array($id) ? array_values($id) : array($id);
         $id = array_combine($this->_class->identifier, $value);
     }
     $id = $this->addInstanceFilter($id);
     if ($lockMode == LockMode::NONE) {
         return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
     } else {
         if ($lockMode == LockMode::OPTIMISTIC) {
             if (!$this->_class->isVersioned) {
                 throw OptimisticLockException::notVersioned($this->_entityName);
             }
             $entity = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id);
             $this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
             return $entity;
         } else {
             if (!$this->_em->getConnection()->isTransactionActive()) {
                 throw TransactionRequiredException::transactionRequired();
             }
             return $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName)->load($id, null, null, array(), $lockMode);
         }
     }
 }
 /**
  * Performs an UPDATE statement for an entity on a specific table.
  * The UPDATE can optionally be versioned, which requires the entity to have a version field.
  *
  * @param object $entity The entity object being updated.
  * @param string $quotedTableName The quoted name of the table to apply the UPDATE on.
  * @param array $updateData The map of columns to update (column => value).
  * @param boolean $versioned Whether the UPDATE should be versioned.
  */
 protected final function _updateTable($entity, $quotedTableName, array $updateData, $versioned = false)
 {
     $set = $params = $types = array();
     foreach ($updateData as $columnName => $value) {
         if (isset($this->_class->fieldNames[$columnName])) {
             $set[] = $this->_class->getQuotedColumnName($this->_class->fieldNames[$columnName], $this->_platform) . ' = ?';
         } else {
             $set[] = $columnName . ' = ?';
         }
         $params[] = $value;
         $types[] = $this->_columnTypes[$columnName];
     }
     $where = array();
     $id = $this->_em->getUnitOfWork()->getEntityIdentifier($entity);
     foreach ($this->_class->identifier as $idField) {
         if (isset($this->_class->associationMappings[$idField])) {
             $targetMapping = $this->_em->getClassMetadata($this->_class->associationMappings[$idField]['targetEntity']);
             $where[] = $this->_class->associationMappings[$idField]['joinColumns'][0]['name'];
             $params[] = $id[$idField];
             $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
         } else {
             $where[] = $this->_class->getQuotedColumnName($idField, $this->_platform);
             $params[] = $id[$idField];
             $types[] = $this->_class->fieldMappings[$idField]['type'];
         }
     }
     if ($versioned) {
         $versionField = $this->_class->versionField;
         $versionFieldType = $this->_class->fieldMappings[$versionField]['type'];
         $versionColumn = $this->_class->getQuotedColumnName($versionField, $this->_platform);
         if ($versionFieldType == Type::INTEGER) {
             $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
         } else {
             if ($versionFieldType == Type::DATETIME) {
                 $set[] = $versionColumn . ' = CURRENT_TIMESTAMP';
             }
         }
         $where[] = $versionColumn;
         $params[] = $this->_class->reflFields[$versionField]->getValue($entity);
         $types[] = $this->_class->fieldMappings[$versionField]['type'];
     }
     $sql = "UPDATE {$quotedTableName} SET " . implode(', ', $set) . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';
     $result = $this->_conn->executeUpdate($sql, $params, $types);
     if ($versioned && !$result) {
         throw OptimisticLockException::lockFailed($entity);
     }
 }
Example #4
0
 /**
  * Finds an Entity by its identifier.
  *
  * @param string       $entityName  The class name of the entity to find.
  * @param mixed        $id          The identity of the entity to find.
  * @param integer|null $lockMode    One of the \Doctrine\DBAL\LockMode::* constants
  *                                  or NULL if no specific lock mode should be used
  *                                  during the search.
  * @param integer|null $lockVersion The version of the entity to find when using
  *                                  optimistic locking.
  *
  * @return object|null The entity instance or NULL if the entity can not be found.
  *
  * @throws OptimisticLockException
  * @throws ORMInvalidArgumentException
  * @throws TransactionRequiredException
  * @throws ORMException
  */
 public function find($entityName, $id, $lockMode = null, $lockVersion = null)
 {
     $class = $this->metadataFactory->getMetadataFor(ltrim($entityName, '\\'));
     if (is_object($id) && $this->metadataFactory->hasMetadataFor(ClassUtils::getClass($id))) {
         $id = $this->unitOfWork->getSingleIdentifierValue($id);
         if ($id === null) {
             throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
         }
     }
     if (!is_array($id)) {
         $id = array($class->identifier[0] => $id);
     }
     $sortedId = array();
     foreach ($class->identifier as $identifier) {
         if (!isset($id[$identifier])) {
             throw ORMException::missingIdentifierField($class->name, $identifier);
         }
         $sortedId[$identifier] = $id[$identifier];
     }
     $unitOfWork = $this->getUnitOfWork();
     // Check identity map first
     if (($entity = $unitOfWork->tryGetById($sortedId, $class->rootEntityName)) !== false) {
         if (!$entity instanceof $class->name) {
             return null;
         }
         switch (true) {
             case LockMode::OPTIMISTIC === $lockMode:
                 $this->lock($entity, $lockMode, $lockVersion);
                 break;
             case LockMode::NONE === $lockMode:
             case LockMode::PESSIMISTIC_READ === $lockMode:
             case LockMode::PESSIMISTIC_WRITE === $lockMode:
                 $persister = $unitOfWork->getEntityPersister($class->name);
                 $persister->refresh($sortedId, $entity, $lockMode);
                 break;
         }
         return $entity;
         // Hit!
     }
     $persister = $unitOfWork->getEntityPersister($class->name);
     switch (true) {
         case LockMode::OPTIMISTIC === $lockMode:
             if (!$class->isVersioned) {
                 throw OptimisticLockException::notVersioned($class->name);
             }
             $entity = $persister->load($sortedId);
             $unitOfWork->lock($entity, $lockMode, $lockVersion);
             return $entity;
         case LockMode::NONE === $lockMode:
         case LockMode::PESSIMISTIC_READ === $lockMode:
         case LockMode::PESSIMISTIC_WRITE === $lockMode:
             if (!$this->getConnection()->isTransactionActive()) {
                 throw TransactionRequiredException::transactionRequired();
             }
             return $persister->load($sortedId, null, null, array(), $lockMode);
         default:
             return $persister->loadById($sortedId);
     }
 }
Example #5
0
 /**
  * Acquire a lock on the given entity.
  *
  * @param object $entity
  * @param int    $lockMode
  * @param int    $lockVersion
  *
  * @return void
  *
  * @throws ORMInvalidArgumentException
  * @throws TransactionRequiredException
  * @throws OptimisticLockException
  */
 public function lock($entity, $lockMode, $lockVersion = null)
 {
     if ($entity === null) {
         throw new \InvalidArgumentException("No entity passed to UnitOfWork#lock().");
     }
     if ($this->getEntityState($entity, self::STATE_DETACHED) != self::STATE_MANAGED) {
         throw ORMInvalidArgumentException::entityNotManaged($entity);
     }
     $class = $this->em->getClassMetadata(get_class($entity));
     switch (true) {
         case LockMode::OPTIMISTIC === $lockMode:
             if (!$class->isVersioned) {
                 throw OptimisticLockException::notVersioned($class->name);
             }
             if ($lockVersion === null) {
                 return;
             }
             $entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
             if ($entityVersion != $lockVersion) {
                 throw OptimisticLockException::lockFailedVersionMismatch($entity, $lockVersion, $entityVersion);
             }
             break;
         case LockMode::NONE === $lockMode:
         case LockMode::PESSIMISTIC_READ === $lockMode:
         case LockMode::PESSIMISTIC_WRITE === $lockMode:
             if (!$this->em->getConnection()->isTransactionActive()) {
                 throw TransactionRequiredException::transactionRequired();
             }
             $oid = spl_object_hash($entity);
             $this->getEntityPersister($class->name)->lock(array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), $lockMode);
             break;
         default:
             // Do nothing
     }
 }
Example #6
0
 /**
  * {@inheritdoc}
  */
 public function walkSelectStatement(AST\SelectStatement $AST)
 {
     $sql = $this->walkSelectClause($AST->selectClause);
     $sql .= $this->walkFromClause($AST->fromClause);
     $sql .= $this->walkWhereClause($AST->whereClause);
     $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : '';
     $sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : '';
     if (($orderByClause = $AST->orderByClause) !== null) {
         $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
     } else {
         if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') {
             $sql .= ' ORDER BY ' . $orderBySql;
         }
     }
     $sql = $this->platform->modifyLimitQuery($sql, $this->query->getMaxResults(), $this->query->getFirstResult());
     if (($lockMode = $this->query->getHint(Query::HINT_LOCK_MODE)) !== false) {
         switch ($lockMode) {
             case LockMode::PESSIMISTIC_READ:
                 $sql .= ' ' . $this->platform->getReadLockSQL();
                 break;
             case LockMode::PESSIMISTIC_WRITE:
                 $sql .= ' ' . $this->platform->getWriteLockSQL();
                 break;
             case LockMode::OPTIMISTIC:
                 foreach ($this->selectedClasses as $selectedClass) {
                     if (!$selectedClass['class']->isVersioned) {
                         throw \Doctrine\ORM\OptimisticLockException::lockFailed($selectedClass['class']->name);
                     }
                 }
                 break;
             case LockMode::NONE:
                 break;
             default:
                 throw \Doctrine\ORM\Query\QueryException::invalidLockMode();
         }
     }
     return $sql;
 }
Example #7
0
 /**
  * Acquire a lock on the given entity.
  *
  * @param object $entity
  * @param int $lockMode
  * @param int $lockVersion
  */
 public function lock($entity, $lockMode, $lockVersion = null)
 {
     if ($this->getEntityState($entity) != self::STATE_MANAGED) {
         throw new InvalidArgumentException("Entity is not MANAGED.");
     }
     $entityName = get_class($entity);
     $class = $this->em->getClassMetadata($entityName);
     if ($lockMode == \Doctrine\DBAL\LockMode::OPTIMISTIC) {
         if (!$class->isVersioned) {
             throw OptimisticLockException::notVersioned($entityName);
         }
         if ($lockVersion != null) {
             $entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
             if ($entityVersion != $lockVersion) {
                 throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entityVersion);
             }
         }
     } else {
         if (in_array($lockMode, array(\Doctrine\DBAL\LockMode::PESSIMISTIC_READ, \Doctrine\DBAL\LockMode::PESSIMISTIC_WRITE))) {
             if (!$this->em->getConnection()->isTransactionActive()) {
                 throw TransactionRequiredException::transactionRequired();
             }
             $oid = spl_object_hash($entity);
             $this->getEntityPersister($class->name)->lock(array_combine($class->getIdentifierFieldNames(), $this->entityIdentifiers[$oid]), $lockMode);
         }
     }
 }
Example #8
0
    /**
     * Performs an UPDATE statement for an entity on a specific table.
     * The UPDATE can optionally be versioned, which requires the entity to have a version field.
     *
     * @param object  $entity          The entity object being updated.
     * @param string  $quotedTableName The quoted name of the table to apply the UPDATE on.
     * @param array   $updateData      The map of columns to update (column => value).
     * @param boolean $versioned       Whether the UPDATE should be versioned.
     *
     * @return void
     *
     * @throws \Doctrine\ORM\ORMException
     * @throws \Doctrine\ORM\OptimisticLockException
     */
    protected final function updateTable($entity, $quotedTableName, array $updateData, $versioned = false)
    {
        $set    = array();
        $types  = array();
        $params = array();

        foreach ($updateData as $columnName => $value) {
            $placeholder = '?';
            $column      = $columnName;

            switch (true) {
                case isset($this->class->fieldNames[$columnName]):
                    $fieldName  = $this->class->fieldNames[$columnName];
                    $column     = $this->quoteStrategy->getColumnName($fieldName, $this->class, $this->platform);

                    if (isset($this->class->fieldMappings[$fieldName]['requireSQLConversion'])) {
                        $type        = Type::getType($this->columnTypes[$columnName]);
                        $placeholder = $type->convertToDatabaseValueSQL('?', $this->platform);
                    }

                    break;

                case isset($this->quotedColumns[$columnName]):
                    $column = $this->quotedColumns[$columnName];

                    break;
            }
 
            $params[]   = $value;
            $set[]      = $column . ' = ' . $placeholder;
            $types[]    = $this->columnTypes[$columnName];
        }

        $where      = array();
        $identifier = $this->em->getUnitOfWork()->getEntityIdentifier($entity);

        foreach ($this->class->identifier as $idField) {
            if ( ! isset($this->class->associationMappings[$idField])) {

                $params[]   = $identifier[$idField];
                $types[]    = $this->class->fieldMappings[$idField]['type'];
                $where[]    = $this->quoteStrategy->getColumnName($idField, $this->class, $this->platform);

                continue;
            }

            $params[]       = $identifier[$idField];
            $where[]        = $this->class->associationMappings[$idField]['joinColumns'][0]['name'];
            $targetMapping  = $this->em->getClassMetadata($this->class->associationMappings[$idField]['targetEntity']);

            switch (true) {
                case (isset($targetMapping->fieldMappings[$targetMapping->identifier[0]])):
                    $types[] = $targetMapping->fieldMappings[$targetMapping->identifier[0]]['type'];
                    break;

                case (isset($targetMapping->associationMappings[$targetMapping->identifier[0]])):
                    $types[] = $targetMapping->associationMappings[$targetMapping->identifier[0]]['type'];
                    break;

                default:
                    throw ORMException::unrecognizedField($targetMapping->identifier[0]);
            }

        }

        if ($versioned) {
            $versionField       = $this->class->versionField;
            $versionFieldType   = $this->class->fieldMappings[$versionField]['type'];
            $versionColumn      = $this->quoteStrategy->getColumnName($versionField, $this->class, $this->platform);

            $where[]    = $versionColumn;
            $types[]    = $this->class->fieldMappings[$versionField]['type'];
            $params[]   = $this->class->reflFields[$versionField]->getValue($entity);

            switch ($versionFieldType) {
                case Type::INTEGER:
                    $set[] = $versionColumn . ' = ' . $versionColumn . ' + 1';
                    break;

                case Type::DATETIME:
                    $set[] = $versionColumn . ' = CURRENT_TIMESTAMP';
                    break;
            }
        }

        $sql = 'UPDATE ' . $quotedTableName
             . ' SET ' . implode(', ', $set)
             . ' WHERE ' . implode(' = ? AND ', $where) . ' = ?';

        $result = $this->conn->executeUpdate($sql, $params, $types);

        if ($versioned && ! $result) {
            throw OptimisticLockException::lockFailed($entity);
        }
    }
Example #9
0
 /**
  * {@inheritdoc}
  */
 public function walkSelectStatement(AST\SelectStatement $AST)
 {
     $limit = $this->query->getMaxResults();
     $offset = $this->query->getFirstResult();
     $lockMode = $this->query->getHint(Query::HINT_LOCK_MODE);
     $sql = $this->walkSelectClause($AST->selectClause) . $this->walkFromClause($AST->fromClause) . $this->walkWhereClause($AST->whereClause);
     if ($AST->groupByClause) {
         $sql .= $this->walkGroupByClause($AST->groupByClause);
     }
     if ($AST->havingClause) {
         $sql .= $this->walkHavingClause($AST->havingClause);
     }
     if ($AST->orderByClause) {
         $sql .= $this->walkOrderByClause($AST->orderByClause);
     }
     if (!$AST->orderByClause && ($orderBySql = $this->_generateOrderedCollectionOrderByItems())) {
         $sql .= ' ORDER BY ' . $orderBySql;
     }
     if ($limit !== null || $offset !== null) {
         $sql = $this->platform->modifyLimitQuery($sql, $limit, $offset);
     }
     if ($lockMode === null || $lockMode === false || $lockMode === LockMode::NONE) {
         return $sql;
     }
     if ($lockMode === LockMode::PESSIMISTIC_READ) {
         return $sql . ' ' . $this->platform->getReadLockSQL();
     }
     if ($lockMode === LockMode::PESSIMISTIC_WRITE) {
         return $sql . ' ' . $this->platform->getWriteLockSQL();
     }
     if ($lockMode !== LockMode::OPTIMISTIC) {
         throw QueryException::invalidLockMode();
     }
     foreach ($this->selectedClasses as $selectedClass) {
         if (!$selectedClass['class']->isVersioned) {
             throw OptimisticLockException::lockFailed($selectedClass['class']->name);
         }
     }
     return $sql;
 }
 /**
  * Walks down a SelectStatement AST node, thereby generating the appropriate SQL.
  *
  * @return string The SQL.
  */
 public function walkSelectStatement(AST\SelectStatement $AST)
 {
     $sql = $this->walkSelectClause($AST->selectClause);
     $sql .= $this->walkFromClause($AST->fromClause);
     if (($whereClause = $AST->whereClause) !== null) {
         $sql .= $this->walkWhereClause($whereClause);
     } else {
         if (($discSql = $this->_generateDiscriminatorColumnConditionSQL($this->_rootAliases)) !== '') {
             $sql .= ' WHERE ' . $discSql;
         }
     }
     $sql .= $AST->groupByClause ? $this->walkGroupByClause($AST->groupByClause) : '';
     $sql .= $AST->havingClause ? $this->walkHavingClause($AST->havingClause) : '';
     if (($orderByClause = $AST->orderByClause) !== null) {
         $sql .= $AST->orderByClause ? $this->walkOrderByClause($AST->orderByClause) : '';
     } else {
         if (($orderBySql = $this->_generateOrderedCollectionOrderByItems()) !== '') {
             $sql .= ' ORDER BY ' . $orderBySql;
         }
     }
     $sql = $this->_platform->modifyLimitQuery($sql, $this->_query->getMaxResults(), $this->_query->getFirstResult());
     if (($lockMode = $this->_query->getHint(Query::HINT_LOCK_MODE)) !== false) {
         if ($lockMode == LockMode::PESSIMISTIC_READ) {
             $sql .= " " . $this->_platform->getReadLockSQL();
         } else {
             if ($lockMode == LockMode::PESSIMISTIC_WRITE) {
                 $sql .= " " . $this->_platform->getWriteLockSQL();
             } else {
                 if ($lockMode == LockMode::OPTIMISTIC) {
                     foreach ($this->_selectedClasses as $class) {
                         if (!$class->isVersioned) {
                             throw \Doctrine\ORM\OptimisticLockException::lockFailed();
                         }
                     }
                 }
             }
         }
     }
     return $sql;
 }
 /**
  * Perform UPDATE statement for an entity. This function has support for
  * optimistic locking if the entities ClassMetadata has versioning enabled.
  *
  * @param object $entity        The entity object being updated
  * @param string $tableName     The name of the table being updated
  * @param array $data           The array of data to set
  * @param array $where          The condition used to update
  * @return void
  */
 protected function _doUpdate($entity, $tableName, $data, $where)
 {
     // Note: $tableName and column names in $data are already quoted for SQL.
     $set = array();
     foreach ($data as $columnName => $value) {
         $set[] = $columnName . ' = ?';
     }
     if ($isVersioned = $this->_class->isVersioned) {
         $versionField = $this->_class->versionField;
         $versionFieldType = $this->_class->getTypeOfField($versionField);
         $where[$versionField] = Type::getType($versionFieldType)->convertToDatabaseValue($this->_class->reflFields[$versionField]->getValue($entity), $this->_platform);
         $versionFieldColumnName = $this->_class->getQuotedColumnName($versionField, $this->_platform);
         if ($versionFieldType == 'integer') {
             $set[] = $versionFieldColumnName . ' = ' . $versionFieldColumnName . ' + 1';
         } else {
             if ($versionFieldType == 'datetime') {
                 $set[] = $versionFieldColumnName . ' = CURRENT_TIMESTAMP';
             }
         }
     }
     $params = array_merge(array_values($data), array_values($where));
     $sql = 'UPDATE ' . $tableName . ' SET ' . implode(', ', $set) . ' WHERE ' . implode(' = ? AND ', array_keys($where)) . ' = ?';
     $result = $this->_conn->executeUpdate($sql, $params);
     if ($isVersioned && !$result) {
         throw \Doctrine\ORM\OptimisticLockException::lockFailed();
     }
 }
Example #12
0
 /**
  * Executes a merge operation on an entity.
  *
  * @param object $entity
  * @param array $visited
  * @return object The managed copy of the entity.
  * @throws OptimisticLockException If the entity uses optimistic locking through a version
  *         attribute and the version check against the managed copy fails.
  * @throws InvalidArgumentException If the entity instance is NEW.
  */
 private function _doMerge($entity, array &$visited, $prevManagedCopy = null, $assoc = null)
 {
     $class = $this->_em->getClassMetadata(get_class($entity));
     $id = $class->getIdentifierValues($entity);
     if (!$id) {
         throw new \InvalidArgumentException('New entity detected during merge.' . ' Persist the new entity before merging.');
     }
     // MANAGED entities are ignored by the merge operation
     if ($this->getEntityState($entity, self::STATE_DETACHED) == self::STATE_MANAGED) {
         $managedCopy = $entity;
     } else {
         // Try to look the entity up in the identity map.
         $managedCopy = $this->tryGetById($id, $class->rootEntityName);
         if ($managedCopy) {
             // We have the entity in-memory already, just make sure its not removed.
             if ($this->getEntityState($managedCopy) == self::STATE_REMOVED) {
                 throw new \InvalidArgumentException('Removed entity detected during merge.' . ' Can not merge with a removed entity.');
             }
         } else {
             // We need to fetch the managed copy in order to merge.
             $managedCopy = $this->_em->find($class->name, $id);
         }
         if ($managedCopy === null) {
             throw new \InvalidArgumentException('New entity detected during merge.' . ' Persist the new entity before merging.');
         }
         if ($class->isVersioned) {
             $managedCopyVersion = $class->reflFields[$class->versionField]->getValue($managedCopy);
             $entityVersion = $class->reflFields[$class->versionField]->getValue($entity);
             // Throw exception if versions dont match.
             if ($managedCopyVersion != $entityVersion) {
                 throw OptimisticLockException::lockFailed();
             }
         }
         // Merge state of $entity into existing (managed) entity
         foreach ($class->reflFields as $name => $prop) {
             if (!isset($class->associationMappings[$name])) {
                 $prop->setValue($managedCopy, $prop->getValue($entity));
             } else {
                 $assoc2 = $class->associationMappings[$name];
                 if ($assoc2->isOneToOne()) {
                     if (!$assoc2->isCascadeMerge) {
                         $other = $class->reflFields[$name]->getValue($entity);
                         //TODO: Just $prop->getValue($entity)?
                         if ($other !== null) {
                             $targetClass = $this->_em->getClassMetadata($assoc2->targetEntityName);
                             $id = $targetClass->getIdentifierValues($other);
                             $proxy = $this->_em->getProxyFactory()->getProxy($assoc2->targetEntityName, $id);
                             $prop->setValue($managedCopy, $proxy);
                             $this->registerManaged($proxy, $id, array());
                         }
                     }
                 } else {
                     $coll = new PersistentCollection($this->_em, $this->_em->getClassMetadata($assoc2->targetEntityName), new ArrayCollection());
                     $coll->setOwner($managedCopy, $assoc2);
                     $coll->setInitialized($assoc2->isCascadeMerge);
                     $prop->setValue($managedCopy, $coll);
                 }
             }
             if ($class->isChangeTrackingNotify()) {
                 //TODO: put changed fields in changeset...?
             }
         }
         if ($class->isChangeTrackingDeferredExplicit()) {
             //TODO: Mark $managedCopy for dirty check...? ($this->_scheduledForDirtyCheck)
         }
     }
     if ($prevManagedCopy !== null) {
         $assocField = $assoc->sourceFieldName;
         $prevClass = $this->_em->getClassMetadata(get_class($prevManagedCopy));
         if ($assoc->isOneToOne()) {
             $prevClass->reflFields[$assocField]->setValue($prevManagedCopy, $managedCopy);
             //TODO: What about back-reference if bidirectional?
         } else {
             $prevClass->reflFields[$assocField]->getValue($prevManagedCopy)->unwrap()->add($managedCopy);
             if ($assoc->isOneToMany()) {
                 $class->reflFields[$assoc->mappedBy]->setValue($managedCopy, $prevManagedCopy);
             }
         }
     }
     $this->_cascadeMerge($entity, $managedCopy, $visited);
     return $managedCopy;
 }
Example #13
0
 /**
  * Finds an entity by its primary key / identifier.
  *
  * @param $id The identifier.
  * @param int $lockMode
  * @param int $lockVersion
  * @return object The entity.
  */
 public function find($id, $lockMode = LockMode::NONE, $lockVersion = null)
 {
     if (!is_array($id)) {
         $id = array($this->_class->identifier[0] => $id);
     }
     $sortedId = array();
     foreach ($this->_class->identifier as $identifier) {
         if (!isset($id[$identifier])) {
             throw ORMException::missingIdentifierField($this->_class->name, $identifier);
         }
         $sortedId[$identifier] = $id[$identifier];
     }
     // Check identity map first
     if (($entity = $this->_em->getUnitOfWork()->tryGetById($sortedId, $this->_class->rootEntityName)) !== false) {
         if (!$entity instanceof $this->_class->name) {
             return null;
         }
         if ($lockMode !== LockMode::NONE) {
             $this->_em->lock($entity, $lockMode, $lockVersion);
         }
         return $entity;
         // Hit!
     }
     $persister = $this->_em->getUnitOfWork()->getEntityPersister($this->_entityName);
     switch ($lockMode) {
         case LockMode::NONE:
             return $persister->load($sortedId);
         case LockMode::OPTIMISTIC:
             if (!$this->_class->isVersioned) {
                 throw OptimisticLockException::notVersioned($this->_entityName);
             }
             $entity = $persister->load($sortedId);
             $this->_em->getUnitOfWork()->lock($entity, $lockMode, $lockVersion);
             return $entity;
         default:
             if (!$this->_em->getConnection()->isTransactionActive()) {
                 throw TransactionRequiredException::transactionRequired();
             }
             return $persister->load($sortedId, null, null, array(), $lockMode);
     }
 }
Example #14
0
 /**
  * Acquires an optimistic lock within a pessimistic lock transaction. For
  * use in fail-fast scenarios; guaranteed to throw an exception on
  * concurrent modification attempts. The one to first acquire the write lock
  * will update the version field, leading subsequent acquisitions of the
  * optimistic lock to fail.
  *
  * FIXME: Only works on entities implementing VersionLockable and does not
  * work in conjunction with the Doctrine @Version column.
  *
  * @param int $id
  * @param mixed $lockVersion
  * @param callback($entity, Doctrine\ORM\EntityManager, Repository) $callback
  * @return mixed callback return type
  * @throws OptimisticLockException
  */
 public function useWithPessimisticVersionLock($id, $lockVersion, $callback)
 {
     return $this->useWithPessimisticWriteLock($id, function (VersionLockable $entity, EntityManager $em, $self) use($lockVersion, $callback) {
         if ($entity->getVersion() !== $lockVersion) {
             // FIXME: This isn't the appropriate exception type.
             throw OptimisticLockException::lockFailedVersionMissmatch($entity, $lockVersion, $entity->getVersion());
         }
         $entity->incrementVersion();
         return $callback($entity, $em, $self);
     });
 }