【问题标题】:Doctrine merge removes new associations/links教义合并删除了新的关联/链接
【发布时间】:2014-10-03 07:51:15
【问题描述】:

在更新带有关联/链接的分离实体时,我们遇到了一些困难。我们有一个文章实体,它与类别实体具有多对多关系。

当通过表单向现有文章添加类别并提交时,我们会收到以下消息:

执行“INSERT INTO 文章...重复条目“测试”时发生异常

这很奇怪,因为实体是现有的(带有 id),为什么要使用插入?

由于文章实体被分离/序列化,我们尝试合并它,这次我们没有收到错误,但没有新的类别链接插入到数据库中。我们统计了合并之前和之后的类别,发现它们在合并时被重置为数据库状态,所以如果它最初有 2 个类别,我们添加了第三个类别,则合并前的计数为 3,合并后为 2。新的类别也是一个部分实体,只包含类别的 id。

代码如下:

    $connection->beginTransaction();

    try {
        // Manage model by doctrine if it's not.
        $modelState = $entityManager->getUnitOfWork()->getEntityState($model);
        if ($modelState !== UnitOfWork::STATE_MANAGED) {
            $model = $entityManager->merge($model); // Strips new categories.
        }

        $entityManager->persist($model);
        $entityManager->flush();
        $connection->commit();
    } catch (\Exception $e) {
        $connection->rollback();
    }

实体:

<entity name="Article" table="articles" repository-class="Doctrine\Repository\BaseRepository">
    <id name="id" type="integer" column="id">
        <generator strategy="IDENTITY"/>
    </id>

    <field name="title" type="string" column="title" length="50" nullable="true"/>

    <many-to-many field="categories" target-entity="ArticleCategory" inversed-by="articles">
        <cascade>
            <cascade-persist/>
        </cascade>
        <join-table name="article_category_links">
            <join-columns>
                <join-column name="article_id" referenced-column-name="id" on-delete="CASCADE"/>
            </join-columns>
            <inverse-join-columns>
                <join-column name="article_category_id" referenced-column-name="id" on-delete="CASCADE"/>
            </inverse-join-columns>
        </join-table>
    </many-to-many>
</entity>

<entity name="ArticleCategory" table="article_categories" repository-class="Doctrine\Repository\BaseRepository">
    <id name="id" type="integer" column="id">
        <generator strategy="IDENTITY"/>
    </id>

    <field name="name" type="string" column="name" length="200" nullable="true"/>

    <many-to-many field="articles" target-entity="Article" mapped-by="categories">
        <cascade>
            <cascade-persist/>
        </cascade>
    </many-to-many>
</entity>

更新

这就是我们正在做的:

1.表格$_POST数据,第三类是新的:

Array
(
    [id] => 1
    [title] => test
    [categories] => Array
        (
            [0] => Array
                (
                    [id] => 1
                )

            [1] => Array
                (
                    [id] => 2
                )

            [2] => Array
                (
                    [id] => 3
                )

        )
)

2。反序列化的文章(JMS\Serializer):

Article Object
(
    [id:protected] => 1
    [title:protected] => test
    [categories:protected] => Array
        (
            [0] => ArticleCategory Object
                (
                    [id:protected] => 1
                    [name:protected] => 
                    [articles:protected] => 
                )

            [1] => ArticleCategory Object
                (
                    [id:protected] => 2
                    [name:protected] => 
                    [articles:protected] => 
                )

            [2] => ArticleCategory Object
                (
                    [id:protected] => 3
                    [name:protected] => 
                    [articles:protected] => 
                )

        )
)

3。合并分离/反序列化的文章,删除第三类..

$article = $entityManager->merge($article);

4.刷新,更新所有文章数据,但没有链接到第三个类别,因为它被删除了。

$entityManager->flush();

以下是更多示例/测试:

// With attached article and no merging.
// Creates the link to the article, but the category becomes a new one (new id)..
$article = $articleRepository->getById(1)->getItem();
$category = new ArticleCategory();
$category->setId(3); // This doesnt matter since it becomes a new one.
$article->addCategory($category);
$articleRepository->getEntityManager->flush();

// With attached article and merging of category.
// Creates the link to the article but it clears all the category fields..
$article = $articleRepository->getById(1)->getItem();
$category = new ArticleCategory();
$category->setId(3);
$category = $categoryRepository->getEntityManager->merge($category);
$article->addCategory($category);
$articleRepository->getEntityManager->flush();

// With detached and merged article.
// No links no errors, but other article fields gets updated.
$category = new ArticleCategory();
$category->setId(3);
$article->addCategory($category);
echo count($article->getCategories()); // 3
$article = $articleRepository->getEntityManager->merge($article);
echo count($article->getCategories()); // 2, the new one gets removed.
$articleRepository->getEntityManager->flush();

【问题讨论】:

    标签: php doctrine-orm


    【解决方案1】:

    “向现有文章添加类别时...”

    如果这是一篇存在文章,你不应该这样做shit

    $model = $entityManager->merge($model);
    

    Doctrine 允许您直接更改通过 EntityManager 加载的对象的状态。

    “这很奇怪,因为实体是现有的(带有 id),为什么 它使用插入?”

    因为你调用的是persist()方法:

    $entityManager->persist($model);
    

    试试这样的:

    $cat1 = $entityManager->find("ArticleCategory", 1);
    $cat2 = $entityManager->find("ArticleCategory", 2);
    
    $model = $entityManager->find("Article", $id);
    $model->getCategories()->add($cat1);
    $model->getCategories()->add($cat2);
    
    // because the $model exists, you only need to syncronize the changes in the db
    $entityManager->flush();
    

    更新

    如果您需要再次反序列化模型,这应该可以解决问题:

    $detachedEntity = unserialize($model); // some detached entity
    $entity = $entityManager->merge($detachedEntity);
    
    // and then, sync the changes in db
    $entityManager->flush();
    

    注意以下警告:

    When you want to serialize/unserialize entities you have to make all entity properties protected, never private. The reason for this is, if you serialize a class that was a proxy instance before, the private variables won’t be serialized and a PHP Notice is thrown.

    【讨论】:

    • 我们已经成功尝试过这种方法,但是当实体被分离/序列化并且必须合并时会出现问题。
    • @nixxxon,我已经编辑了答案。看看警告。
    • 感谢@manix,我们似乎无法按照我们想要的方式解决它。我已经用更多示例/测试更新了我们的问题。
    • @nixxxon,能否请您显示ArticleArticleCategory 模型定义?看起来您需要级联更新操作。
    • 定义/映射在第一篇文章的 xml 中。当我们要求它时(在进行更改之前),我们设法通过合并分离的实体来使代码工作,而不是添加只有 id 的类别,我们必须循环 id 并获取将它们添加到文章中的真实类别。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 2012-07-09
    • 2011-09-13
    • 2023-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多