Symfony,形式和多对一

时间:2015-02-27 11:41:46

标签: php forms symfony

我们遇到了一个带有symfony和我们表单的小代码设计气味。这本身并不是问题,但让我想知道我们是否能够以任何其他方式实现我们的目标。

为简单起见,让我简要解释一下设置:让“产品”成为代表数据库中产品的实体,意味着在网上商店出售。由于在线商店被设计为具有多种语言,因此可能与语言相关的每一点信息都在实体“Product_descriptions”中,该实体以多种方式与“产品”相关联。最后,我们设计了一个“语言”实体,代表用户可以看到商店的每种语言。

您可以想象,代码非常标准:

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表单Type。我们希望能够以相同的形式设置所有产品信息以及每种可能语言的信息。当我们需要将所有可能的“语言”提供给表单类型时检查是否有气味,检查“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”的做事方式。你会怎么做?你有没有找到更优雅的方法?

非常感谢。

1 个答案:

答案 0 :(得分:1)

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

现有的方法是使用 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();

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