Symfony3 trying to save into db with forms and twig - php

It is my first attempt to store product's information into mysql. I apologize in advance if the code is not in the best form, Im still learning. This is my registration.html.twig
{% extends 'base.html.twig' %}
{% block stylesheets %}
<link href="{{ asset('bundles/framework/css/myStyle.css') }}" rel="stylesheet" />
{% endblock %}
{% block title %}Create your product {% endblock %}
{% block body %}
<div id="container">
<h1 id="header">Create your product and store it in the database!</h1>
</div>
<form method="POST" id="registrationForm">
<input type="text" id="pName" placeholder="Product name">
<input type="text" id="pPrice" placeholder="Product price"> <br>
<textarea id="pDescription" placeholder="Product description"></textarea>
</form>
<input type="submit" value="Submit" >
{% endblock %}
I also have Product entity class with setters&getters, not going to include it here because of amount of code.
This is my ProductForm :
class ProductForm extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('pName')
->add('pPrice')
->add('pDescription')
;
}
}
and here is part of my controller for the /insert page:
public function createAction()
{
$product = 'AppBundle\Entity\Product';
$form = $this->createForm(ProductForm::class, $product);
if($form->isValid())
{
$em= $this->getDoctrine()->getManager();
//Save into database code should go here...
}
}
For starters, it complains that it Expected argument of type "object, array or empty", "string" given which I guess comes from the controller where I try to pass $product variable with specified path to the entity class. In the documentation this part is very confusing. It says $product = ... leaving me without anything, I managed to understand that this is a path to entity class which should be passed to createForm method, but as mentioned before it complains that it is string, not array. Could someone review my code and give a feedback on what is wrong? Im really lost at the moment and not sure what to do next or how to solve this. Thank you in advance.

Your form fields don't have name attributes set, which means that no data will be posted from it.
In the controller, $product should be an instance of AppBundle\Entity\Product, not just a class name:
$product = new \AppBundle\Entity\Product();
The form will not automatically retrieve the data from HTTP request (which are not there because of point 1), you must handle the request manually:
// inject the request here
public function createAction(\Symfony\Component\HttpFoundation\Request $request) {
// ...
$form->handleRequest($request);
if ($form->isValid()) {
// ...
}
}

Related

Using the same form multiple times with different id

In my case, i would like to add for each application OneToMany links of documentation with the same form who contain two inputs, one for the name of the documentation and the second is for the link address documentation, the problem is that it work perfectly only for the first application in the list, but if i try to add a name and link address for the second application on the list or even the third .., when I submit my form, Symfony does not save the information in the database (without error message).
By the way i'm using modals to open the form.
As a beginner and in my first steps with symfony, i tried to read the documentation but i couldn't find a solution to resolve my problem, please find below the related code, thanks!
index.html.twig
{%for applications in applications %}
.....
<div class="tab-pane fade" id="dropdown-kv-21-1{{applications.id}}">
<p>Ajout de liens</p>
{{ form_start(form) }}
<div class="form_group">
<label for="{{form.nom.vars.id}}">Nom</label>
<input type="text" class="form_control" id="{{form.nom.vars.id}}"name={{form.nom.vars.full_name}}" value="{{form.nom.vars.value}}">
{{form_errors(form.nom)}}
{% do form.nom.setRendered %}
</div>
<div class="form_group">
<label for="{{form.lien.vars.id}}">Lien</label>
<input type="text" class="form_control" id="{{form.lien.vars.id}}" name="{{form.lien.vars.full_name}}" value="{{form.lien.vars.value}}">
{{form_errors(form.lien)}}
{% do form.lien.setRendered %}
</div>
<div class="form_group">
<select id="{{form.application.vars.id}}" class="form-control" name="{{form.application.vars.full_name}}" >
<option value="{{applications.id}}">{{applications.id}}</option>
</select>
{{form_errors(form.application)}}
{% do form.application.setRendered %}
</div>
<input type="submit" class="btn btn-success" value="Ajouter" style="transform: translate(27em);"id="carto_cartographiebundle_liendocapp_ajouter"name="carto_cartographiebundle_liendocapp[ajouter]">
{% do form.ajouter.setRendered %}
{{form_end(form)}}
{%endfor%}
AcceuilController
public function indexAction(Request $request)
{
$em = $this->getDoctrine()->getManager();
$applications = $em->getRepository('CartoBundle:Application')->findAll();
if($request->isMethod('POST')){
$nomApp = $request->get('nomApp');
$applications = $em->getRepository('CartoBundle:Application')->findBy(array("nomApp"=>$nomApp));
}
$lienDocApp = new LienDocApp();
$form=$this->createForm(LienDocAppType::class, $lienDocApp);
$form->handleRequest($request);
if ($form->isSubmitted()&& $form->isValid()){
$em = $this->getDoctrine()->getManager();
$em->persist($lienDocApp);
$em->flush();
}
return $this->render('CartoBundle:Accueil:index.html.twig', array(
'applications'=>$applications,
'lienDocApp' => $lienDocApp,
'form' => $form->createView(),
));
}
LienDocAppType
class LienDocAppType extends AbstractType{
/**
* {#inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('nom')
->add('lien')
->add('application')
->add('ajouter', SubmitType::class);
}/**
* {#inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Carto\cartographieBundle\Entity\LienDocApp'
));
}
/**
* {#inheritdoc}
*/
public function getBlockPrefix()
{
return 'carto_cartographiebundle_liendocapp';
}
}
i finally found the solution for my problem and i'm posting it here maybe it will be helpful form some one,
actually there is a hidde, input named _token, all i did is changing the Id of this input with the id of the chosen application
<input type="hidden" id="{{ form._token.vars.id }}" name="{{form._token.vars.full_name}}" value="{{ form._token.vars.value }}">
will be :
<input type="hidden" id="{{ applications.id }}" name="{{form._token.vars.full_name}}" value="{{ form._token.vars.value }}">
cheers !

