From 2ecfee9751548be56d7e3c17dd681e70efb37e37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20Paris?= Date: Wed, 12 Oct 2022 21:18:59 +0200 Subject: [PATCH] Modernize documentation code - migrate to attributes; - add helpful phpdoc annotations; - use typed properties; - add type declarations. --- docs/en/reference/association-mapping.rst | 236 ++++++++---------- docs/en/reference/faq.rst | 9 +- docs/en/reference/security.rst | 25 +- .../reference/working-with-associations.rst | 120 ++++----- docs/en/tutorials/composite-primary-keys.rst | 25 +- 5 files changed, 201 insertions(+), 214 deletions(-) diff --git a/docs/en/reference/association-mapping.rst b/docs/en/reference/association-mapping.rst index 8dbcd1f19a6..c97af40afbe 100644 --- a/docs/en/reference/association-mapping.rst +++ b/docs/en/reference/association-mapping.rst @@ -40,19 +40,17 @@ A many-to-one association is the most common association between objects. Exampl .. code-block:: php */ - private $features; + #[OneToMany(targetEntity: Feature::class, mappedBy: 'product')] + private Collection $features; // ... public function __construct() { @@ -347,16 +338,14 @@ bidirectional many-to-one. } } - /** @Entity */ + #[Entity] class Feature { // ... - /** - * Many features have one product. This is the owning side. - * @ManyToOne(targetEntity="Product", inversedBy="features") - * @JoinColumn(name="product_id", referencedColumnName="id") - */ - private $product; + /** Many features have one product. This is the owning side. */ + #[ManyToOne(targetEntity: Product::class, inversedBy: 'features')] + #[JoinColumn(name: 'product_id', referencedColumnName: 'id')] + private Product|null $product; // ... } @@ -424,30 +413,30 @@ The following example sets up such a unidirectional one-to-many association: .. code-block:: php */ - private $phonenumbers; + #[ORM\JoinTable(name: 'users_phonenumbers')] + #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[ORM\InverseJoinColumn(name: 'phonenumber_id', referencedColumnName: 'id', unique: true)] + #[ORM\ManyToMany(targetEntity: 'Phonenumber')] + private Collection $phonenumbers; public function __construct() { - $this->phonenumbers = new \Doctrine\Common\Collections\ArrayCollection(); + $this->phonenumbers = new ArrayCollection(); } // ... } - /** @Entity */ + #[Entity] class Phonenumber { // ... @@ -526,26 +515,25 @@ database perspective is known as an adjacency list approach. .. code-block:: php */ - private $children; + #[OneToMany(targetEntity: Category::class, mappedBy: 'parent')] + private Collection $children; - /** - * Many Categories have One Category. - * @ManyToOne(targetEntity="Category", inversedBy="children") - * @JoinColumn(name="parent_id", referencedColumnName="id") - */ - private $parent; + /** Many Categories have One Category. */ + #[ManyToOne(targetEntity: Category::class, inversedBy: 'children')] + #[JoinColumn(name: 'parent_id', referencedColumnName: 'id')] + private Category|null $parent; // ... public function __construct() { - $this->children = new \Doctrine\Common\Collections\ArrayCollection(); + $this->children = new ArrayCollection(); } } @@ -597,29 +585,29 @@ entities: .. code-block:: php */ - private $groups; + #[ORM\JoinTable(name: 'users_groups')] ++ #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id')] ++ #[ORM\InverseJoinColumn(name: 'group_id', referencedColumnName: 'id')] ++ #[ORM\ManyToMany(targetEntity: Group::class)] + private Collection $groups; // ... public function __construct() { - $this->groups = new \Doctrine\Common\Collections\ArrayCollection(); + $this->groups = new ArrayCollection(); } } - /** @Entity */ + #[Entity] class Group { // ... @@ -698,37 +686,39 @@ one is bidirectional. .. code-block:: php */ - private $groups; + #[ManyToMany(targetEntity: Group::class, inversedBy: 'users')] + #[JoinTable(name: 'users_groups')] + private Collection $groups; public function __construct() { - $this->groups = new \Doctrine\Common\Collections\ArrayCollection(); + $this->groups = new ArrayCollection(); } // ... } - /** @Entity */ + #[Entity] class Group { // ... /** * Many Groups have Many Users. - * @ManyToMany(targetEntity="User", mappedBy="groups") + * @var Collection */ - private $users; + #[ManyToMany(targetEntity: User::class, mappedBy: 'groups')] + private Collection $users; public function __construct() { - $this->users = new \Doctrine\Common\Collections\ArrayCollection(); + $this->users = new ArrayCollection(); } // ... @@ -806,9 +796,9 @@ understandable: addArticle($this); // synchronously updating inverse side $this->tags[] = $tag; @@ -817,9 +807,9 @@ understandable: class Tag { - private $articles; + private Collection $articles; - public function addArticle(Article $article) + public function addArticle(Article $article): void { $this->articles[] = $article; } @@ -847,30 +837,31 @@ field named ``$friendsWithMe`` and ``$myFriends``. .. code-block:: php */ - private $friendsWithMe; + #[ManyToMany(targetEntity: User::class, mappedBy: 'myFriends')] + private Collection $friendsWithMe; /** * Many Users have many Users. - * @ManyToMany(targetEntity="User", inversedBy="friendsWithMe") - * @JoinTable(name="friends", - * joinColumns={@JoinColumn(name="user_id", referencedColumnName="id")}, - * inverseJoinColumns={@JoinColumn(name="friend_user_id", referencedColumnName="id")} - * ) + * @var Collection */ - private $myFriends; + #[ORM\JoinTable(name: 'friends')] + #[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id')] + #[ORM\InverseJoinColumn(name: 'friend_user_id', referencedColumnName: 'id')] + #[ORM\ManyToMany(targetEntity: 'User', inversedBy: 'friendsWithMe')] + private Collection $myFriends; public function __construct() { - $this->friendsWithMe = new \Doctrine\Common\Collections\ArrayCollection(); - $this->myFriends = new \Doctrine\Common\Collections\ArrayCollection(); + $this->friendsWithMe = new ArrayCollection(); + $this->myFriends = new ArrayCollection(); } // ... @@ -913,8 +904,8 @@ As an example, consider this mapping: .. code-block:: php */ - private $groups; + #[ORM\JoinTable(name: 'User_Group')] + #[ORM\JoinColumn(name: 'User_id', referencedColumnName: 'id')] + #[ORM\InverseJoinColumn(name: 'Group_id', referencedColumnName: 'id')] + #[ORM\ManyToMany(targetEntity: Group::class)] + private Collection $groups; // ... } @@ -1072,7 +1061,7 @@ attribute on ``JoinColumn`` will be inherited from PHP type. So that: .. code-block:: php $userInput */ + public function fromArray(array $userInput): void { foreach ($userInput as $key => $value) { $this->$key = $value; @@ -118,7 +119,7 @@ entity might look like this: } } -Now the possiblity of mass-asignment exists on this entity and can +Now the possiblity of mass-assignment exists on this entity and can be exploited by attackers to set the "isAdmin" flag to true on any object when you pass the whole request data to this method like: diff --git a/docs/en/reference/working-with-associations.rst b/docs/en/reference/working-with-associations.rst index 510d85f05b3..13376b4d86c 100644 --- a/docs/en/reference/working-with-associations.rst +++ b/docs/en/reference/working-with-associations.rst @@ -32,62 +32,64 @@ information about its type and if it's the owning or inverse side. .. code-block:: php */ - private $favorites; + #[ManyToMany(targetEntity: Comment::class, inversedBy: 'userFavorites')] + #[JoinTable(name: 'user_favorite_comments')] + private Collection $favorites; /** * Unidirectional - Many users have marked many comments as read * - * @ManyToMany(targetEntity="Comment") - * @JoinTable(name="user_read_comments") + * @var Collection */ - private $commentsRead; + #[ManyToMany(targetEntity: Comment::class)] + #[JoinTable(name: 'user_read_comments')] + private Collection $commentsRead; /** * Bidirectional - One-To-Many (INVERSE SIDE) * - * @OneToMany(targetEntity="Comment", mappedBy="author") + * @var Collection */ - private $commentsAuthored; + #[OneToMany(targetEntity: Comment::class, mappedBy: 'author')] + private Collection $commentsAuthored; - /** - * Unidirectional - Many-To-One - * - * @ManyToOne(targetEntity="Comment") - */ - private $firstComment; + /** Unidirectional - Many-To-One */ + #[ManyToOne(targetEntity: Comment::class)] + private Comment|null $firstComment; } - /** @Entity */ + #[Entity] class Comment { - /** @Id @GeneratedValue @Column(type="string") */ - private $id; + #[Id] + #[GeneratedValue] + #[Column(type: 'string')] + private string $id; /** * Bidirectional - Many comments are favorited by many users (INVERSE SIDE) * - * @ManyToMany(targetEntity="User", mappedBy="favorites") + * @var Collection */ - private $userFavorites; + #[ManyToMany(targetEntity: User::class, mappedBy: 'favorites')] + private Collection $userFavorites; /** * Bidirectional - Many Comments are authored by one user (OWNING SIDE) - * - * @ManyToOne(targetEntity="User", inversedBy="commentsAuthored") */ - private $author; + #[ManyToOne(targetEntity: User::class, inversedBy: 'commentsAuthored')] + private User|null $author; } This two entities generate the following MySQL Schema (Foreign Key @@ -132,11 +134,13 @@ relations of the ``User``: class User { // ... - public function getReadComments() { + /** @return Collection */ + public function getReadComments(): Collection { return $this->commentsRead; } - public function setFirstComment(Comment $c) { + /** @param Collection $c */ + public function setFirstComment(Comment $c): void { $this->firstComment = $c; } } @@ -172,11 +176,13 @@ fields on both sides: { // .. - public function getAuthoredComments() { + /** @return Collection */ + public function getAuthoredComments(): Collection { return $this->commentsAuthored; } - public function getFavoriteComments() { + /** @return Collection */ + public function getFavoriteComments(): Collection { return $this->favorites; } } @@ -185,11 +191,12 @@ fields on both sides: { // ... - public function getUserFavorites() { + /** @return Collection */ + public function getUserFavorites(): Collection { return $this->userFavorites; } - public function setAuthor(User $author = null) { + public function setAuthor(User|null $author = null): void { $this->author = $author; } } @@ -292,12 +299,12 @@ example that encapsulate much of the association management code: class User { // ... - public function markCommentRead(Comment $comment) { + public function markCommentRead(Comment $comment): void { // Collections implement ArrayAccess $this->commentsRead[] = $comment; } - public function addComment(Comment $comment) { + public function addComment(Comment $comment): void { if (count($this->commentsAuthored) == 0) { $this->setFirstComment($comment); } @@ -305,16 +312,16 @@ example that encapsulate much of the association management code: $comment->setAuthor($this); } - private function setFirstComment(Comment $c) { + private function setFirstComment(Comment $c): void { $this->firstComment = $c; } - public function addFavorite(Comment $comment) { + public function addFavorite(Comment $comment): void { $this->favorites->add($comment); $comment->addUserFavorite($this); } - public function removeFavorite(Comment $comment) { + public function removeFavorite(Comment $comment): void { $this->favorites->removeElement($comment); $comment->removeUserFavorite($this); } @@ -324,11 +331,11 @@ example that encapsulate much of the association management code: { // .. - public function addUserFavorite(User $user) { + public function addUserFavorite(User $user): void { $this->userFavorites[] = $user; } - public function removeUserFavorite(User $user) { + public function removeUserFavorite(User $user): void { $this->userFavorites->removeElement($user); } } @@ -356,7 +363,8 @@ the details inside the classes can be challenging. */ + public function getReadComments(): array { return $this->commentsRead->toArray(); } } @@ -437,8 +445,10 @@ only accessing it through the User entity: // User entity class User { - private $id; - private $comments; + private int $id; + + /** @var Collection */ + private Collection $comments; public function __construct() { @@ -464,11 +474,8 @@ If you then set up the cascading to the ``User#commentsAuthored`` property... class User { // ... - /** - * Bidirectional - One-To-Many (INVERSE SIDE) - * - * @OneToMany(targetEntity="Comment", mappedBy="author", cascade={"persist", "remove"}) - */ + /** Bidirectional - One-To-Many (INVERSE SIDE) */ + #[OneToMany(targetEntity: Comment::class, mappedBy: 'author', cascade: ['persist', 'remove'])] private $commentsAuthored; // ... } @@ -577,31 +584,30 @@ and StandingData: use Doctrine\Common\Collections\ArrayCollection; - /** - * @Entity - */ + #[Entity] class Contact { - /** @Id @Column(type="integer") @GeneratedValue */ - private $id; + #[Id, Column(type: 'integer'), GeneratedValue] + private int|null $id; - /** @OneToOne(targetEntity="StandingData", cascade={"persist"}, orphanRemoval=true) */ - private $standingData; + #[OneToOne(targetEntity: StandingData::class, cascade: ['persist'], orphanRemoval: true)] + private StandingData|null $standingData; - /** @OneToMany(targetEntity="Address", mappedBy="contact", cascade={"persist"}, orphanRemoval=true) */ - private $addresses; + /** @var Collection */ + #[OneToMany(targetEntity: Address::class, mappedBy: 'contact', cascade: ['persist'], orphanRemoval: true)] + private Collection $addresses; public function __construct() { $this->addresses = new ArrayCollection(); } - public function newStandingData(StandingData $sd) + public function newStandingData(StandingData $sd): void { $this->standingData = $sd; } - public function removeAddress($pos) + public function removeAddress(int $pos): void { unset($this->addresses[$pos]); } diff --git a/docs/en/tutorials/composite-primary-keys.rst b/docs/en/tutorials/composite-primary-keys.rst index 12ad55e53bb..9aaae10c06a 100644 --- a/docs/en/tutorials/composite-primary-keys.rst +++ b/docs/en/tutorials/composite-primary-keys.rst @@ -23,33 +23,28 @@ and year of production as primary keys: .. configuration-block:: - .. code-block:: php + .. code-block:: attribute name = $name; - $this->year = $year; + public function __construct( + #[Id, Column(type="string")] + string $name, + #[Id, Column(type="integer")] + int $year, + ) { } - public function getModelName() + public function getModelName(): string { return $this->name; } - public function getYearOfProduction() + public function getYearOfProduction(): int { return $this->year; }