Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

File Upload Field in a Form

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

We're rocking! We know what it looks like to upload a file in Symfony: we can work with the UploadedFile object and we know how to move the file around. That feels good!

It's time to talk about how to work with a file upload field inside a Symfony form. And then, we also need to save the filename of the uploaded image to our Article entity. Because, ultimately, on the homepage, we need to render each image next to the article.

What your Entity Should Look Like

In the src/Entity directory, let's look at the Article entity. Ok great: the entity is already setup! It has an $imageFilename field that is a string. This is important: the uploaded file will be stored... somewhere: on your server, in the cloud, in your imagination - it doesn't matter. But in the database, the only thing you will store is the string filename.

Adding the FileType to the Form

The form that handles this page lives at src/Form/ArticleFormType.php. In ArticleAdminController... if you scroll up a little bit... here is the edit() action and you can see it using this ArticleFormType. Right now, this is a nice traditional form: it handles the request and saves the Article to the database. Beautifully... boring!

In ArticleFormType, add a new field with ->add() and call it imageFilename because that's the name of the property inside Article. For the type, use FileType::class.

... lines 1 - 11
use Symfony\Component\Form\Extension\Core\Type\FileType;
... lines 13 - 19
class ArticleFormType extends AbstractType
{
... lines 22 - 28
public function buildForm(FormBuilderInterface $builder, array $options)
{
... lines 31 - 34
$builder
... lines 36 - 53
->add('imageFilename', FileType::class)
;
... lines 56 - 88
}
... lines 90 - 152
}

But... there's a problem with this. And if you already see it, extra credit points for you! Move over and refresh. Woh.

The form's view data is expected to be an instance of class File but it is a string.

Um... ok. The problem is not super obvious... but it clearly hates something about our new field. Here's the explanation: we know that when you upload a file, Symfony gives you an UploadedFile object, not a string. But, the imageFilename field here on Article... that is a string! Connecting the form field directly to the string property doesn't make sense. We're missing a layer in the middle: something that can work with the UploadedFile object, move the file, and then set the new filename onto the property.

Using an Unmapped Field

How can we do that? Change the field name to just imageFile. There is no property on our entity with this name... so this, on its own, will not work. Pretty commonly, you'll see people create this property on their entity, just to make the form work. They don't persist this property to the database with Doctrine... so the idea works, but I don't love it.

Instead, we'll use a trick that we talked a lot about in our forms tutorial: add an option to the field: 'mapped' => false.

... lines 1 - 19
class ArticleFormType extends AbstractType
{
... lines 22 - 28
public function buildForm(FormBuilderInterface $builder, array $options)
{
... lines 31 - 34
$builder
... lines 36 - 53
->add('imageFile', FileType::class, [
'mapped' => false
])
;
... lines 58 - 90
}
... lines 92 - 154
}