How to write Controller in GRAV CMS

I created a form and want to save submitted data in back-end
//front-end
{% extends 'partials/base.html.twig' %}
{% block content %}
<form method="post" action="savedata">
<input type=text name="data">
<input type="submit">
</form>
{% endblock %}
//back-end
function savedata()
{
echo 'saved data:'.$_POST['data'];
exit;
}
I expect the code like this
$grav->post('/savedata', DataController->savedata);
But I cannot found any tutorial about custom Controller in documentation
How can I achieve this purpose?

Symfony 2: How to build a really simple form, with just one field and use it in controller

I'm building a get started procedure. It is really very simple: just one field for the email and a submit button.
HOW DOES THE PROCEDURE WORKS
I simply have one controller with two methods: indexAction() and endAction()
The indexAction simply set the route using annotations and displays the twig template with an handmade form:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Component\HttpFoundation\Request;
class GetStartedController extends Controller
{
/**
* #Route("getstarted")
* #Template()
*/
public function indexAction()
{
return array(
// ...
);
}
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
// ... other code
As you can see, currently the method really does nothing as the form is handmade directly in the twig template.
Here is the code to render the form (handmade in the twig template):
{% extends "::base.html.twig" %}
{% block title %}AppBundle:GetStarted:index{% endblock %}
{% block body %}
<form id="getStarted" action="{{ path('getStartedEnd') }}" method="post">
<div class="form-group">
<input type="email" name="userEmail" class="form-control input-lg" placeholder="Enter your e-mail (es.: your.name#example.com">
</div>
<button type="submit" name="submit" class="btn btn-default btn-lg">Submit</button>
</form>
{% endblock %}
I know Symfony 2 can handle the creation of the form and its rendering on the page, but this is a very simple form and I know it never will be more complicate, so an handmade one is the simplest and fastest solution.
Now, what's the problem? The problem is I don't understand how to get the form and its submitted values in my controller, in the other method, endAction().
The form has as action the path to the other method, endAction() where, in my intentions, I will retrieve the submitted values and do some stuff with them.
Following the instructions in paragraph Using a Form without a Class, i came up with the following code for the method endAction() that, in my intentions, will retrieve the submitted email and do something with it - will create a user, obviously - (note the exit to simply print the results of the if)
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
$form = $this->createFormBuilder()
->add('userEmail', 'email')
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
print_r($data);
} else {
echo 'no data submitted';
}
exit;
return array(
// ...
);
}
The problem is that if ($form->isValid()) { ever returns false and I ever obtain as result the printing of echo 'no data submitted';
Clearly there is something wrong with my implementation of the form handling, but... What?
This is my question: what am I doing wrong? Why the form isn't "intercepted" in the controller?
You have two things to do:
First, edit your Form template and use the correct names (name="form[userEmail]" instead of name="userEmail"):
<form name="form" method="post" action="{{ path('getStartedEnd') }}">
<div id="form">
<div>
<label for="form_userEmail" class="required">User email</label>
<input type="email" id="form_userEmail" name="form[userEmail]" required="required"/>
</div>
<div>
<button type="submit" id="form_submit" name="form[submit]">Submit</button>
</div>
</div>
</form>
And second disable the csrf protection of the form:
/**
* #Route("getstarted/end", name="getStartedEnd")
* #Template()
*/
public function endAction(Request $request)
{
$data = array();
$form = $this->createFormBuilder($data, array(
'csrf_protection' => false,
))
->add('userEmail', 'email')
->add('submit', 'submit')
->getForm();
$form->handleRequest($request);
if ($form->isValid()) {
$data = $form->getData();
print_r($data);
} else {
echo 'no data submitted';
}
exit;
return array(
// ...
);
}
But recommend strongly not to render the form by hand!
It's not more than:
/**
* #Route("getstarted")
* #Template()
*/
public function indexAction()
{
$data = array();
$form = $this->createFormBuilder($data, array(
'action' => $this->generateUrl('getStartedEnd'),
))
->add('userEmail', 'email')
->add('submit', 'submit')
->getForm();
return array(
'form' => $form->createView(),
);
}
and in your template:
{# just this line #}
{{ form(form) }}

Wrap Symfony form fields in a div in Twig

Given the simple form build:
$form = add('a')
->add('b')
->add('c')
->add('d')
->add('e')
->add('f');
I'd like to wrap a div tag around ABC and DEF, like so:
<div class="section1">
<input type="text" name="a" />
<input type="text" name="b" />
<input type="text" name="c" />
</div>
<div class="section2">
<input type="text" name="d" />
<input type="text" name="e" />
<input type="text" name="f" />
</div>
Problem is, I am just able to use the Symfony Form Component for this project. Is there a way with twig to render the form fields in groups like above? I will need to specify logic that says something simliar to "start section 1 with 'a' and section 2 with 'd'", so this way if any fields change inbetween (say we remove the field with the name 'b') the form will still work.
My twig file looks like this, which is obviously not correct:
<form action="#" method="POST" {{ form_enctype(form) }}>
{% for child in form.children %}
<div class="form_row">
{{ form_label(form) }}
{{ form_errors(form) }}
{{ form_widget(form) }}
</div>
{% endfor %}
</form>
The best way to do it I think is building the form outside the controller (as a class, extending AbstracType)
So, you will need these files:
Your controller (as DefaultController.php)
Your twig template (as index.html.twig)
Your main form (MyFormType.php in this example)
One form for each section (in your example Section1Type.php and Section2Type.php)
So, the idea is building one single form (MyFormType) made by many individual section forms (Section1Type and Section2Type). Then calling this main form in your controller and rendering it in your template (with a "for" loop for each section).
So here you have the code:
Your controller:
<?php
# /src/Acme/DefaultBundle/Controller/DefaultController.php
namespace Acme\DefaultBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Acme\DefaultBundle\Form\MyFormType;
class DefaultController extends Controller
{
public function indexAction()
{
$form = $this->createForm(new MyFormType());
return $this->render('AcmeDefaultBundle:Default:index.html.twig', array(
'form' => $form->createView()
));
}
}
Your template:
{# /src/Acme/DefaultBundle/Resources/views/Default/index.html.twig #}
{{ form_start(form) }}
<div class="section1">
{% for input in form.section1 %}
{{ form_label(input) }}
{{ form_widget(input) }}
<br>
{% endfor %}
</div>
<div class="section2">
{% for input in form.section2 %}
{{ form_label(input) }}
{{ form_widget(input) }}
<br>
{% endfor %}
</div>
{{ form_end(form) }}
The main form:
<?php
# /src/Acme/DefaultBundle/Form/MyFormType.php
namespace Acme\DefaultBundle\Form;
use Acme\DefaultBundle\Form\Section1Type;
use Acme\DefaultBundle\Form\Section2Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class MyFormType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
// This calls your section forms as many as you need
$builder->add('section1', new Section1Type());
$builder->add('section2', new Section2Type());
$builder->add('Send', 'submit');
}
public function getName()
{
return 'myform';
}
}
The Section1 form:
<?php
# /src/Acme/DefaultBundle/Form/Section1Type.php
namespace Acme\DefaultBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class Section1Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('a', 'text');
$builder->add('b', 'text');
$builder->add('c', 'text');
}
public function getName()
{
return 'section1';
}
}
And Section2 form:
<?php
# /src/Acme/DefaultBundle/Form/Section2Type.php
namespace Acme\DefaultBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
class Section2Type extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('d', 'text');
$builder->add('e', 'text');
$builder->add('f', 'text');
}
public function getName()
{
return 'section2';
}
}
And that's it, you can edit each individual section (adding and removing inputs) and you will get all of them in your template without modifying it.

