I'm following the Web API REST with Symfony2 to build a REST API in symfony2 using Fosrestbundle and JMS Serializer and for GET routes works perfectly but not with POST because when I'm validating the form always returns false and even I followed the flow of the functions and in the last step returns true in $form->isValid(), so I'm confused.
And even doesn't take in account the validations.yml file so, Is a lot of code but hope you can help me because for two days I've been trying to figure out the problem
config.yml
imports:
- { resource: parameters.yml }
- { resource: security.yml }
- { resource: doctrine_extensions.yml }
framework:
validation: { enable_annotations: true }
orm:
auto_generate_proxy_classes: "%kernel.debug%"
auto_mapping: true
sensio_framework_extra:
view: { annotations: false }
router: { annotations: true }
request: { converters: true }
fos_rest:
param_fetcher_listener: true
view:
view_response_listener: 'force'
formats:
json: true
xml: true
templating_formats:
html: true
format_listener:
rules:
- { path: ^/api, priorities: ['json', 'xml'], fallback_format: json, prefer_extension: false }
- { path: ^/, priorities: ['html', '*/*'], fallback_format: html, prefer_extension: false }
exception:
codes:
'Symfony\Component\Routing\Exception\ResourceNotFoundException': 404
'Doctrine\ORM\OptimisticLockException': HTTP_CONFLICT
messages:
'Symfony\Component\Routing\Exception\ResourceNotFoundException': true
allowed_methods_listener: true
access_denied_listener:
json: true
body_listener: true
disable_csrf_role: ROLE_API
validations.yml
# src/Acme/MyBundle/Resources/config/validation.yml
Acme\MyBundle\Entity\Vendor:
properties:
name:
- NotBlank: ~
- NotNull: ~
- Length:
min: 2
max: 50
minMessage: "Your name must be at least {{ limit }} characters length"
maxMessage: "Your name cannot be longer than {{ limit }} characters length"
email:
- Email:
message: "The email {{ value }} is not a valid email"
checkMX: true
password:
- NotBlank: ~
- NotNull: ~
Vendor entity
<?php
namespace Acme\MyBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Acme\MyBundle\Model\VendorInterface;
use Gedmo\Mapping\Annotation as Gedmo;
use JMS\Serializer\Annotation\ExclusionPolicy;
use JMS\Serializer\Annotation\Exclude;
use JMS\Serializer\Annotation\Expose;
use JMS\Serializer;
/**
* Vendor
*
* @ORM\Table(name="vendors")
* @ORM\Entity()
* @ExclusionPolicy("all")
*/
class Vendor implements VendorInterface
{
/**
* @var integer
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
* @Expose
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="name", type="string", length=50)
* @Expose
*/
private $name;
/**
* @var string
*
* @ORM\Column(name="email", type="string", length=50)
* @Expose
*/
private $email;
/**
* @var string
*
* @ORM\Column(name="password", type="string", length=255)
*/
private $password;
/**
* @var string
*
* @ORM\Column(name="description", type="string", length=200)
*/
private $description;
/**
* @var string
*
* @ORM\Column(name="picture", type="string", length=50)
* @Expose
*/
private $picture;
/**
* @var \DateTime
*
* @ORM\Column(name="created_at", type="datetime")
* @Gedmo\Timestampable(on="create")
* @Expose
*/
private $createdAt;
/**
* @var \DateTime
*
* @ORM\Column(name="updated_at", type="datetime")
* @Gedmo\Timestampable(on="create")
* @Expose
*/
private $updatedAt;
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Vendor
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set email
*
* @param string $email
* @return Vendor
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set password
*
* @param string $password
* @return Vendor
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password
*
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set description
*
* @param string $description
* @return Vendor
*/
public function setDescription($description)
{
$this->description = $description;
return $this;
}
/**
* Get description
*
* @return string
*/
public function getDescription()
{
return $this->description;
}
/**
* Set picture
*
* @param string $picture
* @return Vendor
*/
public function setPicture($picture)
{
$this->picture = $picture;
return $this;
}
/**
* Get picture
*
* @return string
*/
public function getPicture()
{
return $this->picture;
}
/**
* Set createdAt
*
* @param \DateTime $createdAt
* @return Vendor
*/
public function setCreatedAt($createdAt)
{
$this->createdAt = $createdAt;
return $this;
}
/**
* Get createdAt
*
* @return \DateTime
*/
public function getCreatedAt()
{
return $this->createdAt;
}
/**
* Set updatedAt
*
* @param \DateTime $updatedAt
* @return Vendor
*/
public function setUpdatedAt($updatedAt)
{
$this->updatedAt = $updatedAt;
return $this;
}
/**
* Get updatedAt
*
* @return \DateTime
*/
public function getUpdatedAt()
{
return $this->updatedAt;
}
}
VendorController.php
<?php
namespace Acme\MyBundle\Controller;
.....
class VendorController extends FOSRestController
{
public function postVendorAction(Request $request)
{
try {
$form = new VendorType();
$newVendor = $this->container->get('acme_mybundle.vendor.handler')->post(
$request->request->all()
);
$routeOptions = array(
'id' => $newVendor->getId(),
'_format' => $request->get('_format')
);
return $this->routeRedirectView('api_get_vendor', $routeOptions, Codes::HTTP_CREATED);
} catch (InvalidFormException $exception) {
return $exception->getForm();
}
}
protected function getOr404($id)
{
if (!($vendor = $this->container->get('acme_mybundle.vendor.handler')->get($id))) {
throw new NotFoundHttpException(sprintf('The resource \'%s\' was not found.',$id));
}
return $vendor;
}
}
VendorType.php
namespace Acme\MyBundle\Form;
...
class VendorType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('name')
->add('email')
->add('password')
;
}
/**
* @param OptionsResolverInterface $resolver
*/
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\MyBundle\Entity\Vendor',
));
}
/**
* @return string
*/
public function getName()
{
return '';
}
}
VendiorHandler.php
<?php
namespace Acme\MyBundle\Handler;
....
class VendorHandler implements VendorHandlerInterface
{
private $om;
private $entityClass;
private $repository;
private $formFactory;
public function __construct(ObjectManager $om, $entityClass, FormFactoryInterface $formFactory)
{
$this->om = $om;
$this->entityClass = $entityClass;
$this->repository = $this->om->getRepository($this->entityClass);
$this->formFactory = $formFactory;
}
/**
* Create a new Vendor.
*
* @param array $parameters
*
* @return VendorInterface
*/
public function post(array $parameters)
{
//die(var_dump("post handler"));
$vendor = $this->createVendor();
return $this->processForm($vendor, $parameters, 'POST');
}
/**
* Processes the form.
*
* @param VendorInterface $vendor
* @param array $parameters
* @param String $method
*
* @return VendorInterface
*
* @throws \Acme\MyBundle\Exception\InvalidFormException
*/
private function processForm(VendorInterface $vendor, array $parameters, $method = "PUT")
{
$form = $this->formFactory->create(new VendorType(), $vendor, array('method' => $method));
$form->submit($parameters, 'PATCH' !== $method);
if (!$form->isValid()) {
$vendor = $form->getData();
$this->om->persist($vendor);
$this->om->flush($vendor);
return $vendor;
}
throw new InvalidFormException('Invalid submitted data', $form);
}
private function createVendor()
{
return new $this->entityClass();
}
}
There seems to be two easy ways to set constraints for your entities.
Create an yml
file in your Bundle
:
# src/Acme/BlogBundle/Resources/config/validation.yml
Acme\BlogBundle\Entity\Author:
properties:
name:
- NotBlank: ~
For this to work automatically, it's important that the file has that very same name and is in that exact path. Then, you have enable validation and disable annotations.
# app/config/config.yml
framework:
validation: { enabled: true, enable_annotations: false }
You don't need any special files for this. Just specify the constraints along the properties of your Entity
:
// src/Acme/BlogBundle/Entity/Author.php
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* @Assert\NotBlank()
*/
public $name;
}
Remember to enable the use of annotations:
# app/config/config.yml
framework:
validation: { enable_annotations: true }
If you want to use both validation.yml
and annotations, or you want to use a different name or path for your validation file/s then you have to load them manually.
Take a look at this for further information: https://stackoverflow.com/a/24210501