数据库和原则

编辑该页面

警告:你浏览的文档欧宝体育电话欧宝娱乐app下载地址Symfony 2.6,不再维护。

这个页面的更新版本Symf欧宝娱乐app下载地址ony 6.2(当前的稳定版本)。

< /div>

数据库和原则

最常见的和具有挑战性的任务对于任何应用程序包括持久化和阅读信息和从数据库。虽然Symfony完整不集欧宝娱乐app下载地址成任何ORM框架默认情况下,Symfony的标准版,这是使用最广泛的分布,结合学说、一个图书馆,它的唯一目的是给你强大的工具非常容易。在本章中,您将了解背后的基本哲学学说,看看容易使用数据库。

请注意

原则是完全脱离Symfony和使用它是可选的。欧宝娱乐app下载地址这一章都是关于教义ORM,旨在让您将对象映射到关系数据库(如MySQL,PostgreSQLMicrosoft SQL)。如果你喜欢用原始的数据库查询,这是很容易的,并解释了在“如何使用原则DBAL吗“食谱条目。

你也可以保存数据MongoDB使用原则ODM图书馆。有关更多信息,阅读“DoctrineMongoDBBundle _”文档。欧宝体育电话

< /div>

一个简单的例子:一个产品

理解教义是如何工作的最简单的方法就是看它的实际应用。在本节中,您将配置数据库,创建一个产品对象持久化到数据库并获取它。

配置数据库

在你真正开始之前,您需要配置您的数据库连接信息。按照惯例,这些信息通常是在一个配置应用程序/配置/ parameters.yml文件:

1 2 3 4 5 6 7 8 9
# app / config / parameters.yml参数:database_driver:pdo_mysqldatabase_host:本地主机database_name:test_projectdatabase_user:database_password:密码#……

请注意

通过定义配置parameters.yml只是一个惯例。该文件中定义的参数引用的主要配置文件设置原则时:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6 7 8
# app / config / config.yml原则:dbal:司机:“% database_driver %”主持人:“% database_host %”dbname:“% database_name %”用户:“% database_user %”密码:“% database_password %”

通过将数据库信息到一个单独的文件中,您可以很容易地保持在每个服务器上文件的不同版本。您还可以方便地存储数据库配置(或任何敏感信息)以外的项目,比如在Apache配置。有关更多信息,请参见服务容器外部参数如何设置

< /div>

现在主义知道您的数据库,您可以创建数据库:

1
$ php应用程序/控制台学说:数据库:创建

即使是经验丰富的开发人员常犯的一个错误当开始一个Symfony项目是忘记设置默认数据库字符集和校对,结束了拉丁类型针欧宝娱乐app下载地址对性,它是大多数数据库的默认。他们甚至会记得第一次,但是忘记都是运行在开发过程中比较常见的命令:

1 2
美元php应用程序/控制台学说:数据库:下降,迫使美元php应用程序/控制台学说:数据库:创建

没有办法配置这些原则内违约,因为它试图尽可能不可知论者的环境配置。解决这个问题的一个方法是配置服务器级的违约。

设置UTF8违约MySQL一样简单的几行添加到您的配置文件(通常是my.cnf):

1 2 3 4
(mysqld)5.5.3 #版本引入了“utf8mb4”,这是推荐的collation-server= utf8mb4_general_ci#替换utf8_general_cicharacter-set-server= utf8mb4#替换utf8

我们建议对MySQLuse utf8字符集,因为它不支持4字节unicode字符,和字符串包含将被截断。这是固定的新utf8mb4字符集

< /div>

请注意

如果你想使用SQLite作为您的数据库,您需要设置应该存储在数据库文件的路径:

  • YAML
  • XML
  • PHP
1 2 3 4 5 6
# app / config / config.yml原则:dbal:司机:pdo_sqlite路径:“% kernel.root_dir % / sqlite.db”字符集:use UTF8

创建一个实体类

假设您要构建一个应用程序,需要显示的产品。没想学说或数据库中,你已经知道你需要一个产品对象来表示这些产品。创建这个类里面实体你的AppBundle目录:

