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 :)

Code For This Course

Get the code for this course.

Episodes