Php Can';t在symfony2控制台命令中持久化对象
我制作了一个简单的symfony2控制台脚本,用于将数据从旧模型转换为新模型。 下面是它的样子:Php Can';t在symfony2控制台命令中持久化对象,php,symfony,console,doctrine,Php,Symfony,Console,Doctrine,我制作了一个简单的symfony2控制台脚本,用于将数据从旧模型转换为新模型。 下面是它的样子: class ConvertScreenshotsCommand extends Command { [...] protected function execute(InputInterface $input, OutputInterface $output) { $em = $this->getContainer()->get('doctrine')->getManag
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>");
}
}
很明显我做错了什么,但我不知道是什么。提前谢谢
**更新**
查看实体映射:
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;
}
查看Creenshot地图:
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;
}
}
/**
*获取实体相对于当前工作单元的状态。
*
*@param对象$entity
*@param integer$假定状态为未知状态(未管理或删除)。
*可以设置此参数以提高实体状态检测的性能
*通过潜在地避免数据库查找,如果新的和分离的
*对于方法的调用方来说是已知的或不重要的。
*@return int表示实体状态。
*/
公共函数getEntityState($entity,$ASSUBE=null)
{
$oid=spl\u object\u hash($entity);//EntityState[$oid])){
返回$this->entityStates[$oid];
}
如果($假定!==null){
返回$ASTATE;
}
//状态只能是新的或分离的,因为托管/删除状态是已知的。
//请注意,您无法记住_entityStates中的新状态或分离状态,因为
//UoW不保存对此类对象的引用,并且可以重用对象哈希。
//更一般地说,因为在UoW不知道的情况下,状态可能在新建/分离之间“改变”。
$class=$this->em->getClassMetadata(get_class($entity));
$id=$class->GetIdentifierValue($entity);
如果(!$id){
返回self::STATE_NEW;
}
开关(真){
案例($class->isIdentifierNatural());
//检查版本字段(如果可用),以避免数据库查找。
如果($class->isVersioned){
返回($class->getFieldValue($entity,$class->versionField))
?self::STATE_DETACHED
:self::STATE_NEW;
}
//db查找之前的最后一次尝试:检查标识映射。
if($this->tryGetById($id,$class->rootEntityName)){
返回self::STATE_DETACHED;
}
//数据库查找
如果($this->getEntityPersister($class->name)->存在($entity)){
返回self::STATE_DETACHED;
}
返回self::STATE_NEW;
案例(!$class->idGenerator->isPostInsertGenerator()):
//如果我们有一个预插入生成器,我们不能确定是否有一个id
//确实意味着实体存在。我们必须通过
//最后的手段:数据库查找
//db查找之前的最后一次尝试:检查标识映射。
if($this->tryGetById($id,$class->rootEntityName)){
返回self::STATE_DETACHED;
}
//数据库查找
如果($this->getEntityPersister($class->name)->存在($entity)){
返回self::STATE_DETACHED;
}
返回self::STATE_NEW;
违约:
返回self::STATE_DETACHED;
}
}
我认为问题在于评论::$screenshots
:
您将其映射为OneToMany关联,因此该值应该是ReviewScreenshot
实体的集合。但是方法Review::getLegacyScreenshots()
会将其更改为字符串数组
您可能正在使用更改跟踪策略(默认)。因此,当属性
Review::$screenshots
发生更改时,条令将尝试保持该更改,在需要实体的地方遇到字符串,因此抛出异常。行号是什么?还是第1324行?您应该显示mapping ACCommonBundle:Review以及谢谢您的输入,我已经更新了我的问题谢谢Jasper,我不知道默认情况下条令会持久化实体,即使没有明确的$em->persist()。我已经将GetLegacyScreenshots()方法的内容粘贴到这个控制台脚本中,这是一种有点脏的方法,但在我的例子中做得很好。对不起,我创建了一个类型:默认的更改跟踪策略是DEFERRED_IMPLICIT(而不是DEFERRED_EXPLICIT)。我已经相应地编辑了我的答案。当使用默认的更改跟踪策略(DEFERRED\u IMPLICIT)时,您仍然必须对新(非托管)实体显式调用$em->persist()
。但是,当您调用$em->flush()
时,会自动检查从数据库(托管)中获取的实体的更改。后者会导致你的问题。
/**
* 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;
}
}