Buy Access to Course
07.

File Validation

Share this awesome video!

|

Keep on Learning!

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

I've ignored it long enough - sorry! We've gotta add some validation to the upload field. Because... right now, we can upload any file type - it's madness! This is supposed to be an image field people! We need to only allow pngs, jpegs, gifs, image stuff.

Validating an Unmapped Field

Normally we add validation to the entity class: we would go into the Article class, find the property, and add some annotations. But... the field we want to validate is an unmapped form field - there is no imageFile property in Article.

No worries: for unmapped fields, you can add validation directly to the form with the constraints option. And when it comes to file uploads, there are two really important constraints: one called File and an even stronger one called Image. Add new Image() - the one from the Validator\Constraints.

161 lines | src/Form/ArticleFormType.php
// ... lines 1 - 18
use Symfony\Component\Validator\Constraints\Image;
class ArticleFormType extends AbstractType
{
// ... lines 23 - 29
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ... lines 32 - 35
$builder
// ... lines 37 - 54
->add('imageFile', FileType::class, [
// ... lines 56 - 57
'constraints' => [
new Image()
]
])
;
// ... lines 63 - 95
}
// ... lines 97 - 159
}

The Image Constraint

And... that's all we need! That's enough to make sure the user uploads an image. Check it out: find your browser, Google for "Symfony image constraint" and click into the docs.

The Image constraint extends the File constraint - so both basically have the same behavior: you can define a maxSize or configure different mimeTypes. The Image constraint just adds... more super-powers. First, it pre-configures the mimeType option to only allow images. And you get a crazy-amount of other image stuff - like minWidth, maxWidth or allowPortrait.

So let's test it! Refresh the page and browse. Oh, the Symfony Best Practices PDF snuck into my directory. Select that, update and... boom! This file is not a valid image.

Validating the File Size

Go back to the docs and click to see the File constraint. The other most common option is maxSize. To see what that looks like, set it to something tiny, like 5k.

163 lines | src/Form/ArticleFormType.php
// ... lines 1 - 20
class ArticleFormType extends AbstractType
{
// ... lines 23 - 29
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ... lines 32 - 35
$builder
// ... lines 37 - 54
->add('imageFile', FileType::class, [
// ... lines 56 - 57
'constraints' => [
new Image([
'maxSize' => '5k'
])
]
])
;
// ... lines 65 - 97
}
// ... lines 99 - 161
}

Ok: browse and select any of the files. Hit update and... perfect: the file is too large.

Change that back to 5M, or whatever makes sense for you.

163 lines | src/Form/ArticleFormType.php
// ... lines 1 - 20
class ArticleFormType extends AbstractType
{
// ... lines 23 - 29
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ... lines 32 - 35
$builder
// ... lines 37 - 54
->add('imageFile', FileType::class, [
// ... lines 56 - 57
'constraints' => [
new Image([
'maxSize' => '5M'
])
]
])
;
// ... lines 65 - 97
}
// ... lines 99 - 161
}

Validation and upload_max_filesize

Oh, but, remember a few minutes ago when we tried to upload the stars photo? It's 3 megabytes, which is way under the 5 megabytes we just set, but above my php.ini upload_max_filesize setting. That caused a really nasty error.

Well, try selecting it again and updating. Yes! When you use the File or Image constraint, they also catch any PHP-level upload errors and display them quite nicely. You can customize this message.

Making the Upload Field Required

And... that's it! Sure, there are a more options and you can control all the messages - but that's easy enough. Except... there is one tricky thing: how can we make the upload field required? Like, when someone creates an article, they should be required to upload an image before saving it.

Simple, right? Just add a new NotNull() constraint to the imageFile field. Wait, no, that won't work. If we did that, we would need to upload a file even if we were just editing a field on the article: we would literally need to upload an image every time we changed anything.

Okay: so we want the imageFile to be required... but only if the Article doesn't already have an imageFilename. Start by breaking this onto multiple lines. Then say $imageConstraints =, copy the new Image() stuff and paste it here.

167 lines | src/Form/ArticleFormType.php
// ... lines 1 - 20
class ArticleFormType extends AbstractType
{
// ... lines 23 - 29
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ... lines 32 - 35
$builder
// ... lines 37 - 45
->add('location', ChoiceType::class, [
// ... lines 47 - 53
])
;
// ... line 56
$imageConstraints = [
new Image([
'maxSize' => '5M'
])
];
$builder
->add('imageFile', FileType::class, [
// ... lines 64 - 66
])
;
// ... lines 69 - 101
}
// ... lines 103 - 165
}

Down below, set 'constraints' => $imageConstraints. Oh... and let's spell that correctly.

167 lines | src/Form/ArticleFormType.php
// ... lines 1 - 20
class ArticleFormType extends AbstractType
{
// ... lines 23 - 29
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ... lines 32 - 61
$builder
->add('imageFile', FileType::class, [
// ... lines 64 - 65
'constraints' => $imageConstraints
])
;
// ... lines 69 - 101
}
// ... lines 103 - 165
}

Now we can conditionally add the NotNull() constraint exactly when we need it. Scroll up a little. In our forms tutorial, we used the data option to get the Article object that this form is bound to. If this is a "new" form, there may or may not be an Article object - so this will be an Article object or null. I also used that to create an $isEdit variable to figure out if we're on the edit screen or not.

We can leverage that by saying if this is not the edit page or if the article doesn't have an image filename, then take $imageConstraints and add new NotNull(). We'll even get fancy and customize the message: Please upload an image.

175 lines | src/Form/ArticleFormType.php
// ... lines 1 - 19
use Symfony\Component\Validator\Constraints\NotNull;
class ArticleFormType extends AbstractType
{
// ... lines 24 - 57
$imageConstraints = [
new Image([
'maxSize' => '5M'
])
];
if (!$isEdit || !$article->getImageFilename()) {
$imageConstraints[] = new NotNull([
'message' => 'Please upload an image',
]);
}
// ... lines 69 - 109
}
// ... lines 111 - 173
}

Just saying if !$isEdit is probably enough... but just in case, I'm checking to see if, somehow, we're on the edit page, but the imageFilename is missing, let's require it.

Cool: testing time! Refresh the entire form, but don't select an upload: we know that this Article does have an image already attached. Hit update and... works fine! Now try creating a new Article, fill in a few of the required fields, hit create and... boom! Please upload an image!

Validation, check! Next, let's fix how this renders: we've gotta see the filename after selecting a file - seeing nothing is bummin' me out.