Snake Case On Your Forms, Camel Case On Your Entities
One of my favourite things to cover here at CodeReviewVideos is problems that I have seen in real world code, and - hopefully - I can offer up fixes, or potential fixes for others to benefit from. My theory goes that if I see a problem in real code, then the chances are high others are making this same mistake.
With that in mind, this video addresses a problem that I can cover by way of a very quick example:
Let's say you have a JSON API. You want to accept incoming data via a Symfony form, but you'd like to use snake_case
for your form fields.
{
"title": "some title",
"body": "lorum ipsum gipsum wipsum",
"is_published": true
}
Now, you don't want to use snake case on your entity (you'd like to use $isPublished
in PHP-land), and therein lies a problem. If you create a form based on the above, you are going to hit some Symfony form errors, something akin to:
This form should not contain extra fields
Ok, spoiler alert - the solution to this problem is not to change your entities :D
Let's take a quick look at an example form:
<?php
namespace AppBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class BlogPostType extends AbstractType
{
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('body')
->add('isPublished', CheckboxType::class)
;
}
/**
* {@inheritdoc}
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'AppBundle\Entity\BlogPost',
'csrf_protection' => false,
));
}
/**
* {@inheritdoc}
*/
public function getBlockPrefix()
{
return 'appbundle_blogpost';
}
}
And our entity:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* BlogPost
*
* @ORM\Table(name="blog_post")
* @ORM\Entity(repositoryClass="AppBundle\Repository\BlogPostRepository")
*/
class BlogPost
{
/**
* @var int
*
* @ORM\Column(name="id", type="integer")
* @ORM\Id
* @ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORM\Column(name="title", type="string", length=255)
*/
private $title;
/**
* @var string
*
* @ORM\Column(name="body", type="text")
*/
private $body;
/**
* @var bool
*
* @ORM\Column(name="isPublished", type="boolean")
*/
private $isPublished;
// * getters and setters etc *
/**
* Set isPublished
*
* @param boolean $isPublished
*
* @return BlogPost
*/
public function setIsPublished($isPublished)
{
$this->isPublished = $isPublished;
return $this;
}
/**
* Get isPublished
*
* @return bool
*/
public function getIsPublished()
{
return $this->isPublished;
}
}
The solution to this problem is really straightforward, provided for us by Symfony's form component out of the box. We need to use the property_path
form field option:
/**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title')
->add('body')
->add('is_published', CheckboxType::class. [
'property_path' => 'isPublished'
])
;
}
This allows us to add a field with any name we like - a snake cased version of our real class property in this example - and then tell Symfony's form component to read from, and write back to the isPublished
property behind the scenes.
Simple, effective, and much easier than changing your entities :)