{"action":"created","comment":{"url":"https://api.github.com/repos/symfony/symfony/pulls/comments/87244222","pull_request_review_id":7864160,"id":87244222,"diff_hunk":"@@ -12,15 +12,40 @@\n namespace Symfony\\Component\\Form\\Extension\\Core\\Type;\n \n use Symfony\\Component\\Form\\AbstractType;\n+use Symfony\\Component\\Form\\FormBuilderInterface;\n+use Symfony\\Component\\Form\\FormEvent;\n+use Symfony\\Component\\Form\\FormEvents;\n use Symfony\\Component\\Form\\FormInterface;\n use Symfony\\Component\\Form\\FormView;\n+use Symfony\\Component\\OptionsResolver\\Options;\n use Symfony\\Component\\OptionsResolver\\OptionsResolver;\n \n class FileType extends AbstractType\n {\n     /**\n      * {@inheritdoc}\n      */\n+    public function buildForm(FormBuilderInterface $builder, array $options)\n+    {\n+        if ($options['multiple']) {\n+            $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {\n+                $form = $event->getForm();\n+                $data = $event->getData();\n+\n+                // submitted data for an input file (no required) without choosing any file\n+                if (array(null) === $data) {","path":"src/Symfony/Component/Form/Extension/Core/Type/FileType.php","position":25,"original_position":25,"commit_id":"0aeb078ffffca35adf440d00a2debebbd8e4eb36","original_commit_id":"0aeb078ffffca35adf440d00a2debebbd8e4eb36","user":{"login":"yceruto","id":2028198,"avatar_url":"https://avatars.githubusercontent.com/u/2028198?v=3","gravatar_id":"","url":"https://api.github.com/users/yceruto","html_url":"https://github.com/yceruto","followers_url":"https://api.github.com/users/yceruto/followers","following_url":"https://api.github.com/users/yceruto/following{/other_user}","gists_url":"https://api.github.com/users/yceruto/gists{/gist_id}","starred_url":"https://api.github.com/users/yceruto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yceruto/subscriptions","organizations_url":"https://api.github.com/users/yceruto/orgs","repos_url":"https://api.github.com/users/yceruto/repos","events_url":"https://api.github.com/users/yceruto/events{/privacy}","received_events_url":"https://api.github.com/users/yceruto/received_events","type":"User","site_admin":false},"body":"Nope! `array(null)` mean no file uploaded, otherwise we expect array of UploadedFile instances.","created_at":"2016-11-09T17:59:19Z","updated_at":"2016-11-09T17:59:19Z","html_url":"https://github.com/symfony/symfony/pull/20418#discussion_r87244222","pull_request_url":"https://api.github.com/repos/symfony/symfony/pulls/20418","_links":{"self":{"href":"https://api.github.com/repos/symfony/symfony/pulls/comments/87244222"},"html":{"href":"https://github.com/symfony/symfony/pull/20418#discussion_r87244222"},"pull_request":{"href":"https://api.github.com/repos/symfony/symfony/pulls/20418"}}},"pull_request":{"url":"https://api.github.com/repos/symfony/symfony/pulls/20418","id":92464735,"html_url":"https://github.com/symfony/symfony/pull/20418","diff_url":"https://github.com/symfony/symfony/pull/20418.diff","patch_url":"https://github.com/symfony/symfony/pull/20418.patch","issue_url":"https://api.github.com/repos/symfony/symfony/issues/20418","number":20418,"state":"open","locked":false,"title":"[Form][DX] FileType improvements","user":{"login":"yceruto","id":2028198,"avatar_url":"https://avatars.githubusercontent.com/u/2028198?v=3","gravatar_id":"","url":"https://api.github.com/users/yceruto","html_url":"https://github.com/yceruto","followers_url":"https://api.github.com/users/yceruto/followers","following_url":"https://api.github.com/users/yceruto/following{/other_user}","gists_url":"https://api.github.com/users/yceruto/gists{/gist_id}","starred_url":"https://api.github.com/users/yceruto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yceruto/subscriptions","organizations_url":"https://api.github.com/users/yceruto/orgs","repos_url":"https://api.github.com/users/yceruto/repos","events_url":"https://api.github.com/users/yceruto/events{/privacy}","received_events_url":"https://api.github.com/users/yceruto/received_events","type":"User","site_admin":false},"body":"| Q             | A\r\n| ------------- | ---\r\n| Branch?       | master\r\n| Bug fix?      | no\r\n| New feature?  | yes\r\n| BC breaks?    | no\r\n| Deprecations? | no\r\n| Tests pass?   | yes\r\n| Fixed tickets | https://github.com/symfony/symfony/issues/12547\r\n| License       | MIT\r\n| Doc PR        | -\r\n\r\n# (1st) Derive \"data_class\" option from passed \"multiple\" option\r\n\r\nInformation\r\n-------------\r\n\r\nFollowing this tutorial [\"How to Upload Files\"][1] but storing many `brochures` instead of one, i.e.:\r\n\r\n```php\r\n// src/AppBundle/Entity/Product.php\r\n\r\nclass Product \r\n{\r\n    /**\r\n     * @var string[]\r\n     *\r\n     * @ORM\\Column(type=\"array\")\r\n     */\r\n    private $brochures;\r\n    \r\n    //...\r\n}\r\n```\r\n\r\n```php\r\n//src/AppBundle/Form/ProductType.php\r\n\r\n$builder->add('brochures', FileType::class, array(\r\n    'label' => 'Brochures (PDF files)',\r\n    'multiple' => true,\r\n));\r\n```\r\n\r\nThe Problem\r\n--------------\r\n\r\nI found a pain point here when the form is loaded again after save some brochures (Exception):\r\n\r\n> The form's view data is expected to be an instance of class Symfony\\Component\\HttpFoundation\\File\\File, but is a(n) array. You can avoid this error by setting the \"data_class\" option to null or by adding a view transformer that transforms a(n) array to an instance of Symfony\\Component\\HttpFoundation\\File\\File.\r\n\r\nThe message is very clear, but counter-intuitive in this case, because the form field (`FileType`) was configured with `multiple = true`, so IMHO it shouldn't expect a `File` instance but an array of them at all events.\r\n\r\nThe PR's effect\r\n---------------\r\n\r\n**Before:**\r\n\r\n```php\r\n$form = $this->createFormBuilder($product)\r\n    ->add('brochures', FileType::class, [\r\n        'multiple' => true,\r\n\t'data_class' => null, // <---- mandatory\r\n    ])\r\n    ->getForm();\r\n```\r\n\r\n**After:**\r\n\r\n```php\r\n$form = $this->createFormBuilder($product)\r\n    ->add('brochures', FileType::class, [\r\n        'multiple' => true,\r\n    ])\r\n    ->getForm();\r\n```\r\n\r\n# (2nd) Return empty `array()` at submit no file\r\n\r\nInformation\r\n-------------\r\n\r\nBased on the same information before, but adding some constraints:\r\n\r\n```php\r\n// src/AppBundle/Entity/Product.php\r\n\r\nuse Symfony\\Component\\Validator\\Constraints as Assert;\r\n\r\nclass Product \r\n{\r\n    /**\r\n     * @var string[]\r\n     *\r\n     * @ORM\\Column(type=\"array\")\r\n     *\r\n     * @Assert\\Count(min=\"1\") // or @Assert\\NotBlank()\r\n     * @Assert\\All({\r\n     *     @Assert\\File(mimeTypes = {\"application/pdf\", \"application/x-pdf\"})\r\n     * })\r\n     * \r\n     */\r\n    private $brochures;\r\n}\r\n```\r\n\r\nThis should require at least one file to be stored.\r\n\r\nThe Problem\r\n--------------\r\n\r\nBut, when no file is uploaded at submit the form, it's valid completely. The submitted data for this field was `array(null)` so the constraints pass without any problem:\r\n\r\n* `@Assert\\Count(min=\"1\")` pass! because contains at least one element (No matter what)\r\n* `@Assert\\NotBlank()` it could pass! because no `false` and no `empty()`\r\n* `@Assert\\File()` pass! because the element is `null`\r\n\r\nApart from that really we expecting an empty array instead.\r\n\r\nThe PR's effect\r\n----------------\r\n\r\n**Before:**\r\n\r\n```php\r\n// src/AppBundle/Entity/Product.php\r\n\r\nuse Symfony\\Component\\Validator\\Constraints as Assert;\r\n\r\nclass Product \r\n{\r\n    /**\r\n     * @var string[]\r\n     *\r\n     * @ORM\\Column(type=\"array\")\r\n     *\r\n     * @Assert\\All({\r\n     *     @Assert\\NotBlank,\r\n     *     @Assert\\File(mimeTypes = {\"application/pdf\", \"application/x-pdf\"})\r\n     * })\r\n     * \r\n     */\r\n    private $brochures;\r\n}\r\n```\r\n\r\n**After:**\r\n\r\n```php\r\n// src/AppBundle/Entity/Product.php\r\n\r\nuse Symfony\\Component\\Validator\\Constraints as Assert;\r\n\r\nclass Product \r\n{\r\n    /**\r\n     * @var string[]\r\n     *\r\n     * @ORM\\Column(type=\"array\")\r\n     *\r\n     * @Assert\\Count(min=\"1\") // or @Assert\\NotBlank\r\n     * @Assert\\All({\r\n     *     @Assert\\File(mimeTypes = {\"application/pdf\", \"application/x-pdf\"})\r\n     * })\r\n     * \r\n     */\r\n    private $brochures;\r\n}\r\n```\r\n\r\n\r\n  [1]: http://symfony.com/doc/current/controller/upload_file.html","created_at":"2016-11-05T19:43:59Z","updated_at":"2016-11-09T17:59:19Z","closed_at":null,"merged_at":null,"merge_commit_sha":"c4bec7353767433bc50c1588e41989d359e44e88","assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/symfony/symfony/milestones/9","html_url":"https://github.com/symfony/symfony/milestones/3.2","labels_url":"https://api.github.com/repos/symfony/symfony/milestones/9/labels","id":2117463,"number":9,"title":"3.2","description":"https://symfony.com/roadmap?version=3.2#checker","creator":{"login":"javiereguiluz","id":73419,"avatar_url":"https://avatars.githubusercontent.com/u/73419?v=3","gravatar_id":"","url":"https://api.github.com/users/javiereguiluz","html_url":"https://github.com/javiereguiluz","followers_url":"https://api.github.com/users/javiereguiluz/followers","following_url":"https://api.github.com/users/javiereguiluz/following{/other_user}","gists_url":"https://api.github.com/users/javiereguiluz/gists{/gist_id}","starred_url":"https://api.github.com/users/javiereguiluz/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/javiereguiluz/subscriptions","organizations_url":"https://api.github.com/users/javiereguiluz/orgs","repos_url":"https://api.github.com/users/javiereguiluz/repos","events_url":"https://api.github.com/users/javiereguiluz/events{/privacy}","received_events_url":"https://api.github.com/users/javiereguiluz/received_events","type":"User","site_admin":false},"open_issues":11,"closed_issues":7,"state":"open","created_at":"2016-11-06T20:24:00Z","updated_at":"2016-11-09T17:49:24Z","due_on":"2016-11-30T00:00:00Z","closed_at":null},"commits_url":"https://api.github.com/repos/symfony/symfony/pulls/20418/commits","review_comments_url":"https://api.github.com/repos/symfony/symfony/pulls/20418/comments","review_comment_url":"https://api.github.com/repos/symfony/symfony/pulls/comments{/number}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/20418/comments","statuses_url":"https://api.github.com/repos/symfony/symfony/statuses/0aeb078ffffca35adf440d00a2debebbd8e4eb36","head":{"label":"yceruto:form/file-type-dx-improvement","ref":"form/file-type-dx-improvement","sha":"0aeb078ffffca35adf440d00a2debebbd8e4eb36","user":{"login":"yceruto","id":2028198,"avatar_url":"https://avatars.githubusercontent.com/u/2028198?v=3","gravatar_id":"","url":"https://api.github.com/users/yceruto","html_url":"https://github.com/yceruto","followers_url":"https://api.github.com/users/yceruto/followers","following_url":"https://api.github.com/users/yceruto/following{/other_user}","gists_url":"https://api.github.com/users/yceruto/gists{/gist_id}","starred_url":"https://api.github.com/users/yceruto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yceruto/subscriptions","organizations_url":"https://api.github.com/users/yceruto/orgs","repos_url":"https://api.github.com/users/yceruto/repos","events_url":"https://api.github.com/users/yceruto/events{/privacy}","received_events_url":"https://api.github.com/users/yceruto/received_events","type":"User","site_admin":false},"repo":{"id":51099358,"name":"symfony","full_name":"yceruto/symfony","owner":{"login":"yceruto","id":2028198,"avatar_url":"https://avatars.githubusercontent.com/u/2028198?v=3","gravatar_id":"","url":"https://api.github.com/users/yceruto","html_url":"https://github.com/yceruto","followers_url":"https://api.github.com/users/yceruto/followers","following_url":"https://api.github.com/users/yceruto/following{/other_user}","gists_url":"https://api.github.com/users/yceruto/gists{/gist_id}","starred_url":"https://api.github.com/users/yceruto/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yceruto/subscriptions","organizations_url":"https://api.github.com/users/yceruto/orgs","repos_url":"https://api.github.com/users/yceruto/repos","events_url":"https://api.github.com/users/yceruto/events{/privacy}","received_events_url":"https://api.github.com/users/yceruto/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/yceruto/symfony","description":"The Symfony PHP framework","fork":true,"url":"https://api.github.com/repos/yceruto/symfony","forks_url":"https://api.github.com/repos/yceruto/symfony/forks","keys_url":"https://api.github.com/repos/yceruto/symfony/keys{/key_id}","collaborators_url":"https://api.github.com/repos/yceruto/symfony/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/yceruto/symfony/teams","hooks_url":"https://api.github.com/repos/yceruto/symfony/hooks","issue_events_url":"https://api.github.com/repos/yceruto/symfony/issues/events{/number}","events_url":"https://api.github.com/repos/yceruto/symfony/events","assignees_url":"https://api.github.com/repos/yceruto/symfony/assignees{/user}","branches_url":"https://api.github.com/repos/yceruto/symfony/branches{/branch}","tags_url":"https://api.github.com/repos/yceruto/symfony/tags","blobs_url":"https://api.github.com/repos/yceruto/symfony/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/yceruto/symfony/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/yceruto/symfony/git/refs{/sha}","trees_url":"https://api.github.com/repos/yceruto/symfony/git/trees{/sha}","statuses_url":"https://api.github.com/repos/yceruto/symfony/statuses/{sha}","languages_url":"https://api.github.com/repos/yceruto/symfony/languages","stargazers_url":"https://api.github.com/repos/yceruto/symfony/stargazers","contributors_url":"https://api.github.com/repos/yceruto/symfony/contributors","subscribers_url":"https://api.github.com/repos/yceruto/symfony/subscribers","subscription_url":"https://api.github.com/repos/yceruto/symfony/subscription","commits_url":"https://api.github.com/repos/yceruto/symfony/commits{/sha}","git_commits_url":"https://api.github.com/repos/yceruto/symfony/git/commits{/sha}","comments_url":"https://api.github.com/repos/yceruto/symfony/comments{/number}","issue_comment_url":"https://api.github.com/repos/yceruto/symfony/issues/comments{/number}","contents_url":"https://api.github.com/repos/yceruto/symfony/contents/{+path}","compare_url":"https://api.github.com/repos/yceruto/symfony/compare/{base}...{head}","merges_url":"https://api.github.com/repos/yceruto/symfony/merges","archive_url":"https://api.github.com/repos/yceruto/symfony/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/yceruto/symfony/downloads","issues_url":"https://api.github.com/repos/yceruto/symfony/issues{/number}","pulls_url":"https://api.github.com/repos/yceruto/symfony/pulls{/number}","milestones_url":"https://api.github.com/repos/yceruto/symfony/milestones{/number}","notifications_url":"https://api.github.com/repos/yceruto/symfony/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/yceruto/symfony/labels{/name}","releases_url":"https://api.github.com/repos/yceruto/symfony/releases{/id}","deployments_url":"https://api.github.com/repos/yceruto/symfony/deployments","created_at":"2016-02-04T19:22:07Z","updated_at":"2016-02-04T19:22:22Z","pushed_at":"2016-11-09T15:46:29Z","git_url":"git://github.com/yceruto/symfony.git","ssh_url":"git@github.com:yceruto/symfony.git","clone_url":"https://github.com/yceruto/symfony.git","svn_url":"https://github.com/yceruto/symfony","homepage":"symfony.com","size":74402,"stargazers_count":0,"watchers_count":0,"language":"PHP","has_issues":false,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"symfony:master","ref":"master","sha":"eb1498155b3096f259274b52eb782dcc07fe9536","user":{"login":"symfony","id":143937,"avatar_url":"https://avatars.githubusercontent.com/u/143937?v=3","gravatar_id":"","url":"https://api.github.com/users/symfony","html_url":"https://github.com/symfony","followers_url":"https://api.github.com/users/symfony/followers","following_url":"https://api.github.com/users/symfony/following{/other_user}","gists_url":"https://api.github.com/users/symfony/gists{/gist_id}","starred_url":"https://api.github.com/users/symfony/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/symfony/subscriptions","organizations_url":"https://api.github.com/users/symfony/orgs","repos_url":"https://api.github.com/users/symfony/repos","events_url":"https://api.github.com/users/symfony/events{/privacy}","received_events_url":"https://api.github.com/users/symfony/received_events","type":"Organization","site_admin":false},"repo":{"id":458058,"name":"symfony","full_name":"symfony/symfony","owner":{"login":"symfony","id":143937,"avatar_url":"https://avatars.githubusercontent.com/u/143937?v=3","gravatar_id":"","url":"https://api.github.com/users/symfony","html_url":"https://github.com/symfony","followers_url":"https://api.github.com/users/symfony/followers","following_url":"https://api.github.com/users/symfony/following{/other_user}","gists_url":"https://api.github.com/users/symfony/gists{/gist_id}","starred_url":"https://api.github.com/users/symfony/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/symfony/subscriptions","organizations_url":"https://api.github.com/users/symfony/orgs","repos_url":"https://api.github.com/users/symfony/repos","events_url":"https://api.github.com/users/symfony/events{/privacy}","received_events_url":"https://api.github.com/users/symfony/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/symfony/symfony","description":"The Symfony PHP framework","fork":false,"url":"https://api.github.com/repos/symfony/symfony","forks_url":"https://api.github.com/repos/symfony/symfony/forks","keys_url":"https://api.github.com/repos/symfony/symfony/keys{/key_id}","collaborators_url":"https://api.github.com/repos/symfony/symfony/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/symfony/symfony/teams","hooks_url":"https://api.github.com/repos/symfony/symfony/hooks","issue_events_url":"https://api.github.com/repos/symfony/symfony/issues/events{/number}","events_url":"https://api.github.com/repos/symfony/symfony/events","assignees_url":"https://api.github.com/repos/symfony/symfony/assignees{/user}","branches_url":"https://api.github.com/repos/symfony/symfony/branches{/branch}","tags_url":"https://api.github.com/repos/symfony/symfony/tags","blobs_url":"https://api.github.com/repos/symfony/symfony/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/symfony/symfony/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/symfony/symfony/git/refs{/sha}","trees_url":"https://api.github.com/repos/symfony/symfony/git/trees{/sha}","statuses_url":"https://api.github.com/repos/symfony/symfony/statuses/{sha}","languages_url":"https://api.github.com/repos/symfony/symfony/languages","stargazers_url":"https://api.github.com/repos/symfony/symfony/stargazers","contributors_url":"https://api.github.com/repos/symfony/symfony/contributors","subscribers_url":"https://api.github.com/repos/symfony/symfony/subscribers","subscription_url":"https://api.github.com/repos/symfony/symfony/subscription","commits_url":"https://api.github.com/repos/symfony/symfony/commits{/sha}","git_commits_url":"https://api.github.com/repos/symfony/symfony/git/commits{/sha}","comments_url":"https://api.github.com/repos/symfony/symfony/comments{/number}","issue_comment_url":"https://api.github.com/repos/symfony/symfony/issues/comments{/number}","contents_url":"https://api.github.com/repos/symfony/symfony/contents/{+path}","compare_url":"https://api.github.com/repos/symfony/symfony/compare/{base}...{head}","merges_url":"https://api.github.com/repos/symfony/symfony/merges","archive_url":"https://api.github.com/repos/symfony/symfony/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/symfony/symfony/downloads","issues_url":"https://api.github.com/repos/symfony/symfony/issues{/number}","pulls_url":"https://api.github.com/repos/symfony/symfony/pulls{/number}","milestones_url":"https://api.github.com/repos/symfony/symfony/milestones{/number}","notifications_url":"https://api.github.com/repos/symfony/symfony/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/symfony/symfony/labels{/name}","releases_url":"https://api.github.com/repos/symfony/symfony/releases{/id}","deployments_url":"https://api.github.com/repos/symfony/symfony/deployments","created_at":"2010-01-04T14:21:21Z","updated_at":"2016-11-09T14:14:46Z","pushed_at":"2016-11-09T17:49:11Z","git_url":"git://github.com/symfony/symfony.git","ssh_url":"git@github.com:symfony/symfony.git","clone_url":"https://github.com/symfony/symfony.git","svn_url":"https://github.com/symfony/symfony","homepage":"symfony.com","size":140379,"stargazers_count":13248,"watchers_count":13248,"language":"PHP","has_issues":true,"has_downloads":false,"has_wiki":false,"has_pages":false,"forks_count":5108,"mirror_url":null,"open_issues_count":803,"forks":5108,"open_issues":803,"watchers":13248,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/symfony/symfony/pulls/20418"},"html":{"href":"https://github.com/symfony/symfony/pull/20418"},"issue":{"href":"https://api.github.com/repos/symfony/symfony/issues/20418"},"comments":{"href":"https://api.github.com/repos/symfony/symfony/issues/20418/comments"},"review_comments":{"href":"https://api.github.com/repos/symfony/symfony/pulls/20418/comments"},"review_comment":{"href":"https://api.github.com/repos/symfony/symfony/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/symfony/symfony/pulls/20418/commits"},"statuses":{"href":"https://api.github.com/repos/symfony/symfony/statuses/0aeb078ffffca35adf440d00a2debebbd8e4eb36"}}}}
{"actor":{"display_login":"yceruto"}}