【问题标题】:Symfony, forms and many to oneSymfony、表单和多对一
【发布时间】:2015-02-27 11:41:46
【问题描述】:

我们在 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 表单类型。我们希望能够以相同的形式设置所有产品信息以及每种可能语言的信息。当我们需要将所有可能的“语言”提供给表单类型时,气味就会出现,检查“语言”和“产品”是否存在“产品描述”,显示空文本字段(以防不存在)或填充字段...我们的解决方案要求将所有语言的存储库注入到表单中。让我告诉你它是怎么回事(请考虑到这不是真正的代码......可能会丢失一些东西):

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

非常感谢。

【问题讨论】:

    标签: php forms symfony


    【解决方案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();
    

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

    【讨论】:

    • 我可以与团队讨论。另一层抽象可能会有所帮助,特别是因为实际项目有更多可翻译的实体。后端 GUI 怎么样?我想这个捆绑包会帮助我们吗?
    • 是的,这个包有一个很好的文档。只需在实体表单上添加一个“区域设置”字段以选择语言,然后将此值发送到“$article->setTranslatableLocale($yourvalue)。工作完成。
    • 好的。我会在周一回到城里时阅读并展示给团队。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 2016-02-24
    • 2018-02-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多