如何使用主义协会/关系 编辑该页面一个> 如何使用主义协会/关系一个>< /h1> 截屏视频 你喜欢视频教程?检查<一个href="https://symfonycasts.com/screencast/doctrine-relations" class="reference external" rel="external noopener noreferrer" target="_blank">掌握原则的关系一个>视频系列。 有两个主要的关系/关联类型: ManyToOne/对多 最常见的关系,映射在数据库外键列(例如category_id添加列在产品表)。这是只一个从两种不同的关联类型,但看到国的关系。 多 使用一个连接表时,需要双方的关系可以有许多的另一边。“学生”和“类”:每个学生在许多类,每个类都有许多学生)。 首先,你需要确定使用哪个关系。如果双方的关系将包含许多(如另一边。“学生”和“类”),你需要一个多关系。否则,你可能需要一个ManyToOne。 提示 还有一个OneToOne关系(例如,一个用户有一个概要文件,反之亦然)。在实践中,使用这个类似ManyToOne。 ManyToOne / OneToMany协会一个>< /h2> 假设在应用程序中每个产品属于一个类别。在这种情况下,您将需要一个类别类,和联系方式产品对象一个类别对象。 首先创建一个类别实体与一个的名字字段: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 美元php bin /控制台:实体类别的新属性名(按<返回>停止添加字段):>名称字段类型(输入?查看所有类型)[string]:[255] >字符串字段长度:> 255这个字段可以为空在数据库(可以为空)(yes / no)[不]:>没有新的属性名(按<返回>停止添加字段):>(按回车键完成) 这将生成新的实体类: 1 2 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映射关系一个>< /h2> 在这个例子中,每个类别可以联系在一起许多产品。但是,每个产品只能联系在一起一个类别。这种关系可以概括为:许多产品一个类别(或等价,一个类别许多产品)。 从的角度产品实体,这是一个多对一的关系。从的角度类别实体,这是一对多的关系。 地图,首先创建一个类别财产的产品类的ManyToOne注释。你可以用手或用:实体命令,它会问你几个问题关于你们的关系。如果你不确定的答案,别担心!你可以更改设置后: 1 2 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 32 33 34 美元php bin /控制台:实体类名的实体来创建或更新(例如BraveChef): >产品新属性名称(按<返回>停止添加字段):>类别字段类型(输入?查看所有类型)[string]: >关系这个实体应该与哪个班?:>类别关系类型吗?[ManyToOne、对多,OneToOne]: > ManyToOne产品。分类属性允许null(可以为空)?(yes / no)[是]:>任何你想添加一个新属性类别,这样您就可以访问/更新产品对象——如。美元类别- > getProducts () ?(yes / no)[是]:是的新字段名在类别(产品):>产品你想自动删除孤立应用\ \产品实体对象(orphanRemoval) ?(yes / no)[不]:>没有新的属性名(按<返回>停止添加字段):>(按回车键完成) 这使得更改两个实体。首先,它增加了一个新的类别财产产品实体(和getter和setter方法): 属性 YAML XML 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21日22日23日 / / src /实体/ Product.php名称空间应用程序\实体;/ /……类产品{/ /……# [ORM \ ManyToOne (targetEntity:类别::类,inversedBy:“产品”))私人类别美元类别;公共函数getCategory():哦?类别{返回美元这- >类别;}公共函数setCategory(?类别美元类别):自我{美元这- >类别=美元类别;返回美元这;}} 1 2 3 4 5 6 7 8 9 10 # src /资源/ config /理论/ Product.orm.yml应用实体\ \产品:类型:实体#……manyToOne:类别:targetEntity:应用\ \实体类别inversedBy:产品joinColumn:可以为空:假 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 < !——src /资源/ config /理论/ Product.orm。xml - - >< ?xml version = " 1.0 " encoding = " utf - 8 " ? ><doctrine-mappingxmlns=“http://doctrine-project.org/schemas/orm/doctrine-mapping”xmlns: xsi=“http://www.w3.org/2001/XMLSchema-instance”xsi: schemaLocation=“http://doctrine-project.org/schemas/orm/doctrine-mapping https://doctrine-project.org/schemas/orm/doctrine-mapping.xsd”><实体的名字=“应用程序实体\ \产品”>< !——……- - ><多对一场=“类别”目标实体=“应用程序\ \实体类别”用=“产品”><连接列可以为空=“假”/ >< /多对一>< /实体>< /doctrine-mapping> 这ManyToOne映射是必需的。它告诉原则使用category_id添加列在产品表与该表中的每条记录的记录类别表。 接下来,自一个类别对象将与许多产品对象时,:实体命令也增加了一个产品财产类别类,将这些对象: 属性 YAML XML 1 2 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 / / src /实体/ Category.php名称空间应用程序\实体;/ /……使用学说\常见的\集合\ArrayCollection;使用学说\常见的\集合\集合;类类别{/ /……# [ORM \ OneToMany (targetEntity:产品::类的mappedBy:“类别”))私人数组美元产品;公共函数__construct(){美元这- >产品=新ArrayCollection ();}/ * * *@return收集< int、产品> * /公共函数getProducts():集合{返回美元这- >产品;}/ / addProduct()和removeProduct()也补充道} 1 2 3 4 5 6 7 8 9 10 # src /资源/ config /理论/ Category.orm.yml应用\ \实体类别:类型:实体#……对:产品:targetEntity:应用\ \实体产品的mappedBy:类别#别忘了初始化集合# __construct()方法的实体 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 < !——src /资源/ config /理论/ Category.orm。xml - - >< ?xml version = " 1.0 " encoding = " utf - 8 " ? ><doctrine-mappingxmlns=“http://doctrine-project.org/schemas/orm/doctrine-mapping”xmlns: xsi=“http://www.w3.org/2001/XMLSchema-instance”xsi: schemaLocation=“http://doctrine-project.org/schemas/orm/doctrine-mapping https://doctrine-project.org/schemas/orm/doctrine-mapping.xsd”><实体的名字=“应用程序\ \实体类别”>< !——……- - ><一对多场=“产品”目标实体=“应用程序实体\ \产品”所绘制=“类别”/ >< !——别忘了init __construct()方法的实体集合- - >< /实体>< /doctrine-mapping> 的ManyToOne之前显示的映射要求但是,这对多是可选的:只有添加吗如果您希望能够访问相关的产品类别(这是一个问题:实体问你)。在这个例子中,它将是有用的能够调用$分类- > getProducts ()。如果你不想要,那么你也不需要inversedBy或的mappedBy配置。 ArrayCollection是什么东西? 里面的代码__construct ()重要的是:美元的产品属性必须集合对象实现原则集合接口。在这种情况下,一个<一个href="https://www.doctrine-project.org/projects/doctrine-collections/en/1.6/index.html" class="reference external" rel="external noopener noreferrer" target="_blank">ArrayCollection一个>使用对象。这看起来几乎和行为完全像一个数组,但有一些增加了灵活性。想象一下,这是一个数组你会处于良好状态。 数据库设置!现在,运行迁移像正常: 1 2 美元php bin /控制台学说:迁移:diff美元php bin /控制台学说:迁移:迁移 多亏了关系,这将创建一个category_id添加外键列上产品表。原则是可以坚持我们的关系了! 保存相关的实体一个>< /h2> 现在你可以看到这个新代码在行动!想象一下你在一个控制器: 1 2 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 32 33 34 35 36 / / src /控制器/ ProductController.php名称空间应用程序\控制器;/ /……使用应用程序\实体\类别;使用应用程序\实体\产品;使用学说\ORM\EntityManagerInterface;使用ob娱乐下载\组件\HttpFoundation\响应;使用ob娱乐下载\组件\路由\注释\路线;类ProductController扩展AbstractController{#(路线(“/产品”,名字:“产品”))公共函数指数(EntityManagerInterface美元entityManager):响应{美元类别=新类别();美元类别- >setName (“电脑外围设备”);美元产品=新产品();美元产品- >setName (“键盘”);美元产品- >setPrice (19.99);美元产品- >setDescription (的人体工学和时尚!);/ /该产品涉及的范畴美元产品- >setCategory (美元类别);美元entityManager- >persist (美元类别);美元entityManager- >persist (美元产品);美元entityManager- >冲洗();返回新响应(“保存新产品id:”。美元产品- >getId ()。“和新类别id:”。美元类别- >getId ());}} 当你去/产品,添加一行类别和产品表。的product.category_id列新产品将不管id是新类别的。原则管理的持久性关系给你: 如果你刚开始一个ORM,这是最严重的概念:你需要停止思考你的数据库,而不是只有思考你的对象。而不是设置类别的整数id上产品,你设置整个类别对象。教义负责其余的储蓄。 更新从逆的关系 你也可以叫$分类- > addProduct ()改变的关系?是的,但只是因为:实体命令帮助我们。更多细节,请参阅:<一个href="#associations-inverse-side" class="reference internal">associations-inverse-side一个>。 获取相关对象一个>< /h2> 当你需要获取相关的对象,你的工作流之前的样子。首先,拿一个美元的产品对象,然后访问相关类别对象: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 / / src /控制器/ ProductController.php名称空间应用程序\控制器;使用应用程序\实体\产品;/ /……类ProductController扩展AbstractController{公共函数显示(ProductRepository美元productRepository,int美元id):响应{美元产品=美元productRepository- >找到(美元id);/ /……美元categoryName=美元产品- >getCategory ()- >getName ();/ /……}} 在这个例子中,首先查询产品基于产品的对象id。这个问题一个查询获取只有产品数据和水合物美元的产品。之后,当你调用产品- > getCategory () - > getName (),默默地教义使得第二个查询来找到类别这是与此相关产品。它准备美元的类别对象,并返回给你。 重要的是你获得相关产品的类别,但类别数据并不是直到你要求类别检索(即“延迟加载”)。 因为我们绘制了可选的对多方面,你也可以查询另一个方向: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 / / src /控制器/ ProductController.php/ /……类ProductController扩展AbstractController{公共函数showProducts(CategoryRepository美元categoryRepository,int美元id):响应{美元类别=美元categoryRepository- >找到(美元id);美元产品=美元类别- >getProducts ();/ /……}} 在这种情况下,同样的事情发生:首先查询单类别对象。然后,只有当如果你访问产品,教义使得第二个查询来检索相关产品对象。这种额外的查询可以避免通过添加连接。 关系和代理类 这个“延迟加载”是可能的,因为,在必要的时候,教义返回一个“代理”对象的真正对象。再看看上面的例子: 1 2 3 4 5 6 7 美元产品=美元productRepository- >找到(美元id);美元类别=美元产品- >getCategory ();/ /打印“代理\ AppEntityCategoryProxy”转储(get_class (美元类别));死(); 这个代理对象扩展了真的类别对象,外表和行为完全一样。不同的是,通过使用一个代理对象,教义可以为真正的延迟查询类别直到你真正需要的数据(例如,直到你的电话$分类- > getName ())。 生成的代理类学说并存储在缓存目录。你可能没注意到你美元的类别对象实际上是一个代理对象。 在下一节中,当你检索的产品和类别数据(通过加入),原则将返回真正的类别对象,因为不需要延迟加载。 加入相关记录一个>< /h2> 在上面的例子中,两个查询了—一个用于原来的对象(例如一个类别),一个用于相关对象(s)(如产品对象)。 提示 记住,你可以看到所有的查询在一个请求通过网络调试工具栏。 如果你预先知道你需要访问两个对象,你可以避免第二个查询发出加入原始查询。添加以下方法ProductRepository类: 1 2 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 (“选择p c App \实体\产品p内连接。类别c p。id =: id ')- >setParameter (“id”,美元productId);返回美元查询- >getOneOrNullResult ();}} 这将仍然返回一个数组产品对象。但是现在,当你调用产品- > getCategory ()和使用这些数据,没有第二个查询。 现在,您可以使用这种方法在你的控制器查询产品对象及其相关类别在一个查询: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 / / src /控制器/ ProductController.php/ /……类ProductController扩展AbstractController{公共函数显示(ProductRepository美元productRepository,int美元id):响应{美元产品=美元productRepository- >findOneByIdJoinedToCategory (美元id);美元类别=美元产品- >getCategory ();/ /……}} 从反方面设置信息一个>< /h2> 到目前为止,您已经更新通过调用的关系(产品- > setCategory美元类别)。这绝非偶然!每个关系都有两个方面:在这个例子中,Product.category是拥有一边,Category.products是逆的一面。 在数据库中,你们更新关系必须设置上的关系拥有的一面。拥有一方总是在那里ManyToOne设置(对于一个映射多关系,你可以选择哪一方是拥有的一面)。 这意味着它是不可能打电话给吗$分类- > addProduct ()或$分类- > removeProduct ()更新数据库?事实上,它是可能的,由于一些聪明的代码:实体命令生成: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 / / src /实体/ Category.php/ /……类类别{/ /……公共函数addProduct(产品美元产品):自我{如果(!美元这- >产品- >包含(美元产品)){美元这- >产品[]=美元产品;美元产品- >setCategory (美元这);}返回美元这;}} 的关键是产品- > setCategory美元(美元),拥有的一面。谢谢,,当你保存,关系将更新到数据库中。 是什么删除一个产品从一个类别吗?的:实体命令还生成一个removeProduct ()方法: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 / / src /实体/ Category.php名称空间应用程序\实体;/ /……类类别{/ /……公共函数removeProduct(产品美元产品):自我{如果(美元这- >产品- >包含(美元产品)){美元这- >产品- >removeElement (美元产品);/ /设置拥有一面空(除非已经改变了)如果(美元产品- >getCategory () = = =美元这){美元产品- >setCategory (零);}}返回美元这;}} 谢谢,如果你的电话产品分类- > removeProduct美元(美元),category_id添加在那产品将被设置为零在数据库中。 但是,而不是设置category_id添加空,如果你想要什么产品是删除如果它变成了“孤儿”(即没有类别)?选择行为,使用<一个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 / / src /实体/ Category.php/ /……# [ORM \ OneToMany (targetEntity:产品::类的mappedBy:“类别”,orphanRemoval: true))私人数组美元产品; 由于这一点,如果产品被删除的类别,它将完全从数据库中删除。 更多信息协会一个>< /h2> 本节介绍了一个常见的实体关系,一对多的关系。对于更高级的细节和例子如何使用其他类型的关系(例如一对一、多对多),看到教义的<一个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)),这不是反映在教义的文档。欧宝官网下载app 这项工作,包括代码示例,许可下<一个rel="license" href="https://creativecommons.org/licenses/by-sa/3.0/">Creative Commons冲锋队3.0一个>许可证。