Why did I get a string with when binding a request with Symfony2?

I'm trying to update an object:
public function editAction(Artist $artist)
{
if (!$artist) {
throw $this->createNotFoundException('Unable to find Artist entity.');
}
// We create the form from the external re-usable form made in TestBundle/Form/artist.php
$form = $this->createForm( new ArtistType, $artist);
// We get the request type
$request = $this->get('request');
// If it is a POST request, the user validated the form
if ($request->isMethod('POST')) {
// We make the link Request <-> Form
// Now, $request = Values entered by the user
$form->bind($request);
// We validate the values
if ($form->isValid()) {
// We save $artist in the DB
$em = $this->getDoctrine()->getManager();
$em->persist($artist);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Artist edited successfully');
// Everything is fine, we redirect the user
return $this->redirect($this->generateUrl('ymtest_Artist'));
}
}
// We pass the createView() form method to the viexw so that it can print the form if the user arrived on this page with a GET method (he didnt validate the form yet)
return $this->render('YMTestBundle:Musician:edit.html.twig', array(
'form' => $form->createView(),
'artist' => $artist
));
}
But when I'm validating the form, I get an exception:
Expected argument of type object or array, string given
My form looks like this:
{# src/YM/TestBundle/Resources/views/Musician/add.html.twig #}
{% extends "YMTestBundle::layout.html.twig" %}
{% block bodyAdmin %}
<div class="container">
<form action="{{ path('ymtest_EditArtist', {'id': artist.id}) }}" method="post" {{ form_enctype(form) }}>
<div class="row">
{% if form_errors(form)|length != 0 %}
<div class="span12 alert alert-error" style="margin-left:0px">
{# Les erreurs générales du formulaire. #}
{{ form_errors(form) }}
</div>
{% endif %}
</div>
<div class="row">
<div class="span10 BoxesW">
<div>
{{ form_label(form.name, "Artist Name") }}
{{ form_errors(form.name) }}
{{ form_widget(form.name) }}
</div>
<div>
{{ form_label(form.biography, "Artist Biography") }}
{{ form_errors(form.biography) }}
{{ form_widget(form.biography, {'attr':{'class': 'span10' }, 'id': 'wysiwyg' }) }}
</div>
{{ form_rest(form) }}
</br>
<div>
<input type="submit" class="btn btn-primary" />
</div>
</div>
</div>
</form>
</div>
{% endblock %}
And the route is correct since I get the form before validating it.
Thanks for your help
UPDATE:
Here is My new controller:
<?php
namespace YM\TestBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\EntityRepository;
use YM\TestBundle\Entity\Artist;
use YM\TestBundle\Form\ArtistType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
class MusicianController extends Controller
{
/**
* #Route("/Artist/edit/{id}")
* #ParamConverter("artist", class="YMTestBundle:Artist")
*/
public function editAction(Artist $artist)
{
if (!$artist) {
throw $this->createNotFoundException('Unable to find Artist entity.');
}
// We create the form from the external re-usable form made in TestBundle/Form/artist.php
$form = $this->createForm( new ArtistType, $artist);
// We get the request type
$request = $this->get('request');
// If it is a POST request, the user validated the form
if ($request->isMethod('POST')) {
// We make the link Request <-> Form
// Now, $request = Values entered by the user
$form->bind($request);
// We validate the values
if ($form->isValid()) {
// We save $artist in the DB
$em = $this->getDoctrine()->getManager();
$em->persist($artist);
$em->flush();
$this->get('session')->getFlashBag()->add('info', 'Artist edited successfully');
// Everything is fine, we redirect the user
return $this->redirect($this->generateUrl('ymtest_Artist'));
}
}
// We pass the createView() form method to the viexw so that it can print the form if the user arrived on this page with a GET method (he didnt validate the form yet)
return $this->render('YMTestBundle:Musician:edit.html.twig', array(
'form' => $form->createView(),
'artist' => $artist
));
}
}
EDIT
We found in the chat that the problem was in the entity annotations.
#Assert\Valid() was used on a string variable.
You have this action="{{ path('ymtest_EditArtist', {'id': artist.id}) }}" that I suppose generates an url like editArtist/1234 (so passing a string).
And then you have this public function editAction(Artist $artist) that's requiring an object of type Artist.
You should change it to something like:
public function editAction($artistid)
{
$em = $this->getDoctrine()->getManager();
$artist= $em->getRepository('YourBundle:Artist')->find($artistid);
if (!$artist) {
throw $this->createNotFoundException('No artist found for id '.$artistid);
}
//Do whatever you want
}
Remark: You don't need to call $em->persist($artist); when updating an object (http://symfony.com/doc/current/book/doctrine.html#updating-an-object).

Categories