Symfony: Get parent object within child from formevent - symfony

I have a form that contains 3 fields (date, typeEvent, seller) where Seller is a choiceType that depends on date and typeEvent, and to do that i followed the symfony documentation for dynamics forms.
but the exemple in the doc its about a field that depends on only one other field.
what i did so far :
$formModifier = function (FormInterface $form,DateTime $date = null, TypeEvent $type = null) {
if (($date === null) || ($type === null)) {$sellers = [];return;}
$repo = $this->entityManager->getRepository(User::class);
$start = $date->format("Y-m-d H:i:s");
$end = new DateTime($date->format("Y-m-d H:i:s"));
$end = date_add($end,date_interval_create_from_date_string("60 minutes"))->format('Y-m-d H:i:s');
$organisation = $this->security->getUser()->getOrganisation();
$sellers = $repo->findSellers($organisation,$start,$end);
$form->add('seller', EntityType::class, [
'class' => User::class,
'placeholder' => '',
'choices' => $sellers,
'choice_label' => 'pseudo',
'attr' => ['class'=>'seller-select'],
'required'=>false,
'expanded' =>false,
]);
};
$builder->get('start')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$start = $event->getForm()->getData();
$type = $event->getForm()->getParent()->getData()->getTypeEvent();
$formModifier($event->getForm()->getParent(), $start, $type);
}
);
$builder->get('typeEvent')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$type = $event->getForm()->getData();
$start = $event->getForm()->getParent()->getData()->getStart();
$formModifier($event->getForm()->getParent(), $start, $type);
}
);
the problem here is that, for exemple when i try to add a listener to 'start' field inside of it, i don't have access to the other fields, the typeEvent field specifically, i tried $event->getForm()->getParent()->getData()->getTypeEvent() but it returns null, and that's $event->getForm()
dumped.
As you can see the $event->getForm()->getParent()->getData() it's like a new Event() with all attribute on null.
So my question is: There is any way to get the typeEvent there ? or should i proceed differently?
Thank you.

I am not completely sure if this is what you want, but you should take a look at this answer:
https://stackoverflow.com/a/25890150/17089665
$form->all(); <- Will get you all fields
$child->getName(); <- Will get you the name of every child, if you iterate the $form variable.

Related

$form->isValid() always returning false after the first submit in Symfony3.4

