Read-Only Fields
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
What if we don't want the nickname
to be changeable? After all, we're using
it almost like a primary key for the Programmer
. Yea, I want an API client
to set it on create, but I don't want them to be able to change it afterwards.
Send a new nickname in the body of the PUT request - CowgirlCoder
.
We want the server to just ignore that. At the end, assert that
nickname
still equals CowboyCoder
, even though we're trying to mess
with things:
// ... lines 1 - 71 | |
public function testPUTProgrammer() | |
{ | |
// ... lines 74 - 79 | |
$data = array( | |
'nickname' => 'CowgirlCoder', | |
// ... lines 82 - 83 | |
); | |
// ... lines 85 - 89 | |
// the nickname is immutable on edit | |
$this->asserter()->assertResponsePropertyEquals($response, 'nickname', 'CowboyCoder'); | |
} | |
// ... lines 93 - 94 |
Run just that test:
phpunit -c app --filter testPUTProgrammer
Womp womp - we're failing: the nickname
is updated on the programmer.
That makes perfect sense: our form will update any of the 3 fields we
configured in PogrammerType
:
// ... lines 1 - 8 | |
class ProgrammerType extends AbstractType | |
{ | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder | |
->add('nickname', 'text') | |
->add('avatarNumber', 'choice', [ | |
// ... lines 16 - 27 | |
->add('tagLine', 'textarea') | |
; | |
} | |
// ... lines 31 - 42 | |
} |
Using disabled Form Fields
So how can we make nickname
only writeable when we're adding a new programmer.
If you think about the HTML world, this would be like a form that had a
functional nickname
text box when creating, but a disabled nickname
text box when editing. We can use this idea in our API by giving the nickname
field a disabled
option that's set to true
.
In an API, this will mean that any value submitted to this field will just
be ignored. If we can set this to true
in edit mode only, that would do
the trick!
To do that, reference a new option called is_edit
:
// ... lines 1 - 10 | |
public function buildForm(FormBuilderInterface $builder, array $options) | |
{ | |
$builder | |
->add('nickname', 'text', [ | |
// readonly if we're in edit mode | |
'disabled' => $options['is_edit'] | |
]) | |
// ... lines 18 - 31 | |
; | |
} | |
// ... lines 34 - 47 |
If we're in "edit mode", then the field is disabled. To make this a valid
form option, add a new entry in setDefaultOptions()
and default it to
false
:
// ... lines 1 - 34 | |
public function setDefaultOptions(OptionsResolverInterface $resolver) | |
{ | |
$resolver->setDefaults(array( | |
'data_class' => 'AppBundle\Entity\Programmer', | |
'is_edit' => false, | |
)); | |
} | |
// ... lines 42 - 47 |
Head back to ProgrammerController::updateAction()
and give createForm()
a third array argument. Pass is_edit => true
.
// ... lines 1 - 90 | |
public function updateAction($nickname, Request $request) | |
{ | |
// ... lines 93 - 103 | |
$form = $this->createForm(new ProgrammerType(), $programmer, array( | |
'is_edit' => true, | |
)); | |
// ... lines 107 - 116 | |
} | |
// ... lines 118 - 135 |
Ok, try the test!
phpunit -c app --filter testPUTProgrammer
Yay! That was easy!
Creating a Separate Form Class
And now that it's working, I need to force one small change on us that'll
help us way in the future when we talk about API documentation. Instead
of passing is_edit
in the controller, we'll create a second form type class.
Copy ProgrammerType
to UpdateProgrammerType
. Make this extend ProgrammerType
and git rid of buildForm()
. In setDefaultOptions()
, we only need to set
is_edit
to true
and call the parent function above this. Make sure getName()
returns something unique:
// ... lines 1 - 2 | |
namespace AppBundle\Form; | |
use Symfony\Component\Form\AbstractType; | |
use Symfony\Component\Form\FormBuilderInterface; | |
use Symfony\Component\OptionsResolver\OptionsResolverInterface; | |
class UpdateProgrammerType extends ProgrammerType | |
{ | |
public function setDefaultOptions(OptionsResolverInterface $resolver) | |
{ | |
parent::setDefaultOptions($resolver); | |
// override this! | |
$resolver->setDefaults(['is_edit' => true]); | |
} | |
public function getName() | |
{ | |
return 'programmer_edit'; | |
} | |
} |
The whole purpose of this class is to act just like ProgrammerType
, but
set is_edit
to true instead of us passing that in the controller. Both
approaches are fine - but I'm planning ahead to when we use NelmioApiDocBundle:
it likes 2 classes better. In the controller, use new UpdateProgrammerType
and get rid of the third argument:
// ... lines 1 - 91 | |
public function updateAction($nickname, Request $request) | |
{ | |
// ... lines 94 - 104 | |
$form = $this->createForm(new UpdateProgrammerType(), $programmer); | |
// ... lines 106 - 115 | |
} | |
// ... lines 117 - 134 |
Test out your handy-work:
phpunit -c app --filter testPUTProgrammer
Success!
i changed the disabled to true to see if my defaultoptions was wrong but even after that the nickname field is modified