{"action":"created","issue":{"url":"https://api.github.com/repos/symfony/symfony/issues/27735","repository_url":"https://api.github.com/repos/symfony/symfony","labels_url":"https://api.github.com/repos/symfony/symfony/issues/27735/labels{/name}","comments_url":"https://api.github.com/repos/symfony/symfony/issues/27735/comments","events_url":"https://api.github.com/repos/symfony/symfony/issues/27735/events","html_url":"https://github.com/symfony/symfony/pull/27735","id":335881386,"node_id":"MDExOlB1bGxSZXF1ZXN0MTk3NDY5MDI3","number":27735,"title":"[Validator][DoctrineBridge][FWBundle] Automatic data validation","user":{"login":"dunglas","id":57224,"node_id":"MDQ6VXNlcjU3MjI0","avatar_url":"https://avatars2.githubusercontent.com/u/57224?v=4","gravatar_id":"","url":"https://api.github.com/users/dunglas","html_url":"https://github.com/dunglas","followers_url":"https://api.github.com/users/dunglas/followers","following_url":"https://api.github.com/users/dunglas/following{/other_user}","gists_url":"https://api.github.com/users/dunglas/gists{/gist_id}","starred_url":"https://api.github.com/users/dunglas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dunglas/subscriptions","organizations_url":"https://api.github.com/users/dunglas/orgs","repos_url":"https://api.github.com/users/dunglas/repos","events_url":"https://api.github.com/users/dunglas/events{/privacy}","received_events_url":"https://api.github.com/users/dunglas/received_events","type":"User","site_admin":false},"labels":[{"id":337187703,"node_id":"MDU6TGFiZWwzMzcxODc3MDM=","url":"https://api.github.com/repos/symfony/symfony/labels/DoctrineBridge","name":"DoctrineBridge","color":"dddddd","default":false},{"id":100378,"node_id":"MDU6TGFiZWwxMDAzNzg=","url":"https://api.github.com/repos/symfony/symfony/labels/Feature","name":"Feature","color":"02e10c","default":false},{"id":237003792,"node_id":"MDU6TGFiZWwyMzcwMDM3OTI=","url":"https://api.github.com/repos/symfony/symfony/labels/Status:%20Needs%20Review","name":"Status: Needs Review","color":"ededed","default":false},{"id":99599,"node_id":"MDU6TGFiZWw5OTU5OQ==","url":"https://api.github.com/repos/symfony/symfony/labels/Validator","name":"Validator","color":"DDDDDD","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":{"url":"https://api.github.com/repos/symfony/symfony/milestones/18","html_url":"https://github.com/symfony/symfony/milestone/18","labels_url":"https://api.github.com/repos/symfony/symfony/milestones/18/labels","id":3234645,"node_id":"MDk6TWlsZXN0b25lMzIzNDY0NQ==","number":18,"title":"next","description":"https://symfony.com/roadmap?version=4.2#checker","creator":{"login":"nicolas-grekas","id":243674,"node_id":"MDQ6VXNlcjI0MzY3NA==","avatar_url":"https://avatars3.githubusercontent.com/u/243674?v=4","gravatar_id":"","url":"https://api.github.com/users/nicolas-grekas","html_url":"https://github.com/nicolas-grekas","followers_url":"https://api.github.com/users/nicolas-grekas/followers","following_url":"https://api.github.com/users/nicolas-grekas/following{/other_user}","gists_url":"https://api.github.com/users/nicolas-grekas/gists{/gist_id}","starred_url":"https://api.github.com/users/nicolas-grekas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/nicolas-grekas/subscriptions","organizations_url":"https://api.github.com/users/nicolas-grekas/orgs","repos_url":"https://api.github.com/users/nicolas-grekas/repos","events_url":"https://api.github.com/users/nicolas-grekas/events{/privacy}","received_events_url":"https://api.github.com/users/nicolas-grekas/received_events","type":"User","site_admin":false},"open_issues":140,"closed_issues":79,"state":"open","created_at":"2018-04-02T12:03:21Z","updated_at":"2018-06-27T09:35:01Z","due_on":null,"closed_at":null},"comments":23,"created_at":"2018-06-26T15:45:04Z","updated_at":"2018-06-27T09:55:34Z","closed_at":null,"author_association":"MEMBER","pull_request":{"url":"https://api.github.com/repos/symfony/symfony/pulls/27735","html_url":"https://github.com/symfony/symfony/pull/27735","diff_url":"https://github.com/symfony/symfony/pull/27735.diff","patch_url":"https://github.com/symfony/symfony/pull/27735.patch"},"body":"| Q             | A\r\n| ------------- | ---\r\n| Branch?       | master\r\n| Bug fix?      | no\r\n| New feature?  | yes<!-- don't forget to update src/**/CHANGELOG.md files -->\r\n| BC breaks?    | no     <!-- see https://symfony.com/bc -->\r\n| Deprecations? | no <!-- don't forget to update UPGRADE-*.md and src/**/CHANGELOG.md files -->\r\n| Tests pass?   | yes    <!-- please add some, will be required by reviewers -->\r\n| Fixed tickets | n/a   <!-- #-prefixed issue number(s), if any -->\r\n| License       | MIT\r\n| Doc PR        | todo\r\n\r\nThis feature automatically adds some validation constraints by inferring existing metadata. To do so, it uses the PropertyInfo component and Doctrine metadata, but it has been designed to be easily extendable.\r\n\r\nExample:\r\n\r\n```php\r\nuse Doctrine\\ORM\\Mapping as ORM;\r\n\r\n/**\r\n * @ORM\\Entity\r\n */\r\nclass Dummy\r\n{\r\n    /**\r\n     * @ORM\\Id\r\n     * @ORM\\GeneratedValue(strategy=\"AUTO\")\r\n     * @ORM\\Column(type=\"integer\")\r\n     */\r\n    public $id;\r\n\r\n    /**\r\n     * @ORM\\Column(nullable=true)\r\n     */\r\n    public $columnNullable;\r\n\r\n    /**\r\n     * @ORM\\Column(length=20)\r\n     */\r\n    public $columnLength;\r\n\r\n    /**\r\n     * @ORM\\Column(unique=true)\r\n     */\r\n    public $columnUnique;\r\n}\r\n\r\n$manager = $this->managerRegistry->getManager();\r\n$manager->getRepository(Dummy::class);\r\n\r\n$firstOne = new Dummy();\r\n$firstOne->columnUnique = 'unique';\r\n$firstOne->columnLength = '0';\r\n\r\n$manager->persist($firstOne);\r\n$manager->flush();\r\n\r\n$dummy = new Dummy();\r\n$dummy->columnNullable = 1; // type mistmatch\r\n$dummy->columnLength = '012345678901234567890'; // too long\r\n$dummy->columnUnique = 'unique'; // not unique\r\n\r\n$res = $this->validator->validate($dummy);\r\ndump((string) $res);\r\n\r\n/*\r\nObject(App\\Entity\\Dummy).columnUnique:\\n\r\n    This value is already used. (code 23bd9dbf-6b9b-41cd-a99e-4844bcf3077f)\\n\r\nObject(App\\Entity\\Dummy).columnLength:\\n\r\n    This value is too long. It should have 20 characters or less. (code d94b19cc-114f-4f44-9cc4-4138e80a87b9)\\n\r\nObject(App\\Entity\\Dummy).id:\\n\r\n    This value should not be null. (code ad32d13f-c3d4-423b-909a-857b961eb720)\\n\r\nObject(App\\Entity\\Dummy).columnNullable:\\n\r\n    This value should be of type string. (code ba785a8c-82cb-4283-967c-3cf342181b40)\\n\r\n*/\r\n```\r\n\r\nIt also works for DTOs:\r\n\r\n```php\r\n\r\nclass MyDto\r\n{\r\n    /** @var string */\r\n    private $name;\r\n}\r\n\r\n$dto = new MyDto();\r\n$dto->name = 1; // type error\r\n\r\ndump($validator->validate($dto));\r\n\r\n/*\r\nObject(MyDto).name:\\n\r\n    This value should be of type string. (code ba785a8c-82cb-4283-967c-3cf342181b40)\\n\r\n*/\r\n```\r\n\r\nSupported constraints currently are:\r\n\r\n* `@NotNull` (using PropertyInfo type extractor, so supports Doctrine metadata, getters/setters and PHPDoc)\r\n* `@Type` (using PropertyInfo type extractor, so supports Doctrine metadata, getters/setters and PHPDoc)\r\n* `@UniqueEntity` (using Doctrine's `unique` metadata)\r\n* `@Length` (using Doctrine's `length` metadata)\r\n\r\nMany users don't understand that the Doctrine mapping doesn't validate anything (it's just a hint for the schema generator). It leads to usability and security issues (that are not entirely fixed by this PR!!).\r\nEven the ones who add constraints often omit important ones like `@Length`, or `@Type` (important when building web APIs).\r\nThis PR aims to improve things a bit, and ease the development process in RAD and when prototyping. It provides an upgrade path to use proper validation constraints.\r\n\r\nI plan to make it opt-in, disabled by default, but enabled in the default Flex recipe. (= off by default when using components, on by default when using the full stack framework)\r\n\r\n**Do not use this right now:** it's a POC, aimed to open the discussion. I'm not even sure it's a good idea to merge this in core for now.\r\n\r\nTODO:\r\n\r\n* [ ] Add configuration flags\r\n* [ ] Move the Doctrine-related DI logic from the extension to DoctrineBundle\r\n* [ ] Commit the tests"},"comment":{"url":"https://api.github.com/repos/symfony/symfony/issues/comments/400613924","html_url":"https://github.com/symfony/symfony/pull/27735#issuecomment-400613924","issue_url":"https://api.github.com/repos/symfony/symfony/issues/27735","id":400613924,"node_id":"MDEyOklzc3VlQ29tbWVudDQwMDYxMzkyNA==","user":{"login":"dunglas","id":57224,"node_id":"MDQ6VXNlcjU3MjI0","avatar_url":"https://avatars2.githubusercontent.com/u/57224?v=4","gravatar_id":"","url":"https://api.github.com/users/dunglas","html_url":"https://github.com/dunglas","followers_url":"https://api.github.com/users/dunglas/followers","following_url":"https://api.github.com/users/dunglas/following{/other_user}","gists_url":"https://api.github.com/users/dunglas/gists{/gist_id}","starred_url":"https://api.github.com/users/dunglas/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dunglas/subscriptions","organizations_url":"https://api.github.com/users/dunglas/orgs","repos_url":"https://api.github.com/users/dunglas/repos","events_url":"https://api.github.com/users/dunglas/events{/privacy}","received_events_url":"https://api.github.com/users/dunglas/received_events","type":"User","site_admin":false},"created_at":"2018-06-27T09:55:34Z","updated_at":"2018-06-27T09:55:34Z","author_association":"MEMBER","body":"> hence I feel like DTOs are also a better option for APIs.\r\n\r\nIt's where API Platform differs from Forms. Class marked with `@ApiResource` are essentially DTO, that can also and optionally be entities for RAD: https://github.com/api-platform/core/issues/1747#issuecomment-391308202"}}
{"actor":{"display_login":"dunglas"}}