Symfony form element ID - symfony

I've inherited some code which uses Symfony (v3.3) to generate forms. Some elements are being created with no space between the element type and the auto-generated ID. This means the element doesn't display:
<selectid="someID">
...
</selectid="someID">
This is happening on select elements and textarea elements.
I'm not familiar with Symfony so don't know how to troubleshoot this... any help is much appreciated!
Edit: added code as requested. The problem is I don't know where the issue lies and there are a lot of classes.
Twig template
<form action="" method="post" name="callback" id="request-callback" class="contact-form">
<input type="hidden" name="form-type" value="callback">
{#<input type="hidden" name="mc4wp-subscribe" value="1">#}
<div{% if form.name.vars.errors | length > 0 %} class="form-error"{% endif %}>
{{ form_label(form.name) }} {{ form_errors(form.name) }}
{{ form_widget(form.name) }}
</div>
<div{% if form.phone_number.vars.errors | length > 0 %} class="form-error"{% endif %}>
{{ form_label(form.phone_number) }} {{ form_errors(form.phone_number) }}
{{ form_widget(form.phone_number) }}
</div>
<div{% if form.email.vars.errors | length > 0 %} class="form-error"{% endif %}>
{{ form_label(form.email) }} {{ form_errors(form.email) }}
{{ form_widget(form.email) }}
</div>
<div{% if form.treatment.vars.errors | length > 0 %} class="form-error"{% endif %}>
{{ form_label(form.treatment) }} {{ form_errors(form.treatment) }}
{{ form_widget(form.treatment) }}
</div>
<div class="text-center">
<button class="button bg-darkblue" type="submit" id="contact_send" name="contact[send]">Send My Request</button>
</div>
</form>
Form class
<?php
namespace REDACTED;
use DrewM\MailChimp\MailChimp;
use GuzzleHttp\Exception\ConnectException;
use Symfony\Component\Form\Forms;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Translation\Translator;
use Symfony\Bridge\Twig\Extension\FormExtension;
use Symfony\Bridge\Twig\Extension\TranslationExtension;
use Symfony\Bridge\Twig\Form\TwigRendererEngine;
use Symfony\Bridge\Twig\Form\TwigRenderer;
use Symfony\Component\Form\Extension\Validator\ValidatorExtension;
use Symfony\Component\Validator\Validation;
use GuzzleHttp\Client;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\FormFactoryInterface;
abstract class Form
{
/**
* Recaptcha endpoint
*/
const RECAPTCHA_VERIFY = 'https://www.google.com/recaptcha/api/siteverify';
/**
* Default from name
*/
const EMAIL_FROMNAME = '';
/**
* #var \Twig_Environment
*/
protected $twig;
/**
* #var \Symfony\Component\Form\FormInterface
*/
protected $form;
/**
* #var \Symfony\Component\HttpFoundation\Request
*/
private $request;
/**
* Capture failed
*
* #var bool
*/
protected $captchaFailed = false;
/**
* #var string
*/
protected $template;
/**
* #var string
*/
protected $messageTemplate;
/**
* #var string
*/
protected $subject;
/**
* #var string
*/
protected $emailTo;
/**
* #var string
*/
protected $emailFromName;
/**
* #var array
*/
protected $params = [];
protected $mailchimpList;
private $mailchimpApiKey = '6542760048f1c73d69df8f552d4a2b87-us18';
public $mailerError;
public $redirectTo;
/**
* SunstoneForm constructor
*
* #param Request $request
* #param $emailTo
* #param $emailFromName
* #param array $params
*/
private function __construct(
Request $request = null,
$emailTo = null,
$emailFromName = null,
array $params = []
) {
$this->request = $request;
$this->emailTo = $emailTo;
$this->emailFromName = $emailFromName;
$this->params = $params;
}
/**
* Make the contact form
*
* #param Request $request
* #param string $emailTo
* #param string $emailFromName
* #param array $params
* #return static
*/
public static function make(
Request $request = null,
$emailTo = null,
$emailFromName = self::EMAIL_FROMNAME,
array $params = []
) {
return (new static($request, $emailTo, $emailFromName, $params))
->twig()
->form();
}
/**
* Render the form
*
* #return string
*/
public function renderForm()
{
return $this->twig->render($this->template, [
'form' => $this->form->createView(),
'captchaFailed' => $this->captchaFailed,
]);
}
/**
* Handle a form submission and check form is valid
*
* #return bool
*/
public function handleRequest()
{
$this->form->handleRequest($this->request);
if ($this->form->isSubmitted() && $this->form->isValid()) {
// send the message
return $this->process();
}
return false;
}
/**
* Instantiate Twig
*
* #return $this
*/
protected function twig()
{
// instantiate twig
$translator = new Translator('en');
$loader = new \Twig_Loader_Filesystem([
TWIG_TEMPLATE_DIR,
ABSPATH.'vendor/symfony/twig-bridge/Resources/views/Form',
]);
$twig = new \Twig_Environment($loader, [
'debug' => WP_DEBUG,
]);
$twig->addExtension(new FormExtension());
$twig->addExtension(new TranslationExtension($translator));
if (WP_DEBUG) {
$twig->addExtension(new \Twig_Extension_Debug);
}
// get form engine
$formEngine = new TwigRendererEngine(['form_div_layout.html.twig'], $twig);
$twig->addRuntimeLoader(new \Twig_FactoryRuntimeLoader([
TwigRenderer::class => function() use ($formEngine) {
return new TwigRenderer($formEngine);
},
]));
$this->twig = $twig;
return $this;
}
public function getForm()
{
return $this->form;
}
public function getSubmissionComplete()
{
return sprintf('<div class="form-sent">%s</div>',
get_field('form_submitted_content', 'options')
);
}
/**
* Generate the form
*
* #return $this
*/
protected function form()
{
$this->form = $this->formFields(
Forms::createFormFactoryBuilder()
->addExtension(new HttpFoundationExtension)
->addExtension(new ValidatorExtension(Validation::createValidator()))
->getFormFactory()
)
->getForm();
return $this;
}
/**
* #param array $additionalData
* #return bool
*/
protected function process(array $additionalData = [])
{
$data = $this->form->getData();
$mailer = new \PHPMailer(true);
$mailer->addAddress($this->emailTo);
if (WP_DEBUG && defined('DEBUG_BCC')) {
$mailer->addBCC(DEBUG_BCC);
}
$mailer->From = $this->emailTo;
$mailer->FromName = 'drpuneetgupta.co.uk';
$mailer->Subject = $this->subject;
$mailer->Body = $this->twig->render($this->messageTemplate, [
'data' => $data + $additionalData,
]);
$mailer->isHTML(true);
if ($this->mailchimpList) {
try {
$mailchimp = new MailChimp($this->mailchimpApiKey);
$mailchimp->post("lists/{$this->mailchimpList}/members", [
'email_address' => $data['email'],
'status' => 'subscribed',
]);
} catch (\Exception $e) {}
}
try {
return $mailer->send();
} catch (\phpmailerException $e) {
$this->mailerError = $e->getMessage();
}
return false;
}
/**
* Define form fields
*
* #param FormFactoryInterface $formFactory
* #return mixed
*/
abstract protected function formFields(FormFactoryInterface $formFactory);
}
RequestCallback extends Form class
<?php
namespace REDACTED;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Validator\Constraints\NotBlank;
use Symfony\Component\Validator\Constraints\Email;
use DrewM\MailChimp\MailChimp;
class RequestCallback extends Form
{
protected $template = 'request-callback.twig';
protected $messageTemplate = 'email-callback.twig';
protected $mailchimpList = 'REDACTED';
protected $subject = 'Callback request';
/**
* #param FormFactoryInterface $formFactory
* #return FormBuilderInterface
*/
protected function formFields(FormFactoryInterface $formFactory)
{
return $formFactory->createNamedBuilder('request_callback', FormType::class, null, [
'allow_extra_fields' => true,
])
->add('mc4wp-subscribe', HiddenType::class, [
'data' => 1,
])
->add('name', TextType::class, [
'required' => true,
'label' => 'Your Name',
'attr' => [
'placeholder' => 'Your Name',
],
'label_attr' => [
'class' => 'sr-only',
],
'constraints' => [
new NotBlank(['message' => 'Please enter your name']),
],
])
->add('phone_number', TextType::class, [
'required' => true,
'label' => 'Phone Number',
'attr' => [
'placeholder' => 'Phone Number',
],
'label_attr' => [
'class' => 'sr-only',
],
'constraints' => [
new NotBlank(['message' => 'Please enter your phone number']),
],
])
->add('email', EmailType::class, [
'required' => true,
'label' => 'Your email address',
'attr' => [
'placeholder' => 'Email address',
],
'label_attr' => [
'class' => 'sr-only',
],
'constraints' => [
new NotBlank(['message' => 'Please enter your email address']),
new Email(['message' => 'Please enter a valid email address']),
],
])
->add('treatment', ChoiceType::class, [
'required' => true,
'label' => 'Which treatment would you like to discuss?',
'label_attr' => [
'class' => 'sr-only',
],
'constraints' => [
new NotBlank(['message' => 'Please select a treatment']),
],
'choices' => [
'Which treatment would you like to discuss?' => '',
'Liposuction' => 'Liposuction',
'Lipoedema' => 'Lipoedema',
'Breast reduction' => 'Breast reduction',
'Male chest reduction' => 'Male chest reduction',
],
]);
}
}

I thought I'll create an answer for this as finding the right answer in a comment is not straightforward.
As #DarkBee mentions in one of the question comments the fix on the question PHP 7.4 trimming whitespace between string variables solves this issue.
There is a fix in Twig that prevents the whitespace from being trimmed so updating to a recent Twig version fixes the issue:
composer require "twig/twig:^2.0"

Related

How can I add Commerce Wishlist share mail template variables from form?

I am trying to add 2 additional form fields to the Wishlist Share form where the user input will be rendered in the email. I have been able to add the fields to the form, but I am not sure how to add the user's input in the email twig template.
Here is how I have updated the form() function:
public function form(array $form, FormStateInterface $form_state) {
$form['#tree'] = TRUE;
$form['#attached']['library'][] = 'core/drupal.dialog.ajax';
// Workaround for core bug #2897377.
$form['#id'] = Html::getId($form_state->getBuildInfo()['form_id']);
$form['to'] = [
'#type' => 'email',
'#title' => $this->t('Recipient Email'),
'#required' => TRUE,
];
// COMBAK my edit
$form['sender_name'] = [
'#type' => 'textfield',
'#title' => $this->t('Your Name'),
'#required' => FALSE,
];
$form['sender_message'] = [
'#type' => 'textarea',
'#title' => $this->t('Your Message'),
'#required' => FALSE,
];
// COMBAK eo my edit
return $form;
}
/**
* {#inheritdoc}
*/
protected function actions(array $form, FormStateInterface $form_state) {
$actions['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Send email'),
'#submit' => ['::submitForm'],
];
if ($this->isAjax()) {
$actions['submit']['#ajax']['callback'] = '::ajaxSubmit';
}
return $actions;
}
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
/** #var \Drupal\commerce_wishlist\Entity\WishlistInterface $wishlist */
$wishlist = $this->entity;
$to = $form_state->getValue('to');
// COMBAK: my added vars
$sender_name = $form_state->getValue('sender_name');
$sender_message = $form_state->getValue('sender_message');
$this->wishlistShareMail->send($wishlist, $to, $sender_name, $sender_message);
$this->messenger()->addStatus($this->t('Shared the wishlist to #recipient.', [
'#recipient' => $to,
]));
$form_state->setRedirectUrl($wishlist->toUrl('user-form'));
}
This is the function that calls the mailHandler that I have updated:
public function send(WishlistInterface $wishlist, $to, $sender_name, $sender_message) {
$owner = $wishlist->getOwner();
$subject = $this->t('Check out my #site-name wishlist', [
'#site-name' => $this->configFactory->get('system.site')->get('name'),
]);
$body = [
'#theme' => 'commerce_wishlist_share_mail',
'#wishlist_entity' => $wishlist,
// COMBAK: my added vars
'#sender_name' => $sender_name,
'#sender_message' => $sender_message,
];
$params = [
'id' => 'wishlist_share',
'from' => $owner->getEmail(),
'wishlist' => $wishlist,
];
return $this->mailHandler->sendMail($to, $subject, $body, $params);
}
And this is the preprocees function provided by the commerce wishlist module:
function template_preprocess_commerce_wishlist_share_mail(array &$variables) {
/** #var Drupal\commerce_wishlist\Entity\WishlistInterface $wishlist */
$wishlist = $variables['wishlist_entity'];
$wishlist_url = $wishlist->toUrl('canonical', ['absolute' => TRUE]);
$variables['wishlist_url'] = $wishlist_url->toString();
// COMBAK: my added vars
//$sender_name = $variables['sender_name'];
}
And finally the twig template for the email itself:
{#
/**
* #file
* Template for the wishlist share email.
*
* Available variables:
* - wishlist_entity: The wishlist entity.
* - wishlist_url: The wishlist url.
*
* #ingroup themeable
*/
#}
<p>
{% trans %}Check out my wishlist!{% endtrans %}
</p>
<p>
{% trans %}I use my wishlist for keeping track of items I am interested in.{% endtrans %} <br>
{% trans %}To see the list in the store and buy items from it, click here.{% endtrans %}
</p>
<p>
{% trans %}Thanks for having a look!{% endtrans %}
</p>
I haven't been able to figure out how to access the variables I added to the body[] array in the twig template.
Any help would be greatly appreciated.
Thanks!

Document with EmbedMany as a Form (Symfony 3)

I have a document
/**
* #ODM\Document
*/
class Result
{
/**
* #var int $id
* #ODM\Id
*/
protected $id;
/**
* #var string $name
* #ODM\Field(type="string")
*/
protected $name;
/**
* #var UserComment[] $userComments
* #ODM\EmbedMany(targetDocument="UserComment")
*/
protected $userComments;
}
/** #ODM\EmbeddedDocument() */
class UserComment {
public $addedBy;
public $createdAt;
public $comment;
}
I want to create a form which allows me to add new user comments. But it would only have the $comment as TextAreaType. The other 2 fields should be added automatically.
I have used the collectiontype like this:
class ResultForm extends AbstractType
{
/**
* buildForm
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('userComments', CollectionType::class, [
'entry_type' => UserCommentType::class,
'required' => false,
'allow_add' => true,
'label' => false,
'delete_empty' => true,
'prototype' => true,
'entry_options' => [
'attr' => [
'class' => 'user-comment-widget'
],
'label' => false,
]
]);
$builder->add('submit', SubmitType::class);
}
And added also a UserCommentType:
class UserCommentType extends AbstractType
{
/**
* #var TokenStorage
*/
private $tokenStorage;
public function __construct(TokenStorage $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
/**
* buildForm
* #param FormBuilderInterface $builder
* #param array $options
* #throws \Exception
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$now = new \DateTime('now');
$builder->add('comment', TextareaType::class, [
'label' => false,
])
->add('addedBy', HiddenType::class, [
'data' => $this->tokenStorage->getToken()->getUser()->getUsername(),
])
->add('createdAt', HiddenType::class, [
'data' => $now->format('Y-m-d H:i:s')
]);
}
After having a bunch of exception/errors etc I figured there must be an easier way to do this. This does not work as the UserCommentType has no access to the original data for some reason even though I use this to initialize the ResultForm:
$form = $this->createForm(ResultForm::class, $result);
To access the values inside a collection form, you have to use listeners. By adding an PRE_SET_DATA listener to the form, you could change those values.
Documentation
$builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
Now the function called by the listener. This will iterate through each element in the collection, so you can add your code there.
public function onPreSetData(FormEvent $event) {
$entity = $event->getData();
$form = $event->getForm();
if ($entity) {
$form->add('comment', TextareaType::class, [
'label' => false,
])
->add('addedBy', HiddenType::class, [
'data' => $this->tokenStorage->getToken()->getUser()->getUsername(),
])
->add('createdAt', HiddenType::class, [
'data' => $now->format('Y-m-d H:i:s')
]);
}
}
Make sure to add the if clause, if required, as the first iteration will be to create the collection form prototype and you might don't want it to have presetted values.

Symfony 4.2 Form is not submitted or not valid

I followed the symfony 4.2 documentation, but it seems the form is not submitted...
I spent my whole sunday, but it seems a secret how does it works, in the logs I do not see any errors.
So start it. the config contains these settings:
framework:
validation:
email_validation_mode: 'html5'
enable_annotations: true
Here the entity:
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="App\Repository\FeedbackRepository")
*/
class Feedback extends BaseEntity
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255)
* #Assert\Type("string")
* #Assert\NotBlank
*/
private $name;
/**
* #ORM\Column(type="string", length=255)
* #Assert\Type("string")
* #Assert\Email()
* #Assert\NotBlank
*/
private $email;
AS you can see I use the Assert annotations for the validations.
So here the formtype:
class FeedbackType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name', TextType::class)
->add('email', EmailType::class)
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Feedback::class,
// enable/disable CSRF protection for this form
'csrf_protection' => true,
// the name of the hidden HTML field that stores the token
'csrf_field_name' => '_token',
]);
}
}
Maybe the problem with the token, but I do not know exactly.
Now let see the view:
<form action="{{ path('feedback') }}" type="POST">
<div class="input-field">
<i class="material-icons prefix">account_circle</i>
{{ form_label(feedback.name) }}
{{ form_widget(feedback.name) }}
</div>
<div class="input-field">
<i class="material-icons prefix">email</i>
{{ form_label(feedback.email) }}
{{ form_widget(feedback.email) }}
</div>
{{ form_widget(feedback._token) }}
Next, here the controller which get the request.
/**
* #Route("/feedback", name="feedback", methods="GET|POST")
*/
public function feedbackFormAction(Request $request, EntityManagerInterface $entityManager): JsonResponse
{
$feedbackForm = new Feedback();
$form = $this->createForm(FeedbackType::class, $feedbackForm);
$form->handleRequest($request);
dump($request);
dump($feedbackForm);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($feedbackForm);
$entityManager->flush();
} else {
$errors = $this->getErrorsFromForm($form);
dump($form);die;
return new JsonResponse(['data' => ['result' => 'failed', 'errors' => $errors]]);
}
return new JsonResponse(['data' => ['result' => 'success']]);
}
The errors give me an empty array in Json format.
If I check the dump($feedbackForm) I see that the submitted property is false. and the modeldata, viewdata and normdata values are null... But how is this possible?
Dumping request:
query: ParameterBag {#16 ▼
#parameters: array:1 [▼
"feedback" => array:11 [▼
"name" => "a"
"email" => "a#a.a"
"_token" => "NJHBv7NpwYlugFcU-sE0qoBEQkS38yhxOjbklkHu8j0"
]
]
}
I think, this is correct.
You have not loaded the form data into the entity and trying to persist an empty new Feedback.
if ($form->isSubmitted() && $form->isValid()) {
// add line below
$feedbackForm = $form->getData();
$entityManager->persist($feedbackForm);
$entityManager->flush();
} else { ...
Read carefully https://symfony.com/doc/current/forms.html#handling-form-submissions
Did you create your FeedbackType, Controller action and the form view manually?
Remove all and use
php bin/console make:crud Feedback
This will generate operational files :-)
I think that using form_row is apropriate that using form_widget
Your Controller
/**
* #Route("/feedback", name="feedback", methods="GET|POST")
*/
public function feedbackFormAction(Request $request, EntityManagerInterface $entityManager): JsonResponse
{
$feedback = new Feedback();
$form = $this->createForm(FeedbackType::class, $feedback);
$form->handleRequest($request);
if ($form->isSubmitted()) {
if ($form->isValid()) {
$entityManager->persist($feedbackForm);
$entityManager->flush();
return new JsonResponse(['data' => ['result' => 'success']]);
}
else {
$errors = $this->getErrorsFromForm($form);
return new JsonResponse(['data' => ['result' => 'failed', 'errors' => $errors]]);
}
}
return $this->render('path_to_your_feed_back.html.twig', [
'feedback' => $feedback,
'form' => $form->createView(),
]);
}
Your form.html.twig
{{ form_start(form, {'method': 'POST', 'attr' : {'class' : 'formFeedback'}}) }}
<div class="input-field">
<i class="material-icons prefix">account_circle</i>
{{ form_row(form.name) }}
</div>
<div class="input-field">
<i class="material-icons prefix">email</i>
{{ form_row(form.email) }}
</div>
{{ form_end(form) }}

