Symfony2实体表单类型获取数据 [英] Symfony2 Entity Form Type gets data

查看:252
本文介绍了Symfony2实体表单类型获取数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有2个实体:音频和目的地

在音频中:

  * @ ORM \OneToOne(targetEntity =HearWeGo \HearWeGoBundle \Entity \Destination,inversedBy =audio)
* @ Assert \NotBlank(message =此栏位必须填写)
*
* /
私人$ destination;

我创建了一个用于将音频上传到数据库的表单类型名称AddAudioType

 <?php 

命名空间HearWeGo \HearWeBundle \Form;

使用Symfony \Component\Form\AbstractType;
使用Symfony \Component\Form\FormBuilderInterface;
使用Symfony \ Component\OptionsResolver\OptionsResolver;
使用HearWeGo \HearWeBundle \Entity \Audio;

class AddAudioType extends AbstractType
{
public function buildForm(FormBuilderInterface $ builder,array $ options)
{
$ builder
- > ; add('name','text')
- > add('content','file')
- > add('destination','entity',array('class' =>'HearWeGoHearWeGoBundle:Destination','property'=>'name'))
;


public function configureOptions(OptionsResolver $ resolver)
{
$ resolver-> setDefaults(array('data_class'=>HearWeGo \\\ \\HearWeGoBundle\\Entity\\Audio));
}

public function getName()
{
return'add_audio';
}
}
?>

In Controller