The following is the code of a controller that will generate:
A form for entering Jobcode if none is mentioned.
Generate a form for entering values into the Jobcard (if Jobcode is present).
Generate Jobcard as PDF on submission of form mentioned in 2.
Problem: First time the form (mentioned in 2) is submitted the controller works as desired. But problem starts there after. The aforesaid form on submission yield the following:
$form_jobcard->isSubmitted() is true.
$form_jobcard->isValid() is false.
`
use AppBundle\Entity\Job;
use AppBundle\Entity\ActivityLog;
use AppBundle\Entity\Complaint;
use Symfony\Component\Form\FormError;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Knp\Bundle\SnappyBundle\Snappy\Response\PdfResponse;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
class DocumentController extends Controller
{
/**
* #Route("/admin/jobcard/{jobcode}", name="jobcard", defaults={"jobcode" = null})
*/
public function generateJobCardAction($jobcode, Request $request)
{
$em = $this->getDoctrine()->getManager();
if($jobcode === null){
$form_jc = $this->createFormBuilder()
->setAction($this->generateUrl('jobcard'))
->setMethod('POST')
->add('JobCode', TextType::class)
->add('submit', SubmitType::class, [
'label' => 'Submit'
])
->getForm();
$form_jc->handleRequest($request);
if ($form_jc->isSubmitted() && $form_jc->isValid()) {
$errors = 0;
if (!isset($form_jc->getData()["JobCode"])
|| $form_jc->getData()["JobCode"] === ""
|| 1 !== preg_match('/^[0-9]{6}-[SCREWTH]$/', strtoupper($form_jc->getData()["JobCode"]))){
$errors++;
$form_jc->addError(new FormError("Invalid JobCode.")); //Jobcode pattern doesn't match.
} else if (1 == preg_match('/^[0-9]{6}-[SCREWTH]$/', strtoupper($form_jc->getData()["JobCode"]))){
$jcode = strtoupper($form_jc->getData()["JobCode"]);
$job = $em->getRepository(Job::class)->findOneBy(array('complaint' => (int)$jcode, 'section' => substr($jcode, 7, 1)));
if ($job == NULL){
$errors++;
$form_jc->addError(new FormError("Invalid JobCode.")); //Job doesn't exist.
}
}
if (!$errors){
return $this->redirectToRoute('jobcard', array("jobcode" => $form_jc->getData()["JobCode"]));
}
}
return $this->render('document/jobcard.html.twig', array("formJobCode" => $form_jc->createView()));
}
$jcode = strtoupper($jobcode);
$job = $em->getRepository(Job::class)->findOneBy(array('complaint' => (int)$jcode, 'section' => substr($jcode, 7, 1)));
$act = $em->getRepository(ActivityLog::class)->createQueryBuilder('a')
->where('a.job = :jobid')
->setParameter('jobid', $job->getId())
->andWhere('a.activityDesc LIKE :activity')
->setParameter('activity', '%Jobcard Generated%')
->getQuery()
->getResult();
$complaint = $em->getRepository(Complaint::class)->findOneBy(array('id' => (int)$jcode));
$timestamp = (json_decode($complaint->getDatetimeIpJson()))->timestamp;
$refDate_timestamp = time();
$location = $complaint->getLocation();
if(count($act) == 0){
//if(!isset($jobcardformparams)){$jobcardformparams = null;}
$form_jobcard = $this->createFormBuilder() //$jobcardformparams, array('allow_extra_fields' => true))
->setAction($this->generateUrl('jobcard',array('jobcode'=> $jcode)))
->setMethod('POST')
->add('JobReference', TextType::class)
->add('Location', TextType::class, array(
'data' => $location
))
->add('JobDescription', TextareaType::class)
->add('submit', SubmitType::class, [
'label' => 'Submit'
])
->getForm();
$form_jobcard->handleRequest($request);
if ($form_jobcard->isSubmitted() && $form_jobcard->isValid()) {
$errors = 0;
if (!isset($form_jobcard->getData()["JobReference"]) || $form_jobcard->getData()["JobReference"] === ""){$errors++;$form->addError(new FormError("Invalid Job Reference No."));}
if (!isset($form_jobcard->getData()["Location"]) || $form_jobcard->getData()["Location"] === ""){$errors++;$form->addError(new FormError("Invalid Location."));}
if (!isset($form_jobcard->getData()["JobDescription"]) || $form_jobcard->getData()["JobDescription"] === ""){$errors++;$form->addError(new FormError("Invalid Job Description."));}
if (!$errors){
$jobcardParamArray = array(
"JobReference" => $form_jobcard->getData()["JobReference"],
"ReferenceDate" => date('d.m.Y', $refDate_timestamp),
"ComplaintDate" => date('d.m.Y', $timestamp),
"Location" => $form_jobcard->getData()["Location"],
"JobDescription" => $form_jobcard->getData()["JobDescription"]
);
$jobcardParamArray = json_encode(array("activity" => "Jobcard Generated", "document_data" => $jobcardParamArray));
$thejob = $em->getRepository(Job::class)->findOneBy(array('id' => $jcode));
$activityLogEntry = new ActivityLog();
$activityLogEntry->setJob($thejob);
$activityLogEntry->setTimestamp(new \DateTime());
$activityLogEntry->setUser($this->getUser());
$activityLogEntry->setActivityDesc($jobcardParamArray);
$em->clear();
$em->merge($activityLogEntry);
$em->flush();
return $this->redirect($this->generateUrl('jobcard',array('jobcode'=> $jcode)));
}
}
return $this->render('document/jobcard.html.twig', array("formJobCard" => $form_jobcard->createView(), "ComplaintDate" => date('d.m.Y', $timestamp)));
} else {
//var_dump(json_decode($act[0]->getActivityDesc(),true));exit();
$doc_html = $this->renderView('document/jobcard.html.twig', array('jobcardParam' => json_decode($act[0]->getActivityDesc(),true)));
return new PdfResponse(
$this->get('knp_snappy.pdf')->getOutputFromHtml($doc_html),
'jobid-'.$jobcode.'.pdf'
);
}
}
}
Always Provide the entity to createFormBuilder
$job = new Job();
$form = $this->createFormBuilder($job)
// You code
->getForm();
Check this : https://symfony.com/doc/current/forms.html#building-the-form

Symfony getData event subscriber is null

I know this question has been asked already a couple of times, but there hasn't been an answer that actually helped me solving my problem.
I've got three EventSubscribers for three Dropdowns who are dependent on each other.
So in my FormType I say:
public function buildForm(FormBuilderInterface $builder, array $options)
{
// solution showmethecode
$pathToAgencies = 'agencies';
//
$builder
->addEventSubscriber(new AddChannel1Subscriber($pathToAgencies))
->addEventSubscriber(new AddChannel3Subscriber($pathToAgencies))
->addEventSubscriber(new AddAgencySubscriber($pathToAgencies));
}
and one of my EventSubscribers looks like that:
...
...
public static function getSubscribedEvents() {
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::PRE_SUBMIT => 'preSubmit'
);
}
private function addChannel1Form($form, $channel1s = null) {
$formOptions = array(
'class' => 'AppBundle:Channel1',
'property' => 'name',
'label' => 'label.channel1s',
'empty_value' => 'label.select_channel1s',
'mapped' => false,
'expanded' => false,
'translation_domain' => 'UploadProfile',
'multiple' => true,
'required' => false,
'attr' => array(
'class' => 'channel1s'
),
);
if ($channel1s){
$formOptions['data'] = $channel1s;
}
$form->add('channel1s', 'entity', $formOptions);
}
public function preSetData(FormEvent $event) {
$data = $event->getData();
$form = $event->getForm();
if (null === $data) {
return;
}
$accessor = PropertyAccess::createPropertyAccessor();
$agency = $accessor->getValue($data, $this->pathToAgency);
$channel1s = ($agency) ? $agency->getChannel3s()->getChannel1s() : null;
$this->addChannel1Form($form, $channel1s);
}
public function preSubmit(FormEvent $event) {
$form = $event->getForm();
$this->addChannel1Form($form);
}
...
Now I'm getting the error "Attempted to call an undefined method named "getChannel3s" of class "Doctrine\Common\Collections\ArrayCollection"." and (I think) this is because my $data in my preSetData is NULL but I don't know why it's null. Am I looking at the wrong spot or where is my mistake here?
preSetData is executed before the original data (which shall be modified if given) is bound to the form ( which is then stored in $options['data']).
The "data" in preSetData is the one you provide to createForm($type, $data = null, array $options = array()).
So before this is set -> the form obviously doesn't have any data and the event-data isn't set either. That's why $data is null inside your listener's onPreSetData method.
You're using the wrong event. Use preSubmit and build your logic around the data submitted by the user ($event->getData()). This will solve your issue.
Quick overview:
onPreSubmit:
$form->get('someButton')->isClicked() returns false
$event->getForm()->getData() returns $options['data'] if any or $options['empty_data']
$event->getData returns the submitted data (array)
you can use setData()
you can add/remove fields
onSubmit:
You can't use setData() here as data was already bound to the form
$form->isSubmitted() still returns false
$form->get('someButton')->isClicked() returns true
You can still add/remove fields
onPostSubmit:
$form->isSubmitted() returns true
"You cannot remove children from a submitted form"
"You cannot add children to a submitted form"
$form->get('someButton')->isClicked() returns true
In the preSetData declaration you get the bad class. Try this :
public function preSetData(GenericEvent $event)
Add the next use :
use Symfony\Component\EventDispatcher\GenericEvent;

doctrine queryBuilder where IN collection

On my entity I have an array collection of users
/**
* #ORM\ManyToMany(targetEntity="\UserBundle\Entity\User", mappedBy="acr_groups")
*/
protected $users;
public function __construct() {
$this->users = new \Doctrine\Common\Collections\ArrayCollection();
}
In my FormType I want to filter out those groups wherein current user is a member:
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($user) { // 3. use the user variable in the querybilder
$qb = $er->createQueryBuilder('g');
$qb->where(':user IN (g.users)');
$qb->setParameters( array('user' => $user) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))
My problem is obviously on this line:
$qb->where(':user IN (g.users)');
How can I use my collection of users as the argument for the IN()?
Try below code
$user = array(12,211,612,84,63,23); // Assuming User Ids whose groups you want to retrive
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($user) {
$qb = $er->createQueryBuilder('g');
$qb->innerJoin('g.users', 'u'); // Inner Join with users
$qb->where('u.id IN (:user)');
$qb->setParameters( array('user' => $user) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))
I have tried it in symfony 2.3 with doctrine2. You can use select function with createQueryBuilder() to get specific columns.
$q = $this->createQueryBuilder('v')
->select('v')
->andWhere('v.workingHours IN (:workingHours)')
->setParameter('workingHours', $workingHours);
From : Doctrine 2 WHERE IN clause using a collection of entities
Or according to doctrine documentation : http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html#the-expr-class
To insert IN condition in query builder with doctrine you can use expr()
$qb->add('select', new Expr\Select(array('u')))
->add('from', new Expr\From('User', 'u'))
->add('where', $qb->expr()->orX(
$qb->expr()->eq('u.id', '?1'),
$qb->expr()->like('u.nickname', '?2')
))
->add('orderBy', new Expr\OrderBy('u.name', 'ASC'));
Syntaxe of IN :
$qb->expr()->in('u.id', array(1, 2, 3))
Also, Make sure that you do NOT use something similar to $qb->expr()->in('value', array('stringvalue')) as this will cause Doctrine to throw an Exception. Instead, use $qb->expr()->in('value', array('?1')) and bind your parameter to ?1
I ended up turning thing around a bit after unsuccessfully attempting some of your solutions. I manually created an array of the IDs I wanted.
There is probably a native way of doing this, seems like a pretty standard thing... this works however.
// 1. to inject user entity into this builder first make a construct function (remember to inject it from controller!)
function __construct($user)
{
$this->user = $user;
}
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$user = $this->user; // 2. instantiate the variable we created in our construct above
//create group list array
$groupList = $this->user->getACRGroups();
$gla = array();
foreach ($groupList as $g) {
$gla[] = $g->getId();
};
$builder
->add('acr_group', EntityType::class, array(
'label' => 'ATS',
'class' => 'HazardlogBundle:ACRGroup',
'query_builder' => function (EntityRepository $er) use ($gla) { // 3. use the user variable in the querybilder
$qb = $er->createQueryBuilder('g');
$qb->where('g.id IN (:gla)');
$qb->setParameters( array('gla' => $gla) );
$qb->orderBy('g.name', 'ASC');
return $qb;
},
'choice_label' => 'name'
))

Symfony FormEvent. Entities passed to the choice field must be managed

I try to use EventListener but i have error:
Entities passed to the choice field must be managed. Maybe persist them in the entity manager?
ExampleType.php
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) {
$form = $event->getForm();
// this would be your entity, i.e. SportMeetup
$data = $event->getData();
$category = $data->getCategory();
$tagi = null === $category ? array() : $category->getTags();
$form->add('tags', 'entity', array(
'class' => 'MyAppBundle:Tag',
'placeholder' => '',
'choices' => $tagi,
));
}
);
//Category.php (entity)
/**
* Get tags
*
* #return \Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}

How to use Repository custom functions in a FormType

The problem I'm facing is I have to create a selectbox in a form that holds all the parent entities (Category Entity). Now i managed to do this with:
$builder->add('parent', 'entity', array(
'class' => 'KprCentarZdravljaBundle:Category',
'query_builder' => function($repository) use ($param, $catID) {
return $repository->createQueryBuilder('p')
->where('p.id != :id AND p.parent = :parent')
->setParameters(array('id' => $param, 'parent' => $catID));},
'property' => 'name',
'required' => false,
'attr' => array('data-placeholder' => '--Izaberite Opciju--'),
));
As u can see i pass 2 arguments first is the current category.id(a category cant be its own parent) and a second which is a parent id, because i want all the children from that parent. This works nice but it doesn't give me the parents children's children.
I created a CategoryRepository with a recursive function that returns all the children:
<?php
namespace Kpr\CentarZdravljaBundle\Entity;
use Doctrine\ORM\EntityRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Kpr\CentarZdravljaBundle\Entity\Category;
class CategoryRepository extends EntityRepository
{
public function findByParenting($parent)
{
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->add('select', 'cat')
->add('from', 'KprCentarZdravljaBundle:Category cat')
->add('where', 'cat.parent = :parent')
->setParameter('parent', $parent);
// $qb instanceof QueryBuilder
$query = $qb->getQuery();
$results = $query->getResult();
foreach($results as $result){
if($result->getParent()){
$newResult = $this->findByParenting($result->getId());
$results = array_merge($results, $newResult);
}
}
return $results;
}
}
How can I use the findByParenting($parent) function in a entity field?
I posted the answer: Symfony2 choice field not working. Thanks redbirdo.
You are having this error because you have to return the QueryBuilder object on your findByParenting($parent) function
public function findByAllocation($alloc)
{
return $qb = $this->createQueryBuilder('r')
->select('r')
->where('r.showRangeStart < :alloc', 'r.showRangeStop >= :alloc')
->setParameter('alloc', $alloc)
;
}

Resources