1 2 3 4 5 6 7 8 9
/ / src / AppBundle /实体/ Product.php名称空间AppBundle\实体;产品{受保护的美元的名字;受保护的美元价格;受保护的美元描述;}

类——通常被称为一个“实体”的意思一个基本类,保存数据——很简单,帮助满足需要的产品在您的应用程序的业务需求。这个类不能被持久化到数据库——这只是一个简单的PHP类。

提示

一旦你学习原则背后的概念,你可以有教义为您创建简单的实体类。这将问你互动问题帮助你建立任何实体:

1
$ php应用程序/控制台学说:生成:实体

添加映射信息

教义允许您工作与数据库更加有趣的方式不仅仅是获取基于列的表的行到一个数组中。相反,教义允许您保存整个对象到数据库,从数据库中获取整个对象。这是一个PHP类映射到数据库表,和PHP类的属性列在表:

的学说能够做到这一点,你只需要创建“元数据”,或告诉学说如何配置产品类及其属性映射到数据库。此元数据可以指定在许多不同的格式包括YAML、XML或直接在产品类通过注释:

  • 注释
  • 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 30 31 32 33
/ / src / AppBundle /实体/ Product.php名称空间AppBundle\实体;使用学说\ORM\映射作为ORM;/ * * *@ORM* \实体@ORM\表(name = "产品")* /产品{/ * * *@ORM\列(type =“整数”)*@ORM\ Id *@ORM\ GeneratedValue(策略=“汽车”)* /受保护的美元id;/ * * *@ORM\列(类型=“字符串”,长度= 100)* /受保护的美元的名字;/ * * *@ORM\列(type =“小数”,规模= 2)* /受保护的美元价格;/ * * *@ORM\列(type = " text ") * /受保护的美元描述;}

请注意

一捆只能接受一个元数据定义格式。例如,它是不可能的混合YAML的元数据定义与注释PHP实体类定义。

< /div>

提示

表名是可选的,如果省略,将自动确定基于实体类的名称。

< /div>

教义允许您选择各种不同的字段类型,每个都有自己的选择。有关可用的字段类型的信息,请参阅数据库和原则部分。

另请参阅

你也可以看看学说基本的映射文档欧宝体育电话对所有细节映射信息。如果你使用注释,需要预先考虑所有注释ORM \(如。ORM \列(…)所示),这不是教条的文档。欧宝体育电话你还需要包括使用原则\ ORM \ ORM映射;声明,进口ORM注释前缀。

< /div>

谨慎

不小心,你的类名和属性映射到SQL关键字(如保护集团用户)。例如,如果您的实体类的名字集团那么,在默认情况下,你的表名集团,这将导致一个SQL错误在某些引擎。看到学说的保留SQL关键字的文档欧宝体育电话如何正确地逃避这些名字。另外,如果你自由选择数据库模式,简单地映射到不同的表名或列名。看到学说的持久化类属性映射欧宝体育电话文档。

< /div>

请注意

当使用另一个库或程序(例如Doxygen),使用注释,你应该把@IgnoreAnnotation注释的类来表示注释Symfony应该忽略。欧宝娱乐app下载地址

例如,为了防止@fn注释从抛出异常,添加以下:

1 2 3 4 5
/ * * *@IgnoreAnnotation(fn) * /产品/ /……

生成getter和setter方法

尽管学说现在知道如何坚持产品对象到数据库,类本身还没有真正有用的。自产品只是一个常规的PHP类,您需要创建getter和setter方法(如。getName (),setName ())来访问其属性(自属性受保护的)。幸运的是,教义为您可以通过运行:

1
$ php应用程序/控制台学说:生成:实体AppBundle /实体产品

这个命令确保所有的生成getter和setter方法产品类。这是一个安全的命令,您可以运行它一遍又一遍:它只生成getter和setter方法,不存在(即它不会取代你现有的方法)。

谨慎

