I have a simple form with title and content field, however, the content from the Quill editor is not being sent through the form so I used their form-submit example(https://quilljs.com/playground/#form-submit). I'm trying to get the content via $request->request->get() in my Controller but when I dump it, it's null. Here is my code:
FormType:
class MenuType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, [
'label' => 'Title',
])
->add('content', TextareaType::class, [
'label' => false,
'attr' => [
'class' => 'editor'
]
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Menu::class,
]);
}
}
Form rendering in twig:
{% extends 'admin/shared/layout.html.twig' %}
{% block body %}
<div class="row">
<div class="col-lg-12">
<div class="row">
<h1 class="page-header">{% if item.id %}Edit{% else %}Add{% endif %} menu</h1>
</div>
{{ form_start(form) }}
{{ form_row(form.title) }}
<label>Content</label>
<input name="about" type="hidden">
<div id="editor">
{{ form_row(form.content) }}
</div>
<br>
<button class="btn btn-success" type="submit"><i class="fa fa-save"></i> Save</button>
{{ form_end(form) }}
</div>
</div>
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('admin/quill/quill.min.js') }}"></script>
<script>
var editor = new Quill('#editor', {
theme: 'snow',
modules: {
toolbar: toolbarOptions
},
});
var form = document.querySelector('form');
form.onsubmit = function() {
// Populate hidden form on submit
var about = document.querySelector('input[name=about]');
about.value = JSON.stringify(editor.getContents());
};
</script>
{% endblock %}
Not the most beautiful solution, but it works - found it in another similar question about Django (QuillJS doesn't work with textarea)
$('.editor').each(function(i, el) {
var el = $(this), id = 'quilleditor-' + i, val = el.val(), editor_height = 200;
var div = $('<div/>').attr('id', id).css('height', editor_height + 'px').html(val);
el.addClass('d-none');
el.parent().append(div);
var quill = new Quill('#' + id, {
modules: { toolbar: toolbarOptions },
theme: 'snow'
});
quill.on('text-change', function() {
el.val(quill.root.innerHTML);
});
})
Related
I need to create a form adding faculty to the database. First, the user selects a region from the list ( / ChoiceType), then the city of this region from the following list, the university, and finally enters the name of the faculty. The default values are the first region from the database, its first city and first university.
Sending the page with default data works, the choice of the region works, but the the choice of the city return to 500 status
Form:
Twig and ajax:
{% extends 'admin/insert/insert.html.twig' %}
{% block title %}Add Faculty{% endblock %}
{% block body %}
<div class="insert">
<h1 class="insert__title">Add Faculty</h1>
{{ form_start(insert_faculty, { 'attr' : {'class' : 'insert__form'} }) }}
{% for message in app.flashes('success') %}
<div class="insert__success">
{{ message }}
</div>
{% endfor %}
<div class="insert__errors">
{{ form_errors(insert_faculty) }}
</div>
{{ form_label(insert_faculty.region, 'Region:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.region, { 'attr' : {'class' : 'insert__input'} }) }}
{{ form_label(insert_faculty.city, 'City:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.city, { 'attr' : {'class' : 'insert__input'} }) }}
{{ form_label(insert_faculty.university, 'University:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.university, { 'attr' : {'class' : 'insert__input'} }) }}
{{ form_label(insert_faculty.name, 'Name:', { 'label_attr' : {'class' : 'insert__label'} }) }}
{{ form_widget(insert_faculty.name, { 'attr' : {'class' : 'insert__input insert__input_name'} }) }}
<button type="submit" class="insert__button">Save</button>
{{ form_end(insert_faculty) }}
<div class="insert__buttons">
Back
</div>
</div>
{% block javascripts_footer %}
{{ parent() }}
<script>
let $region = $('#insert_faculty_region');
$region.change(function() {
let $form = $(this).closest('form');
let data = {};
data[$region.attr('name')] = $region.val();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(get) {
$('#insert_faculty_city').html(
$(get).find('#insert_faculty_city').html()
);
$('#insert_faculty_university').html(
$(get).find('#insert_faculty_university').html()
);
}
});
});
let $city = $('#insert_faculty_city');
$city.change(function() {
let $form = $(this).closest('form');
let data = {};
data[$city.attr('name')] = $city.val();
$.ajax({
url : $form.attr('action'),
type: $form.attr('method'),
data : data,
success: function(get) {
$('#insert_faculty_university').html(
$(get).find('#insert_faculty_university').html()
);
}
});
});
</script>
{% endblock %}
{% endblock %}
Form class:
class InsertFacultyType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('region', ChoiceType::class, [
'choices' => $options['regions_array'],
'mapped' => false,
])
->add('city', ChoiceType::class, [
'choices' => null,
'mapped' => false,
])
->add('university', ChoiceType::class, [
'choices' => null,
])
->add('name')
;
$formModifier = function (FormInterface $form, $entity_parent) {
if (get_class($entity_parent) === 'App\Entity\Region') {
if (!$entity_parent->getCities()->count()) {
$form->add('city', ChoiceType::class, [
'choices' => null,
'mapped' => false,
]);
}
else {
$cities_in_database = $entity_parent->getCities();
foreach ($cities_in_database as $city) {
$cities[$city->getName()] = $city;
}
$form->add('city', ChoiceType::class, [
'choices' => $cities,
'mapped' => false,
]);
}
}
else if (get_class($entity_parent) === 'App\Entity\City') {
if (!$entity_parent->getUniversities()->count()) {
$form->add('university', ChoiceType::class, [
'choices' => null,
]);
}
else {
$university_in_database = $entity_parent->getUniversities();
foreach ($university_in_database as $university) {
$universities[$university->getName()] = $university;
}
$form->add('university', ChoiceType::class, [
'choices' => $universities,
]);
}
}
};
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function (FormEvent $event) use ($options, $formModifier, $builder) {
$region = $options['regions_array'][array_key_first($options['regions_array'])];
$city = $region->getCities()[0];
$formModifier($event->getForm(), $region);
$formModifier($event->getForm(), $city);
}
);
$builder->get('region')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$region = $event->getForm()->getData();
$city = $region->getCities()[0];
$formModifier($event->getForm()->getParent(), $region);
$formModifier($event->getForm()->getParent(), $city);
}
);
$builder->get('city')->addEventListener(
FormEvents::POST_SUBMIT,
function (FormEvent $event) use ($formModifier) {
$city = $event->getForm()->getData();
$formModifier($event->getForm()->getParent(), $city);
}
);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => Faculty::class,
'regions_array' => null,
]);
$resolver->setAllowedTypes('regions_array', 'array');
}
}
Controller:
/**
* #Route("/admin/insert/faculty", name="faculty")
*/
public function faculty(Request $request)
{
$regions_in_database = $this->getDoctrine()->getRepository(Region::class)->findAll();
$regions = [];
foreach ($regions_in_database as $region) {
$regions[(string)$region->getName()] = $region;
}
$faculty = new Faculty();
$insert_faculty = $this->createForm(InsertFacultyType::class, $faculty, [
'regions_array' => $regions,
]);
if (!$regions_in_database) {
$insert_faculty->addError(new FormError("There are no regions!"));
}
$insert_faculty->handleRequest($request);
if ($insert_faculty->isSubmitted() && $insert_faculty->isValid()) {
$repository = $this->getDoctrine()->getRepository(University::class);
$faculty_in_database = $repository->findOneBy(
[
'name' => $faculty->getName(),
'university' => $faculty->getUniversity(),
]
);
if ($faculty_in_database) {
$insert_faculty->addError(new FormError('Such a faculty is already in the database!'));
}
else {
$faculty->setRating(0);
if(!$faculty->getUniversity()) {
$insert_faculty->addError(new FormError("Select the university!"));
}
else {
$entity_manager = $this->getDoctrine()->getManager();
$entity_manager->persist($faculty);
$entity_manager->flush();
$this->addFlash(
'success',
'Faculty "' . $faculty->getName() . '" successfully saved!'
);
}
}
}
return $this->render('admin/insert/faculty/faculty.html.twig', [
'insert_faculty' => $insert_faculty->createView(),
]);
}
I invite you to use the Symfony debug toolbar. It will allow you to see the different problems associated with your code and give much more information about the problems that appear.
Profiler Symfony
About your problem, I think it is necessary to debug at the level of what your form sends to your application. But if you want more help, you must provide the error message that come with your 500 error.
so I have this Symfony Form which I created, and there are these 3 inputs that are related to each other. I'm trying to align all three of them in the same line to make it looks better.
Here's my code:
the FormType
<?php
namespace App\Form;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;
use App\Entity\TypeParking;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class TypeParkingType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('libelle')
->add('tempsmax')
->add('jourdebut')
->add('jourfin')
//these three are the fields that I'm trying to put into a single line
->add('jourstravail_jour', TextType::class, ['property_path' => 'jourstravail[jour]'])
->add('jourstravail_debut', TextType::class, ['property_path' => 'jourstravail[debut]'])
->add('jourstravail_fin', TextType::class, ['property_path' => 'jourstravail[fin]'])
->add('Exception_Name', TextType::class, ['property_path' => 'exception[name]'])
->add('Starting_date', TextType::class, ['property_path' => 'exception[datedebut]'])
->add('Ending_date', TextType::class, ['property_path' => 'exception[datefin]'])
->add('Starting_time', TextType::class, ['property_path' => 'exception[heuredebut]'])
->add('Ending_time', TextType::class, ['property_path' => 'exception[heurefin]'])
;
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => TypeParking::class,
]);
}
}
and here's my _form.html.twig file
{{ form_start(form) }}
{{ form_row(form.libelle, { 'label': 'Label' }) }}
{{ form_row(form.tempsmax, { 'label': 'Max Time' }) }}
{{ form_row(form.jourdebut, { 'label': 'Start Date' }) }}
{{ form_row(form.jourfin, { 'label': 'Ending Date' }) }}
<h3>these are the fields</h3>
{{ form_row(form.jourstravail_jour, { 'label': 'Day' }) }}
{{ form_row(form.jourstravail_debut, { 'label': 'start' }) }}
{{ form_row(form.jourstravail_fin, { 'label': 'end' }) }}
<h3>Exception</h3>
{{ form_row(form.Exception_Name, { 'label': 'Exception Name' }) }}
{{ form_row(form.Starting_date, { 'label': 'Starting Date' }) }}
{{ form_row(form.Ending_date, { 'label': 'Ending Date' }) }}
{{ form_row(form.Starting_time, { 'label': 'Starting Time' }) }}
{{ form_row(form.Ending_time, { 'label': 'Ending Time' }) }}
<button class="btn">{{ button_label|default('Save') }}</button>
{{ form_end(form) }}
The fields that I'm trying to format are jourstravail_jour, jourstravail_debut and jourstravail_fin
Here is an answer based on my comments that seemed to help you, so you can tag your questions as answered.
You can use it "normally", by using form_label() and form_widget()(not sure about this one) and wrap them with Bootstrap classes.
Or you can customize the form_row() behaviour and include your classes in your own form templates.
I havenĀ“t found the solution to manually render a form which contains a collection.
Here is my code in twig:
<ul id="document-fields-list" data-prototype="{{ form_widget(formulario.documentos.vars.prototype)|e }}">
<div><button class="pull-right" href="#" id="add-another-document">Agregar documento</button></div>
{% for documento in formulario.documentos %}
<li>
{{ form_label(documento) }}
{{ form_widget(documento) }}
Eliminar
</li>
{% endfor %}
</ul>
FormType
In your case we need to create formType for PersonaDocumento. Imagine, that this entity has field documentName:
class PersonaDocumentoType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
* #SuppressWarnings(unused)
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('documentName', TextType::class, [
'label' => false,
'translation_domain' => 'messages'
])
;
}
/**
* #return string
*/
public function getName()
{
return 'app_persona_documento_type';
}
/**
* #return null|string
*/
public function getBlockPrefix()
{
return 'app_persona_documento';
}
/**
* #param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => PersonaDocumento::class,
'csrf_protection' => true,
'validation' => true,
));
}
}
Form that contain collection
Consider you entity Formulario. It has a OneToMany relation to PersonaDocumento. And Form will be:
class FormularioFormType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder
->add('documentos', CollectionType::class, [
'entry_type' => PersonaDocumentoType::class,
'entry_options' => [
'label' => false,
],
'label' => false,
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false, // Very important thing!
])
;
}
// ...
}
Widget for collection
We have a form (FormularioFormType) that contain collection of small forms with type PersonaDocumentoType.
New widget you can create in file with standard widgets, and name of the file is fields.html.twig : path_to_your_project/src/AppBundle/Resources/views/Form/fields.html.twig.
Name of the block will be app_persona_documento_widget.
Thus, example of fields.html.twig :
{% trans_default_domain 'messages' %}
{% block app_persona_documento_widget %}
{% spaceless %}
<div class="form-group" {{ block('widget_container_attributes') }}>
<div class="col-sm-12">
{{ form_widget(form.name, {'attr' : { 'placeholder' : 'app.form.label.name'|trans , 'class' : 'form-control' }}) }}
</div>
</div>
{% endspaceless %}
{% endblock app_persona_documento_widget %}
Also pay attention that "app_persona_documento_widget" - assembled from the getBlockPrefix() of you PersonaDocumentoType plus string "_widget"
Register new form themes in config.yml
# Twig Configuration
twig:
debug: '%kernel.debug%'
strict_variables: '%kernel.debug%'
form_themes:
# other form themes
# ...
- 'AppBundle:Form:fields.html.twig'
Render collection in parent form
{{ form_start(formulario_form) }}
<div class="form-group">
<label for="" class="col-sm-2 control-label">
Label
</label>
<div class="col-sm-10 documentos" data-prototype="{{ form_widget(formulario_form.documentos.vars.prototype)|e('html_attr') }}">
{% for document in formulario_form.documentos %}
<div>
{{ form_widget(document) }}
</div>
{% endfor %}
</div>
</div>
<span>
{{ form_errors(formulario_form) }}
</span>
{# Here you can add another fields of form #}
{{ form_end(formulario_form) }}
Of course, you also need buttons: one "Add another document" button and "Remove" buttons for each "Documento" item.
Symfony documentation suggests that we use JavaScript for this purpose.
You can read more here in official docs
Also you can install Ninsuo/symfony-collection - A jQuery plugin that manages adding, deleting and moving elements from a Symfony collection
i need your help please , I want to display my created form in Symfony2. I want to display my created form 92 times becouse i have 92 numbers in my database(every number is a form) , i didn't know how to do it here is my code:
controller:
class DefaultController extends Controller
{
public function QuestionsAction(Request $request)
{
$questions = $this->getDoctrine()->getEntityManager()
->getRepository('Tests\TestsPhpBundle\Entity\Question')
->findAll();
$task = new Question();
$forms = $this->createForm(new QuestionType(), $task);
if ($request->getMethod() == 'POST') {
$forms->bindRequest($request);
if ($forms->isValid())
{
$em = $this->getDoctrine()->getEntityManager();
$em->persist($task);
$em->flush();
}
}
{
return $this->render('TestsTestsPhpBundle:Default:index.html.twig', array(
'questions' => $questions,
'forms' => $forms->createView()
));
}
}
}
my form file:
class QuestionType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('categories', null, array('required' => false,
))
->add('text', 'entity', array(
'class' => 'TestsTestsPhpBundle:Question',
'query_builder' => function($repository) {
return $repository->createQueryBuilder('p')->orderBy('p.id', 'ASC'); },
'property' => 'text'))
;
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Tests\TestsPhpBundle\Entity\Question',);
}
public function getName()
{
return 'question';
}
}
my twig file:
{% block content %}
<h2>Questions</h2>
{% for question in questions %}
<dl>
<dt>Number</dt>
<dd>{{ question.number }}<dd>
{% for form in forms %}
{{ form_row(forms.categories) }}
{{ form_row(forms.text) }}
</dl>
{% endfor %}
<hr />
{% endfor %}
{% endblock %}
I recommend to read capter: Embedding Controller
http://symfony.com/doc/2.0/book/templating.html
<div id="sidebar">
{% render "AcmeArticleBundle:Article:recentArticles" with {'max': 3} %}
</div>
You can make a for loop within Twig Template and call an action (with parameter if needed) where you render the form. -> QuestionsAction in your case.
This is my file:
<?php
namespace EM\ExpensesBundle\Entity;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\AbstractType;
class ChooseCatType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name', 'entity', array(
'class' => 'EMMyFriendsBundle:Category',
'property' => 'name',
'empty_value' => 'All items',
'required' => false,
'query_builder' => function ($repository)
{ return $repository->createQueryBuilder('cat')
->select('cat')
->orderBy('cat.name', 'ASC');
}, ));
}
public function getName()
{
return 'choose_category';
}
}
Here I create the form:
namespace EM\ExpensesBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use EM\ExpensesBundle\Entity\Category;
use EM\ExpensesBundle\Entity\ChooseCatType;
class HomeController extends Controller
{
public function indexAction()
{
//Categories
$cat = new Category();
$dd_form = $this->createForm(new ChooseCatType(), $cat);
return $this->render('EMExpensesBundle:Home:index.html.twig', array(
'dd_form' => $dd_form->createView()));
}
}
and template:
{% extends "::base.html.twig" %}
{% block title %}
Expenses
{% endblock %}
{% block body %}
<div class="content">
<p> Choose category: </p>
<form class="cat" action="" method="post" {{ form_enctype(dd_form) }}>
{{ form_widget(dd_form.name) }}
{{ form_rest(dd_form) }}
<input type="submit" value="Show items" />
</form>
Manage Categories
</div>
{% endblock %}
but I get an error:
Fatal error: Declaration of EM\ExpensesBundle\Entity\ChooseCatType::buildForm()
must be compatible with that
of Symfony\Component\Form\FormTypeInterface::buildForm()
in C:\xampp\htdocs\Expenses\src\EM\ExpensesBundle\Entity\ChooseCatType.php
on line 9
Any ideas?
Use FormBuilderInterface in your method signature:
public function buildForm(FormBuilderInterface $builder, array $options)