CraueFormFlowBundle in symfony 3 error - symfony

I try to follow this link to install and create a form with CraueFormFlowBundle
CraueFormFlowBundle tutorial
i create the file
/src/AppBundle/Form/InterventoFlow.php
namespace AppBundle/Form;
use Craue\FormFlowBundle\Form\FormFlow;
use Craue\FormFlowBundle\Form\FormFlowInterface;
class InterventoFlow extends FormFlow {
protected function loadStepsConfig() {
return array(
array(
'label' => 'wheels',
'form_type' => 'AppBundle\Form\InterventoForm',
),
array(
'label' => 'engine',
'form_type' => 'AppBundle\Form\InterventoForm',
'skip' => function($estimatedCurrentStepNumber, FormFlowInterface $flow) {
return $estimatedCurrentStepNumber > 1 && !$flow->getFormData()->canHaveEngine();
},
),
array(
'label' => 'confirmation',
),
);
}
}
after i create
/src/AppBundle/Form/InterventoForm.php
<?php
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
namespace AppBundle/Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
class InterventoForm extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
switch ($options['flow_step']) {
case 1:
$validValues = array(2, 4);
$builder->add('numberOfWheels', 'Symfony\Component\Form\Extension\Core\Type\ChoiceType', array(
'choices' => array_combine($validValues, $validValues),
'placeholder' => '',
));
break;
case 2:
// This form type is not defined in the example.
$builder->add('engine', 'TextType', array(
'placeholder' => 'prova',
));
break;
}
}
public function getBlockPrefix() {
return 'createIntervento';
}
}
after i create the service
services:
app.form.InterventoFlow:
class: AppBundle\Form\InterventoFlow
parent: craue.form.flow
and the controller
public function interventoAction(Request $request)
{
$formData = new Validate(); // Your form data class. Has to be an object, won't work properly with an array.
$flow = $this->get('AppBundle.form.InterventoFlow'); // must match the flow's service id
$flow->bind($formData);
// form of the current step
$form = $flow->createForm();
if ($flow->isValid($form)) {
$flow->saveCurrentStepData($form);
if ($flow->nextStep()) {
// form for the next step
$form = $flow->createForm();
} else {
// flow finished
$em = $this->getDoctrine()->getManager();
$em->persist($formData);
$em->flush();
$flow->reset(); // remove step data from the session
return $this->redirect($this->generateUrl('home')); // redirect when done
}
}
return $this->render('interventi/intervento.html.twig', array(
'form' => $form->createView(),
'flow' => $flow,
));
}
and my twig file
{% extends 'base.html.twig' %}
{% block body %}
<div>
Steps:
{% include '#CraueFormFlow/FormFlow/stepList.html.twig' %}
</div>
{{ form_start(form) }}
{{ form_errors(form) }}
{% if flow.getCurrentStepNumber() == 1 %}
<div>
When selecting four wheels you have to choose the engine in the next step.<br />
{{ form_row(form.numberOfWheels) }}
</div>
{% endif %}
{{ form_rest(form) }}
{% include '#CraueFormFlow/FormFlow/buttons.html.twig' %}
{{ form_end(form) }}
{% endblock %}
{% stylesheets '#CraueFormFlowBundle/Resources/assets/css/buttons.css' %}
<link type="text/css" rel="stylesheet" href="{{ asset_url }}" />
{% endstylesheets %}
always give me error
Attribute "autowire" on service "app.form.intervento" cannot be inherited from "_defaults" when a "parent" is set. Move your child definitions to a separate file or define this attribute explicitly in C:\xampp\htdocs\Myprject\app/config\services.yml (which is being imported from "C:\xampp\htdocs\Myprject\app/config\config.yml").
and if i comment
parent: craue.form.flow
You have requested a non-existent service "app.form.interventoFlow".

Try to dalete _defaults section from your service.yml file.

Related

My custom Form Type Template is ignored when rendering form

