数据库和理论

编辑本页

警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 2.4,现已不再维护。

本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。

数据库和理论

对于任何应用程序来说,最常见和最具挑战性的任务之一是持久化和从数据库中读取信息。幸运的是,Symfony集ob娱乐下载成了学说,这个库的唯一目标就是为您提供强大的工具来简化此过程。在本章中,您将学习Doctrine背后的基本哲学,并了解使用数据库是多么容易。

请注意

Doctrine完全与Symfony分离,使用它是可选的。ob娱乐下载本章是关于ORM原则的,它的目的是让您将对象映射到关系数据库(例如MySQLPostgreSQLMicrosoft SQL).如果您更喜欢使用原始数据库查询,这很容易,并在“如何使用Doctrine的DBAL层“食谱条目。

还可以将数据持久化到MongoDB使用Doctrine ODM库。要了解更多信息,请阅读“DoctrineMongoDBBundle”文档。欧宝官网下载app

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

要理解教条主义是如何运作的,最简单的方法就是看它的实际行动。在本节中,您将配置数据库,创建一个产品对象,将其持久化到数据库并将其取出。

如果您想跟随本章中的示例,请创建一个AcmeStoreBundle通过:

1
$ php应用程序/控制台生成:bundle——namespace=Acme/StoreBundle

配置数据库

在真正开始之前,您需要配置数据库连接信息。根据约定,此信息通常配置在应用程序/配置/ 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只是一种惯例。该文件中定义的参数在设置Doctrine时由主配置文件引用:

  • 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配置中。有关更多信息,请参见如何设置服务容器的外部参数

现在Doctrine已经知道了你的数据库,你可以让它为你创建数据库:

1
$ PHP应用程序/控制台原则:数据库:创建

即使是经验丰富的开发人员在启动Symfony项目时也会犯的一个错误是忘记在数据库上设置默认字符集和排序规则,最终使用ob娱乐下载拉丁类型的排序规则,这是大多数数据库的默认设置。他们甚至可能在第一次就记得要这样做,但在开发过程中运行一个相对常见的命令后就忘记了:

1 2
$ PHP应用程序/控制台原则:数据库:drop——force $ PHP应用程序/控制台原则:数据库:create

在Doctrine内部没有办法配置这些默认值,因为它试图在环境配置方面尽可能地不可知。解决这个问题的一种方法是配置服务器级默认值。

设置MySQL的UTF8默认值非常简单,只需在配置文件中添加几行代码即可(通常情况下)my.cnf):

1 2 3
(mysqld)collation-server= utf8_general_cicharacter-set-server= utf8

请注意

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

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

创建实体类

假设您正在构建一个需要显示产品的应用程序。甚至不考虑Doctrine或数据库,您已经知道您需要一个产品对象来表示这些产品。类中创建这个类实体贵公司目录AcmeStoreBundle

1 2 3 4 5 6 7 8 9 10 11
/ / src / Acme / / Product.php StoreBundle /实体名称空间AcmeStoreBundle实体产品受保护的的名字受保护的价格受保护的描述;}

类——常称为“实体”,意思是保存数据的基本类-简单且有助于满足应用程序中所需产品的业务需求。这个类还不能持久化到数据库中——它只是一个简单的PHP类。

提示

一旦你学习了Doctrine背后的概念,你就可以让Doctrine为你创建简单的实体类。它会问你一些互动问题来帮助你构建任何实体:

1
$ PHP应用程序/控制台原则:生成:实体

添加映射信息

Doctrine允许您以一种更有趣的方式使用数据库,而不仅仅是将基于列的表中的行获取到数组中。相反,Doctrine允许您完整地持久化对象并从数据库中获取整个对象。这是通过将一个PHP类映射到一个数据库表,并将该PHP类的属性映射到表中的列来实现的:

为了让Doctrine能够做到这一点,您只需要创建“元数据”,或者配置,告诉Doctrine确切地如何产品类及其属性应该是映射到数据库。该元数据可以以多种不同的格式指定,包括YAML、XML或直接在产品通过注释创建类:

  • 注释
  • 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 32 33
