Symfony,形式和多对一


Symfony, forms and many to one

我们遇到了symfony和表单的小代码设计气味。这本身不是问题,但让我怀疑我们是否可以通过其他方式实现目标。

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

正如你所能想象的,代码是相当标准的东西:

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.
};

好的,现在是问题本身。正如预期的那样,这种设置非常有效。它位于气味所在的后端。

为了创造新产品,我们设计了一个符号形式Type。以同样的形式,我们希望能够设置所有产品信息以及每种可能语言的信息。当我们需要将所有可能的"语言"输入表单类型时,气味就会出现,检查"语言"answers"产品"是否存在"Productdescription",显示空文本字段(如果不存在)或填充字段。。。我们的解决方案要求在表单中注入所有语言的存储库。让我告诉你它是如何进行的(请考虑到这不是真正的代码……可能缺少一些东西):

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"的做事方式。你会怎么做?。你找到更优雅的方法了吗?。

非常感谢。

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

现有的方法是使用DoctrineExtensionBundle,确切地说是translatable。。。

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

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();

(现在文章有德语翻译)