记住,教义的实体发生器产生简单的getter / setter。您应该检查生成的实体和getter / setter逻辑来自己的需要进行调整。

< /div>

原则:生成实体命令,您可以:

  • 生成getter和setter方法;
  • 配置了生成库类@ORM \实体(repositoryClass = "…")注释;
  • 生成适当的构造函数为1:n和n: m关系。

原则:生成实体命令保存一个备份原来的Product.php命名Product.php ~。在某些情况下,这个文件的存在会导致一个“不能重新定义类”错误。它可以安全地删除。您还可以使用——没有备份的防止生成这些备份文件的选项。

注意,你不需要使用此命令。教义不依赖于代码生成。像正常的PHP类,你只需要确保你的保护/私有属性的getter和setter方法。因为这是一种常见的事情使用原则时,这个命令创建。

< /div>

你也可以生成所有已知的实体(即任何教条的PHP类映射信息)的包或整个命名空间:

1 2 3 4 5
# AppBundle生成所有实体php应用程序/控制台学说:美元AppBundle生成:实体#生成包的所有实体Acme名称空间$ php应用程序/控制台学说:生成:Acme的实体

请注意

教义并不关心你的属性受保护的私人,或者你是否有一个属性的getter或setter函数。这里的生成getter和setter只是因为你需要他们与PHP对象进行交互。

< /div>

创建数据库表/模式

你现在有一个可用的产品类映射信息,以便教义确切地知道如何坚持。当然,你还没有相应的产品表在数据库中。幸运的是,教义可以自动创建所需的所有数据库表为每一个已知的实体在您的应用程序。要做到这一点,运行:

1
php应用程序/控制台学说:美元模式:更新——力量

提示

实际上,这个命令是非常强大的。比较你的数据库应该看起来像(基于实体的映射信息)与它实际上看起来,并生成所需的SQL语句更新数据库在它应该在的地方。换句话说,如果你添加一个新的属性映射元数据产品再次运行这个任务,它将生成“alter table语句需要添加新列的现有产品表。

一个更好的方法是通过利用这个功能迁移,它允许您生成这些SQL语句和存储他们移民类,可以生产服务器上运行系统为了安全可靠地跟踪和迁移数据库模式。

< /div>

数据库现在有一个全功能产品表和列匹配你指定的元数据。

< /div>

持久化对象到数据库

现在,您已经有了一个映射产品实体和相应的产品表,你准备保存数据到数据库中。在一个控制器,这是非常容易的。添加以下方法DefaultController包:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/ / src / AppBundle /控制器/ DefaultController.php/ /……使用AppBundle\实体\产品;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;/ /……公共函数createAction(){美元产品=产品();美元产品- >setName (“Foo酒吧”);美元产品- >setPrice (“19.99”);美元产品- >setDescription (“Lorem ipsum悲哀”);美元新兴市场=美元- >getDoctrine ()- >getManager ();美元新兴市场- >persist (美元产品);美元新兴市场- >冲洗();返回响应(“创建产品id”美元产品- >getId ());}

请注意

如果你按照这个例子中,您需要创建一个指向这个动作路线工作。

< /div>

提示

本文展示了处理原则从控制器使用getDoctrine ()控制器的方法。这是一个快捷的方法学说服务。你可以在任何地方使用原则通过注入服务的其他服务。看到服务容器在创建自己的服务。

< /div>

更详细地看一看前面的示例:

  • 第10 - 13行在本节中,您实例化和工作美元的产品像任何其他对象,正常的PHP对象。
  • 第15行这条线获取学说实体管理器对象,它负责处理的过程中坚持和抓取对象数据库。
  • 线16persist ()方法告诉学说“管理”美元的产品对象。这并不会导致查询到数据库(还)。
  • 第17行冲洗()方法被调用,原则通过的所有对象的管理,看看他们是否需要被持久化到数据库中。在这个例子中,美元的产品对象尚未保存,所以实体管理器执行一个插入查询和创建一个行产品表。

请注意