If you've never seen this before, we'll explain it in a minute. Now that we have a new imageFile field, let's go render it! Open edit.html.twig. Remove the HTML form - we're done with that. The Symfony form lives in _form.html.twig. After the title, add {{ form_row(articleForm.imageFile }}.

{{ form_start(articleForm) }}
{{ form_row(articleForm.title, {
label: 'Article title'
}) }}
{{ form_row(articleForm.imageFile) }}
... lines 6 - 23
{{ form_end(articleForm) }}

Nothing special here.

This submits back to ArticleAdminController::edit(). Go inside the $form->isValid() block. When you have an unmapped field, the data will not be put onto your Article object. So, how can we get it? dd($form['imageFile']->getData()).

... lines 1 - 16
class ArticleAdminController extends BaseController
{
... lines 19 - 48
public function edit(Article $article, Request $request, EntityManagerInterface $em)
{
... lines 51 - 55
if ($form->isSubmitted() && $form->isValid()) {
dd($form['imageFile']->getData());
... lines 58 - 66
}
... lines 68 - 71
}
... lines 73 - 128
}

Let's try that! Go back to your browser and hit enter on the URL: we need the form to totally re-render. Hey! There's our new field! Select the astronaut again. Um... did that work? Cause... I don't see the filename on my field. Yes: it did work - we don't see anything because of a display bug if you're using Symfony's Bootstrap 4 form theme. We'll talk about that later. But, the file is attached to the field. Hit Update!

Yes! It's our beloved UploadedFile object! We totally know how to work with that! Oh, but before we do: I want to point out something cool. Inspect element and find the form tag. Hey! It has the enctype="multipart/form-data" attribute! We get that for free because we use the {{ form_start() }} function to render the <form> tag. As soon as there is even one file upload field in the form, Symfony adds this attribute for you. High-five team!

Moving the Uploaded File

Time to finish this. Let's upload a different file - earth.jpeg. And... there's the dump. We have two jobs in our controller: move this file to the final location and store the new filename on the $imageFilename property. Back in the controller, scroll down to temporaryUploadAction(), steal all its code, and delete it.

Up in edit(), remove the dd() and set this to an $uploadedFile variable. Add the same inline phpdoc as last time

... lines 1 - 16
class ArticleAdminController extends BaseController
{
... lines 19 - 48
public function edit(Article $article, Request $request, EntityManagerInterface $em)
{
... lines 51 - 55
if ($form->isSubmitted() && $form->isValid()) {
/** @var UploadedFile $uploadedFile */
$uploadedFile = $form['imageFile']->getData();
$destination = $this->getParameter('kernel.project_dir').'/public/uploads';
... lines 60 - 77
}
... lines 79 - 82
}
... lines 84 - 121
}

then paste the code. Yep! We'll move the file to public/uploads and give it a unique filename. Take off the dd() around move().

... lines 1 - 16
class ArticleAdminController extends BaseController
{
... lines 19 - 48
public function edit(Article $article, Request $request, EntityManagerInterface $em)
{
... lines 51 - 55
if ($form->isSubmitted() && $form->isValid()) {
/** @var UploadedFile $uploadedFile */
$uploadedFile = $form['imageFile']->getData();
$destination = $this->getParameter('kernel.project_dir').'/public/uploads';
$originalFilename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
$newFilename = Urlizer::urlize($originalFilename).'-'.uniqid().'.'.$uploadedFile->guessExtension();
$uploadedFile->move(
$destination,
$newFilename
);
... lines 68 - 77
}
... lines 79 - 82
}
... lines 84 - 121
}

Now, call $article->setImageFilename($newFilename)

... lines 1 - 16
class ArticleAdminController extends BaseController
{
... lines 19 - 48
public function edit(Article $article, Request $request, EntityManagerInterface $em)
{
... lines 51 - 55
if ($form->isSubmitted() && $form->isValid()) {
/** @var UploadedFile $uploadedFile */
$uploadedFile = $form['imageFile']->getData();
$destination = $this->getParameter('kernel.project_dir').'/public/uploads';
$originalFilename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
$newFilename = Urlizer::urlize($originalFilename).'-'.uniqid().'.'.$uploadedFile->guessExtension();
$uploadedFile->move(
$destination,
$newFilename
);
$article->setImageFilename($newFilename);
... lines 69 - 77
}
... lines 79 - 82
}
... lines 84 - 121
}

and let Doctrine save the entity, just like it already was.

Beautiful! I do want to point out that the $newFilename string that we're storing in the database is just the filename: it doesn't contain the directory or the word uploads: it's... the filename. Oh, for my personal sanity, let's upload things into an article_image sub-directory: that'll be cleaner when we start uploading multiple types of things. Remove the old files.

... lines 1 - 16
class ArticleAdminController extends BaseController
{
... lines 19 - 48
public function edit(Article $article, Request $request, EntityManagerInterface $em)
{
... lines 51 - 55
if ($form->isSubmitted() && $form->isValid()) {
/** @var UploadedFile $uploadedFile */
$uploadedFile = $form['imageFile']->getData();
$destination = $this->getParameter('kernel.project_dir').'/public/uploads/article_image';
... lines 60 - 77
}
... lines 79 - 82
}
... lines 84 - 121
}

Moment of truth! Find your browser, roll up your sleeves, and refresh! Um... it probably worked? In the uploads/ directory... yea! There's our Earth file! Let's see what the database looks like - find your terminal and run:

php bin/console doctrine:query:sql 'SELECT * FROM article WHERE id = 1'

Let's see, the id of this article is 1. Yes! the image_filename column is totally set! Fist-pumping time!

Avoid Processing when no Upload

Oh, but there is one tiny thing we need to clean up before moving on. What if we just want to, I don't know, edit the article's title, but we don't need to change the image. No problem - hit Update! Oh... That's HTML5 validation. You might remember from the forms tutorial that this required attribute is added to every field... unless you're using form field type guessing. It's annoying - fix it by adding 'required' => false.

... lines 1 - 19
class ArticleFormType extends AbstractType
{
... lines 22 - 28
public function buildForm(FormBuilderInterface $builder, array $options)
{
... lines 31 - 34
$builder
... lines 36 - 53
->add('imageFile', FileType::class, [
... line 55
'required' => false,
])
;
... lines 59 - 91
}
... lines 93 - 155
}

Let's try it again. Refresh, change the title, submit and... oof.

Call to a member function getClientOriginalName on null

Of course! We're not uploading a file! So the $uploadedFile variable is null! That's ok! If the user didn't upload a file, we don't need to do any of this logic. In other words, if ($uploadedFile), then do all of that. Otherwise, skip it!

... lines 1 - 16
class ArticleAdminController extends BaseController
{
... lines 19 - 48
public function edit(Article $article, Request $request, EntityManagerInterface $em)
{
... lines 51 - 55
if ($form->isSubmitted() && $form->isValid()) {
/** @var UploadedFile $uploadedFile */
$uploadedFile = $form['imageFile']->getData();
if ($uploadedFile) {
$destination = $this->getParameter('kernel.project_dir').'/public/uploads/article_image';
$originalFilename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME);
$newFilename = Urlizer::urlize($originalFilename).'-'.uniqid().'.'.$uploadedFile->guessExtension();
$uploadedFile->move(
$destination,
$newFilename
);
$article->setImageFilename($newFilename);
}
... lines 71 - 79
}
... lines 81 - 84
}
... lines 86 - 123
}