/ / src / Acme / / Product.php StoreBundle /实体名称空间AcmeStoreBundle实体使用学说ORM映射作为ORM/ * * *@ORM* \实体@ORM\表(name = "产品")* /产品/ * * *@ORM\列(type =“整数”)*@ORM\ Id *@ORM\ GeneratedValue(策略=“汽车”)* /受保护的id/ * * *@ORM\Column(type="string", length=100) */受保护的的名字/ * * *@ORM\Column(type="decimal", scale=2) */受保护的价格/ * * *@ORM\列(type = " text ") * /受保护的描述;}

请注意

一个bundle只能接受一种元数据定义格式。例如,不可能将YAML元数据定义与带注释的PHP实体类定义混合使用。

提示

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

Doctrine允许您从各种不同的字段类型中进行选择,每种类型都有自己的选项。有关可用字段类型的信息,请参见数据库和理论部分。

另请参阅

你也可以看看Doctrine的基础制图文档欧宝官网下载app查看映射信息的所有详细信息。如果使用注释,则需要在所有注释前加上ORM \(如。ORM \列(. .)),并没有在Doctrine的文档中显示。欧宝官网下载app您还需要包括使用Doctrine\ORM\Mapping作为ORM;声明,进口ORM注释前缀。

谨慎

注意不要将类名和属性映射到受保护的SQL关键字(例如集团用户).例如,如果您的实体类名是集团,那么,默认情况下,您的表名将为集团,这将在某些引擎中导致SQL错误。看到学说的保留SQL关键字文档欧宝官网下载app如何正确地转义这些名称。或者,如果您可以自由选择数据库模式,只需映射到不同的表名或列名。看到学说的持久化类而且属性映射欧宝官网下载app文档。

请注意

当使用另一个库或程序(例如。Doxygen)使用注释,您应该放置@IgnoreAnnotation注释,以指示Symfony应该忽略哪些注释。ob娱乐下载

例如,防止@fn注释抛出异常时,添加以下内容:

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

生成getter和setter

尽管教条主义现在知道如何坚持产品对象,类本身还没有真正有用。自产品是一个普通的PHP类,你需要创建getter和setter方法(例如。getName ()setName ())来访问它的属性(因为属性是受保护的).幸运的是,Doctrine可以通过运行:

1
$ php应用程序/控制台原则:生成:实体Acme/StoreBundle/实体/产品

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

谨慎

请记住,Doctrine的实体生成器生成简单的getter /setter。您应该检查生成的实体,并根据自己的需要调整getter/setter逻辑。

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

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

原则:生成实体命令保存原始文件的备份Product.php命名Product.php ~.在某些情况下,此文件的存在可能导致“不能重新声明类”错误。它可以安全地移除。你也可以使用——没有备份的选项,以防止生成这些备份文件。

注意,你没有需要使用实例Doctrine不依赖于代码生成。与普通PHP类一样,您只需要确保受保护/私有属性具有getter和setter方法。因为这是使用Doctrine时经常做的事情,所以创建了这个命令。

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

1 2
$ php应用程序/控制台原则:generate:entities AcmeStoreBundle $ php应用程序/控制台原则

请注意

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

创建数据库表/模式

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

1
$ PHP应用程序/控制台原则:schema:update——force

提示

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

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

您的数据库现在具有完整的功能产品表,其中的列与所指定的元数据匹配。

持久化对象到数据库

现在你有了一个映射产品实体和对应的产品表,您就可以将数据持久化到数据库中。从控制器内部来看,这非常简单。属性中添加以下方法DefaultController关于捆绑:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / src / Acme / StoreBundle /控制器/ DefaultController.php/ /……使用AcmeStoreBundle实体产品使用ob娱乐下载组件HttpFoundation响应公共函数createAction()产品产品();产品->setName ('A Foo Bar');产品->setPrice (“19.99”);产品->setDescription (“Lorem ipsum dolor”);新兴市场->getDoctrine ()->getManager ();新兴市场->persist (产品);新兴市场->冲洗();返回响应(“创建的产品id”产品->getId ());}

请注意

如果您遵循这个示例,您将需要创建一个指向此操作的路由,以查看它是否工作。

提示