事实上,由于教义意识到你所有的管理实体,当你调用冲洗()方法,它计算一个整体变更集和执行查询以正确的顺序。它利用缓存准备语句稍微提高性能。例如,如果您坚持共有100产品然后随后调用对象冲洗(),100年学说将执行插入使用一个事先准备好的声明中对象的查询。

< /div>

在创建或更新对象,工作流总是相同的。在下一节中,您将看到如何学说是一个足够聪明来自动问题更新数据库中查询如果记录已经存在。

提示

学说提供了一个库,允许您以编程方式测试数据加载到您的项目(即。“固定数据”)。信息,请参阅“DoctrineFixturesBundle _”文档。欧宝体育电话

< /div>

从数据库中获取对象

获取一个对象的数据库更容易。例如,假设您已经配置了一个显示一个特定的路线产品基于其id值:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
公共函数showAction(美元id){美元产品=美元- >getDoctrine ()- >getRepository (“AppBundle:产品”)- >找到(美元id);如果(!美元产品){美元- >createNotFoundException (“没有发现产品id”美元id);}/ /……做点什么,比如通过美元产品对象到一个模板}

提示

可以达到相当于这个没有编写任何代码通过使用@ParamConverter快捷方式。看到FrameworkExtraBundle文欧宝体育电话档为更多的细节。

< /div>

查询一个特定类型的对象时,你总是用所谓的“仓库”。你可以认为一个存储库是一个PHP类唯一的工作就是帮助你获取某个类的实体。您可以访问存储库对象为一个实体类通过:

1 2
美元存储库=美元- >getDoctrine ()- >getRepository (“AppBundle:产品”);

请注意

AppBundle:产品字符串是一个快捷方式可以使用任何学说,而不是实体的完整类名(即。AppBundle \ \实体产品)。只要你的实体下的生活实体你的包名称空间,这将工作。

< /div>

一旦您的存储库,你可以访问各种各样的有用的方法:

1 2 3 4 5 6 7 8 9 10 11 12
/ /主键查询的(通常是“id”)美元产品=美元存储库- >找到(美元id);/ /动态方法名称找到基于列值美元产品=美元存储库- >findOneById (美元id);美元产品=美元存储库- >findOneByName (“foo”);/ /找到* *所有产品美元产品=美元存储库- >findAll ();/ /找到一群产品基于任意列值美元产品=美元存储库- >findByPrice (19.99);

请注意

当然,你也可以问题复杂的查询,你会了解更多的数据库和原则部分。

< /div>

你也可以利用有用的findByfindOneBy方法方便地获取对象根据多个条件:

1 2 3 4 5 6 7 8 9 10
/ /查询一个产品匹配的名字和价格美元产品=美元存储库- >findOneBy (数组(“名字”= >“foo”,“价格”= >19.99));/ /查询所有产品匹配的名称、订购价格美元产品=美元存储库- >findBy (数组(“名字”= >“foo”),数组(“价格”= >“ASC”));

提示

当你渲染任何页面中,您可以看到有多少查询在网络调试工具栏的右下角。

如果你单击图标,分析器会打开,显示你的查询。

图标会变黄,如果有50多个查询页面。这可能表明,东西是不正确的。

< /div>

更新一个对象

一旦你获取一个对象从教义,更新很容易。假设您有一个路线,产品id映射到一个更新控制器中的动作:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
公共函数updateAction(美元id){美元新兴市场=美元- >getDoctrine ()- >getManager ();美元产品=美元新兴市场- >getRepository (“AppBundle:产品”)- >找到(美元id);如果(!美元产品){美元- >createNotFoundException (“没有发现产品id”美元id);}美元产品- >setName (“新产品的名字!”);美元新兴市场- >冲洗();返回美元- >redirectToRoute (“主页”);}

更新对象包括三个步骤:

  1. 获取对象从教义;
  2. 修改对象;
  3. 调用冲洗()在实体管理器

注意,调用(em - >坚持美元产品)是没有必要的。回想一下,这个方法简单告诉原则管理或“观察”美元的产品对象。在这种情况下,因为你把美元的产品对象从教义,它已经成功。