Refresh now. Got it!

Next: This is looking good! Except that... we need this exact same logic in the new() action. To make a truly killer upload system, we need to refactor the upload logic into a reusable service.

Leave a comment!

38
Login or Register to join the conversation
Rony Avatar

Hello, I have an issue with rendering the input file..Well the input file doesn't show, just the label appears.What is the problem?

1 Reply

Hey Cool,

What do you mean about "the input file doesn't show"? What browser do you use? Could you provide a screenshot maybe? It would help to understand the problem

Cheers!

Reply
Rony Avatar

Thank you Victor Bocharsky for your response,
Well, the input file that rendered by the FileType in ArticleFormType does not appear. Although, it perfectly works..
I use Symfony4.3 and Google chrome as defaut browser. I even tried it in FireFox browser and the same problem remains.
I'm looking forward to your response.
Thanks.
The link of the problem's screenshot : https://i.imgur.com/fqIdXJS...

Reply

Hey Cool,

Hm, that's weird, I just double-checked with downloaded course code and I do have file path and even a "Browse" button to choose a file - I don't see the button on your screenshot as well. What is your OS? Windows? I'd recommend you to check your browser version, probably there might be some updates and you just need to install and reload your browser first. Also, make sure you don't have any extensions (most probably some add blockers etc.) that might block or modify your page content. Also, try to open this page in Chrome Incognito mode where all plugins are turned off by default.

If you still does not see it - most probably you have some style issues, it would be good to double check styles and HTML markup, you can debug things in Chrome with Chrome developer toolbar. Btw, did you download the course code for this tutorial and start from the start/ directory? Or did you start a completely separate fresh project? If the second, please, try to download the course code and bootstrap the project from the final/ directory. Did you see the file path and button there?

Anyway, it sounds more like browser problem, especially if it works perfectly but you can't see the path - that's not something you can tweak with styles or HTML code, it just works out of the box in browsers. So I mostly lean toward that some browser extension modifies the page behind the scene :/

I hope this helps!

Cheers!

1 Reply
Rony Avatar

Hello Victor Bocharsky, I did desactivate all the extensions, my browser is in his latest version and I do even open the app in Chrome Incognito mode, but the problem is still remaining.

My PC's OS is Windows 8.1.

In fact, i did not download this couse code, I've started the project from Zero, step by step, from the first chapiter "Stellar Development with Symfony 4" to the Forms chapiter and continue with this course.

when I debug with Chrome developer toolbar, it sounds that this fiels has a style of "opacity:0", but when I make the "opacity:1", it gives me the standard file input not the bootsraped one.

This is the first issue I faced, and it sounds weird.

This is my Github project's link: https://github.com/mohamedT...

Thanks a lot for your help and I'm looking forward to your response again :)

Reply

Hey Cool,

Ah, I see... this really sounds like your project is missing some styles, actually that "Browse" button is added with CSS styles. It's difficult to say what exactly you're missing, I'd recommend you to download our course code and compare your HTML structure with the one we have in this screencast. Also keep an eye on styles you're including. Except of possible missing HTML/CSS in your project I find it difficult to say what exactly may cause this problem.