方法在控制器中使用DoctrinegetDoctrine ()控制器的方法。此方法是获取学说服务。您可以在其他任何地方通过在服务中注入该服务来使用Doctrine。看到服务容器有关创建自己的服务的更多信息。

更详细地看看前面的例子:

  • 行9 - 12方法的实例化和使用美元的产品对象,就像任何其他普通PHP对象一样。
  • 第14行这一行是Doctrine的实体管理器对象,该对象负责处理持久化和从数据库获取对象的过程。
  • 第15行persist ()方法告诉Doctrine“管理”美元的产品对象。这实际上不会导致对数据库的查询。
  • 线16冲洗()方法时,Doctrine会查看它所管理的所有对象,以确定它们是否需要持久化到数据库中。在本例中,美元的产品对象尚未持久化,因此实体管理器将执行插入查询中创建一行产品表格

请注意

实际上,由于Doctrine知道您的所有托管实体,当您调用冲洗()方法,它计算总体更改集并执行最有效的查询/查询。例如,如果您总共持久化了100产品对象,然后调用冲洗()教条会造就一个预处理语句,并为每个插入重用它。这个模式叫做工作单元使用它是因为它快速有效。

当创建或更新对象时,工作流总是相同的。在下一节中,您将看到Doctrine是如何智能地自动发出一个更新查询该记录是否已经存在于数据库中。

提示

Doctrine提供了一个库,允许您以编程的方式将测试数据加载到您的项目中(例如。“固定数据”)。有关信息,请参见DoctrineFixturesBundle。

从数据库中获取对象

从数据库中取回对象更加简单。例如,假设您配置了一条路由来显示特定的路由产品基于它id值:

12 3 4 5 6 7 8 9 10 11 12 13 14
公共函数showActionid产品->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”->找到(id);如果(!产品) {->createNotFoundException (“没有为id找到产品”id);}/ /……做一些事情,比如将$product对象传递到模板中

提示

方法可以实现与此等效的功能,而无需编写任何代码@ParamConverter快捷方式。有关更多细节,请参阅frameworkextrabunda欧宝官网下载appnce文档。

当你查询一个特定类型的对象时,你总是使用所谓的“存储库”。您可以将存储库视为一个PHP类,它的唯一任务是帮助您获取某个类的实体。你可以通过以下方式访问实体类的存储库对象:

1 2
存储库->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”);

请注意

AcmeStoreBundle:产品string是你可以在Doctrine中的任何地方使用的快捷方式,而不是实体的完整类名(即。Acme \ \ StoreBundle \实体产品).只要你的实体还在实体命名空间,这将工作。

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

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

请注意

当然,您还可以发出复杂的查询,您将在数据库和理论部分。

你也可以利用有用的findBy而且findOneBy基于多个条件轻松获取对象的方法:

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

提示

当您呈现任何页面时,您可以在web调试工具栏的右下角看到进行了多少查询。

如果单击图标,分析器将打开,显示所执行的确切查询。

更新对象

一旦你从Doctrine中获取了一个对象,更新它就很容易了。假设你有一个将产品id映射到控制器中的更新操作的路由:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
公共函数updateActionid新兴市场->getDoctrine ()->getManager ();产品新兴市场->getRepository (“AcmeStoreBundle:产品”->找到(id);如果(!产品) {->createNotFoundException (“没有为id找到产品”id);}产品->setName (“新产品名称!”);新兴市场->冲洗();返回->重定向(->generateUrl (“主页”));}

更新对象只涉及三个步骤:

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

请注意(em - >坚持美元产品)是没有必要的。回想一下,这个方法只是告诉Doctrine管理或“监视”对象美元的产品对象。在本例中,由于您获取了美元的产品来自Doctrine的对象,它已经被管理好了。

删除对象

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

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

如你所料,remove ()方法通知Doctrine您希望从数据库中删除给定对象。实际的删除查询,但是直到冲洗()方法。

查询对象

你已经看到了repository对象是如何让你不做任何工作就可以运行基本查询的:

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

当然,Doctrine还允许您使用Doctrine Query Language (DQL)编写更复杂的查询。DQL类似于SQL,除了您应该想象您正在查询一个实体类的一个或多个对象(例如。产品),而不是查询表中的行(例如。产品).

