When I try to execute the action below I'm getting this error:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'category_id' cannot be null (500 Internal Server Error)
As you can see in the action I'm setting the field category_id (setCategoryId()), so what's the reason of the error?
Note: as you can see I'm trying to save in the database a proposal (propuesta) and tags associated to it.
public function savePropuestaAction() {
$em = $this->get('doctrine.orm.entity_manager');
$propuesta = new Propuestas();
//retrieve the values inserted in the form.
$form = $this->getRequest()->request->get('form');
$propuestaContent = $form['propuesta'];
$propuestaCategory = $form['CategorÃa'][0];
$propuesta->setTitulo('$propuestaCategory');
$propuesta->setContenido($propuestaContent);
$propuesta->setCategoryId(1);
//retrieve the tags inserted in the form...
$tags = array();
foreach($form as $key => $field)
{
if(substr($key, 0, 3) == 'tag')
{
$tagsName[] = $field;
}
}
// ...and then store them and associate them to the proposal.
if(count($tagsName))
{
foreach($tagsName as $tagName)
{
$tag = new TagPropuesta();
$tag->setName($tagName);
$em->persist($tag);
$em->flush();
$propuesta->addTagPropuesta($tag);
$em->persist($propuesta);
$em->flush();
}
}
return new Response();
}
EDIT: after a couple of answers I've tried it replacing the line
$propuesta->setCategoryId(1);
with
$repository = $this->getDoctrine()->getRepository('JanderJanderBundle:PropuestasCategory');
$category = $repository->find(1);
//die(get_class($category));
$propuesta->setCategory($category);
but the error message is the same..
Here you have also the .yml file and the entity Propuesta (I didn't modify Propuesta class at all, I just generated it using generate:entities task):
Jander\JanderBundle\Entity\Propuestas:
type: entity
table: propuestas
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
category_id:
type: integer
nullable: true
user_id:
type: integer
nullable: true
titulo:
type: string
length: 230
fixed: false
contenido:
type: string
length: 230
fixed: false
tema:
type: string
length: 40
fixed: false
nullable: true
eliminado:
type: boolean
nullable: true
created:
type: date
gedmo:
timestampable:
on: create
votes_up:
type: integer
nullable: true
votes_down:
type: integer
nullable: true
manyToOne:
category:
targetEntity: PropuestasCategory
inversedBy: propuestas
joinColumn:
name: category_id
referencedColumnName: id
usuario:
targetEntity: Usuario
inversedBy: propuestas
joinColumn:
name: user_id
referencedColumnName: id
manyToMany:
tags:
targetEntity: TagPropuesta
inversedBy: propuestas
lifecycleCallbacks: { }
<?php
namespace Jander\JanderBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Jander\JanderBundle\Entity\Propuestas
*/
class Propuestas
{
/**
* #var integer $id
*/
private $id;
/**
* #var string $contenido
*/
private $contenido;
/**
* #var string $tema
*/
private $tema;
/**
* #var boolean $eliminado
*/
private $eliminado;
/**
* #var date $created
*/
private $created;
/**
* #var integer $votes
*/
private $votes;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set contenido
*
* #param string $contenido
*/
public function setContenido($contenido)
{
$this->contenido = $contenido;
}
/**
* Get contenido
*
* #return string
*/
public function getContenido()
{
return $this->contenido;
}
/**
* Set tema
*
* #param string $tema
*/
public function setTema($tema)
{
$this->tema = $tema;
}
/**
* Get tema
*
* #return string
*/
public function getTema()
{
return $this->tema;
}
/**
* Set eliminado
*
* #param boolean $eliminado
*/
public function setEliminado($eliminado)
{
$this->eliminado = $eliminado;
}
/**
* Get eliminado
*
* #return boolean
*/
public function getEliminado()
{
return $this->eliminado;
}
/**
* Set created
*
* #param date $created
*/
public function setCreated($created)
{
$this->created = $created;
}
/**
* Get created
*
* #return date
*/
public function getCreated()
{
return $this->created;
}
/**
* Set votes
*
* #param integer $votes
*/
public function setVotes($votes)
{
$this->votes = $votes;
}
/**
* Get votes
*
* #return integer
*/
public function getVotes()
{
return $this->votes;
}
/**
* #var integer $category_id
*/
private $category_id;
/**
* #var Jander\JanderBundle\Entity\PropuestasCategory
*/
private $category;
/**
* Set category_id
*
* #param integer $categoryId
*/
public function setCategoryId($categoryId)
{
$this->category_id = $categoryId;
}
/**
* Get category_id
*
* #return integer
*/
public function getCategoryId()
{
return $this->category_id;
}
/**
* Set category
*
* #param Jander\JanderBundle\Entity\PropuestasCategory $category
*/
public function setCategory(\Jander\JanderBundle\Entity\PropuestasCategory $category)
{
$this->category = $category;
}
/**
* Get category
*
* #return Jander\JanderBundle\Entity\PropuestasCategory
*/
public function getCategory()
{
return $this->category;
}
/**
* #var Jander\JanderBundle\Entity\TagPropuesta
*/
private $tags;
public function __construct()
{
$this->tags = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Add tags
*
* #param Jander\JanderBundle\Entity\TagPropuesta $tags
*/
public function addTagPropuesta(\Jander\JanderBundle\Entity\TagPropuesta $tags)
{
$this->tags[] = $tags;
}
/**
* Get tags
*
* #return Doctrine\Common\Collections\Collection
*/
public function getTags()
{
return $this->tags;
}
/**
* #var integer $user_id
*/
private $user_id;
/**
* #var Jander\JanderBundle\Entity\Usuario
*/
private $usuario;
/**
* Set user_id
*
* #param integer $userId
*/
public function setUserId($userId)
{
$this->user_id = $userId;
}
/**
* Get user_id
*
* #return integer
*/
public function getUserId()
{
return $this->user_id;
}
/**
* Set usuario
*
* #param Jander\JanderBundle\Entity\Usuario $usuario
*/
public function setUsuario(\Jander\JanderBundle\Entity\Usuario $usuario)
{
$this->usuario = $usuario;
}
/**
* Get usuario
*
* #return Jander\JanderBundle\Entity\Usuario
*/
public function getUsuario()
{
if($this->usuario == null)
{
return "anonimo";
}else{
return $this->usuario;
}
}
/**
* #var integer $jander
*/
private $jander;
/**
* Set jander
*
* #param integer $jander
*/
public function setJander($jander)
{
$this->jander = $jander;
}
/**
* Get jander
*
* #return integer
*/
public function getJander()
{
return $this->jander;
}
/**
* #var integer $votes_up
*/
private $votes_up;
/**
* #var integer $votes_down
*/
private $votes_down;
/**
* Set votes_up
*
* #param integer $votesUp
*/
public function setVotesUp($votesUp)
{
$this->votes_up = $votesUp;
}
/**
* Get votes_up
*
* #return integer
*/
public function getVotesUp()
{
if($this->votes_up == null)
{
return 0;
}
else
{
return $this->votes_up;
}
}
/**
* Set votes_down
*
* #param integer $votesDown
*/
public function setVotesDown($votesDown)
{
$this->votes_down = $votesDown;
}
/**
* Get votes_down
*
* #return integer
*/
public function getVotesDown()
{
if($this->votes_down == null)
{
return 0;
}
else
{
return $this->votes_down;
}
}
public function getTotalVotes()
{
return ($this->getVotesDown()+$this->getVotesUp());
}
/**
* #var string $titulo
*/
private $titulo;
/**
* Set titulo
*
* #param string $titulo
*/
public function setTitulo($titulo)
{
$this->titulo = $titulo;
}
/**
* Get titulo
*
* #return string
*/
public function getTitulo()
{
return $this->titulo;
}
}
Javi
I think in your db table named 'propuesta' have a foreignkey category_id,right? Then in your entity must have a field category not category_id and its set and get function are setCategory() and getCategory(),not setCategoryId(). For a foreignkey related field must have expected its object. So in your case
$category=$em->getDoctrine()->getEnitityManager()->getRepository('YourBundleName:Category')->find($id);
$propuesta->setCategory($category);//here category is an object.
so first check your entity of propuesta and its yml file.
updated
1. Change your yml like this
Jander\JanderBundle\Entity\Propuestas:
type: entity
table: propuestas
fields:
id:
id: true
type: integer
unsigned: false
nullable: false
generator:
strategy: IDENTITY
user_id:
type: integer
nullable: true
titulo:
type: string
length: 230
fixed: false
contenido:
type: string
length: 230
fixed: false
tema:
type: string
length: 40
fixed: false
nullable: true
eliminado:
type: boolean
nullable: true
created:
type: date
gedmo:
timestampable:
on: create
votes_up:
type: integer
nullable: true
votes_down:
type: integer
nullable: true
manyToOne:
category:
targetEntity: PropuestasCategory
inversedBy: propuestas
joinColumn:
name: category_id
referencedColumnName: id
usuario:
targetEntity: Usuario
inversedBy: propuestas
joinColumn:
name: user_id
referencedColumnName: id
manyToMany:
tags:
targetEntity: TagPropuesta
inversedBy: propuestas
lifecycleCallbacks: { }
You do'nt need to specify the category_id in your yml file,just specify the relation.
Change your entity file also
remove the field categoryid its setCategoryId() and getCategoryId() function.
You must change all your yml file of other table and give the relation as the above.Also change their entity files.
Just go through how to write yml file and its relation,then use generate:enitites command of symfony2.
If you don't know how to write yml and its entity Another one good method is reverse process
1. First create your db and its tables, give its foreignkey relations as you need in your msql.
set your database connections in parameters.ini file of your project.(hopes you know,just give dbname,username,password(if any)).
Delete all your yml file in res/config/doctrine/ and Entity files.
4.open your terminal just give the following commands.These below commands generate automatically all your entity files and yml files with correct relations as you specify in your db.
a).php app/console doctrine:mapping:convert yml ./src/Acme/BlogBundle/Resources/config/doctrine --from-database --force //Acme/BlogBundle/ is namespace
Symfony2 generate entity from Database
b).php app/console doctrine:mapping:import AcmeBlogBundle yml //AcmeBlogBundle is bundle name
c).php app/console doctrine:generate:entities AcmeBlogBundle
if you have any doubts refer this linkClick here
Hope this helps you
Need to thinks objects, not ids';
$category = $em=>getReference('Category',1);
$propuesta->setCategory($category);
I'm assuming that you did define a relation between propuesta and category.
Related
I have an entity with one TEXT (MySQL) attributes
<?php
namespace App\Entity;
use ApiPlatform\Core\Annotation\ApiResource;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\ORM\Mapping\Table;
use Doctrine\ORM\Mapping\Index;
use ApiPlatform\Core\Annotation\ApiProperty;
/**
* #ApiResource(
* attributes={},
* collectionOperations={
* "get"={},
* "post"={
* "access_control"="is_granted('ROLE_COMPANY')"
* },
* },
* itemOperations={
* "get"={},
* "put"={"access_control"="is_granted('ROLE_COMPANY')"},
* }
* )
* #ORM\Entity(
* repositoryClass="App\Repository\SettingRepository",
* )
* #ORM\Table(
* indexes={#Index(name="domain_idx", columns={"domain"})}
* )
*/
class Setting
{
/**
* #var Uuid
* #ApiProperty(identifier=true)
* #ORM\Id
* #ORM\Column(type="string")
* #ORM\GeneratedValue(strategy="NONE")
*/
private $identifier;
/**
* #ORM\Column(type="text", nullable=true)
*/
private $data = array();
/**
* #ORM\Column(type="string", nullable=true)
*/
private $domain = array();
public function getData()
{
if($this->data == null) return array();
$data = unserialize($this->data);
return $data;
}
public function setData($data): self
{
$this->data = serialize($data);
return $this;
}
/**
* #return mixed
*/
public function getIdentifier()
{
return $this->identifier;
}
/**
* #param mixed $key
*/
public function setIdentifier($identifier): self
{
$this->identifier = $identifier;
return $this;
}
/**
* #return mixed
*/
public function getDomain()
{
return $this->domain;
}
/**
* #param mixed $domain
*/
public function setDomain($domain): self
{
$this->domain = $domain;
return $this;
}
}
If I try to invoke the service with the following parameter structure it works fine:
{
"data": "testData",
"identifier": "testIdentifier",
"domain": "domain1"
}
But If I would like to store an embedded JSON string, for example:
"data": {"temp": 123}
I receive the following error:
hydra:description": "The type of the \"data\" attribute must be \"string\", \"array\" given.",
I tried to convert the object into an string in the method setData. But this method will not be invoked. It seams, that the API-Platform detects the wrong type and throws the exception.
I found some comments, that it is necessary to decorate the property:
https://api-platform.com/docs/core/serialization/#decorating-a-serializer-and-adding-extra-data
Can anyone give me an example? It does not work!
Where is the right place to serialise and unserialise the property data?
Does anyone have an idea?
Kind regards
You need to set the column type to json in MySQL. It should behave as expected.
/**
* #var array Additional data describing the setting.
* #ORM\Column(type="json", nullable=true)
*/
private $data = null;
I think null is more consistent than an empty array, but that's your choice.
I want to have a file or list that I can update easily with values that might change throughout my application.
I don't really want to hard code text values into the templates. I prefer to have all of these values in one place and labelled correctly.
Examples of values that might get updated are:
Page title
Logo text
Brand or company name
I have thought about two options:
Add them to the twig config in config.yml. This is a bit messy and doesn't seem organised if I decide to put a lot of values there.
Make a database table for these and include the entity in each controller where I need to use the values. This might be creating too much work.
Are there any other options or are one of these more suitable?
Thank you.
You need to create a twig function and use it to return the value you want. For example:
namespace AppBundle\Twig;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerAwareTrait;
use Symfony\Component\DependencyInjection\ContainerInterface;
class TwigExtension extends \Twig_Extension implements ContainerAwareInterface
{
use ContainerAwareTrait;
/**
* #var ContainerInterface
*/
protected $container;
public function getFunctions()
{
return array(
new \Twig_SimpleFunction('parameter', function($name)
{
try {
return $this->container->getParameter($name);
} catch(\Exception $exception) {
return "";
}
})
);
}
/**
* Returns the name of the extension.
*
* #return string The extension name
*/
public function getName()
{
return 'app.twig.extension';
}
}
This will create a function called parameter and once you call it in twig {{ parameter('my.parameter') }} it will return the parameter. You need to load it as a service, which you can do by adding the following to your services.yml file:
app.twig.extension:
class: AppBundle\Twig\TwigExtension
calls:
- [setContainer, ["#service_container"]]
tags:
- { name: twig.extension }
From personal experience people usually want to be able to change some of the parameters. This is why I usually prefer to create a Setting or Parameter entity which would look something like this:
/**
* Setting
*
* #ORM\Table(name="my_parameters")
* #ORM\Entity(repositoryClass="AppBundle\Repository\ParameterRepository")
*/
class Parameter
{
/**
* #var integer
*
* #ORM\Id
* #ORM\Column(name="parameter_id", type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="name", type="string", length=255)
*/
private $name;
/**
* #var string
*
* #ORM\Column(name="value", type="text", nullable=true)
*/
private $value;
/**
* #param string|null $name
* #param string|null $value
*/
public function __construct($name = null, $value = null)
{
$this->setName($name);
$this->setValue($value);
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* #param string $name
*
* #return Parameter
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* #return string
*/
public function getName()
{
return $this->name;
}
/**
* Set value
*
* #param string $value
*
* #return Parameter
*/
public function setValue($value = null)
{
$this->value = serialize($value);
return $this;
}
/**
* Get value
*
* #return string
*/
public function getValue()
{
$data = #unserialize($this->value);
return $this->value === 'b:0;' || $data !== false ? $this->value = $data : null;
}
}
Then I would add a CompilerPass which will help get all of the parameters from the database and cache them so that your app doesn't make unnecessary sql queries to the database. That might look something similar to the following class:
// AppBundle/DependencyInjection/Compiler/ParamsCompilerPass.php
namespace AppBundle\DependencyInjection\Compiler;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class ParamsCompilerPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$em = $container->get('doctrine.orm.default_entity_manager');
$settings = $em->getRepository('AppBundle:Parameter')->findAll();
foreach($settings as $setting) {
// I like to prefix the parameters with "app."
// to avoid any collision with existing parameters.
$container->setParameter('app.'.strtolower($setting->getName()), $setting->getValue());
}
}
}
And finally, in your bundle class (i.e. src/AppBundle/AppBundle.php) you add the compiler pass:
namespace AppBundle;
use AppBundle\DependencyInjection\Compiler\ParamsCompilerPass;
use Symfony\Component\DependencyInjection\Compiler\PassConfig;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
public function build(ContainerBuilder $builder)
{
parent::build($builder);
$builder->addCompilerPass(new ParamsCompilerPass(), , PassConfig::TYPE_AFTER_REMOVING);
}
}
Now you can create a DoctrineFixture template to load the parameters you use all the time. With the TwigExtension you will still be able to call the parameter from the twig template and you can create a web UI to change some of the parameters/settings.
I am trying to handle image uploads via a form and then display these image uploads elsewhere in my website.
So far I have been looking at the Cookbook and also at other tutorials.I have created an Entity and a Form and am able to submit , but I am not sure if I am storing the file itself in my DB or just the name of it.
When I try to display the images in my view resource not found error on the images. Any suggestions on the correct way to upload images in Symfony?
and this is what I have so far. Entity
<?php
namespace BlogBundle\Entity;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* Photo
*
*
* #ORM\Entity
* #ORM\HasLifecycleCallbacks
*/
class Upload
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*Image File
* #var File
* #Assert\File( maxSize = "5M",mimeTypes = {"image/jpeg", "image/gif", "image/png", "image/tiff"}, mimeTypesMessage = "Please upload a valid Image")
*
*/
private $file;
/**
* #ORM\Column(type="string", length=500)
*/
private $title;
/**
* #ORM\Column(type="string", length=500)
*/
private $description;
/**
* Image path
*
* #var string
*
* #ORM\Column(type="text", length=255, nullable=false)
*/
protected $path;
protected function getUploadRootDir()
{
return __DIR__.'/../../../../web/'.$this->getUploadDir();
}
protected function getUploadDir()
{
// get rid of the __DIR__ so it doesn't screw up
// when displaying uploaded doc/image in the view.
return 'uploads/documents';
}
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set title
*
* #param string $title
* #return Upload
*/
public function setTitle($title)
{
$this->title = $title;
return $this;
}
/**
* Get title
*
* #return string
*/
public function getTitle()
{
return $this->title;
}
/**
* Set description
*
* #param string $description
* #return Upload
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* #return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set path
*
* #param string $path
* #return Upload
*/
public function setPath($path)
{
$this->path = $path;
return $this;
}
/**
* Get path
*
* #return string
*/
public function getPath()
{
return $this->path;
}
/**
* Called before saving the entity
* #ORM\PrePersist()
* #ORM\PreUpdate()
*/
public function preUpload()
{
if (null !== $this->file) {
// do whatever you want to generate a unique name
$filename = sha1(uniqid(mt_rand(), true));
$this->path = $filename.'.'.$this->file->guessExtension();
}
}
/**
* Called before entity removal
*
* #ORM\PreRemove()
*/
public function removeUpload()
{
if ($file = $this->getAbsolutePath())
{
unlink($file);
}
}
/**
* Called after entity persistence
*
* #ORM\PostPersist()
* #ORM\PostUpdate()
*/
public function upload()
{
// The file property can be empty if the field is not required
if (null === $this->file) {
return;
}
// Use the original file name here but you should
// sanitize it at least to avoid any security issues
// move takes the target directory and then the
// target filename to move to
$this->file->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
// set the path property to the filename where you've saved the file
$this->path = $this->getFile()->getClientOriginalName();
// Clean up the file property as you won't need it anymore
$this->file = null;
}
/**
* Sets file.
*
* #param UploadedFile $file
*#return Upload
*/
public function setFile(File $file = null)
{
$this->file = $file;
}
/**
* Get file.
*
* #return UploadedFile
*/
public function getFile()
{
return $this->file;
}
}
Controller methods
/** This is the homepage for the admin area, for adding,deleting,editing of blog posts.
* #Route("/posted/admin/upload", name="upload")
* #Template()
*/
public function uploadimageAction(Request $request)
{
$upload = new Upload();
//create checkboxtype form
$form = $this->createForm(new ImageFormType(), $upload, array(
'action' => $this->generateUrl('upload'),
'method' => 'POST',
));
$form->handleRequest($request);
if($form->isValid()){
$em = $this->getDoctrine()->getManager();
$upload->upload();
$em->persist($upload);
$em->flush();
// exit(\Doctrine\Common\Util\Debug::dump($post));
return $this->render('BlogBundle:Default:success.html.twig'
);
;
}
return $this->render('BlogBundle:Default:upload.html.twig',array(
'form' =>$form->createView(),
));
}
You are only storing the path in the DB, and only it's necesary that.
Using path you can show the file in your view
pd: you are storing the file in your server with:
$this->file->move(
$this->getUploadRootDir(),
$this->getFile()->getClientOriginalName()
);
i build a form in symfony 2.3. Which shows and save user profile and addresses for this profile.
I have two entities. A user and a address entity. (I will only show some code parts, this code is not complete here)
User Entity:
/**
* #ORM\Entity
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="Ebm\UserBundle\Entity\UserRepository")
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $firstName;
/**
* #ORM\Column(type="string", length=255)
*/
protected $lastName;
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
private $username;
/**
* #var \Doctrine\Common\Collections\Collection
* #Assert\Valid
* #ORM\ManyToMany(targetEntity="Ebm\UserBundle\Entity\Address", cascade={"persist", "remove"})
* #ORM\JoinTable(name="user_address",
* joinColumns={#ORM\JoinColumn(name="user_id", referencedColumnName="id")},
* inverseJoinColumns={#ORM\JoinColumn(name="address_id", referencedColumnName="id")}
* )
*/
protected $addresses;
}
Address Entity:
/**
* #ORM\Entity
* #ORM\Table(name="address")
*/
class Address
{
/**
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", length=255)
*/
protected $street;
}
And a form which embed the address entity as collection into the user form "usertype".
Usertype:
class UserType extends AbstractType
{
protected $securityContext;
public function __construct(SecurityContext $securityContext)
{
$this->securityContext = $securityContext;
}
/**
* (non-PHPdoc)
* #see \Symfony\Component\Form\AbstractType::buildForm()
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->->add('addresses', 'collection', array(
'type' => new AddressType() ,
'allow_add' => true,
'allow_delete' => true,
'label' => false,
'cascade_validation' => true)
));
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Ebm\UserBundle\Entity\User',
'cascade_validation' => true,
));
}
public function getName()
{
return 'user';
}
}
In my controller the validation part look like this:
$form = $this->createForm($this->get('form.type.user'), $user)->add('save', 'submit');
// On initial page load handleRequest() recognizes that the form was not submitted and does nothing
// isValid returns false
$form->handleRequest($request);
// Check if form isValid
if ($form->isValid()) {
}
And my validation yml (UserBundle/config/validation.yml) file look like this
Ebm\UserBundle\Entity\User:
properties:
firstName:
- NotBlank: ~
- Length:
max: 255
addresses:
- Valid: ~
Ebm\UserBundle\Entity\Address:
properties:
street:
- NotBlank: { message: "validate.not_blank" }
- Type:
type: string
- Length:
max: 255
If the address field is emtyp "street" as example no error was occured. Only validation errors for
entity "user" is displayed.
I'm looking since a week for a solution, i will be very lucky if anyone can help me
As of >= Symfony 2.5 you can use the deep parameter of getErrors
If you want to display errors for compound forms use the deep parameter in the getErrors() function:
// Check if form isValid
if ($form->isValid() === false) {
echo $form->getErrors(true);
}
Otherwise use the all function recursive:
public function getErrorsRecursive($form) {
$errors = array();
foreach ($form->getErrors() as $error) {
$errors[] = $error->getMessage();
}
foreach ($form->all() as $fieldName => $formType) {
if ($errorMessage = $this->getErrorsRecursive($formType)) {
$errors[$fieldName] = array_pop($errorMessage);
}
}
return $errors;
}
I'm trying to send a post request including two timestamps to my REST api.
The problem is that the timestamps are marked as invalid. "This value is not valid."
What am I doing wrong?
This is the request:
POST http://localhost:8000/app_test.php/api/projects/1/tasks/1/timetrackings
Accept: application/json
Content-Type: application/json
{"timeStart":1390757625,"timeEnd":1390757625,"projectMember":1}
The Controller looks as follows:
class MemberController extends BaseApiController implements ClassResourceInterface
{
public function postAction($projectId, $taskId, Request $request)
{
/** #var EntityManager $em */
$em = $this->getDoctrine()->getManager();
$this->findProject($em, $projectId);
$task = $this->findTask($em, $projectId, $taskId);
$request->request->add(array(
'task' => $taskId,
));
$form = $this->createForm(new TimeTrackType(), new TimeTrack());
$form->submit($request->request->all());
if ($form->isValid())
{
/** #var TimeTrack $tracking */
$tracking = $form->getData();
$task->addTimeTrack($tracking);
$em->flush();
return $this->redirectView(
$this->generateUrl('api_get_project_task_timetracking', array(
'projectId' => $projectId,
'taskId' => $taskId,
'trackingId' => $tracking->getId(),
)),
Codes::HTTP_CREATED
);
}
return View::create($form, Codes::HTTP_BAD_REQUEST);
}
}
The TimeTrackType class:
namespace PMTool\ApiBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TimeTrackType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('timeStart', 'datetime', array(
'input' => 'timestamp',
))
->add('timeEnd', 'datetime', array(
'input' => 'timestamp',
))
->add('projectMember')
->add('task')
;
}
/**
* #param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'PMTool\ApiBundle\Entity\TimeTrack',
'csrf_protection' => false,
));
}
/**
* #return string
*/
public function getName()
{
return 'timetrack';
}
}
The entity class:
namespace PMTool\ApiBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use \DateTime;
/**
* TimeTrack
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="PMTool\ApiBundle\Entity\TimeTrackRepository")
*/
class TimeTrack
{
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #var DateTime
*
* #ORM\Column(name="timeStart", type="datetime")
*/
private $timeStart;
/**
* #var DateTime
*
* #ORM\Column(name="timeEnd", type="datetime")
*/
private $timeEnd;
/**
* #var ProjectMember
*
* #ORM\ManyToOne(targetEntity="ProjectMember")
*/
private $projectMember;
/**
* #var Task
*
* #ORM\ManyToOne(targetEntity="Task", inversedBy="timeTracks")
* #ORM\JoinColumn(name="taskId", referencedColumnName="id")
*/
private $task;
/**
* Get id
*
* #return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set timeStart
*
* #param DateTime $timeStart
* #return TimeTrack
*/
public function setTimeStart($timeStart)
{
$this->timeStart = $timeStart;
return $this;
}
/**
* Get timeStart
*
* #return DateTime
*/
public function getTimeStart()
{
return $this->timeStart;
}
/**
* Set timeEnd
*
* #param DateTime $timeEnd
* #return TimeTrack
*/
public function setTimeEnd($timeEnd)
{
$this->timeEnd = $timeEnd;
return $this;
}
/**
* Get timeEnd
*
* #return DateTime
*/
public function getTimeEnd()
{
return $this->timeEnd;
}
/**
* #return \PMTool\ApiBundle\Entity\Task
*/
public function getTask()
{
return $this->task;
}
/**
* #param \PMTool\ApiBundle\Entity\Task $task
* #return $this
*/
public function setTask($task)
{
$this->task = $task;
return $this;
}
/**
* #return \PMTool\ApiBundle\Entity\ProjectMember
*/
public function getProjectMember()
{
return $this->projectMember;
}
/**
* #param \PMTool\ApiBundle\Entity\ProjectMember $projectMember
* #return $this
*/
public function setProjectMember($projectMember)
{
$this->projectMember = $projectMember;
return $this;
}
}
You can use a transformer to achieve this. (See Symfony Transformers)
Here is an Example of my FormType:
use Symfony\Component\Form\Extension\Core\DataTransformer\DateTimeToTimestampTransformer;
...
$transformer = new DateTimeToTimestampTransformer();
$builder->add($builder->create('validFrom', 'text')
->addModelTransformer($transformer)
)
Be sure to use "'text'" as an input type, otherwise it didn't work for me.
the input option is just for the model side of your underlying data object. In your case it should be datetime.
Your problem is, that you want to transform the timestamp into view data that the symfony form datetime form type does recognise. And I dont know how to do that, unfortunately.
http://symfony.com/doc/current/reference/forms/types/datetime.html#input