  / ** 
* @Route(/ admin / add / audio,name =add_audio)
* /
public function addAudioAction(Request $ request)
{
if(!$ this-> get('security.authorization_checker') - > isGranted('IS_AUTHENTICATED_FULLY')){
return new Response('Please login');


$ this-> denyAccessUnlessGranted('ROLE_ADMIN',null,'无法访问此页!');

$ audio = new Audio();
$ form = $ this-> createForm(new AddAudioType(),$ audio,array(
'method'=>'POST',
'action'=> $ this - > generateUrl('add_audio')
));
$ form-> add('submit','submit');
if($ request-> getMethod()=='POST')
{
$ form-> handleRequest($ request);
if($ form-> isValid())
{
$ destination = $ this-> getDoctrine() - > getRepository('HearWeGoHearWeGoBundle:Destination')
- > findByName($形式 - >获得( '目标') - >的getData() - >的getName());
$ audio-> setDestination($ destination);
$ name = $ _ FILES ['add_audio'] ['name'] ['content'];
$ tmp_name = $ _ FILES ['add_audio'] ['tmp_name'] ['content'];
if(isset($ name))
{
if(!empty($ name))
{
$ location = $ _ SERVER ['DOCUMENT_ROOT']。 /捆绑/ hearwegohearwego /上传/;
move_uploaded_file($ tmp_name,$ location。$ name);
$ audio-> setContent($ location。$ name);
$ em = $ this-> getDoctrine() - > getEntityManager();
$ em-> persist($ audio);
$ em-> flush();
返回新的响应('Audio'。$ audio-> getName()。'已创建!');



$ b return $ this-> render('@ HearWeGoHearWeGo / manage / addAudio.html.twig',array('form' => $形状配合> CreateView的()));
}

在AddAudioType中,我声明了它从Destination实体表中获取所有记录,允许用户选择其中的一个,然后将其保存到数据库中

现在有另一个我必须处理的事情:因为音频和目标之间的关系是一对一的,用户不能选择已经出现在音频表中的目的地。现在在AddAudioType中,我不想从Destination表中获取所有记录,但只有一些尚未出现在Audio表中。我应该怎么做?

解决方案

当您在表单构建器中执行操作时

   - > add('destination','entity',array(
'class'=>'HearWeGoHearWeGoBundle:Destination',
'property' =>'name'
));

你说你想要所有可能的 Destination

如果你想过滤它们,你有两种可能:

第一个(推荐)



编写自己的方法,将已经关联目的地排除到 DestionationRepository 。如果您不知道什么是存储库或者您不知道如何编写它,请参阅 document 。方法实现留给你作为练习(不,实际上,我不知道所有的实体,所以我不能猜测)。

一旦你完成了这些,你必须将 DestinationRepository 传递给你的表单,作为一个选项( required 我想[见 setRequired()

  // AddAudioType $(code>>)},所以,像这样的东西(我会省略无趣的代码) b $ b <?php 
[...]
public function buildForm(FormBuilderInterface $ builder,array $ options)
{
$ destination_repo = $ options ['dr' ]。

$ builder-> [...]
- > add('destination','entity',array(
'class'=>'HearWeGoHearWeGoBundle: Destination',
'choices'=> $ destination_repo-> yourCustomRepoFunctionName(),
'property'=>'name'));


$ resolver-> setRequired(array(
'dr',
));

现在您已经为表单设置了所有内容,您需要传递 DestinationRepository 到您的表单。


这确实很简单


  //在控制器中,你正在安装你的形式
[...]
public function addAudioAction()
{
[...]
$ destination_repo = $ this-> getDoctrine()
- > getManager()
- > getRepository('HearWeGoHearWeGoBundle:Destination');
$ b $ form = $ this-> createForm(new AddAudioType(),$ audio,array(
'method'=>'POST',
'action'= > $ this-> generateUrl('add_audio'),
'dr'=> $ destination_repo,
));

$ / code>

只要你写出一个好的过滤器 方法(即:你排除不在子句中所有目的地将密钥存入其他表)你只需将你的方法写成


$ b


第二个

/ p>

  // AddAudioType 
使用Doctrine \ORM \EntityRepository;

$ php

public function buildForm(FormBuilderInterface $ builder,array $ options)
{
$ destination_repo = $选项[ '博士'];

$ builder-> [...]
- > add('destination','entity',array(
'class'=>'HearWeGoHearWeGoBundle:目标',
'选择'=>函数(EntityRepository $ repository)use($ someParametersIfNeeded){
return $ repository-> createQueryBuilder('d')
- > [。 ..];},
'property'=>'name'));



$ b $ p
$ b

在第二种情况下, createQueryBuilder 也没有实现并留给你。有一点你需要记住: choices 需要一个查询生成器,所以不要调用 - > getQuery() - > getResult()









  • 自定义函数应始终保留在回收站内。因此,你正在编写一些代码到必须的地方(见下面的知识点)
  • 因为代码是可重用的(DRY原则)

    $ b

    自定义回购函数 / h3>

      public function findDestinationWithoutAudio(){
    $ query =SELECT d
    FROM HearWeGoHearWeGoBundle:Destination d
    WHERE d NOT IN(SELECT IDENTITY(a.destination)
    FROM HearWeGoHearWeGoBundle:Audio a)
    ;
    $ b $ return $ this-> getEntityManager() - > createQuery($ query) - > getResult();





    $ b

    如果你想知道为什么你应该使用 IDENTITY ()函数而不是直接外键: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#dql功能


    I have 2 entities: Audio and Destination

    In Audio:

    /**
         * @ORM\OneToOne(targetEntity="HearWeGo\HearWeGoBundle\Entity\Destination", inversedBy="audio")
         * @Assert\NotBlank(message="This field must be filled")
         * 
         */
        private $destination;
    

    I created a Form Type name AddAudioType used to upload an audio to database

    <?php
    
    namespace HearWeGo\HearWeGoBundle\Form;
    
    use Symfony\Component\Form\AbstractType;
    use Symfony\Component\Form\FormBuilderInterface;
    use Symfony\Component\OptionsResolver\OptionsResolver;
    use HearWeGo\HearWeGoBundle\Entity\Audio;
    
    class AddAudioType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('name','text')
                ->add('content','file')
                ->add('destination','entity',array('class'=>'HearWeGoHearWeGoBundle:Destination','property'=>'name'))
                ;
        }
    
        public function configureOptions(OptionsResolver $resolver)
        {
            $resolver->setDefaults(array('data_class'=>"HearWeGo\\HearWeGoBundle\\Entity\\Audio"));
        }
    
        public function getName()
        {
            return 'add_audio';
        }
    }
    ?>
    

    In Controller

    /**
         * @Route("/admin/add/audio",name="add_audio")
         */
        public function addAudioAction(Request $request)
        {
            if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')){
                return  new Response('Please login');
            }
    
            $this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
    
            $audio=new Audio();
            $form=$this->createForm(new AddAudioType(),$audio,array(
                'method'=>'POST',
                'action'=>$this->generateUrl('add_audio')
            ));
            $form->add('submit','submit');
            if ($request->getMethod()=='POST')
            {
                $form->handleRequest($request);
                if ($form->isValid())
                {
                    $destination=$this->getDoctrine()->getRepository('HearWeGoHearWeGoBundle:Destination')
                        ->findByName($form->get('destination')->getData()->getName());
                    $audio->setDestination($destination);
                    $name=$_FILES['add_audio']['name']['content'];
                    $tmp_name=$_FILES['add_audio']['tmp_name']['content'];
                    if (isset($name))
                    {
                        if (!empty($name))
                        {
                            $location=$_SERVER['DOCUMENT_ROOT']."/bundles/hearwegohearwego/uploads/";
                            move_uploaded_file($tmp_name,$location.$name);
                            $audio->setContent($location.$name);
                            $em=$this->getDoctrine()->getEntityManager();
                            $em->persist($audio);
                            $em->flush();
                            return new Response('Audio '.$audio->getName().' has been created!');
                        }
                    }
                }
            }
            return $this->render('@HearWeGoHearWeGo/manage/addAudio.html.twig',array('form'=>$form->createView()));
        }
    

    In AddAudioType, I declared so that it gets all records from Destination entity table and allows user to choose one of them, then persist it to database

    Now there's something another I have to handle: Because relationship between Audio and Destination is one-to-one, user is not allowed to choose a Destination which already appeared in Audio table. Now in AddAudioType, I don't want to get all records from Destination table, but only some that hasn't appeared in Audio table yet. How should I do it?

    解决方案

    When you do in your form builder

    ->add('destination', 'entity', array(
        'class'=>'HearWeGoHearWeGoBundle:Destination',
        'property'=>'name'
    ));
    

    you're saying that you want all of possible Destination entities

    If you want to filter them, you have two possibilities

    First one (recommended)

    Write your own method to exclude already "associated" Destinations into DestionationRepository. If you don't know what is a repository or you don't know how to write one, please refer to this document. Method implementation is left to you as an exercise (No, really, I don't know all entities so I cannot make any guess)

    Once you've done this, you have to pass DestinationRepository to your form, as an option (required I suppose [see setRequired() method below]), so, something like this (I'll omit uninteresting code)

    //AddAudioType
    <?php
        [...]
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $destination_repo = $options['dr'];
    
            $builder->[...]
                    ->add('destination','entity',array(
                        'class'=>'HearWeGoHearWeGoBundle:Destination',
                        'choices'=> $destination_repo->yourCustomRepoFunctionName(),
                        'property'=>'name'));
        }
    
        $resolver->setRequired(array(
            'dr',
        ));
    

    Now that you have setted all for your form, you need to pass DestinationRepository to your form. How do you that?
    It's quite simple indeed

    //In controller you're instatiating your form
    [...]
    public function addAudioAction()
    {
        [...]
        $destination_repo = $this->getDoctrine()
                                 ->getManager()
                                 ->getRepository('HearWeGoHearWeGoBundle:Destination');
    
        $form=$this->createForm(new AddAudioType(), $audio, array(
                'method' => 'POST',
                'action' => $this->generateUrl('add_audio'), 
                'dr' => $destination_repo,
        ));
    }
    

    It's going to work like a charm as long as you write a good "filter" method (ie.: you exlude with NOT IN clause all Destinations that got the key into other table)


    Second one

    You simply write your method into the form

    //AddAudioType
    use Doctrine\ORM\EntityRepository;
    
    <?php 
        [...]
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $destination_repo = $options['dr'];
    
            $builder->[...]
                    ->add('destination','entity',array(
                        'class'=>'HearWeGoHearWeGoBundle:Destination',
                        'choices'=> function(EntityRepository $repository) use ($someParametersIfNeeded) { 
                                        return $repository->createQueryBuilder('d')
                                            ->[...];},
                        'property'=>'name'));
        }
    

    In this second case, createQueryBuilder is also not implemented and left to you. One thing you need to remember: choices will need a query builder, so don't call ->getQuery() and ->getResult()


    Why fist one?

    • Custom function should always stay within repos. So you are writing some code into the place that has to be (see points below to know way)
    • Because code, that way, is reusable (DRY principle)
    • Because you can test code more easily

    Custom repo function

    public function findDestinationWithoutAudio() { 
        $query= "SELECT d 
                 FROM HearWeGoHearWeGoBundle:Destination d 
                 WHERE d NOT IN (SELECT IDENTITY(a.destination) 
                                 FROM HearWeGoHearWeGoBundle:Audio a)"
        ; 
    
        return $this->getEntityManager()->createQuery($query)->getResult();
    }
    

    If you want to know why you should use IDENTITY() function and not foreign key directly: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/dql-doctrine-query-language.html#dql-functions

    这篇关于Symfony2实体表单类型获取数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