在Doctrine中查询时,您有两个选择:编写纯Doctrine查询或使用Doctrine的Query Builder。

使用Doctrine的查询生成器查询对象

假设您想查询产品,但只返回成本高于19.99从最便宜的到最贵的。你可以使用Doctrine的QueryBuilder:

1 2 3 4 5 6 7 8 9 10
存储库->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”);查询存储库->createQueryBuilder (“p”->(在哪里'p.price >:价格'->setParameter (“价格”“19.99”->orderBy (“p.price”“ASC”->getQuery ();产品查询->getResult ();

QueryBuilder对象包含构建查询所需的每个方法。通过调用getQuery ()方法时,查询构建器将返回一个法线查询对象,该对象可用于获取查询的结果。

提示

请注意setParameter ()方法。在使用Doctrine时,将任何外部值设置为“占位符”总是一个好主意(:价格例如上面的例子),因为它可以防止SQL注入攻击。

getResult ()方法返回结果数组。为了只得到一个结果,您可以使用getSingleResult ()(抛出异常,没有结果)或getOneOrNullResult ()

1
产品查询->getOneOrNullResult ();

有关Doctrine的查询生成器的更多信息,请参阅Doctrine的查询构建器欧宝官网下载app文档。

使用DQL查询对象

而不是使用QueryBuilder,您也可以直接使用DQL编写查询:

1 2 3 4 5 6 7 8 9
新兴市场->getDoctrine ()->getManager ();查询新兴市场->createQuery (“SELECT p FROM AcmeStoreBundle:Product p WHERE p.price >:price ORDER BY p.price ASC”->setParameter (“价格”“19.99”);产品查询->getResult ();

如果您熟悉SQL,那么DQL应该感觉非常自然。最大的区别是您需要考虑“对象”,而不是数据库中的行。因此,您选择AcmeStoreBundle:产品对象然后用别名表示p(如您所见,这与您在前一节中所做的相同)。

DQL语法非常强大,允许您轻松地在实体之间连接(本文的主题是关系稍后将讨论)、组等。欲了解更多信息,请参阅官方教义Doctrine查询语言欧宝官网下载app文档。

自定义存储库类

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

为此,将存储库类的名称添加到映射定义中:

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

Doctrine可以通过运行前面用来生成缺少的getter和setter方法的相同命令为您生成存储库类:

1
$ php应用/控制台原则:生成:实体Acme

接下来,添加一个新方法-findAllOrderedByName ()-到新生成的存储库类。该方法将查询所有的产品实体,按字母顺序排列。

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ / src / Acme / / ProductRepository.php StoreBundle /实体名称空间AcmeStoreBundle实体使用学说ORMEntityRepositoryProductRepository扩展EntityRepository公共函数findAllOrderedByName()返回->getEntityManager ()->createQuery (SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC->getResult ();}}

提示

实体管理器可以通过$ this - > getEntityManager ()从存储库内部。

你可以像使用存储库的默认查找器方法一样使用这个新方法:

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

请注意

在使用自定义存储库类时,仍然可以访问默认查找器方法,例如find ()而且findAll ()

实体关系/协会

假设应用程序中的产品完全属于一个“类别”。在这种情况下,你需要一个类别对象和关联对象的方法产品对象的类别对象。首先创建类别实体。因为您知道您最终需要通过Doctrine来持久化类,所以您可以让Doctrine为您创建类。

1
$ PHP应用程序/控制台原则:生成:实体——实体=“AcmeStoreBundle:类别”——字段=“名字:字符串(255)”

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

关系映射元数据

联系类别而且产品实体,首先创建一个产品的属性类别类:

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

首先,由于类别对象会涉及到很多产品对象,产品添加数组属性来保存它们产品对象。同样,这样做并不是因为Doctrine需要它,而是因为它在每个应用程序中都有意义类别保存…的数组产品对象。

请注意

的代码__construct ()方法很重要,因为教义要求美元的产品属性为ArrayCollection对象。这个物体的样子和作用几乎是一样的完全类似于数组,但有一些额外的灵活性。如果这让你不舒服,别担心。想象一下这是一个数组你的身材会很好。

提示

上面使用的装饰器中的targetEntity值可以引用具有有效名称空间的任何实体,而不仅仅是同一名称空间中定义的实体。要关联到不同类或包中定义的实体,请输入完整的名称空间作为targetEntity。

接下来,由于产品类只能与一个相关类别对象时,您需要添加一个美元的类别属性产品类:

  • 注释
  • YAML
  • XML
12 3 4 5 6 7 8 9 10 11 12 13
/ / src / Acme / / Product.php StoreBundle /实体/ /……产品/ /……/ * * *@ORM\ManyToOne(targetEntity="Category", inversedBy="products") *@ORM\JoinColumn(name="category_id", referencedColumnName="id") */受保护的类别;}

最后,现在您已经向两个类别而且产品类,告诉Doctrine为你生成缺少的getter和setter方法:

1
$ php应用/控制台原则:生成:实体Acme

暂时忽略Doctrine元数据。你现在有两个类类别而且产品自然的一对多关系。的类别类的数组产品对象和产品物体可以容纳一个类别对象。换句话说,您已经以一种对您的需求有意义的方式构建了类。数据需要持久化到数据库始终是次要的。

现在,看看上面的元数据美元的类别的属性产品类。这里的信息告诉Doctrine相关的类是类别它应该存储id的类别记录上category_id添加的场产品表格换句话说,是相关的类别对象将存储在美元的类别属性,但在幕后,Doctrine将通过将类别的id值存储在category_id添加的列产品表格

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

在继续之前,请务必告诉Doctrine添加新的类别表,product.category_id列和新外键:

1
$ PHP应用程序/控制台原则:schema:update——force

请注意

这个任务应该只在开发期间使用。有关系统地更新生产数据库的更健壮的方法,请阅读Doctrine迁移。

现在您可以看到这个新代码的实际运行!假设你在一个控制器中:

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
/ /……使用AcmeStoreBundle实体类别使用AcmeStoreBundle实体产品使用ob娱乐下载组件HttpFoundation响应DefaultController扩展控制器公共函数createProductAction()类别类别();类别->setName (主要产品的);产品产品();产品->setName (“Foo”);产品->setPrice (19.99);//将该产品与类别联系起来产品->setCategory (类别);新兴市场->getDoctrine ()->getManager ();新兴市场->persist (类别);新兴市场->persist (产品);新兴市场->冲洗();返回响应('创建的产品id: '产品->getId()。和类别id: '类别->getId ());}}

现在,一行被添加到两个类别而且产品表。的product.category_id新产品的列设置为id属于新类别。教条为你管理这种关系的持久性。

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

1 2 3 4 5 6 7 8 9 10
公共函数showActionid产品->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”->找到(id);categoryName产品->getCategory ()->getName ();/ /……

在本例中,首先查询a产品对象基于产品的id.的查询只是该产品的数据和补水美元的产品对象使用该数据。等会儿,等你打电话的时候产品- > getCategory()——> getName (),教义默默地进行第二次查询,以找到类别这和这个有关产品.它准备美元的类别对象并将它返回给你。

重要的是,您可以很容易地访问产品的相关类别,但直到您请求类别时,类别数据才会实际检索到(即它是“惰性加载”)。

您也可以向其他方向查询:

1 2 3 4 5 6 7 8 9 10
公共函数showProductActionid类别->getDoctrine ()->getRepository (“AcmeStoreBundle:类别”->找到(id);产品类别->getProducts ();/ /……

在本例中,发生了相同的情况:首先查询单个类别对象,然后Doctrine执行第二个查询以检索相关的产品对象,但只有一次/如果你要求他们(即当你调用- > getProducts ()).的美元的产品Variable是all的数组产品与给定对象相关的对象类别对象通过category_id添加价值。

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

1 2 3 4 5 6 7 8
产品->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”->找到(id);类别产品->getCategory ();//打印“代理\AcmeStoreBundleEntityCategoryProxy”回声get_class (类别);

这个代理对象扩展了true类别对象,并且外观和行为与它完全相同。区别在于,通过使用代理对象,Doctrine可以延迟对实数据的查询类别数据,直到你真正需要的数据(例如,直到你调用分类- > getName ()).

代理类由Doctrine生成并存储在缓存目录中。虽然你可能永远都不会注意到你的美元的类别Object实际上是一个代理对象,记住这一点很重要。

在下一节中,当您同时检索产品和类别数据时(通过加入),教义将返回真正的类别对象,因为不需要延迟加载任何东西。

在上面的例子中,进行了两个查询——一个是原始对象(例如a类别)和一个用于相关对象(例如产品对象)。

提示

请记住,您可以通过web调试工具栏查看请求期间进行的所有查询。

当然,如果您事先知道需要访问这两个对象,则可以通过在原始查询中发出连接来避免第二个查询。属性中添加以下方法ProductRepository类:

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/ / src / Acme / / ProductRepository.php StoreBundle /实体公共函数findOneByIdJoinedToCategoryid查询->getEntityManager ()->createQuery ('SELECT p, c FROM AcmeStoreBundle:Product p JOIN p.category c WHERE p.id =:id'->setParameter (“id”id);试一试返回查询->getSingleResult ();}(\ \ ORM \ NoResultException教义e) {返回;}}

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

1 2 3 4 5 6 7 8 9 10
公共函数showActionid产品->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”->findOneByIdJoinedToCategory (id);类别产品->getCategory ();/ /……

更多有关关联的资料

本节介绍了一种常见的实体关系类型,即一对多关系。有关如何使用其他类型关系(例如,一对一,多对多)的更高级细节和示例,请参阅Doctrine关联映射文档欧宝官网下载app

请注意

如果使用注释,则需要在所有注释前加上ORM \(如。ORM \ OneToMany),并没有反映在Doctrine的文档中。欧宝官网下载app您还需要包括使用Doctrine\ORM\Mapping作为ORM;声明,进口ORM注释前缀。

配置

Doctrine是高度可配置的,尽管您可能永远不需要担心它的大多数选项。要了解有关配置Doctrine的更多信息,请参阅配置引用

生命周期回调

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

如果要对元数据使用注释,首先要启用生命周期回调。如果您使用YAML或XML进行映射,则不需要这样做。

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

现在,您可以告诉Doctrine在任何可用的生命周期事件上执行一个方法。例如,假设你想要设置一个createdAtDate列到当前日期,仅当实体第一次被持久化(即插入)时:

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

请注意

上面的示例假设您已经创建并映射了一个createdAt属性(此处未显示)。

现在,在首次持久化实体之前,Doctrine将自动调用此方法和createdAt字段将被设置为当前日期。

还有一些其他的生命周期事件可以被您钩入。有关其他生命周期事件和生命周期回调的更多信息,请参阅Doctrine生命周期事件文档欧宝官网下载app

注意setCreatedAtValue ()方法不接收参数。生命周期回调总是如此,并且是有意为之的:生命周期回调应该是简单的方法,关注实体中的内部数据转换(例如,设置创建/更新字段,生成一个slug值)。

如果您需要做一些更重的提升——比如执行日志记录或发送电子邮件——您应该将一个外部类注册为事件侦听器或订阅者,并让它访问所需的任何资源。有关更多信息,请参见如何注册事件监听器和订阅者

理论领域类型

Doctrine提供了大量可用的字段类型。它们中的每一个都将PHP数据类型映射到您所使用的数据库中的特定列类型。对于每个字段类型,可以进一步配置,设置长度可以为空的行为,的名字还有其他选择。要查看所有可用类型的列表和更多信息,请参阅Doctrine的映射类型文档欧宝官网下载app

总结

使用Doctrine,您可以专注于对象以及它们在应用程序中的用处,然后再考虑数据库持久性。这是因为Doctrine允许您使用任何PHP对象来保存数据,并依赖于映射元数据信息将对象的数据映射到特定的数据库表。

尽管Doctrine围绕着一个简单的概念,但它非常强大,允许您创建复杂的查询和订阅事件,允许您在对象经历其持久性生命周期时采取不同的操作。

了解更多

有关Doctrine的更多信息,请参见学说部份食谱.一些有用的文章可能是:

此工作,包括代码示例,是根据创作共用BY-SA 3.0许可证。