< /div>

删除一个对象

删除一个对象非常相似,但需要调用remove ()实体管理器的方法:

1 2
美元新兴市场- >remove (美元产品);美元新兴市场- >冲洗();

如您所料,remove ()方法通知学说,你想从数据库中删除给定对象。实际的删除查询,但是,并不是实际执行,直到冲洗()方法被调用。

查询对象

您已经看到如何存储库对象允许您运行基本查询没有任何工作:

1 2 3
美元存储库- >找到(美元id);美元存储库- >findOneByName (“Foo”);

当然,教义也允许您编写更复杂的查询使用查询语言(DQL)原则。DQL类似于SQL除外,你应该想象你查询一个或多个对象的一个实体类(如。产品)而不是桌子上(如查询行。产品)。

教义查询时,你有两种选择:写作纯粹主义查询或使用原则的查询生成器。

查询与DQL对象

想象一下你想要查询产品,但只返回产品,超过成本19.99命令从便宜的到最昂贵的。您可以使用原则的原生sql语言称为DQL进行查询:

1 2 3 4 5 6 7 8 9 10 11
美元新兴市场=美元- >getDoctrine ()- >getManager ();美元查询=美元新兴市场- >createQuery (“从AppBundle选择p:产品p, p。价格>:价格按p。ASC的价格)- >setParameter (“价格”,“19.99”);美元产品=美元查询- >getResult ();/ /得到只有一个结果:/ /产品=美元查询- > setMaxResults (1) - > getOneOrNullResult ();

如果您熟悉SQL,那么DQL应该感到很自然。最大的区别是,你需要考虑“对象”而不是数据库中的行。出于这个原因,你选择AppBundle:产品对象(一个可选的快捷方式AppBundle \ \实体产品它),然后别名p

提示

注意的setParameter ()方法。在处理原则时,它总是一个好主意任何外部值设置为“占位符”(:价格在上面的示例中),因为它可以防止SQL注入攻击。

< /div>

getResult ()方法返回一个数组的结果。只有一个结果,您可以使用getOneOrNullResult ():

1
美元产品=美元查询- >setMaxResults (1)- >getOneOrNullResult ();

DQL语法是非常强大的,允许您轻松地加入实体之间的话题关系稍后将介绍)、组等。有关更多信息,看到官方的吗教义的查询语言欧宝体育电话文档。

< /div>

查询对象使用原则的查询生成器