I hope this helps!

Cheers!

1 Reply
Rony Avatar

Hello Victor Bocharsky ,
Thanks a lot for your help, I fixed the problem, I've just updated the bootstrap stylesheet in base.html.twig to the latest version 4.4.1 and the issue is fixed, it is as simple as that. :D

Reply

Hey Cool,

Ah, cool! :) Glad you was able to fix it! And thank you for sharing your solution.

Cheers!

Reply
Default user avatar

Hi, I implements Upload File, and it works on http request, but when i use a domain and https request it fails and returns error 403.

Reply

Hey Jose,

Hm, I don't see any reasons why it should fail on HTTPS fairly speaking. Please, double-check our logs, there might be a tip why it fails. Also, make sure you send your request to the HTTPS and server works by HTTPS, i.e. can load pages in HTTPS.

I hope this helps!

Cheers!

Reply
Alessandro V. Avatar
Alessandro V. Avatar Alessandro V. | posted 11 months ago

hi, i'm using the FileType in symfony 5.3 but instead of an instance of UploadedFile i receive null. The others two fields works fine, i have problem only with file upload

Reply

Hey Alessandro,

Hm, if you get null instead of UploadedFile - most probably your file wasn't sent to the server. Are you sure selected and attached the file in your form? Are you sure the form is sent via POST instead of GET method? How do you render the form? Try to use special Twig functions to render form data, like form()/form_start()/form_end() instead of rendering the form tag manually, you may miss some important details when you render manually.

Cheers!

Reply
Alessandro V. Avatar
Alessandro V. Avatar Alessandro V. | victor | posted 11 months ago

Yes to all but the effective difference Is the upload, i'm trying to use Ajax and It works for all param but not for files. If i use the normal submit works(i've tryed in the morning). I Always render the form with twig tag, it's so more clean and Easy

Reply

Hey Alessandro Vacca!

Hmmm. So it sounds like it might be an issue with your Ajax call not sending the file directly. What JavaScript are you using to submit the form via Ajax? If you open your network tools in your browser and find the Ajax request... then scroll down to the bottom where it says "Request body", what does it look like? Do you see plain-text fields that look like firstName=Ryan@lastName=Weaver ? Or do you see it structured as "multipart/form-data" (you can also know by looking at the headers on the request: there should be a Content-Type header set to "multipart/form-data".

I think you did a good job debugging by testing it with a normal (non-Ajax) submit. This definitely makes me think that we should look at the Ajax JavaScript first :).

Cheers!

Reply
Alessandro V. Avatar

Hey, i've tested without the ajax and the form works. https://pastebin.com/CQNX7625 this is the js code that i'm using. it serialize the content of the form. Maybe it can't serialize files

Reply

Hey Alessandro Vacca!

Yup, that's correct :). If you want to upload a file, you need to use the FormData object. Here's an example - https://stackoverflow.com/q... - the code is a little fancy (they created separate Upload object, which is not necessary), but hopefully you can see how they used FormData to attach the file, then used jQuery to upload that FormData.

Cheers!

1 Reply
JavierMendezK Avatar
JavierMendezK Avatar JavierMendezK | posted 1 year ago

How can I force a custom mime type for my file uploader? I am trying to upload .usdz files and the uploader converts them to .zip
If I use this:
'constraints' => [
new File([
'mimeTypes' => [
'model/vnd.usdz+zip',
'model/vnd.pixar.usd',
'model/usd',
'model/usdz'
],
'mimeTypesMessage' => 'Please upload a valid .usdz 3D file.',
])
]
in my FormType it will not let me upload the file, and if I do not use a constraint it will just convert it to .zip.
Thank you for the help.

Reply

Hey Javier Mendez !

I've answered this question over on the other thread :) https://symfonycasts.com/sc...

Cheers!

Reply
Default user avatar
Default user avatar ujjpeet | posted 1 year ago

Hi,

Thank you for this awesome tutorial.

Where is the setImageFileName method defined? I dont have it and obviously my script runs into an "Attempted to call an undefined method..." error.
Please help.

Thank you

Reply
Default user avatar

sorry. I have just found. It in the entity but mine is setPicutre, not setImageFileName. That was the problem....

Reply

Hey @ujjpeet!

I'm glad you worked it out :).

Cheers!

Reply
Default user avatar