I created a custom field type class BootstrapToggleType extends AbstractType which is child of CheckboxType I also prefixed it just in case as follows
public function getBlockPrefix(): string
{
return 'bootstrap_toggle';
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$form = $event->getForm();
$config = $form->getParent()->get($form->getName())->getConfig();
$options = $config->getOptions();
$defaultDataOptions = $this->_bootstrapToggleDataDefaults;
$form->getParent()->add($form->getName(), CheckboxType::class, array_replace_recursive($options, [
'attr' => $defaultDataOptions,
]))
;
});
}
I added custom form theme to twig.yaml form_themes: ['Form/custom_types.html.twig', 'bootstrap_4_layout.html.twig'] and created mentioned file with following code
{% block bootstrap_toggle_row %}
{% for child in form.children if not child.rendered %}
<div class="my-custom-wrapper">
<div class="form-group">
{{ form_label(child) }}
{{ form_widget(child) }}
{{ form_help(child) }}
{{ form_errors(child) }}
</div>
</div>
{% endfor %}
{% endblock %}
However the field is rendered using standard theme ignoring bootstrap_toggle_row What am I doing wrong?
The problem is basically with this part of code $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) { . However I need to use such construction to create a field using name provided by FormType object $builder->add('is_published', BootstrapToggleType::class)
if I change that part of code to something like that
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addChild('testName', CheckboxType::class);
}
my custom template is rendered correctly.
added
Because I re-added another child I have to provide it's correct block prefix.
$form->getParent()->add($form->getName(), CheckboxType::class, array_replace_recursive($options, [
'attr' => $defaultDataOptions,
'block_prefix' => 'bootstrap_toggle'
]));
Then in my custom template I need to reffer directly to the form variable as a single child, not an array. So instead of looping over children
{% for child in form.children if not child.rendered %}
just direct call
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_help(form) }}
{{ form_errors(form) }}
Now it works.

Symfony3.4 Form errors rendered twice

I have a form with author and message fields and NotBlank() validation on both.
In twig, I do this:
{{ form_start(form) }}
{{ form_errors(form.author) }}
{{ form_label(form.author) }}
{{ form_widget(form.author) }}
{{ form_errors(form.message) }}
{{ form_label(form.message) }}
{{ form_widget(form.message) }}
{{ form_end(form) }}
If I press Save button with empty fields I EXPECT to see this:
But I get this:
Somehow the bottom error message comes from the {{ form_label(...) }} I say this, because if I comment the labels out and use static HTML for labels, the output is like on first picture.
I would prefer not to use static HTML for labels, but I don't understand where the second error messages came from.
Below my code:
Form
class TestFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('author', TextType::class, ['required' => false, 'constraints' => [new NotBlank()]])
->add('message', TextType::class, ['required' => false, 'constraints' => [new NotBlank()]])
->add('save', SubmitType::class)
;
}
}
Controller
class TestController extends Controller
{
/**
* #Route("/testing", name="test")
* #param Request $request
* #return RedirectResponse|Response
*/
public function index(Request $request)
{
$form = $this->createForm(TestFormType::class);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid())
{
return $this->redirectToRoute('test');
}
return $this->render('test/index.html.twig', [
'form' => $form->createView(),
]);
}
}
Template
{% extends 'base.html.twig' %}
{% block title %}Hello TestController!{% endblock %}
{% block body %}
<p>This is a test...</p>
{{ form_start(form) }}
{{ form_errors(form.author) }}
{{ form_label(form.author) }}
{{ form_widget(form.author) }}
{{ form_errors(form.message) }}
{{ form_label(form.message) }}
{{ form_widget(form.message) }}
{{ form_end(form) }}
{% endblock %}
For bootstrap theme error block is integrated in label.
So you need either to remove form_errors block in your template or to override form_label block.
You can use form_row (as #Adrien suggests in commentaries) as there is no form_errors call
You've explicitly added form_errors whereas error message already rendered via form_label. either you can remove form_errors or form_label.

Show the thumbnails instead of the name of img with Symfony2 formbuilder

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 %}

Symfony2 form in base.html.twig

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

Form theming a collection widget

I have a collection widget in my form. That's displayed like:
Teams 0 player1 inputfield
1 player2 inputfield
I would like to not display the word 'teams' and the '0' and the '1'.
I've got this block in my fields.html.twig template, but not really sure how to edit this.
{% block collection_widget %}
{% spaceless %}
{% if prototype is defined %}
{% set attr = attr|merge({'data-prototype': form_row(prototype) }) %}
{% endif %}
{{ block('form_widget') }}
{% endspaceless %}
{% endblock collection_widget %}
{% block form_label %}
{% spaceless %}
<div class="hidden">
{{ block('generic_label') }}
</div>
{% endspaceless %}
{% endblock form_label %}
ChallengeType form:
class ChallengeType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('teams', 'collection', array(
'type' => new TeamType(),
'allow_add' => true
))
->add('place')
->add('date');
}
public function getName()
{
return 'challenge';
}
public function getDefaultOptions(array $options)
{
return array('data_class' => 'Tennisconnect\DashboardBundle\Entity\Challenge');
}
}
Thx.
Those lables are created in form_label block. I usually wrap them in a div and set them hidden when needed.
Edit:
There is a better solution :).
Change collection section of the ChallengeType.php with following
->add('teams', 'collection', array(
'type' => new TeamType(),
//label for Teams text
'attr' => array('class' => 'team-collection'),
//label for each team form type
'options' => array(
'attr' => array('class' => 'team-collection')
),
'allow_add' => true
))
Now those unwanted labels will have team-collection class. In your css file you can set display:none for label.team-collection. No need to change form theme block definition.

Resources