4

我制作了一个简单的 symfony2 控制台脚本,它应该将数据从旧模型转换为新模型。这是它的样子:

class ConvertScreenshotsCommand extends Command 
{
[...]
protected function execute(InputInterface $input, OutputInterface $output)
{
    $em = $this->getContainer()->get('doctrine')->getManager();

    $output->writeln('<info>Conversion started on ' . date(DATE_RSS) . "</info>");

    $output->writeln('Getting all reviews...');
    $reviews = $em->getRepository('ACCommonBundle:Review')->findAll(); // Putting all Review entities into an array
    $output->writeln('<info>Got ' . count($reviews) . ' reviews.</info>');

    foreach ($reviews as $review) {
        $output->writeln("<info>Screenshots for " . $review->getTitle() . "</info>");
        if ($review->getLegacyScreenshots()) {
            foreach ($review->getLegacyScreenshots() as $filename) { // fn returns array of strings
                $output->writeln("Found " . $filename);
                $screenshot = new ReviewScreenshot();        // new object

                $screenshot->setReview($review);     // review is object
                $screenshot->setFilename($filename); // filename is string

                $em->persist($screenshot);
                $em->flush();                        // this is where it dies
                $output->writeln("Successfully added to the database.");
            }
        } else $output->writeln("No legacy screenshots found.");
    }
    $output->writeln('<info>Conversion ended on ' . date(DATE_RSS) . "</info>");
 }
 }

脚本在 $em->flush() 上中断,出现以下错误:

 [ErrorException]                                                                                                                                                       
 Warning: spl_object_hash() expects parameter 1 to be object, string given in      
 /[...]/vendor/doctrine/orm/lib/Doctrine/ORM/UnitOfWork.php line 1324     

显然我做错了什么,但无法弄清楚它是什么。提前致谢!

** 更新 **

查看实体映射:

class Review
{
[...]

/**
 * @ORM\OneToMany(targetEntity="ReviewScreenshot", mappedBy="review")
 */
protected $screenshots;

/**
 * Won't be stored in the DB
 * @deprecated
 */
private $legacyScreenshots;

/**
 * New method to get screenshots, currently calls old method for the sake of compatibility
 * @return array Screenshot paths
 */

public function getScreenshots()
{
//        return $this->getLegacyScreenshots();  // Old method
    return $this->screenshots;                   // New method
}


/**
 * Get Screenshot paths
 * @return array Screenshot paths
 * @deprecated
 */
public function getLegacyScreenshots()
{
    $dir=$this->getUploadRootDir();
    if (file_exists($dir)) {
        $fileList = scandir($dir);

    $this->screenshots = array();
    foreach ($fileList as $fileName)
    {
        preg_match("/(screenshot-\d+.*)/", $fileName, $matches);
        if ($matches)
            $this->screenshots[]=$matches[1];
    }
    return $this->screenshots;
    }
    else return null;
}

回顾截图映射:

class ReviewScreenshot
{
/**
 * @var integer $id
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string $filename
 *
 * @ORM\Column(name="filename", type="string", length=255)
 */
private $filename;

/**
 * @ORM\ManyToOne(targetEntity="Review", inversedBy="screenshots")
 * @ORM\JoinColumn(name="review_id", referencedColumnName="id")
 */
protected $review;

/**
 * @var integer $priority
 *
 * @ORM\Column(name="priority", type="integer", nullable=true)
 */
protected $priority;

/**
 * @var string $description
 *
 * @ORM\Column(name="description", type="string", nullable=true)
 */
protected $description;

/**
 * @Assert\File(maxSize="2097152")
 */
public $screenshot_file;

protected $webPath;

UnitOfWork.php

/**
 * Gets the state of an entity with regard to the current unit of work.
 *
 * @param object $entity
 * @param integer $assume The state to assume if the state is not yet known (not MANAGED or REMOVED).
 *                        This parameter can be set to improve performance of entity state detection
 *                        by potentially avoiding a database lookup if the distinction between NEW and DETACHED
 *                        is either known or does not matter for the caller of the method.
 * @return int The entity state.
 */
public function getEntityState($entity, $assume = null)
{
    $oid = spl_object_hash($entity); // <-- Line 1324

    if (isset($this->entityStates[$oid])) {
        return $this->entityStates[$oid];
    }

    if ($assume !== null) {
        return $assume;
    }

    // State can only be NEW or DETACHED, because MANAGED/REMOVED states are known.
    // Note that you can not remember the NEW or DETACHED state in _entityStates since
    // the UoW does not hold references to such objects and the object hash can be reused.
    // More generally because the state may "change" between NEW/DETACHED without the UoW being aware of it.
    $class = $this->em->getClassMetadata(get_class($entity));
    $id    = $class->getIdentifierValues($entity);

    if ( ! $id) {
        return self::STATE_NEW;
    }

    switch (true) {
        case ($class->isIdentifierNatural());
            // Check for a version field, if available, to avoid a db lookup.
            if ($class->isVersioned) {
                return ($class->getFieldValue($entity, $class->versionField))
                    ? self::STATE_DETACHED
                    : self::STATE_NEW;
            }

            // Last try before db lookup: check the identity map.
            if ($this->tryGetById($id, $class->rootEntityName)) {
                return self::STATE_DETACHED;
            }

            // db lookup
            if ($this->getEntityPersister($class->name)->exists($entity)) {
                return self::STATE_DETACHED;
            }

            return self::STATE_NEW;

        case ( ! $class->idGenerator->isPostInsertGenerator()):
            // if we have a pre insert generator we can't be sure that having an id
            // really means that the entity exists. We have to verify this through
            // the last resort: a db lookup

            // Last try before db lookup: check the identity map.
            if ($this->tryGetById($id, $class->rootEntityName)) {
                return self::STATE_DETACHED;
            }

            // db lookup
            if ($this->getEntityPersister($class->name)->exists($entity)) {
                return self::STATE_DETACHED;
            }

            return self::STATE_NEW;

        default:
            return self::STATE_DETACHED;
    }
}
4

1 回答 1

1

我认为问题在于Review::$screenshots

您将其映射为 OneToMany 关联,因此该值应该是ReviewScreenshot实体的集合。但是该方法Review::getLegacyScreenshots()会将其更改为字符串数组。

您可能正在使用更改跟踪策略 DEFERRED_IMPLICIT(这是默认设置)。因此,当属性Review::$screenshots更改时,Doctrine 将尝试保持该更改,在它期望实体的地方遇到字符串,因此抛出异常。

于 2013-01-31T13:28:13.573 回答