Im new to Symfony2 / twig and I need to render a form with pre-saved values.
I can get the form to load / save data but I cant get it to show the pre-saved values. What am I missing?
Here is my controller code:
/**
* #Route("/build/{id}")
* #Template()
*/
public function buildAction($id)
{
$request = $this->getRequest();
$em = $this->getDoctrine()->getEntityManager();
$repo = $em->getRepository('ImagineNewsletterBundle:Section');
$section = $repo->find($id);
$newsletter = $section->getNewsletter();
$globalMag = $newsletter->getMagazine();
//$globalMag = unserialize($globalMag[0]);
$builder = $this->get('newsletter.builders')->getBuilder($section->getBuilder());
$builder->setSearchUrl($this->generateUrl('imagine_newsletter_section_search', array('builder' => $section->getBuilder())));
$form = $this->createForm($builder->getSearchFormBuilder());
$prevArticles = $section->getArticles();
// $form->bind($prevArticles);
return $this->render('ImagineNewsletterBundle:Section:'.$builder->getTemplate() , array('prevArticles' => $prevArticles,'builder' => $builder, 'form' => $form->createView(), 'section' => $section, 'newsletter' => $newsletter, 'globalmag' => $globalMag));
}
Here my twig/form code:
<form id="advert" action="{{ addAticle }}" method="post" {{ form_enctype(form) }} class="form-stacked">
<div class="form_element">
{{ form_label(form.topLogoAdvert) }}
{{ form_errors(form.topLogoAdvert) }}
{{ form_widget(form.topLogoAdvert) }}
</div>
<div class="form_element">
{{ form_label(form.topLogoAlt) }}
{{ form_errors(form.topLogoAlt) }}
{{ form_widget(form.topLogoAlt) }}
</div>
<div class="form_element">
{{ form_label(form.topLogoLink) }}
{{ form_errors(form.topLogoLink) }}
{{ form_widget(form.topLogoLink) }}
</div>
<input type="hidden" name="section" value="{{ section.id }}" />
{{ form_widget(form) }}
<div class="well">
<button type="submit" class="btn primary">Save</button>
<button id="reset" type="reset" class="btn">Reset</button>
</div>
{{ form_rest(form) }}
</form>
You need to bind the form to the request, that will then populate the form with the data the user entered. I have updated your code.
$request = $this->getRequest();
$em = $this->getDoctrine()->getEntityManager();
$repo = $em->getRepository('ImagineNewsletterBundle:Section');
$section = $repo->find($id);
$builder = $this->get('newsletter.builders')->getBuilder($section->getBuilder());
$builder->setSearchUrl($this->generateUrl('imagine_newsletter_section_search', array('builder' => $section->getBuilder())));
$form = $this->createForm($builder->getSearchFormBuilder(), $section);
if ($request->isMethod('post')) {
$form->bind($request);
}
return $this->render('ImagineNewsletterBundle:Section:'.$builder->getTemplate() , array('builder' => $builder, 'form' => $form->createView(), 'section' => $section));
You also need to ensure that when the form is submitted by the user that it goes to the action that output the form. I suspect now that you have a separate action to process the form.
Create a custom FormType http://symfony.com/doc/current/cookbook/form/create_custom_field_type.html
Fill your entity with the data you want and pass it to the form. (From database/staticly/api/etc.)
Then follow this existing answer on how to get data from the entity to the form so that when you display the form you can create existing values: How to pass entity atribute value to form Symfony2? Of course instead of the attribute type you make it a default choice.
Related
I'm trying to retrieve the values of an attribute passed in request, I have tried, I can not, and it returns a NULL, when I make a var_dump on the attribute.
First of all I created a form to enter a comment, and when we click on validate, this page will have to take you back to a page where I have the comment list for each doctor, but I only receive this error which tells me "Call to a member function getId () on null" because I can not recover the value of doctor
Controller of create comments
public function commentCreateAction(Request $request, Booking $bookings)
{
$em = $this->getDoctrine()->getEntityManager();
// $medecin = $booking->getMedecin();
$patient = $bookings->getPatient();
$repoMedecin = $em->getRepository('DoctixMedecinBundle:Medecin');
$medecin = $repoMedecin->findOneBy(array(
'id' => $request->query->get("medecin")
));
$bookings = $em->getRepository("DoctixFrontBundle:Booking")->findBy(array(
"patient" => $patient
));
$comments = new Comment();
$comments->setMedecin($medecin);
$comments->setPatient($patient);
$form = $this->createForm('Doctix\PatientBundle\Form\CommentType', $comments);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em = $this->getDoctrine()->getEntityManager();
$em->persist($comments);
$em->flush();
return $this->render('DoctixPatientBundle:Patient:comments.html.twig', array(
'id' => $comments->getMedecin()->getId(),
'comments' => $comments,
'bookings' => $bookings
));
}
return $this->render('DoctixPatientBundle:Patient:create.html.twig', array(
'comment' => $comment,
'form' => $form->createView()
));
}
Controller from the list of comments
public function commentsAction(Request $request){
$em = $this->getDoctrine()->getManager();
$repoMedecin = $em->getRepository('DoctixMedecinBundle:Medecin');
$medecin = $repoMedecin->findOneBy(array(
'id' => $request->query->get("medecin")
));
$patient = $em->getRepository("DoctixPatientBundle:Patient")->findOneBy(array(
'user' => $this->getUser(),
));
$bookings = $em->getRepository("DoctixFrontBundle:Booking")->findBy(array(
"patient" => $patient
));
***
$comments = $em->getRepository("DoctixPatientBundle:Comment")
->getCommentsForDoc($medecin->getId());
***
return $this->render('DoctixPatientBundle:Patient:comments.html.twig', array(
'comments' => $comments,
'bookings' => $bookings
));
}
My mistake is in this controller.
to have the comments of a doctor, I have a function in my repository
Repository
public function getCommentsForDoc($docId, $approved = true)
{
$qb = $this->createQueryBuilder('c')
->select('c')
->where('c.medecin = :medecin_id')
->addOrderBy('c.created')
->setParameter('medecin_id', $docId);
if (false === is_null($approved))
$qb->andWhere('c.approved = :approved')->setParameter('approved', $approved);
return $qb->getQuery()->getResult();
}
Routing of commentsAction and createcomment
patient_comments:
path: /patient/medecin/comments
defaults: { _controller: DoctixPatientBundle:Patient:comments}
patient_comments_create:
path: /patient/medecin/{id}/create
defaults: { _controller: DoctixPatientBundle:Patient:commentCreate}
View of commentsAction
{% for booking in bookings %}
<div class="list_general" id="liste">
<ul>
<li>
<figure>
<img src="{{ vich_uploader_asset(booking.medecin.media, 'imageFile') }}"
alt="{{ booking.medecin.media.imagename }}">
</figure>
<h4>
Dr. {{ booking.medecin.user.prenom|capitalize }} {{ booking.medecin.user.nom|upper }}
<i class="pending">Pending</i>
</h4>
<a href="{{ path('patient_comments_create', {'id': booking.id}) }}?medecin={{ booking.medecin.id }}">
Ajouter un Commentaire
</a>
<header>
{% for comment in comments %}
{% if comment is defined %}
<label>Nom</label>
<input type="text" class="form-control" readonly
value=" {{ comment.patient.user.nom }} ">
<label>Nom</label>
<input type="text" class="form-control" readonly
value=" {{ comment.patient.user.prenom }} ">
<p> a commenté </p>
<p>
<time datetime="{{ comment.created|date('c') }}">
{{ comment.created|date('l, F j, Y') }}
</time>
</p>
</header>
<label>My Comments</label>
<input type="text" class="form-control" readonly
value=" {{ comment.comment }} ">
{% else %}
<p> Il n'y a pas encore de commentaires à propos de ce médecin. Soyez le premier à commenter....</p>
{% endif %}
{% endfor %}
{% endfor %}
my layout where there is the Feedback tab and where I call my route
<li class="nav-item" data-toggle="tooltip" data-placement="right" title="Mon profil">
<a class="nav-link link-white" href="{{ path('patient_comments')}}">
<i class="fa fa-fw fa-comments"></i>
<span class="nav-link-text">FeedBack</span>
</a>
</li>
Although the question is still lacking the information how exactly you are accessing this route - i.e. why you have made the assumption that the medicin id would be passed in the request params - to me it just looks like your route definition is missing the id:
patient_comments:
path: /patient/medecin/{id}/comments
defaults: { _controller: DoctixPatientBundle:Patient:comments}
and
public function commentsAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$repoMedecin = $em->getRepository('DoctixMedecinBundle:Medecin');
$medecin = $repoMedecin->find($id);
...
I am making the form which allow a user to select a img.
Symfony2.8 with sonataMediaBundle/
$form = $this->createFormBuilder($form)
->add('media',EntityType::class,array(
'expanded' => true,
'class' => "Application\Sonata\MediaBundle\Entity\Media"
}))
->add('save', SubmitType::class, array('label' => 'Create Post'))
->getForm();
in twig
{{form_widget(form.media)}}
However it shows the radio buttons with only the name of img.
○AAA.jpg ○BBB.jpg ○CCC.jpg ○DDD.jpg ('○' is radio button)
It is not good design for users.
I want to show the thumbnail of imgs here.
Is there good way to do this?
The easiest way i can imagine is to actually check the radio button with javascript.
Your Controller :
//src/AppBundle/Controller/YourController.php
public function yourAction()
{
$em = $this->getDoctrine()->getManager();
$medias = $em->getRepository('AppBundle:Media')->findAll();
$entity = new YourEntity();
$form = $this->createForm(YourEntityType::class, $entity);
if ($form->isSubmitted() && $form->isValid()) {
//... your logic
}
return $this->render('AppBundle::template.html.twig', array(
'form' => $form->createView(),
'medias' => $medias
));
}
Then in your twig file
{% for media in medias %}
<img class="to-select" src="{{ media.pathToThumbnail }}" data-id="{{ media.id }}" />
{% endfor %}
{{ form_start(form) }}
<!-- assuming you are using bootstrap -->
<div class="hidden">{{ form_widget(form.media) }}</div>
{{ form_widget(form.submit) }}
{{ form_end(form) }}
{% block javascripts %}
<script>
//assuming you use jquery
$('.to-select').click(function () {
var id = '#form_name_media_' + $(this).data('id');
var media = $(id);
if (media.is(':checked')) {
media.prop('checked', false);
} else {
media.prop('checked', true);
}
});
</script>
{% endblock javascripts %}
I have the following form where questionaire.Questions is a collection of QuestionType which is just a yes/no <select>.
Here's what the twig looks like:
Expected:
{{ form_start(questionaire) }}
{% for question in questionaire.Questions %}
<div class="question">
{{ form_label(question) }}
</div>
<div>
{{ form_widget(question) }}
</div>
{% endfor %}
{{ form_end(questionaire) }}
However it gets rendered like this:
<div class="question">
//This is where I want the label. But instead I get this:
<label></label>//Unsure why it's empty. Maybe it's questionaire.Question's label?
</div>
<div>
<label>lorem ipsum...</label> //Wrong place. Label gets rendered here instead.
<select>...</select> //Selection widget is correctly rendered.
</div>
I think the label is getting rendered along with the widget. Here's my QuestionType just in case.
class QuestionType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
$question = $event->getData();
$form = $event->getForm();
$form->add('Answer', 'choice', array(
'label' => $question->getQuestion(),
'choices' => array(
'' => 'Select One',
'Yes',
'No'
)
)
);
}
);
}
...
}
How can I get the label to where I want it?
You have to call the form_widget and form_label for the answer type
{{ form_label(question.Answer) }}
{{ form_widget(question.Answer) }}
You need to define the block question_row in a form theme, and use {{ form(questionaire_form) }} to render the entire form.
Acme/DemoBundle/Form/Type/QuestionType.php
// ...
public function getName(){
return 'question';
}
// ...
Acme/DemoBundle/Controller/DefaultController.php
// ...
public function questionaireAction(){
$form = $this->createForm(new BriefQuestionaireType());
return $this->render('AcmeDemoBundle:Default:brief_questionaire.html.twig', array(
'questionaire_form' => $form->createView()
));
}
// ...
Acme/DemoBundle/Resources/views/Default/brief_questionaire.html.twig
<html>
<head>
<title>Questionaire</title>
</head>
<body>
{% form_theme questionaire_form 'AcmeDemoBundle:Form:form.html.twig' %}
{{ form(questionaire_form) }}
</body>
</html>
Acme/DemoBundle/Resources/views/Form/form.html.twig
We create a block named [block_prefix]_row, where block_prefix is derived from getName() in QuestionType above. When this form theme is used, all QuestionType rows are rendered this way.
{% block question_row %}
<div class="question">
{{ form_label(form) }}
</div>
<div>
{{ form_widget(form) }}
{{ form_error(form) }}
</div>
{% endblock %}
I am new to Symfony2 and I am just trying some stuff to learn Symfony2.
Currently, I have a few controllers which can perfrom some simple tasks. A simple crud controller for example.
Al my pages extend a base.html.twig file and insert their block into it. Now I want to add a login part to my base file, because I want to be able to login at every page. I was thinking to use the formbuilder for this, but this is php code which can not be in a html.twig file. I have absolutly no idea how to handle this. Can someone push me in the right direction of give my some working example?
Thanking in advance
Actions:
public function loginFormAction(Request $request){
// create a task and give it some dummy data for this example
$message = "";
$account = new Account();
$account->setEmail('login#login.nl');
$form = $this->createFormBuilder($account)
->add('email', 'text')
->add('wachtwoord', 'text')
->add('login', 'submit')
->getForm();
$form->handleRequest($request);
if ($_POST != null && $_POST['form']['email'] != null) {
$repository = $this->getDoctrine()->getRepository('AcmeBlogBundle:Account');
$email =$_POST['form']['email'];
$wachtwoord =$_POST['form']['wachtwoord'];
$foundAccount = $repository->findOneByemail($email);
if (!$foundAccount) {
$message = "Ingevuld email en wachtwoord komen niet overeen.";
}else if($foundAccount->getWachtwoord() == $wachtwoord){
$session = $request->getSession();
$session->set('name', $foundAccount->getVoornaam() . " " . $foundAccount->getAchternaam());
$session->set('email', $foundAccount->getEmail());
//return $this->redirect($this->generateUrl('melding', array('melding' => 'mooiman')));
//return $this->render('AcmeBlogBundle:Account:melding.html.twig', array('melding' => 'Hallo, '.$foundAccount->getVoornaam() . " " . $foundAccount->getAchternaam()));
$message = 'Hallo, '.$foundAccount->getVoornaam() . " " . $foundAccount->getAchternaam();
$form = $this->createFormBuilder()
->add('logout', 'submit')
->getForm();
return $this->render('AcmeBlogBundle:Account:login.html.twig', array('form'=> $form->createView(), 'message' => $message));
}else{
$message = "Ingevuld email en wachtwoord komen niet overeen.";
}
}
return $this->render('AcmeBlogBundle:Account:login.html.twig', array('form'=> $form->createView(), 'message' => $message));
}
public function logoutFormAction(Request $request){
// create a task and give it some dummy data for this example
$session = $request->getSession();
$message = 'Hallo, '.$session->get('name');
$form = $this->createFormBuilder()
->add('logout', 'submit')
->getForm();
$form->handleRequest($request);
if ($_POST != null) {
$account = new Account();
$account->setEmail('login#login.nl');
$form = $this->createFormBuilder($account)
->add('email', 'text')
->add('wachtwoord', 'text')
->add('login', 'submit')
->getForm();
$session->set('name', null);
$session->set('email', null);
$message = "U bent nu uitgelogd";
}
return $this->render('AcmeBlogBundle:Account:login.html.twig', array('form'=> $form->createView(), 'message' => $message));
}
Base.html.twig:
{% if(app.session.get('name') == null) %}
{{ render(controller('AcmeBlogBundle:Account:loginForm')) }}
{% else %}
{{ render(controller('AcmeBlogBundle:Account:logoutForm')) }}
{% endif %}
You can include whole output of other controller/action in template. Just include this in your taplate:
<div id="sidebar">
{{ render(controller('YourBundle:User:login')) }}
</div>
This will inject whole output of YourBundle/UserController/loginAction to the #sidebar div. Remember that YourBundle/UserController/loginAction template can't extend other twig templates and hould be simple.
You can read more about embedding controllers here http://symfony.com/doc/current/book/templating.html#embedding-controllers
[EXAMPLE]:
An form action:
/**
* #Route("/contact", name="contact")
*/
public function contactAction(Request $request){
$form = $this->createFormBuilder()
->setAction($this->generateUrl('contact'))
->add('email', 'email', array(
'constraints' => array(
new \Symfony\Component\Validator\Constraints\Email()
)
))
->add('add', 'submit')
->getForm()
;
if ($request->getMethod() == 'POST') {
$form->handleRequest($request);
if ($form->isValid()) {
//do whatever you want and redirect
return $this->redirect($this->generateUrl('homepage'));
}else{
// this must be a full page (extending from base tamplate)
// that show form and errors
return $this->render(
'YourBundle:ControllerName:contact_faild.html.twig',
array('form' => $form->createView())
);
}
}
return $this->render(
'YourBundle:ControllerName:contact.html.twig',
array('form' => $form->createView())
);
}
There must be action URL in form "->setAction($this->generateUrl('contact'))" because form should be handled on separate request.
Next a form template (YourBundle:ControllerName:contact.html.twig):
{{ form(form) }}
Then we need an extra template for showing form errors when something went wrong. That should extend base template and probably override place where form appear in the first time
A form with errors tempalte (YourBundle:ControllerName:contact_faild.html.twig):
{% extends '::base.html.twig' %}
{% block sidebar -%}
{{ form(form) }}
{% endblock sidebar %}
And base teplate:
(...)
{% block sidebar %}
<div id="sidebar">
{{ render(controller('ClickAdUserBundle:User:contact')) }}
</div>
{% endblock %}
(...)
read this
you will find some html/twig code, put that in a "login.html.twig" and include it where you want
If you're completly new to Symfony2 i can suggest the symfony2 symblock tutorial
You'll work with Doctrine 2, creating Forms and design them, Twig Layouts and all the other stuff while creating a little blog.
Taken from the example, you would create the form in your controller like this
// src/Blogger/BlogBundle/Controller/PageController.php
public function contactAction()
{
$enquiry = new Enquiry(); //This creates an Instance of the Entity you need for the form
$form = $this->createForm(new EnquiryType(), $enquiry);
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
// Perform some action, such as sending an email
// Redirect - This is important to prevent users re-posting
// the form if they refresh the page
return $this->redirect($this->generateUrl('BloggerBlogBundle_contact'));
}
}
return $this->render('BloggerBlogBundle:Page:contact.html.twig', array(
'form' => $form->createView()
));
}
and the form as .html.twig would look like this
{# src/Blogger/BlogBundle/Resources/views/Page/contact.html.twig #}
{% extends 'BloggerBlogBundle::layout.html.twig' %}
{% block title %}Contact{% endblock%}
{% block body %}
<header>
<h1>Contact symblog</h1>
</header>
<p>Want to contact symblog?</p>
<form action="{{ path('BloggerBlogBundle_contact') }}" method="post" {{ form_enctype(form) }} class="blogger">
{{ form_errors(form) }}
{{ form_row(form.name) }}
{{ form_row(form.email) }}
{{ form_row(form.subject) }}
{{ form_row(form.body) }}
{{ form_rest(form) }}
<input type="submit" value="Submit" />
</form>
{% endblock %}
I hardly suggest working through this tutorial to have a nice start with symfony2
I am working on a college project where I want to take the attendance of all the students. I have created a model with 3 fields i,e date, present (boolean) and student_id. Now when I try to generate a form out of it, it will display me only these 3 fields. However I want all the students of the class. So I created a loop for students and created an array of attendance objects. Now I am stuck, I don't know how I can pass them to my TWIG file, and I am also confused if it ist the right way to do this. Here is my Model and Controller code
FORM
namespace College\StudentBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class StudentAttendanceType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('date')
->add('present')
;
}
public function getName()
{
return 'college_studentbundle_studentattendancetype';
}
}
CONTROLLER
public function takeAttendanceAction($Department_Id)
{
$students = $this->getDoctrine()
->getRepository('CollegeStudentBundle:Student')
->findAll($Department_Id);
foreach($students as $key => $student){
$attendance[$key] = new StudentAttendance();
$form[$key] = $this->createForm(new StudentAttendanceType(), $attendance[$key]);
}
$request = $this->getRequest();
if ($request->getMethod() == 'POST') {
$form->bindRequest($request);
if ($form->isValid()) {
$em = $this->getDoctrine()
->getEntityManager();
$em->persist($attendance);
$em->flush();
return $this->redirect($this->generateUrl('CollegeStudentBundle_index', array('id' => $Department_Id)));
}
}
return $this->render('CollegeStudentBundle:StudentAttendance:take-attendance.html.twig', array(
'form' => $form->createView(), 'department' => $Department_Id, 'students' => $students,
));
}
How can I render the form in a way that it will display me all the students with a separate checkbox ?
HTML.TWIG
{% block body %}
<form action="{{ path('CollegeStudentBundle_take_attendance',{'id':department} ) }}" method="post" {{ form_enctype(form) }} name="acadimics-form" id="acadimics-form" >
{{ form_errors(form) }}
{{ form_row(forms[0].date) }}
<table id="mytabs" border="1" cellpadding="5" cellspacing="2" width="100%" >
<tr>
<th> Enrolment No. </th>
<th> Student's Name </th>
<th> Present </th>
</tr>
{% for student in students %}
<tr>
<td> {{ student.enrolmentNo }} </td>
<td> {{ student.firstname }} {{ student.lastname }} </td>
<td> {{ form_row(form.present) }} </td>
</tr>
{% endfor %}
{{ form_rest(form) }}
</table>
<input type="submit" value="Take Attendance" />
</form>
</div>
{% endblock %}
Not tested. But there should be a little modification.
The loop in the controller:
foreach($students as $key => $student){
$attendance[$key] = new StudentAttendance();
$form[$key] = $this->createForm(new StudentAttendanceType(), $attendance[$key])->createView();
}
return the array:
return $this->render('CollegeStudentBundle:StudentAttendance:take-attendance.html.twig', array(
'form' => $form, 'department' => $Department_Id, 'students' => $students,
));
In the template:
{% for sform in form %}
{{ form_widget(sform) }}
{% endfor %}
I think you may be on the wrong track. S2 already deals with arrays out of the box.
Read through:
http://symfony.com/doc/current/cookbook/form/form_collections.html
http://symfony.com/doc/current/reference/forms/types/collection.html
You basically want to create a master ListofStudents form and then imbed a StudentAttendence form. You can then pass your array of students to the master form and all the array processing will happen magically.
In your ListOfStudents form you will have something like:
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('students', 'collection', array('type' => new StudentAttendenceType()));
You return the variable students to the template and you're calling student.firstname in twig, it should be students.firstname and you should keep your 'form' => $form->createView() in the return of your controller.
I think it should work better, hope this help!