原则-以一对多的关系替换项目

时间:2018-09-17 19:08:27

标签: php symfony doctrine-orm

我有两个实体,分别用于产品和翻译:

class ProductEntity
{
    /**
    * @Id
    * @var string
    * @Column(type="string", length=3)
    */
    protected $code;

    /**
     * @OneToMany(targetEntity="ProductTranslationEntity", mappedBy="product")
     */
    private $translations;

    public function __construct()
    {
        $this->translations = new ArrayCollection();
    }


    /.../ getters and setters


    public function addTranslation(ProductTranslationEntity $productTranslation)
    {
        $this->translations->add($productTranslation);
    }

    public function clearTranslations()
    {
        $this->translations->clear();
    }
}

class ProductTranslationEntity
{
    /**
    * @ManyToOne(targetEntity="ProductEntity", inversedBy="translations")
    * @JoinColumn(name="product_code", referencedColumnName="code", onDelete="CASCADE")
    * @Id
     */
    private $product;

    /**
    * @var string
    * @Column(type="string", name="language_code", length=5)
    * @Id 
    */
    protected $languageCode;

    /**
    * @var string
    * @Column(type="string", name="product_name", length=128)
    */
    protected $productName;

    /.../ getters and setters
}

我喜欢用这样的数组将所有翻译替换为新的翻译:

['en' => ['name' => 'name_en'], 'de' => ['name' => 'name_de']];

因为在此数组中,我设置了所有受支持的语言,所以我看到的最有效的方法是删除所有现有翻译并放入新的翻译:

$product // Existing product entity
$product->clearTranslations();
$this->entityManager->flush($product);

foreach ($translations as $code => $translation) {
    $t = new ProductTranslationEntity();
    $t->setProduct($product);
    $t->setLanguageCode($code);
    $t->setProductName($translation['name']);
    $this->entityManager->persist($t);
    $product->addTranslation($t);
}

$this->entityManager->flush($product);

此解决方案不起作用,因为在第一个$this->entityManager->flush($product);之后,数据库中仍然存在翻译,因此我得到有关重复项的错误。

我的解决方案做错了什么?也许还有另一种方法可以解决此类问题?

3 个答案:

答案 0 :(得分:1)

这可能有点过载,但仍然不使用对数据库的额外请求:

$current_translations = $product->getTranslations();
foreach ($translations as $code => $translation) {
    $translation_found = false;
    foreach ($current_translations as $current_translation) {
        if ($current_translation->getLanguageCode() === $code) {
            // you've found the translation - replace value
            $current_translation->setProductName($translation['name']);
            $translation_found = true;
            break;
        }
    }

    if (!$translation_found) {
        // translation with such code not found - add a new one
        $t = new ProductTranslationEntity();
        $t->setProduct($product);
        $t->setLanguageCode($code);
        $t->setProductName($translation['name']);
        $this->entityManager->persist($t);
        $product->addTranslation($t);
    }
}
$this->entityManager->persist($product);

答案 1 :(得分:1)

使用orphanRemoval = true:

/**
 * @OneToMany(targetEntity="ProductTranslationEntity", mappedBy="product", orphanRemoval=true)
 */
private $translations;

答案 2 :(得分:0)

如学说文档所述:

  

仅对关联的反面所做的更改将被忽略。   确保更新双向关联的两端(或在   从教义的角度来看,至少是拥有方。

因此,要正确清除产品的翻译,您应该将Product实体内部的clearTranslations()函数更改为:

public function clearTranslations()
{
    foreach ($this->translations as $translation) {
        $translation->setProduct(null);
    }
    $this->translations->clear();
}

,以便您还可以在删除前更新关联的拥有方。