Symfony2,Doctrine2 - 强制更新 - 多对多关系中的表已经存在

23

在我成功创建了具有类别和任务之间一对多关系的TaskBundle之后,现在我正在尝试创建一个具有多对多关系的新TaskBundle。在此关系中选择复选框也存在问题,但现在这不是主要问题(也许在解决此问题之后)。我删除了所有使用TaskBundle的表并尝试创建新表,但是出现了问题(底部有描述)。

我的Task对象:

<?php

namespace Acme\TaskBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="tasks") 
 */
class Task
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;              

    /**
     * @ORM\Column(type="string", length=200)    
     * @Assert\NotBlank(
     *      message = "Task is empty"      
     * )    
     * @Assert\Length(
     *      min = "3",
     *      minMessage = "Task is too short"         
     * )     
     */     
    protected $task;

    /**
     * @ORM\Column(type="datetime")    
     * @Assert\NotBlank()
     * @Assert\Type("\DateTime")
     */
    protected $dueDate;

    /**
     * @Assert\True(message = "You have to agree.")    
     */         
    protected $accepted;

    /**
     * @ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
     * @ORM\JoinTable(name="categories")                         
     */
    protected $category;

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->category = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set task
     *
     * @param string $task
     * @return Task
     */
    public function setTask($task)
    {
        $this->task = $task;

        return $this;
    }

    /**
     * Get task
     *
     * @return string 
     */
    public function getTask()
    {
        return $this->task;
    }

    /**
     * Set dueDate
     *
     * @param \DateTime $dueDate
     * @return Task
     */
    public function setDueDate($dueDate)
    {
        $this->dueDate = $dueDate;

        return $this;
    }

    /**
     * Get dueDate
     *
     * @return \DateTime 
     */
    public function getDueDate()
    {
        return $this->dueDate;
    }

    /**
     * Add category
     *
     * @param \Acme\TaskBundle\Entity\Category $category
     * @return Task
     */
    public function addCategory(\Acme\TaskBundle\Entity\Category $category)
    {
        $this->category[] = $category;

        return $this;
    }

    /**
     * Remove category
     *
     * @param \Acme\TaskBundle\Entity\Category $category
     */
    public function removeCategory(\Acme\TaskBundle\Entity\Category $category)
    {
        $this->category->removeElement($category);
    }

    /**
     * Get category
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getCategory()
    {
        return $this->category;
    }
}

和类别对象

<?php

namespace Acme\TaskBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity
 * @ORM\Table(name="categories") 
 */