The controller must return a response (1 given). 500 Internal Server Error - LogicException

I started out with the following controller code
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\events;
//use AppBundle\Entity\eventtype;
use AppBundle\Entity\users;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class DefaultController extends Controller {
/**
* #Route("/home", name="homepage")
*/
public function indexAction(Request $request) {
$events = new events();
$form = $this->createFormBuilder($events)
->add('eT1', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'Choose After Breakfast Event',
))
->add('eT2', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'Choose After Snacks Event',
))
->add('save', SubmitType::class, array('label' => 'Submit'))
->getForm();
if ($request->isMethod('POST')) {
$form->submit($request);
$formData = $form->getData();
//check to see if the after breakfast event (eT1) is full (ie.has reached the maxlimit)
//for now the maxLimit is set to 4
$ET1 = $formData->getET1();
$repository = $this->getDoctrine()->getRepository('AppBundle:events');
$query1 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT1 = :eT1')
->setParameter('eT1', $ET1)
->getQuery();
$a = $query1->getSingleScalarResult();
if ($a >= $maxLimit=4) {
echo 'choose another event, this one is full';
}
else{
return $ET1;
}
//check to see if the after snacks event (eT2) is full (ie.has reached the maxlimit)
//for now the maxLimit is set to 4
$ET2 = $formData->getET2();
$query2 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT2 = :eT2')
->setParameter('eT2', $ET2)
->getQuery();
$b = $query2->getSingleScalarResult();
if ($b >= $maxLimit=4) {
echo 'choose another event, this one is full';
}
else{
return $ET2;
}
//get the user_id of the logged in user
$user = $this->container->get('security.context')->getToken()->getUser();
$events->setUser($user);
$x = $events->getUser()->getID();
if ($form->isValid()) {
//check if the user has already registered or not
$y = $repository->findOneBy(array('user' => $x));
// If the user is registering for the first time (execute the insert query)
if (!$y) {
$em = $this->getDoctrine()->getManager();
$em->persist($events);
$em->flush();
return $this->redirectToRoute('homepage');
}
//If the user has registered already and want change his registered events (execute the update query)
else {
$formData = $form->getData();
$ET1 = $formData->getET1();
$ET2 = $formData->getET2();
$events->setET1($ET1);
$events->setET2($ET2);
$em = $this->getDoctrine()->getManager();
$em->persist($events);
$em->flush();
return $this->redirectToRoute('homepage');
}
}
}
return $this->render('default/index.html.twig', array(
'form' => $form->createView(),
));
}
}
But then i modified the code to display different pages for a user who has already registered and who is registering for the for the first time.
The following code gives the error - The controller must return a response (1 given). 500 Internal Server Error - LogicException
My guess is that the error lies in statements return $ET1; and return $ET2;
Here are the questions I cannot find answers to:
1. Why the error - The controller must return a response (1 given). 500 Internal Server Error - LogicException
2. I have to repeat the same code twice, for new user and already registered user. Is there a work around?
3. How do i set the different $maxLimit parameter values for different events(eg. for poker=12, chess=4, cricket=10 etc.) from inside the config.yml file?
Here is the modified controller code
<?php
namespace AppBundle\Controller;
use AppBundle\Entity\events;
//use AppBundle\Entity\eventtype;
use AppBundle\Entity\users;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
class DefaultController extends Controller {
/**
* #Route("/home", name="homepage")
*/
public function indexAction(Request $request) {
$events = new events();
// check if the user is registering for the first time or else
$user = $this->container->get('security.context')->getToken()->getUser();
$events->setUser($user);
$x = $events->getUser()->getID();
$repository = $this->getDoctrine()->getRepository('AppBundle:events');
$y = $repository->findOneBy(array('user' => $x));
// If the user is registering for the first time (execute the insert query)
if (!$y) {
echo 'Welcome to the Birthday party';
echo 'You can choose from the below options:';
$form = $this->createFormBuilder($events)
->add('eT1', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'After Breakfast Event',
))
->add('eT2', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'After Snacks Event',
))
->add('save', SubmitType::class, array('label' => 'Submit'))
->getForm();
if ($request->isMethod('POST')) {
$form->submit($request);
$formData = $form->getData();
//check to see if the after breakfast event (eT1) is full (ie.has reached the maxlimit)
$ET1 = $formData->getET1();
$repository = $this->getDoctrine()->getRepository('AppBundle:events');
$query1 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT1 = :eT1')
->setParameter('eT1', $ET1)
->getQuery();
$a = $query1->getSingleScalarResult();
// var_dump($a);
// exit;
if ($a >= $maxLimit = 4) {
echo 'choose another event, this one is full';
} else {
return $ET1;
}
//check to see if the after snacks event (eT2) is full (ie.has reached the maxlimit)
$ET2 = $formData->getET2();
$query2 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT2 = :eT2')
->setParameter('eT2', $ET2)
->getQuery();
$b = $query2->getSingleScalarResult();
// var_dump($a);
// exit;
if ($b >= $maxLimit = 4) {
echo 'choose another event, this one is full';
} else {
return $ET2;
}
if ($form->isValid()) {
$em = $this->getDoctrine()->getManager();
// tells Doctrine you want to (eventually) save the Product (no queries yet)
$em->persist($events);
// actually executes the queries (i.e. the INSERT query)
$em->flush();
return $this->redirectToRoute('homepage');
}
}
}
// If the user has registered already and want change his registered events (execute the update query)
else {
echo 'Welcome back';
echo 'You registered for:';
echo 'After Breakfast event:.$eventName.';
echo 'After Snacks event:.$eventName.';
echo 'You can change your selection by choosing again from below options:';
$form = $this->createFormBuilder($events)
->add('eT1', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'After Breakfast Event',
))
->add('eT2', ChoiceType::class, array(
'choices' => array(
'Poker' => 1,
'Chess' => 2,
'Cricket' => 3,
'Marbles' => 4,
'Football' => 5,
),
'choices_as_values' => true,
'expanded' => true,
'multiple' => false,
'label' => 'After Snacks Event',
))
->add('save', SubmitType::class, array('label' => 'Submit'))
->getForm();
if ($request->isMethod('POST')) {
$form->submit($request);
$formData = $form->getData();
//check to see if the after Breakfast event (eT1) is full (ie.has reached the maxlimit)
$ET1 = $formData->getET1();
$repository = $this->getDoctrine()->getRepository('AppBundle:events');
$query1 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT1 = :eT1')
->setParameter('eT1', $ET1)
->getQuery();
$a = $query1->getSingleScalarResult();
// var_dump($a);
// exit;
if ($a >= $maxLimit = 4) {
echo 'choose another event, this one is full';
} else {
return $ET1;
}
//check to see if the after snacks event (eT2) is full (ie.has reached the maxlimit)
$ET2 = $formData->getET2();
$query2 = $repository->createQueryBuilder('p')
->select('count(p)')
->where('p.eT2 = :eT2')
->setParameter('eT2', $ET2)
->getQuery();
$b = $query2->getSingleScalarResult();
// var_dump($a);
// exit;
if ($b >= $maxLimit = 4) {
echo 'choose another event, this one is full';
} else {
return $ET2;
if ($form->isValid()) {
$formData = $form->getData();
$ET1 = $formData->getET1();
$ET2 = $formData->getET2();
$events->setET1($ET1);
$events->setET2($ET2);
$em = $this->getDoctrine()->getManager();
$em->persist($events);
$em->flush();
return $this->redirectToRoute('homepage');
}
}
}
}
return $this->render('default/index.html.twig', array(
'form' => $form->createView(),
));
}
}
Below is the users entity
<?php
namespace AppBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* users
*
* #ORM\Table(name="users")
* #ORM\Entity(repositoryClass="AppBundle\Repository\usersRepository")
*/
class users extends BaseUser
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* Get id
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* #ORM\OneToOne(targetEntity="events", mappedBy="user")
*/
protected $event;
// public function __construct()
// {
// parent::__construct();
// }
}
Below is the events entity
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
//use Symfony\Component\Validator\Constraints as Assert;
/**
* events
*
* #ORM\Table(name="events")
* #ORM\Entity(repositoryClass="AppBundle\Repository\eventsRepository")
*/
class events {
/**
* #var int
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #var int
*
* #ORM\Column(name="ET1", type="integer")
*/
protected $eT1;
/**
* #var int
*
* #ORM\Column(name="ET2", type="integer")
*/
protected $eT2;
/**
* #ORM\OneToOne(targetEntity="users", inversedBy="event")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
protected $user;
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/**
* Set user
*
* #param users $user
* #return events
*/
public function setUser($user) {
$this->user = $user;
return $this;
}
/**
* Get user
*
* #return events
*/
public function getUser() {
return $this->user;
}
/**
* Set eT1
*
* #param integer $eT1
* #return events
*/
public function setET1($eT1) {
$this->eT1 = $eT1;
return $this;
}
/**
* Get eT1
*
* #return integer
*/
public function getET1() {
return $this->eT1;
}
/**
* Set eT2
*
* #param integer $eT2
* #return events
*/
public function setET2($eT2) {
$this->eT2 = $eT2;
return $this;
}
/**
* Get eT2
*
* #return integer
*/
public function getET2() {
return $this->eT2;
}
// public function __construct()
// {
// parent::__construct();
// }
}
to answer your first question: the Controller must always return an object of type Response, if it cannot return a response object then it must forward the control by doing an internal sub-request to a controller that does return a response object. in symfony, twig templates are used to display the outputs to the user, using
return $this->render('navigation/homepage.html.twig', array('et1' => $et1, 'et2' => $et2));
would make the two variables et1 and et2 to be available in the homepage.html.twig template.
to answer the second question: you get the current logged in user by doing $this->getUser() which is a short cut for security.context token link
i'm making a guess here, you are trying to check if the userId matches with a userId of an 'events' object, you consider it to be a returning user, and if it does't then its a new user, on that premise
$user = $this->getUser()->getUserId();
$event = $this->getDoctrine()->getRepository('AppBundle:events')->findOneBy(array('user' => $this->getUser()->getUserId() ));
if($event instanceof events){ //events refers to 'events' entity
// code to handle returning user
}
answer to the third question: avoid using Global variables, by using config.yml to store maxValue, you are making maxValue available for the entire project. you could change things up in your code to not use golbal variables, but just for the sake of answering a question
store your global variables inside parameters.yml instead of config.yml just to keep things understandable, do something like this link
parameters:
cricketMaxLimit: 10
retrieve it with $cricMaxLimit = $this->container->getParameter('cricketMaxLimit');
sidenote: stuff like if ($a >= $maxLimit = 4) should not even exist in this world, do if ($a >= 4)
-dheeraj :)
Elements like $ET2 and $ET2 are no responses but are formData arrays, Symfony expects you to return a response so make sure to render it into a view, or maybe return it as data via a new JsonResponse object?
You could also add the #Template annotation in order to be able to return data. That annotation tells Symfony that a template should be rendered based on the returned data.
your controller is unreadable... You may want to put your business code in services, formTypes in dedicated classes and queries into repository classes.
I think you don't go inside the if(!$y ) condition
Put at the end:
return $this->render('default/index.html.twig'));