i have a specific question, i'm generating the controller thanks to make:controller, so it also make a new files in the folder src/form/entityNameType.php, but here is my issues, i did manualy deleted an entity i already made so i could use the maker again to have the matching form once i'll be in the /edit/ page, but still, when i'm tryint to re-make:entity, its tell me thats the previous entityNameFile.php is not found, this is due to a cache file it seems, so i tryed to purge it out, but that still doesnt work, here is the error message i've got when i try my make:crud :
mChoose a name for your controller class (e.g. ?[39m?[33mArticleController?[39m?[32m)?[39m [?[33mArticleController?[39m]:
>




?[33mIn DebugClassLoader.php line 346:?[39m
?[37;41m
?[39;49m
?[37;41m Warning: include(C:\wamp64\www\WorkPlace\Site_CETIS_Centre_Entrainement_Technique_Incendie_Survie\cetis_last\vendor\composer/../../src/Form/Ar
ticle ?[39;49m
?[37;41m 1Type.php): failed to open stream: No such file or directory
?[39;49m
?[37;41m
?[39;49m


?[32mmake:crud [?[39m?[32m<entity-class>?[39m?[32m]?[39m

i dont understand this because yesterday this process was working as a charm... so i dont know what i did here, but a fix could be really helpful

Reply

Hey yacine,

I'm not sure if that's bug on the bundle but what you can do is to manually delete the cache folder (rm -rf var/cache) and try again, or remove all the files generated by the command and try again.

Cheers!

Reply
Default user avatar

hello Diego

thanks for your reply, i just did delete the cache folder, then i launched make:crud, Then Article, and still getting the same error :/

[...]\vendor\composer/../../src/Form/Ar
ticle ?[39;49m
?[37;41m Type.php): failed to open stream: No such file or directory

for now i'm just dealing with the Article1Type with commenting it all, but thats not clean .

also, i already did try to remove all the files generated by the command, to be clear i'm deleting : ArticleController, the entire Article Folder in template, ArticleType.php (and Article1type), but the issue here is that somewhere in var\cache\dev\containerSOMEID -> getArticleControllerService.php there is a line that is callinig my controller class and this line is still here even when i did your rm -rf var\cache... i dont understand why :/ i already try to edit my ArticleController like this before and it worked perfectly..

Reply

Hey @yacine

Could you double check that all generated files were removed, and also, check for any code reference to any of the files, for example, any use statement of the entity.

Reply
Default user avatar

basicaly what i need here is to delete the Article1Type.php that war previously generated to only keep the ArticleType.php that was here at first.

(since i posted as a guest i cannot update my previous message to remove the part with the name of my project, if someone could errase this part from my previous message that would be perfect ♥ )

Reply
Default user avatar
Default user avatar zdzzdzqzd | posted 1 year ago

Whats Urlize?

Reply

It's a library for making a string URL compliant. For example, if your string has a slash / in it, it will replace by a -

Cheers!

Reply
Oliver W. Avatar
Oliver W. Avatar Oliver W. | posted 1 year ago

Hi,
I am uploading a file via a form. The file(s) are images with OneToMany. The corresponding class is Images. When trying to use Articles::adImage() I get the error the passed argument is of type UploladedFile instead of Images but I have no idea what to do to meet the condition of Images!?
Thx
Oliver

Reply

Hey @Oliver!

Hmmm. It sounds possibly like the form fields might not be setup quite right for an embedded form - the images form field needs to be a collection of ImageFormType, not of FileType - I’m just guessing this is the issue.

Anyways, post some form code here and we’ll see if we can help :).

Cheers!

Reply
Oliver W. Avatar

weaverryan

Hi Ryan,

sorry for the long timeout - I had to make some money ;-).

I've tryied to replace FileType::class by ImageFormType, but (of course?) this class does not exist. It seems as if I am still not on top of the mountain and further hints are welcome.
Thx

Reply

Hey Oliver Wagner!

> sorry for the long timeout - I had to make some money ;-).

I get that ;)

> I've tryied to replace FileType::class by ImageFormType, but (of course?) this class does not exist

Hmm. It should exist - I'm specifically talking about the FileType from Symfony - this one - https://symfony.com/doc/cur... - maybe check the use statement on that?

Cheers!

Reply
Oliver W. Avatar

Hi Ryan,

it is still impossible to post an entire php-file. The systems cuts the leading lines!? Therefor find the files here: http://cloud.ad-libitum.inf...

Thx
Oliver

Reply

Hey Oliver Wagner!

It is possible, but it is *not* simple with the Disqus comments :/. Sorry about that!

Ok, thanks for posting the code! And I apologize for the confusion! I gave you (what I believe is) the correct advice initially, but it was not clear enough and then I actually got a bit confused in my replies. And also, now that I see the code, I can understand what you're doing a bit better!

To review, here is your setup:

1) On ArticleFormType, for the image field, you were originally setting this to a FileType from Symfony. Now that I see your setup, you SHOULD continue to do this again. You're setting this to mapped => false, which you must do for your setup, because you need to do extra processing before the data is ultimately set on Article.

2) Then, in ArticleAdminController, you are reading the UploadedFile objects off of the image field via $uploadedFiles = $request->files->getIterator('image'). That's technically fine... but really, you should be able to read them off of the form with $form['image']->getData() (btw, I would probably call this field "images" since it is many images.

3) Again in ArticleAdminController, you loop over $uploadedFiles, which is an array of UploadedFile objects. This is good! And you build a $bildnamen array where each item is eventually set onto $article->addImage($bild). Here is the problem. These $bild variables are *still* UploadedFile objects. At some point, you need to create new Image objects, set the filename on each, and set THAT onto $article->addImage().

Anyways, another solution to all of this would be to build a form class for your Image entity - ImageFormType - and embed that into the ArticleFormType.image field. This would cause more things to happen automatically for you... but honestly, you already have things 95% working and I find your solution quite readable and you have a lot of control. So would continue to use an unmapped field like you are and get the last details smoothed out so it's working. And also, if you eventually find that users are needing to uploading multiple images, using a JavaScript uploader will get you a bit nicer UI :).

Cheers!

Reply
Oliver W. Avatar

I've got it now. I fixed it using $bildobject = new Images(), $bildobjekt->setFilename() and $bildobjekt->setArticle() on the images-table. Not using articles.
Thx again

1 Reply
Oliver W. Avatar

Hi weaverryan

Thx for your answer and as you can see I am even falling when making one step after the other. So guess what will happen when I take two steps at a time ;-). I will look at the JS-solution later. I've seen that video.
For the rest I have to wait for some free time to test your hints. I'll return to you - for shure!!
Oliver

Reply
Allan O. Avatar
Allan O. Avatar Allan O. | posted 2 years ago

How can I use this to upload several images in the same form? I am using Symfony 5.

Reply

Hey Allan,

Have you watched this tutorial to the end? :) We're talking about multiple files uploading further in https://symfonycasts.com/sc... . Also, we explain why multiple files uploading has bad UI e.g. if one of selected files does not pass validation - none files are saved. So, if you have not watched this course to the end - I'd recommend you to do this first, and I bet you will get answers to your questions :)

I hope this helps!

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

This tutorial is built on Symfony 4 but works great in Symfony 5!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "aws/aws-sdk-php": "^3.87", // 3.87.10
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.1
        "knplabs/knp-paginator-bundle": "^2.7", // v2.8.0
        "knplabs/knp-time-bundle": "^1.8", // 1.9.0
        "league/flysystem-aws-s3-v3": "^1.0", // 1.0.22
        "league/flysystem-cached-adapter": "^1.0", // 1.0.9
        "liip/imagine-bundle": "^2.1", // 2.1.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.1.0
        "oneup/flysystem-bundle": "^3.0", // 3.0.3
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.2.4
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.2.3
        "symfony/console": "^4.0", // v4.2.3
        "symfony/flex": "^1.9", // v1.17.6
        "symfony/form": "^4.0", // v4.2.3
        "symfony/framework-bundle": "^4.0", // v4.2.3
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/security-bundle": "^4.0", // v4.2.3
        "symfony/serializer-pack": "^1.0", // v1.0.2
        "symfony/twig-bundle": "^4.0", // v4.2.3
        "symfony/validator": "^4.0", // v4.2.3
        "symfony/web-server-bundle": "^4.0", // v4.2.3
        "symfony/yaml": "^4.0", // v4.2.3
        "twig/extensions": "^1.5" // v1.5.4
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.1.0
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.7
        "fzaninotto/faker": "^1.7", // v1.8.0
        "symfony/debug-bundle": "^3.3|^4.0", // v4.2.3
        "symfony/dotenv": "^4.0", // v4.2.3
        "symfony/maker-bundle": "^1.0", // v1.11.3
        "symfony/monolog-bundle": "^3.0", // v3.3.1
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.2.3
        "symfony/profiler-pack": "^1.0", // v1.0.4
        "symfony/var-dumper": "^3.3|^4.0" // v4.2.3
    }
}