如何处理教义协会/关系
编辑本页一个>如何处理教义协会/关系一个>
截屏视频
你更喜欢视频教程吗?请查看<一个href="https://symfonycasts.com/screencast/doctrine-relations" class="reference external" rel="external noopener noreferrer" target="_blank">掌握教义关系一个>视频系列。
有两个主要关系/关联类型:
-
ManyToOne
/对多
-
最常见的关系,在数据库中映射一个外键列(例如a
category_id添加
列关于产品
表)。这实际上只是一个联想类型,但从两者看有所不同国关系的。 -
多
- 使用一个连接表,当关系的双方可以有许多其他的关系(例如。“学生”和“班级”:每个学生在很多班级,每个班级有很多学生)。
首先,您需要确定使用哪种关系。如果关系的双方将包含许多另一方(例如。"students"和"classes"),你需要一个多
关系。否则,你可能需要一个ManyToOne
.
提示
还有一种一对一关系(例如,一个用户有一个配置文件,反之亦然)。在实践中,使用此方法类似于ManyToOne
.
多对一/一对多协会一个>
假设应用程序中的每个产品都只属于一个类别。在这种情况下,你需要一个类别
类,以及一种关联产品
对象的类别
对象。
首先创建一个类别
具有的名字
字段:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
$ php bin/console make:entity新建属性名(按<返回>停止添加字段):> name字段类型(输入?[string]: > string字段长度[255]:> 255该字段可以为空吗在the database (nullable) (yes/no) [no]: > no返回>停止添加字段):>(再次按enter键完成)
这将生成你的新实体类:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/ / src /实体/ Category.php名称空间应用程序\实体;/ /……# (ORM \实体(repositoryClass: CategoryRepository::类))类类别{# (ORM \ Id)# (ORM \ GeneratedValue)# (ORM \列)私人$id;# (ORM \列)私人字符串$的名字;/ /……getter和setter}
映射多对一关系一个>
在本例中,每个类别都可以与许多产品。但是,每个产品只能与一个类别。这种关系可以概括为:许多产品一个类别(或等价地,一个类别许多产品)。
从这个角度来看产品
实体,这是一个多对一关系。从这个角度来看类别
实体,这是一个一对多的关系。
要映射此映射,首先创建一个类别
的属性产品
类的ManyToOne
注释。可以手动完成,也可以使用:实体
命令,它会问你几个关于你们关系的问题。如果你不确定答案,别担心!你可以稍后更改设置:
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
$ php bin/console make:entity要创建或更新的实体的类名称(例如BraveChef): > Product新属性名称(按<返回>停止添加字段):>类别字段类型(输入?[string]: > relation这个实体应该与什么类相关?: >类别关系类型?[多对一,多对多,多对多,一对一]:>多对一是产品。类别属性允许为空(可空)?(yes/no) [yes]: > no是否要向Category添加一个新属性,以便您可以从它访问/更新Product对象?$类别- > getProducts () ?(是/否)[yes]: >是在类别[产品]:>产品内的新字段名称你想自动删除孤立的应用程序\实体\产品对象(orphanRemoval)?(yes/no) [no]: > no新属性名称(按<返回>停止添加字段):>(再次按enter键完成)
这使得两个实体。首先,它增加了一个新的类别
属性产品
实体(和getter和setter方法):
- 注释
- 属性
- YAML
- XML
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
/ / src /实体/ Product.php名称空间应用程序\实体;/ /……类产品{/ /……/ * * *@ORM\ManyToOne(targetEntity="应用\实体\类别",inversedBy="产品")*/私人$类别;公共函数getCategory(): ?类别{返回$这->类别;}公共函数setCategory(?类别$类别):自我{$这->类别=$类别;返回$这;}}
这ManyToOne
映射是必需的。它告诉教义使用category_id添加
列关于产品
表中关联该表中的每个记录与类别
表格
接下来,自一个类别
对象将与许多产品
对象时,:实体
命令也增加了一个产品
属性类别
将保存这些对象的类:
- 注释
- 属性
- YAML
- XML
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/ / src /实体/ Category.php名称空间应用程序\实体;/ /……使用学说\常见的\集合\ArrayCollection;使用学说\常见的\集合\集合;类类别{/ /……/ * * *@ORM\OneToMany(targetEntity="App\Entity\Product", mappedBy="category") */私人$产品;公共函数__construct(){$这->产品=新ArrayCollection ();}/ * * *@return收藏|产品[]* /公共函数getProducts():集合{返回$这->产品;}// addProduct()和removeProduct()也被添加}
的ManyToOne
前面显示的映射为要求,但是,这个对多
是可选的:只添加吗如果您希望能够访问与某个类别相关的产品(这是问题之一:实体
问你)。在这个例子中,它将能打电话是有用的分类- > getProducts ()
.如果你不想要它,那么你也不需要它inversedBy
或的mappedBy
配置。
数据库已经设置好了!现在,像往常一样运行迁移:
1 2
$PHP bin/控制台原则:迁移:diff$PHP bin/控制台原则:迁移:迁移
多亏了这种关系,这就产生了category_id添加
上的外键列产品
表格原则准备坚持我们的关系!
保存相关实体一个>
现在您可以看到这个新代码的实际运行!假设你在一个控制器中:
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;/ /……使用应用程序\实体\类别;使用应用程序\实体\产品;使用学说\持久性\ManagerRegistry;使用ob娱乐下载\组件\HttpFoundation\响应;使用ob娱乐下载\组件\路由\注释\路线;类ProductController扩展AbstractController{#[路由('/product',名称:'product')]公共函数指数(ManagerRegistry$学说):响应{$类别=新类别();$类别->setName (“电脑外围设备”);$产品=新产品();$产品->setName (“键盘”);$产品->setPrice (19.99);$产品->setDescription (“符合人体工学,时尚!”);//将该产品与类别联系起来$产品->setCategory ($类别);$entityManager=$学说->getManager ();$entityManager->persist ($类别);$entityManager->persist ($产品);$entityManager->冲洗();返回新响应('保存id为'的新产品.$产品->getId()。'和新类别id: '.$类别->getId ());}}
当你去/产品
时,将单行添加到两个类别
而且产品
表。的product.category_id
新产品的列设置为id
属于新类别。Doctrine为你管理这种关系的持久性:
如果您是ORM的新手,这是最严重的概念:您需要停止考虑您的数据库,而是只有想想你的对象。而不是将类别的整数id设置为产品
,你定了整个类别
对象.在储蓄的时候,教条会照顾其他的人。
获取相关对象一个>
当您需要获取关联对象时,您的工作流看起来就像以前一样。首先,获取美元的产品
对象,然后访问其相关类别
对象:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;使用应用程序\实体\产品;/ /……类ProductController扩展AbstractController{公共函数显示(ManagerRegistry$学说, int$id):响应{$产品=$学说->getRepository(产品::类)->找到($id);/ /……$categoryName=$产品->getCategory ()->getName ();/ /……}}
在本例中,首先查询a产品
对象基于产品的id
.这将发出一个要获取的查询只有该产品的数据和补水美元的产品
.等会儿,等你打电话的时候产品- > getCategory()——> getName ()
,教义默默地进行第二次查询,以找到类别
这和这个有关产品
.它准备美元的类别
对象并将它返回给你。
重要的是,您可以访问产品的相关类别,但类别数据直到您请求类别才会实际检索到(即它是“惰性加载”)。
因为我们映射了可选的对多
侧,也可向其他方向查询:
12 3 4 5 6 7 8 9 10 11 12 13 14
/ / src /控制器/ ProductController.php/ /……类ProductController扩展AbstractController{公共函数showProducts(ManagerRegistry$学说, int$id):响应{$类别=$学说->getRepository(类别::类)->找到($id);$产品=$类别->getProducts ();/ /……}}
在这种情况下,会发生相同的情况:首先查询单个类别
对象。然后,只有当(以及如果)您访问产品时,Doctrine才会进行第二次查询以检索相关的产品产品
对象。通过添加join可以避免这种额外的查询。
加入相关记录一个>
在上面的例子中,进行了两个查询——一个是原始对象(例如a类别
)和一个用于相关对象(例如产品
对象)。
提示
请记住,您可以通过web调试工具栏查看请求期间进行的所有查询。
如果您事先知道需要访问这两个对象,则可以通过在原始查询中发出连接来避免第二个查询。属性中添加以下方法ProductRepository
类:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / src /仓库/ ProductRepository.php/ /……类ProductRepository扩展ServiceEntityRepository{公共函数findOneByIdJoinedToCategory(int$productId): ?产品{$entityManager=$这->getEntityManager ();$查询=$entityManager->createQuery ('SELECT p, c FROM App\Entity\Product p INNER JOIN p.category c WHERE p.id =:id')->setParameter (“id”,$productId);返回$查询->getOneOrNullResult ();}}
这将仍然返回一个数组产品
对象。但是现在,当你打电话的时候产品- > getCategory ()
使用这些数据,不会进行第二次查询。
现在,你可以在控制器中使用这个方法来查询产品
对象及其相关对象类别
在一个查询中:
12 3 4 5 6 7 8 9 10 11 12 13 14
/ / src /控制器/ ProductController.php/ /……类ProductController扩展AbstractController{公共函数显示(ManagerRegistry$学说, int$id):响应{$产品=$学说->getRepository(产品::类)->findOneByIdJoinedToCategory ($id);$类别=$产品->getCategory ();/ /……}}
反向设置信息一个>
到目前为止,您已经通过呼叫更新了关系产品- > setCategory(类别)
.这不是偶然!每个关系都有两个方面:在这个例子中,Product.category
是拥有一边,Category.products
是逆的一面。
若要更新数据库中的关系,请必须上设置关系拥有的一面。拥有的一方永远是最重要的ManyToOne
的映射设置为多
关系,你可以选择哪一方是拥有方)。
这是不是意味着不能打电话了分类- > addProduct ()
或分类- > removeProduct ()
更新数据库?事实上,它是有可能,多亏了一些聪明的代码:实体
命令生成:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/ / src /实体/ Category.php/ /……类类别{/ /……公共函数addProduct(产品$产品):自我{如果(!$这->产品->包含($产品)) {$这->产品[]=$产品;$产品->setCategory ($这);}返回$这;}}
的关键是产品- > setCategory ($)
,它将设置拥有的一面。多亏了这个,当你拯救了这段关系将在数据库中更新。
是什么删除一个产品
从一个类别
?的:实体
命令还生成了removeProduct ()
方法:
12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21
/ / src /实体/ Category.php名称空间应用程序\实体;/ /……类类别{/ /……公共函数removeProduct(产品$产品):自我{如果($这->产品->包含($产品)) {$这->产品->removeElement ($产品);//设置所属端为空(除非已经更改)如果($产品->getCategory () = = =$这) {$产品->setCategory (零);}}返回$这;}}
多亏了这个,如果你打电话产品分类- > removeProduct ($)
,category_id添加
在那产品
将被设置为零
在数据库中。
但是,不是设置category_id添加
null,如果你想要产品
是删除如果它变成“孤儿”(即没有类别
) ?要选择该行为,请使用<一个href="https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/working-with-associations.html" class="reference external" rel="external noopener noreferrer" target="_blank">orphanRemoval一个>选项里面类别
:
- 注释
- 属性
1 2 3 4 5 6 7 8
/ / src /实体/ Category.php/ /……/ * * *@ORM\OneToMany(targetEntity="App\Entity\Product", mappedBy="category", orphanRemoval=true) */私人$产品;
多亏了这个,如果产品
从类别
,它将从数据库中完全删除。
更多有关关联的资料一个>
本节介绍了一种常见的实体关系类型,即一对多关系。有关如何使用其他类型关系(例如,一对一,多对多)的更高级细节和示例,请参阅Doctrine<一个href="https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/association-mapping.html" class="reference external" rel="external noopener noreferrer" target="_blank">关联映射文档欧宝官网下载app一个>.
请注意
如果使用注释,则需要在所有注释前加上@ORM \
(如。@ORM \ OneToMany
),并没有反映在Doctrine的文档中。欧宝官网下载app