Symfony2 simple many-to-many issue

I have a simple question,
I have two tables in relation many to many, Post and Category,
in an intact form PostType a collection of form CategoryType, but here the problems begin ..
I followed the instructions on the cookbook collection form to persist the data, I just do not get the desired result ..
Here's the code:
class Post
{
/**
*
* #ORM\ManyToMany(targetEntity="Categories", inversedBy="posts", cascade={"persist", "remove"})
* #ORM\JoinTable(name="AnCat",
* joinColumns={
* #ORM\JoinColumn(name="post_id", referencedColumnName="id")
* },
* inverseJoinColumns={
* #ORM\JoinColumn(name="categories_id", referencedColumnName="id")
* }
* )
**/
protected $categories;
public function __construct()
{
$this->categories = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addCategory($categories)
{
foreach ($categories as $category) {
$category->addPosts($this);
}
$this->categories[] = $categories;
}
class Categories
{
/**
*
* #ORM\ManyToMany(targetEntity="Post", mappedBy="categories")
*/
protected $posts;
public function __construct()
{
$this->posts = new ArrayCollection();
}
/**
*
* #param Post $post
* #return Categories
*/
public function addPosts(Post $posts)
{
// I tried it but I get the same result!
/*if (!$this->posts->contains($posts)) {
$this->posts->add($posts);
}*/
$posts->addCategory($this);
$this->posts[] = $posts;
}
class PostType extends AbstractType
{
->add('Categories', 'collection', array('type' => new CategoriesType(),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
'prototype_name' => '__categ__',
'by_reference' => false
))
class CategoriesType extends AbstractType
{
->add('category', 'entity', array(
'attr' => array('class' => 'cat'),
'class' => 'MyBusinessBundle:Categories',
'property' => 'category',
'label' => 'Categories'
))
The problem is that inserts a new field Category, instead of creating a simple relationship Post-Category.
I don't understand where I'm wrong ..
In your postType, change collection type into entity Type
class PostType extends AbstractType
{
$builder->add('Categories', 'entity',
array( 'label' => 'Categories',
'required' => false,
'expanded' => true,
'class' => 'xxx\xxxBundle\Entity\Categories',
'property' => 'title',
'multiple' => true,
));
In your post creation form you will have checkboxes with categories. If you want a multi select field, change expanded by false

Resources