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!
Related
I am starting with Symfony2, excuse me if my question is very easy but:
I have 2 entities:
News (id, title, tags)
Tag (id, news)
I have relations many-to-many
Sample code of News:
/**
* #ORM\ManyToMany(targetEntity="Tag", inversedBy="news", cascade={"persist", "remove"}))
* #ORM\JoinTable(name="news_tags")
*/
private $tags;
Sample code of Tag:
/**
* #ORM\ManyToMany(targetEntity="News", mappedBy="tags")
*/
private $news;
I have a TagType to adding tags to DB, just simple
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('name', 'text');
}
NewsType
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder
->add('title', 'text', array(
'attr'=>array(
'maxlength'=>60
)
))
->add('tags', 'collection', array(
'type'=>new TagType(),
'allow_add'=>true,
'allow_delete'=>true,
'by_reference'=>false,
))
;
}
Sample of News form HTML:
<table class="tags table table-hover">
<thead>
<tr>
<th>name</th>
<th>action</th>
</tr>
</thead>
<tbody data-prototype="{% filter escape %}{% include 'XAdminBundle:Form:news_tags_prototype.html.twig' with {'form': form.tags.vars.prototype} %}{% endfilter %}">
{% for tag in form.tags %}
<tr>
<td>
{{ tag.vars.data.name }}
{{ form_widget(tag.name, {'attr': {'class': 'hidden'} }) }}
</td>
<td class="action">
</td>
</tr>
{% endfor %}
</tbody>
</table>
news_tags_prototype.html.twig
<tr>
<td>{{ form_widget(form.name, {'attr': { 'class': 'form-control' } }) }}</td>
<td class="action"></td>
</tr>
In this case when I click Add Tag, I've got a text input and I can add completely new tag, but the only one I want after click Add Tag get a select box with names of Tag entitiess from DB.
How to do this throw js and prototype (suggested by http://symfony.com/doc/current/cookbook/form/form_collections.html).
Thanks for your help!
In TagType, you should go with something like:
$builder->add('name', 'entity', array(
'class' => 'SomeBundle:Tag'));
Which will ensure that the prototype renders as a dropdown instead of a text element. Hope that helps.
Edit: I just realized that you probably need your existing TagType in order to add the tag to the database in the first place. In that case, create a new file called TagDropdownType with my suggested change above and then in your NewsType, change:
->add('tags', 'collection', array(
'type'=>new TagType(),
'allow_add'=>true,
'allow_delete'=>true,
'by_reference'=>false,
))
to:
->add('tags', 'collection', array(
'type'=>new TagDropdownType(),
'allow_add'=>true,
'allow_delete'=>true,
'by_reference'=>false,
))
What is best practice to include another info in form from another entity?
I have Student Entity, Group Entity, StudentsGroup and Attendance Entity.
Student have id, code, and name.
Group have id and name.
StudentsGroup have id, group_id, and student_id.
Attendance have id, students_group_id, date and status.
Group can have many Student which saved in StudentsGroup. Why I make StudentsGroup? Because actually 1 Group can have some sub groups like SubjectsGroup etc. And Attendance save student information by StudentsGroup Id which same student can have different students_group_id.
Now, the problem is : How to show Student information in collection form of attendance?
All Entity relationship is declared as object, so actually we can access em freely from any entity. But I don't know how to do that in form. Here my form :
<?php
/* Collection Form */
$tanggal = new \DateTime($request->request->get('sifo_adminbundle_studentsgrouping')['tanggal']);
$attendances = new StudentsGrouping();
foreach ($entities as $temp) {
$entity = new Attendance();
$entity = $em->getRepository('SifoAdminBundle:Attendance')->findOneBy(array('studentsGrouping' => $temp, 'date' => $tanggal));
if ($entity){
$attendances->getAttendances()->add($entity);
}
}
$form = $this->createCollectionForm($attendances, $id, $tanggal);
return $this->render('SifoAdminBundle:DftAbsensi:manage.html.twig', array(
'form' => $form->createView(),
));
This is how I render it in twig :
{{ form_start(form_collection) }}
{{ form_row(form_collection.tanggal) }}
{% for attendance in form_collection.attendances %}
{{ form_row(attendance.status) }}
{% endfor %}
{{ form_end(form_collection) }}
## Concept ##
I'm thinking about creating entity and pass it into form like this :
foreach ($entities as $temp) {
$entity = new Student();
$entity = $em->getRepository('SifoAdminBundle:Student')->find($temp->getId());
if ($entity){
$entities[i] = $entity;
}
$i++
}
and then in twig show it like this :
{{ form_start(form_collection) }}
{{ form_row(form_collection.tanggal) }}
{% for key, attendance in form_collection.attendances %}
{{ entities[key].code }}
{{ entities[key].name }}
{{ form_row(attendance.status) }}
{% endfor %}
{{ form_end(form_collection) }}
But I feel not comfort with this. Am I really need to make new entity just for showing name and code from Student Entity? Is there a best practice to do this?
You can use entity field.
$builder->add('users', 'entity', array(
'class' => 'AcmeHelloBundle:User',
'multiple' => true, /* you can choose more than one */
'mapped' => false, /* if you are using the form with an entity */
'query_builder' => function(EntityRepository $er) {
/* use query builder to get correct results */
return $er->createQueryBuilder('u')->orderBy('u.username', 'ASC');
},
));
There are two important keys in above.
'multiple' => true ----- you can able to choose more than one
'mapped' => false ----- if you are using form with an entity, your form will automatically looks for a connection between these entities and if can not found, throws exception. to avoid of that problem you should set this option to false
http://symfony.com/doc/current/reference/forms/types/entity.html
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 want my choice labels rendered unescaped
here my FormType code:
$builder
->add('banner', new BannerType())
->add('runtime', 'entity', array(
'class' => 'AdvertisingBundle:Runtime',
'expanded' => true,
'property' => 'label'
))
here the label property:
public function getLabel()
{
return "<div class labelLeft>".$this->description.
"</div><div class labelLeft>".$this->hint."</div>";
}
I want the label rendered raw, which form_theme fragment I need to override?
here my solution:
<table>
{% for option in form.runtime %}
<tr>
<td class="choiceColumn">
{{ form_widget(option) }}
</td>
{{ option.vars.label |raw}}
</tr>
{% endfor %}
</table>
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.