MySQL 使用Doctrine创建一个一对多多态关联关系
在MySQL数据库中,多态关系是一种非常有用的概念。一个多态关系可以允许一个表中的一个字段引用到多个表中的多个字段。这种关系在数据建模时非常常见,例如,一个博客可以有多种类型的评论,包括文本评论、图片评论和视频评论。在这种情况下,“评论”表的“评论类型”字段就是一个多态关系。
在本教程中,我们将介绍如何使用Doctrine创建一个一对多多态关联关系。我们将使用Symfony 4作为开发框架,但本教程中的概念和代码适用于任何PHP应用程序。
阅读更多:MySQL 教程
多态关系
在MySQL中,多态关系指一个单一的字段可以引用多个表中的多个字段。例如,考虑一个名为“Comments”的表。该表有一个“commentable_id”字段和一个“commentable_type”字段。这个模式允许评论表引用到另外两个表中,比如“Posts”和“Pictures”,就像下面这样:
+----+--------------+----------------+
| id | commentable_id | commentable_type |
+----+--------------+----------------+
| 1 | 2 | Post |
| 2 | 4 | Picture |
| 3 | 5 | Post |
+----+--------------+----------------+
在上面的表格中,我们可以看到评论的类型可以是“Post”或“Picture”。对于每种类型,我们可以根据“commentable_id”字段的值引用到相应的主键。因此,在上面的示例中,“commentable_id”为“2”的评论实际上是对“Posts”表中的帖子的评论。
多态关系可以使数据库模型更加灵活,可以很容易地管理表与表之间的关系,因为我们可以使用相同的模式在单个“Comments”中引用到不同类型的对象。
创建多态关联关系
让我们使用Doctrine在Symfony 4应用程序中创建一个多态关联关系。我们将创建一个名为“Commentable”的实体,该实体表示可以接受评论的任何事物。我们还将创建一个“Comment”实体,该实体将引用多个“Commentable”实体,并且将“Comment”实体的类型保存在“commentable_type”字段中。
创建Commentable实体
首先,我们将创建一个名为“Commentable”的实体,该实体将表示可以接受评论的实体。我们将使用Doctrine的InheritanceType关系设置。
首先,在具有注释“@ORM\Entity”和“@ORM\Table(name=”commentable”)”的类文件中创建一个实体。然后,我们将通过继承我们的父实体来配置InheritanceType关系。最后,我们将定义一个类变量,该变量将对“Commentable”实体的类型进行标识。
// src/Entity/Commentable.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="commentable")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({
* "post" = "Post",
* "picture" = "Picture"
* })
*/
abstract class Commentable
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private id;
/**
* @ORM\Column(type="string", length=255)
*/
privatetitle;
/**
* @ORM\Column(type="text")
*/
private content;
/**
* @ORM\Column(type="datetime")
*/
privatecreatedAt;
/**
* @ORM\Column(type="datetime")
*/
private $updatedAt;
abstract public function getCommentableType(): string;
}
如上所述,我们已经需要在“Commentable”实体中命名一个getCommentableType() 方法,在子类中实现该方法并返回实体的类型。
接下来,我们将创建两个子类:Post和Picture。
创建子类
创建Post和Picture类,并继承自“Commentable”类。这两个类都将分别实现getCommentableType() 方法以返回它们的类型。
// src/Entity/Post.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Post extends Commentable
{
public function getCommentableType(): string
{
return 'post';
}
}
// src/Entity/Picture.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
*/
class Picture extends Commentable
{
public function getCommentableType(): string
{
return 'picture';
}
}
现在我们已经设置好了父实体和子实体,接下来我们将创建评论实体。
创建评论实体
我们将创建一个名为“Comment”的实体类,该实体将引用多个“Commentable”实体并保存它们的类型。我们将使用Doctrine的ManyToOne和Polymorphic associations来定义这种关系。
首先,我们需要在具有注释“@ORM\Entity”和“@ORM\Table(name=”comments”)”的类文件中创建一个实体。
// src/Entity/Comment.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="comments")
*/
class Comment
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private id;
/**
* @ORM\Column(type="text")
*/
privatecomment;
/**
* @ORM\ManyToOne(targetEntity="Commentable", polymorphic=true)
*/
private commentable;
/**
* @ORM\Column(type="string", length=255)
*/
privatecommentableType;
/**
* @ORM\Column(type="datetime")
*/
private createdAt;
/**
* @ORM\Column(type="datetime")
*/
privateupdatedAt;
// Getters and setters omitted for brevity
}
在上述代码中,我们定义了一个名为commentable的ManyToOne关联关系。该关系可以引用到任何实现“Commentable”类的实体。我们还定义了一个名为commentableType的字符串变量,用于保存引用实体的类型。
更新Commentable实体
为了在Doctrine中正确使用Polymorphic associations,我们需要几个更新。我们需要使用Doctrine的joinColumn和DiscriminatorMap注释来更新Commentable实体。
// src/Entity/Commentable.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="commentable")
* @ORM\InheritanceType("JOINED")
* @ORM\DiscriminatorColumn(name="type", type="string")
* @ORM\DiscriminatorMap({
* "post" = "Post",
* "picture" = "Picture"
* })
*/
abstract class Commentable
{
/**
* @ORM\OneToMany(targetEntity="Comment", mappedBy="commentable")
*/
private comments;
public function __construct()
{this->comments = new ArrayCollection();
}
abstract public function getCommentableType(): string;
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection
{
return this->comments;
}
public function addComment(Commentcomment): self
{
if (!this->comments->contains(comment)) {
this->comments[] =comment;
comment->setCommentable(this);
}
return this;
}
public function removeComment(Commentcomment): self
{
if (this->comments->removeElement(comment)) {
// set the owning side to null (unless already changed)
if (comment->getCommentable() ===this) {
comment->setCommentable(null);
}
}
returnthis;
}
// ...
}
我们还将在“Commentable”实体中添加一个注释以反转$comments属性和实体之间的关系。
// src/Entity/Comment.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* @ORM\Entity
* @ORM\Table(name="comments")
*/
class Comment
{
/**
* @ORM\Id
* @ORM\GeneratedValue
* @ORM\Column(type="integer")
*/
private id;
/**
* @ORM\Column(type="text")
*/
privatecomment;
/**
* @ORM\ManyToOne(targetEntity="Commentable", inversedBy="comments")
* @ORM\JoinColumn(name="commentable_id", referencedColumnName="id")
*/
private commentable;
/**
* @ORM\Column(type="string", length=255)
*/
privatecommentableType;
/**
* @ORM\Column(type="datetime")
*/
private createdAt;
/**
* @ORM\Column(type="datetime")
*/
privateupdatedAt;
// Getters and setters omitted for brevity
}
在上述代码中,我们将$comments属性更新为OneToMany关联关系,并且现在一条评论的反向关系是由它的主实体Commentable维护的。我们还通过joinColumn注释添加了一个commentable_id字段来确定commentable的外键。
创建迁移和更新数据模式
完成以上更新后,我们需要使用Doctrine创建一个迁移,并更新我们的数据模式:
- 创建迁移:
php bin/console make:migration
- 运行迁移:
php bin/console doctrine:migrations:migrate
创建评论
现在我们已经成功的创建了多态关联关系。我们可以像下面这样创建评论并将其附加到任何“Commentable”实体上:
$post = new Post();
$post->setTitle('My first blog post');
$post->setContent('Hello, world!');
$post->setCreatedAt(new DateTimeImmutable());
$comment = new Comment();
$comment->setComment('Great post!');
$comment->setCreatedAt(new DateTimeImmutable());
$comment->setCommentable($post);
$comment->setCommentableType($post->getCommentableType());
$entityManager->persist($post);
$entityManager->persist($comment);
$entityManager->flush();
现在我们已经成功的创建了一个可以接受多种实体评论的系统。通过多态关系,我们的数据库模型可以更加灵活和可扩展。
总结
在本教程中,我们介绍了如何使用Doctrine和Symfony 4创建一个多态一对多关系。我们需要使用Doctrine的InheritanceType、joinColumn和DiscriminatorMap注释来设置关系,并使用OneToMany、ManyToOne和Polymorphic associations来实现这种关系。多态关系使得数据库模型更加灵活,可以很容易的管理不同实体之间的关系。我们建议Django开发者们在进行数据建模时,多加尝试使用多态关系,这将让你的数据库设计更加高效和人性化。
极客笔记