class Category
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")             
     */
    protected $id; 

    /**
     * @ORM\Column(type="string", length=200, unique=true)   
     * @Assert\NotNull(message="Categories cannot be empty", groups = {"adding"})                   
     */         
    protected $name;  

    /**
     * @ORM\ManyToMany(targetEntity="Task", mappedBy="category")
     */
    private $tasks;            


    public function __toString()
    {
        return strval($this->name);
    }
    /**
     * Constructor
     */
    public function __construct()
    {
        $this->tasks = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Category
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Add tasks
     *
     * @param \Acme\TaskBundle\Entity\Task $tasks
     * @return Category
     */
    public function addTask(\Acme\TaskBundle\Entity\Task $tasks)
    {
        $this->tasks[] = $tasks;

        return $this;
    }

    /**
     * Remove tasks
     *
     * @param \Acme\TaskBundle\Entity\Task $tasks
     */
    public function removeTask(\Acme\TaskBundle\Entity\Task $tasks)
    {
        $this->tasks->removeElement($tasks);
    }

    /**
     * Get tasks
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getTasks()
    {
        return $this->tasks;
    }
}

那么,在我执行doctrine:schema:update --force之后,我会收到错误信息:Table 'symfony.categories' already exists。我尝试删除所有缓存,但仍然存在同样的问题。有什么想法吗?

只有在它作为m2m(多对多)关系时才会出现问题。

PS:我在Google上寻找了这个问题,但没有人回答。只有问题却没有正确的答案,即问题出在哪里以及如何解决。


1
也许尝试执行 doctrine:schema:update --dump-sql 命令,然后直接在数据库上运行 SQL 语句,看看是否可能存在格式不正确的 SQL 语句,但是没有抛出异常?虽然可能性很小,但至少可以缩小问题范围。 - Ken Hannel
@Ken Hannel:同样的信息: [Doctrine\DBAL\Schema\SchemaException] 表名为'symfony.categories'的表已经存在。doctrine:schema:update [--complete] [--dump-sql] [--force] [--em[="..."]] - Lkopo
2
不幸的是,在完全创建 SQL 之前就发生了错误,因此 dump-sql 并没有真正帮助。 - Scott
8个回答

19

看起来你在该数据库中已经有了名为“categories”的表。请移除这行代码:@ORM\JoinTable(name="categories"),然后尝试不带它运行。

P.S. “Categories”真的是一个奇怪的连接表名称。你应该遵循一些惯例,让Doctrine命名它。常见的连接表名称是category_taskcategory2task,因为它们更加自解释。没有什么重要的,只是想提供我认为好的实践建议。


但是如果我删除JoinTable,多对多关系还能正常工作吗?我真的没有categories表,即使我有,Doctrine也会自动更改这个表结构。 - Lkopo
3
它将由Doctrine自动创建,因为它知道这是多对多关系并需要创建它。 - Igor Pantović
5
这个答案是错误的。如果您删除JoinTable,它将创建一个具有一个多对多关系表的另一个表。 - user3455726
1
@user3455726,你不应该有两个拥有方。选择一个作为拥有方(对于ManyToMany,我认为这并不重要!),并指定inversedBy和mappedBy属性。正如文档所述:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#many-to-many-bidirectional。Doctrine将意识到这两个是相互关联的,并通过阅读拥有方注释来创建仅一个JoinTable。那么@JoinTable可以省略不写。 - Igor Pantović
有点晚了,但对于后来的读者来说:这是一个坏主意。它创建了一个新的联接表而不使用现有的表。 - RichieHH
显示剩余4条评论

7
事实是,doctrine不知道如何使用您现有的表格。但您可以给他一些帮助。
您有两个选择:
1. 您不关心现有的表格:简单地删除@ORM\JoinTable(name =“categories”)注释,doctrine将创建另一个表格等等。
2. 您想保留您的现有表格,这听起来非常合理:您必须在注释中更加明确,添加@ORM\JoinColumn注释。
以下是一个例子:
class 

<?php
...
/**
 * @ORM\Entity
 * @ORM\Table(name="tasks") 
 */
class Task
{
    ...
    /**
     * @ORM\ManyToMany(targetEntity="Category", inversedBy="tasks")
     * @ORM\JoinTable(name="categories",
     *       joinColumns={@ORM\JoinColumn(name="category_id", referencedColumnName="id")},
     *       inverseJoinColumns={@ORM\JoinColumn(name="task_id", referencedColumnName="id")})                         
     */
    protected $category;
    ...
}

并且类别对象

<?php
...    
/**
 * @ORM\Entity
 * @ORM\Table(name="categories") 
 */
class Category
{
    ...
    /**
     * @ORM\ManyToMany(targetEntity="Task", mappedBy="category")
     * @ORM\JoinTable(name="categories",
     *       joinColumns={@ORM\JoinColumn(name="task_id", referencedColumnName="id")},
     *       inverseJoinColumns={@ORM\JoinColumn(name="category_id", referencedColumnName="id")})
     */
    private $tasks;            
...

通过这样做,您将能够保持您的表格没有任何学说错误。

这对我解决了问题 - 我在一个对象上缺少了 mappedBy,而在另一个对象上有 inversedBy - dhh
3
在非拥有端(如果我没记错的话,还要加上mappedBy属性),@ORM\JoinTable被忽略且完全不必要。此外,这可能会使开发人员忘记在第二个位置更新它,因此你不应该把它放两次。 - SteveB

3
据我所知,这个问题的解决方法与表名的大小写敏感有关。Doctrine让我创建了一个Users和一个users表,但之后在执行migrations:diff或migrations:migrate时会出错。
我使用了-vvv选项来获取更多有关此错误消息的详细信息;似乎当Doctrine加载当前数据库架构的内部表示时,错误就会发生。因此,如果您当前的数据库具有Doctrine不理解的表名(例如两个表是相同的,不区分大小写),那么它将以这种方式崩溃。
似乎大多数上面的答案都假设错误在您的代码中,但在我的情况下,错误在数据库中。

这与问题无关。 - RichieHH

2

我在使用两个ManyToMany关联同一个实体(以下示例中为User)时遇到了这个错误。

要创建表名,Doctrine会使用实体和目标实体的名称。

所以在我的情况下,它试图创建两次thread_user表。

要调试此问题很容易。只需使用“@ORM \ JoinTable”注释并指定表名即可。

以下是一个可行的示例。

原始答案:Original Answer

/**
 * @ORM\ManyToMany(targetEntity="App\Entity\User")
 * @ORM\JoinTable(name="thread_participant")
 */
private $participants;

/**
 * @ORM\ManyToMany(targetEntity="App\Entity\User")
 * @ORM\JoinTable(name="thread_recipient")
 */
private $recipients;

0
在Symfony4.1中,您可以使用迁移版本强制进行迁移。
doctrine:migrations:execute <migration version>

ex

对于迁移 version123456.php 使用

doctrine:migrations:execute 123456

0

还有一种方法是使用表名,在你的项目中搜索它。可能是演示,我认为它...

对不起我的中式英语!


嗯...看起来你想评论其中的一个其他答案?如果是这样,请等到你有足够的声望再进行评论 :-) - kleopatra

0

我在检查每个bundle上的其他实体后解决了相同的问题,请注意。


0

尝试删除代理目录中的所有内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接