{"action":"created","issue":{"url":"https://api.github.com/repos/symfony/symfony/issues/20418","repository_url":"https://api.github.com/repos/symfony/symfony","labels_url":"https://api.github.com/repos/symfony/symfony/issues/20418/labels{/name}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/20418/comments","events_url":"https://api.github.com/repos/symfony/symfony/issues/20418/events","html_url":"https://github.com/symfony/symfony/pull/20418","id":187518746,"number":20418,"title":"[Form][DX] FileType \"multiple\" fixes","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},"labels":[{"id":107512439,"url":"https://api.github.com/repos/symfony/symfony/labels/DX","name":"DX","color":"5319e7","default":false},{"id":100378,"url":"https://api.github.com/repos/symfony/symfony/labels/Feature","name":"Feature","color":"02e10c","default":false},{"id":99594,"url":"https://api.github.com/repos/symfony/symfony/labels/Form","name":"Form","color":"DDDDDD","default":false},{"id":237003792,"url":"https://api.github.com/repos/symfony/symfony/labels/Status:%20Needs%20Review","name":"Status: Needs Review","color":"ededed","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":15,"created_at":"2016-11-05T19:43:59Z","updated_at":"2016-11-18T17:48:38Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/symfony/symfony/pulls/20418","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"},"body":"| Q             | A\r\n| ------------- | ---\r\n| Branch?       | 2.7\r\n| Bug fix?      | yes\r\n| New feature?  | no\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"},"comment":{"url":"https://api.github.com/repos/symfony/symfony/issues/comments/261595406","html_url":"https://github.com/symfony/symfony/pull/20418#issuecomment-261595406","issue_url":"https://api.github.com/repos/symfony/symfony/issues/20418","id":261595406,"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},"created_at":"2016-11-18T17:48:38Z","updated_at":"2016-11-18T17:48:38Z","body":"Can anyone remove ``Feature`` and add ``Bug`` label ?"}}
{"actor":{"display_login":"yceruto"}}