0

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?

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

6 Comments

I followed the first solution, I write the following method in DestinationRepository: public function findDestinationWithoutAudio() { $query='SELECT d FROM HearWeGoHearWeGoBundle:Destination d WHERE d NOT IN (SELECT a.destination FROM HearWeGoHearWeGoBundle:Audio a)'; return $this->getEntityManager()->createQuery($query)->getResult(); } I encountered new problem: Error: Invalid PathExpression. Must be a StateFieldPathExpression. In Audio entity, $destination is an instance of Destination type, so I can't use id in query
@necroface: and ... ? (btw from a first look, I'm not sure about your return statement)
I edited the above comment, it concerns with createQuery
The error stuck right in the query. And I don't know how to write a proper query in this case
I have to dig out this old question to ask for one thing: If my customRepoFunction has some parameters, for example: public function findByAudioId($id) { return $this->getEntityManager()->createQuery('SELECT d FROM HearWeGoHearWeGoBundle:Destination d,HearWeGoHearWeGoBundle:Audio a WHERE d.id=IDENTITY (a.destination)')->getResult(); } What will I put in 'choices'=> $destination_repo->yourCustomRepoFunctionName() Could you please help me again
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.