Symfony,表单和多对一

Symfony, forms and many to one

我们 运行 对 symfony 和我们的表单有一点代码设计的感觉。这本身不是问题,但让我想知道我们是否可以通过其他方式实现我们的目标。

为了简单起见,让我简要解释一下设置:让 "Product" 成为一个实体,代表数据库中的产品,意味着在在线商店中销售。由于在线商店设计为包含多种语言,因此可能与一种语言相关的每一位信息都在以多对一方式与 "Product" 相关的实体 "Product_descriptions" 中。最后,我们设计了一个 "Language" 实体,代表用户可以看到商店的每一种语言。

如您所想,代码是非常标准的东西:

class Language
{
    private $language_id;
    private $language_name;
    private $language_code;

    //Some other stuff.
};

class Product
{
    private $product_id;
    private $product_reference;
    private $product_weight;

    private $product_descriptions; //As an arrayCollection of "Product_description" objects.

    //Some other stuff.
};

class Product_description
{
    private $product_description_id;
    private $product_name;
    private $product_long_description;
    private $product_short_description;

    private $product;   //A reference to the Product itself.
    private $language;  //A reference to the language this is meant to be seen in.
};

好了,现在来说说问题本身。正如预期的那样,该设置非常有效。它在气味所在的后端。

为了创建新产品,我们设计了一个 symfony 表单类型。在相同的表格中,我们希望能够设置所有产品信息以及每种可能语言的信息。当我们需要将所有可能的 "Language" 提供给表单类型时,气味就会出现,检查 "Language" 和 "Product" 是否存在 "Product_description",显示空文本字段(以防它不存在)或填充字段...我们的解决方案要求将所有语言的存储库注入到表单中。让我向您展示它是如何进行的(请考虑到这不是真正的代码......可能缺少一些东西):

class ProductType extends AbstractType
{
    private $language_repo;

    public function __construct($r)
    {
        $this->language_repo=$r;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('product_name', 'text')
            ->add('product_code', 'text');

        $product=$builder->getData();

        //We retrieve all languages here, to check if an entry for that 
        //language exists and show its data.

        $languages=$this->language_repo->findAll();
        foreach($languages as $key => &$lan)
        {
            //Here we look for existing data... This will return null if there's none.
            $product_description=$product->get_description_for_language($lan);

            $default_name=$product_description ? $product_description->getProductName() : '';
            $default_long=$product_description ? $product_description->getProductLongDescription() : '';
            $default_short=$product_description ? $product_description->getProductShortDescription() : '';

            //Here we manually create the name_#language_id# form data... That we will retrieve later.
            $builder->add('name_'.$lan->getLanguageId(), 'text', array(
                    'label' => 'Name for '.$lan->getName(), 
                    'mapped' => false, 
                    'data' => $default_name))
                ->add('long_'.$lan->getLanguageId(), 'text', array(
                    'label' => 'Name for '.$lan->getName(), 
                    'mapped' => false, 
                    'data' => $default_long))
                ->add('short_'.$lan->getLanguageId(), 'text', array(
                    'label' => 'Name for '.$lan->getName(), 
                    'mapped' => false, 
                    'data' => $default_short));
        }

        $builder->add('save', 'submit', array('label' => 'Save data'));
    }

    //And some other stuff here.
}

如您所见,我们正在手动设置一些稍后需要在控制器中检索的数据键。当然,设置有效。任何新语言都会产生一个空的表单域。任何现有语言显示相关信息。

现在对于控制器来说,这甚至变得更加混乱......当我们提交表单时,我们是这样的:

private function process_form_data(Form &$f, Product &$item, Request &$request)
{
    //Find all languages...
    $em=$this->getDoctrine()->getManager();
    $languages=$em->getRepository("MyBundle:Language")->findAll();

    //Get submitted data for that language..
    foreach($languages as $key => &$lan)
    {
        $name_language=$f->get('name_'.$lan->getLanguageId())->getData();
        $long_language=$f->get('long_'.$lan->getLanguageId())->getData();
        $short_language=$f->get('short_'.$lan->getLanguageId())->getData();

        //Check if the language entry exists... Create it, if it doesn't. Feed the data.            
        $product_description=$product->get_description_for_language($lan);

        if(!$product_description)
        {
            $product_description=new Product_description();
            $product_description->setLanguage($lan);
            $product_description->setProduct($product);
        }

        $product_description->setName($name_language);
        $product_description->setLongDescription($long_language);
        $product_description->setShortDescription($short_language);

        $em->persist($product_description);
    }

    //Do the product stuff, persist, flush, generate a redirect...Not shown.
}

它有效,但在我看来这不是 "symfony" 做事的方式。你会怎么做?你找到更优雅的方法了吗?

非常感谢。

我认为您应该重新审视翻译实体的方式...

现有的方法是使用 DoctrineExtensionBundletranslatable 准确地说...

您可以在此处找到更多信息

https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/translatable.md

这里是一个摘录,看看它是如何工作的:

<?php
// first load the article
$article = $em->find('Entity\Article', 1 /*article id*/);
$article->setTitle('my title in de');
$article->setContent('my content in de');
$article->setTranslatableLocale('de_de'); // change locale
$em->persist($article);
$em->flush();

(现在这篇文章有德语翻译)