而不是写一个DQL字符串,或者可以使用称为有用的对象QueryBuilder为你构建字符串:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
美元存储库=美元- >getDoctrine ()- >getRepository (“AppBundle:产品”);/ /从AppBundle createQueryBuilder自动选择:产品/ /,别名“p”美元查询=美元存储库- >createQueryBuilder (“p”)- >(在哪里“p。price > :price'< /span>)- >setParameter (“价格”,“19.99”)- >orderBy (“p.price”,“ASC”)- >getQuery ();美元产品=美元查询- >getResult ();/ /得到只有一个结果:/ /产品=美元查询- > setMaxResults (1) - > getOneOrNullResult ();

QueryBuilder对象包含每个方法需要建立你的查询。通过调用getQuery ()方法,查询构建器返回正常查询对象,该对象可以用来获取查询的结果。

教义的查询构建器的更多信息,参考原则查询构建器欧宝体育电话文档。

自定义库类

在前面的章节中,您开始从内部构造和使用更复杂的查询控制器。为了隔离,测试和重用这些查询,它是一个很好的实践为实体创建一个自定义存储库类添加方法和查询逻辑。

要做到这一点,将存储库类的名称添加到你的映射定义:

  • 注释
  • YAML
  • XML
1 2 3 4 5 6 7 8 9 10 11 12
/ / src / AppBundle /实体/ Product.php名称空间AppBundle\实体;使用学说\ORM\映射作为ORM;/ * * *@ORM\ \实体(repositoryClass = " AppBundle \实体ProductRepository”) * /产品{/ /……}

原则可以为您生成存储库类通过运行相同的命令使用生成早些时候失踪的getter和setter方法:

1
php应用程序/控制台学说:美元AppBundle生成:实体

接下来,添加一个新方法findAllOrderedByName ()新生成的库类。该方法将为所有的查询产品实体,命令按字母顺序。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ / src / AppBundle /实体/ ProductRepository.php名称空间AppBundle\实体;使用学说\ORM\EntityRepository;ProductRepository扩展EntityRepository{公共函数findAllOrderedByName(){返回美元- >getEntityManager ()- >createQuery (“从AppBundle选择p:产品p p.name ASC的顺序)- >getResult ();}}

提示

实体管理器可以通过访问$ this - > getEntityManager ()在存储库中。

< /div>

您可以使用此新方法就像默认查找器方法的存储库:

1 2 3
美元新兴市场=美元- >getDoctrine ()- >getManager ();美元产品=美元新兴市场- >getRepository (“AppBundle:产品”)- >findAllOrderedByName ();

请注意

当使用一个自定义存储库类,你仍然可以访问默认查找器方法等find ()findAll ()

< /div>

实体关系/协会

假设应用程序中的产品都属于一个“类别”。在这种情况下,您将需要一个类别与一个对象和一个方法产品对象一个类别对象。首先创建的类别实体。既然你知道你最终会通过原则需要持久化类,你可以让学说为你创建类。

1 2 3
$ php应用程序/控制台学说:生成:实体\——实体=“AppBundle:类别”\——字段=“名字:字符串(255)”

这个任务生成类别为你的实体,一个id场,的名字领域和相关的getter和setter函数。

关系映射元数据

联系到一起类别产品实体,首先创建一个产品财产的类别类:

  • 注释
  • YAML
  • XML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / src / AppBundle /实体/ Category.php/ /……使用学说\常见的\集合\ArrayCollection;类别{/ /……/ * * *@ORM\ OneToMany (targetEntity =“产品”的mappedBy =“类别”)* /受保护的美元产品;公共函数__construct(){美元- >产品=ArrayCollection ();}}

首先,自类别对象将与许多产品对象,产品数组属性添加到保存产品对象。再一次,这不是因为原则需要它,而是因为它是有意义的应用程序中类别的数组产品对象。

请注意

中的代码__construct ()方法很重要,因为理论要求美元的产品房地产是一个ArrayCollection对象。几乎这个对象的外观和行为完全像一个数组,但有一些增加了灵活性。如果这让你不舒服,不要担心。想象一下,这是一个数组你会处于良好状态。

< /div>

提示

targetEntity值在上面使用的装饰可以引用任何实体与一个有效名称空间,不仅仅是相同的名称空间中定义的实体。与一个不同的类中定义一个实体或包,输入一个完整的名称空间作为targetEntity。

< /div>

接下来,因为每个产品类可以关联到一个类别对象,您需要添加一个美元的类别财产产品类:

  • 注释
  • YAML
  • XML
1 2 3 4 5 6 7 8 9 10 11 12 13
/ / src / AppBundle /实体/ Product.php/ /……产品{/ /……/ * * *@ORM\ ManyToOne (targetEntity =“类别”,inversedBy = "产品")*@ORM\ JoinColumn (name = " category_id添加",referencedColumnName = " id ") * /受保护的美元类别;}

最后,现在您已经添加了一个新的属性的类别产品类,告诉学说为您生成失踪的getter和setter方法:

1
php应用程序/控制台学说:美元AppBundle生成:实体

忽略教义的元数据。你现在有两类类别产品与自然的一对多关系。的类别类的数组产品对象和产品对象可以容纳一个类别对象。换句话说,你建造类的方式适合您的需要。这一事实的数据需要被持久化到数据库总是次要的。

现在,看看上面的元数据美元的类别财产的产品类。这里的信息告诉学说相关的类类别和存储id类别的记录上category_id添加领域的生活产品表。换句话说,相关的类别将存储在对象美元的类别属性,但在幕后,学说将持续这种关系通过存储类别的id值category_id添加列的产品表。

上面的元数据美元的产品财产的类别对象不是那么重要,只是告诉学说看Product.category属性如何映射的关系。

在您继续之前,一定要告诉添加新的学说类别表,product.category_id列,和新外键:

1
php应用程序/控制台学说:美元模式:更新——力量

请注意

这个任务只能在开发过程中使用。一种更健壮的方法系统地更新你的生产数据库,读到迁移

< /div>

现在你可以看到这个新代码在行动!想象一下你在一个控制器:

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
/ /……使用AppBundle\实体\类别;使用AppBundle\实体\产品;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;DefaultController扩展控制器{公共函数createProductAction(){美元类别=类别();美元类别- >setName (主要产品的);美元产品=产品();美元产品- >setName (“Foo”);美元产品- >setPrice (19.99);美元产品- >setDescription (“Lorem ipsum悲哀”);/ /与本产品类别美元产品- >setCategory (美元类别);美元新兴市场=美元- >getDoctrine ()- >getManager ();美元新兴市场- >persist (美元类别);美元新兴市场- >persist (美元产品);美元新兴市场- >冲洗();返回响应(“创造了产品id:”美元产品- >getId ()。和类别id:美元类别- >getId ());}}

现在,添加一行到类别产品表。的product.category_id列新产品将不管id是新类别的。原则管理这种关系的持久性。

< /div>

当您需要获取相关的对象,你的工作流看起来就像以前一样。首先,拿一个美元的产品对象,然后访问相关类别:

1 2 3 4 5 6 7 8 9 10
公共函数showAction(美元id){美元产品=美元- >getDoctrine ()- >getRepository (“AppBundle:产品”)- >找到(美元id);美元categoryName=美元产品- >getCategory ()- >getName ();/ /……}

在这个例子中,首先查询产品基于产品的对象id。这问题一个查询只是产品数据和水合物美元的产品对象的数据。之后,当你调用产品- > getCategory () - > getName (),默默地教义使得第二个查询来找到类别这是与此相关产品。它准备美元的类别对象,并返回给你。

重要的是你有容易获得相关产品的类别,但类别数据并不是直到你要求类别检索(即“延迟加载”)。

你也可以查询另一个方向:

1 2 3 4 5 6 7 8 9 10
公共函数showProductsAction(美元id){美元类别=美元- >getDoctrine ()- >getRepository (“AppBundle:类别”)- >找到(美元id);美元产品=美元类别- >getProducts ();/ /……}

在这种情况下,同样的事情发生了:你第一次查询单类别对象,然后教义使得第二个查询来检索相关产品对象,但只有当/如果你问他们(即当你调用- > getProducts ())。的美元的产品变量是一个数组产品与给定的对象类别通过他们的对象category_id添加价值。

这个“延迟加载”是可能的,因为,在必要的时候,教义返回一个“代理”对象的真正对象。再看看上面的例子:

1 2 3 4 5 6 7 8 9
美元产品=美元- >getDoctrine ()- >getRepository (“AppBundle:产品”)- >找到(美元id);美元类别=美元产品- >getCategory ();/ /打印“代理\ AppBundleEntityCategoryProxy”转储(get_class (美元类别));();

这个代理对象扩展了真的类别对象,外表和行为完全一样。不同的是,通过使用一个代理对象,教义可以为真正的延迟查询类别直到你真正需要的数据(例如,直到你的电话$分类- > getName ())。

生成的代理类学说并存储在缓存目录。虽然你可能从未注意到你美元的类别对象实际上是一个代理对象,重要的是要记住的。

在下一节中,当你检索的产品和类别数据(通过加入),原则将返回真正的类别对象,因为不需要延迟加载。

< /div>

在上面的例子中,两个查询了—一个用于原来的对象(例如一个类别),一个用于相关对象(s)(如产品对象)。

提示

记住,你可以看到所有的查询在一个请求通过网络调试工具栏。

< /div>

当然,如果你预先知道你需要访问两个对象,你可以避免第二个查询发出加入原始查询。添加以下方法ProductRepository类:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ / src / AppBundle /实体/ ProductRepository.php公共函数findOneByIdJoinedToCategory(美元id){美元查询=美元- >getEntityManager ()- >createQuery (“选择p c AppBundle:产品p加入p。类别c p。id =: id ')- >setParameter (“id”,美元id);试一试{返回美元查询- >getSingleResult ();}(\ \ ORM \ NoResultException教义美元e){返回;}}

现在,您可以使用这种方法在你的控制器查询产品对象及其相关类别只有一个查询:

1 2 3 4 5 6 7 8 9 10
公共函数showAction(美元id){美元产品=美元- >getDoctrine ()- >getRepository (“AppBundle:产品”)- >findOneByIdJoinedToCategory (美元id);美元类别=美元产品- >getCategory ();/ /……}

更多信息协会

本节介绍了一个常见的实体关系,一对多的关系。对于更高级的细节和例子如何使用其他类型的关系(例如一对一、多对多),看到教义的协会映射文档欧宝体育电话

请注意

如果你使用注释,需要预先考虑所有注释ORM \(如。ORM \ OneToMany),这不是反映在教义的文档。欧宝体育电话你还需要包括使用原则\ ORM \ ORM映射;声明,进口ORM注释前缀。

< /div>

配置

教义是高度可配置的,虽然你可能不会需要担心的大多数选项。找到更多关于配置原则,看到的理论部分配置引用

< /div>

生命周期回调

有时,您需要执行一个动作之前或之后插入一个实体,更新或删除。这些类型的行为被称为“生命周期”回调,作为他们回调方法,您需要执行在一个实体的生命周期的不同阶段(如实体插入、更新、删除等)。

如果你使用元数据注释,首先使生命周期回调。这不是必要的,如果你使用YAML或XML映射。

1 2 3 4 5 6 7 8
/ * * *@ORM()* \实体@ORM\ HasLifecycleCallbacks () * /产品{/ /……}

现在,你可以告诉原则来执行一个方法在任何可用的生命周期事件。例如,假设您想要设置一个createdAt日期列当前日期,只有当首先坚持实体(即插入):

  • 注释
  • YAML
  • XML
1 2 3 4 5 6 7 8 9
/ / src / AppBundle /实体/ Product.php/ * * *@ORM\ PrePersist * /公共函数setCreatedAtValue(){美元- >createdAt =\ DateTime ();}

请注意

上面的示例假设您已经创建了映射createdAt财产(这里没有显示)。

< /div>

首先在实体是坚持,教义将自动调用这个方法,createdAt字段将被设置为当前日期。

还有其他几个生命周期可以钩到的事件。更多信息在其他生命周期事件和生命周期回调,看到教义的生命周期事件文档欧宝体育电话

请注意,setCreatedAtValue ()方法不接受任何参数。总是这样生命周期回调,故意:生命周期回调应关注内部的简单方法转换数据的实体(例如,设置一个创建/更新字段,生成一个鼻涕虫值)。

如果你需要做一些较重的任务——就像执行日志记录或发送电子邮件,你应该注册一个外部类作为一个事件侦听器或用户并赋予它获得您需要的任何资源。有关更多信息,请参见如何注册事件侦听器和用户

< /div>

教义字段类型引用

教义有很多可用的字段类型。这些PHP数据类型映射到一个特定的列类型无论你使用数据库。对于每个字段类型,进一步可以配置,设置长度,可以为空的行为,的名字和其他选项。看到所有可用的列表类型和更多信息,参见学说映射类型的文档欧宝体育电话

< /div>

总结

用原则,你可以专注于你的对象和如何在您的应用程序中使用它们,其次才是考虑数据库持久性。这是因为教义允许您使用任何PHP对象保存数据和依赖映射元数据信息对象的数据映射到一个特定的数据库表。

尽管主义围绕着一个简单的概念,它非常强大,允许您创建复杂的查询和订阅事件,让你采取不同的行动作为对象进行持久的生命周期。

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。