数据库和理论
编辑本页警告:您正在浏览的文档欧宝官网下载appob娱乐下载Symfony 2.0,现已不再维护。
读本页的更新版本用于Syob娱乐下载mfony 6.2(当前稳定版本)。
数据库和理论
对于任何应用程序来说,最常见和最具挑战性的任务之一是持久化和从数据库中读取信息。幸运的是,Symfony集ob娱乐下载成了学说,这个库的唯一目标就是为您提供强大的工具来简化此过程。在本章中,您将学习Doctrine背后的基本哲学,并了解使用数据库是多么容易。
请注意
Doctrine完全与Symfony分离,使用它是可选的。ob娱乐下载本章是关于ORM原则的,它的目的是让您将对象映射到关系数据库(例如MySQL,PostgreSQL或Microsoft SQL).如果您更喜欢使用原始数据库查询,这很容易,并在“如何使用Doctrine的DBAL层“食谱条目。
还可以将数据持久化到MongoDB使用Doctrine ODM库。要了解更多信息,请阅读DoctrineMongoDBBundle文档。欧宝官网下载app
一个简单的例子:一个产品
要理解教条主义是如何运作的,最简单的方法就是看它的实际行动。在本节中,您将配置数据库,创建一个产品
对象,将其持久化到数据库并将其取出。
与示例一起编写代码
如果您想跟随本章中的示例,请创建一个AcmeStoreBundle
通过:
1
$ php应用程序/控制台生成:bundle——namespace=Acme/StoreBundle
配置数据库
在真正开始之前,您需要配置数据库连接信息。根据约定,此信息通常配置在应用程序/配置/ parameters.ini
文件:
1 2 3 4 5 6 7
;应用程序/配置/ parameters.ini(参数)database_driver= pdo_mysqldatabase_host= localhostdatabase_name= test_projectdatabase_user=根database_password=密码
请注意
通过定义配置parameters.ini
只是一种惯例。该文件中定义的参数在设置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 %”
1 2 3 4 5 6 7 8 9 10
<!——app/config/config.xml——><原则:配置><原则:dbal司机=“% database_driver %”宿主=“% database_host %”dbname=“% database_name %”用户=“% database_user %”密码=“% database_password %”>原则:配置>
1 2 3 4 5 6 7 8 9 10
/ / app / config / config . php$配置->loadFromExtension (“原则”,数组(“dbal”= >数组(“司机”= >“% database_driver %”,“主机”= >“% database_host %”,“dbname”= >“% database_name %”,“用户”= >“% database_user %”,“密码”= >“% database_password %”,),));
通过将数据库信息分离到一个单独的文件中,您可以轻松地在每个服务器上保存文件的不同版本。您还可以轻松地将数据库配置(或任何敏感信息)存储在项目之外,例如在Apache配置中。有关更多信息,请参见如何设置服务容器的外部参数.
现在Doctrine已经知道了你的数据库,你可以让它为你创建数据库:
1
$ PHP应用程序/控制台原则:数据库:创建
将数据库设置为UTF8
即使是经验丰富的开发人员在启动Symfony2项目时也会犯的一个错误是忘记在数据库上设置默认字符集和排序规则,最终使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
1 2 3 4 5 6 7 8
<!——app/config/config.xml——><原则:配置司机=“pdo_sqlite”路径=“% kernel.root_dir % / sqlite.db”字符集=“utf - 8”><!——……-->原则:配置>
1 2 3 4 5 6 7 8
/ / app / config / config . php$容器->loadFromExtension (“原则”,数组(“dbal”= >数组(“司机”= >“pdo_sqlite”,“路径”= >' % kernel.root_dir % / sqlite.db ',“字符集”= >“utf - 8”,),));
创建实体类
假设您正在构建一个需要显示产品的应用程序。甚至不考虑Doctrine或数据库,您已经知道您需要一个产品
对象来表示这些产品。类中创建这个类实体
贵公司目录AcmeStoreBundle
:
1 2 3 4 5 6 7 8 9 10 11
/ / src / Acme / / Product.php StoreBundle /实体名称空间Acme\StoreBundle\实体;类产品{受保护的$的名字;受保护的$价格;受保护的$描述;}
类——常称为“实体”,意思是保存数据的基本类-简单且有助于满足应用程序中所需产品的业务需求。这个类还不能持久化到数据库中——它只是一个简单的PHP类。
提示
一旦你学习了Doctrine背后的概念,你可以让Doctrine为你创建简单的实体类:
1
$ PHP应用程序/控制台原则:生成:实体——实体=“AcmeStoreBundle:产品”——字段="名称:字符串(255)价格:浮动描述:文本"
添加映射信息
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 /实体名称空间Acme\StoreBundle\实体;使用学说\ORM\映射作为ORM;/ * * *@ORM* \实体@ORM\表(name = "产品")* /类产品{/ * * *@ORM\ Id *@ORM\列(type =“整数”)*@ORM\ GeneratedValue(策略=“汽车”)* /受保护的$id;/ * * *@ORM\Column(type="string", length=100) */受保护的$的名字;/ * * *@ORM\Column(type="decimal", scale=2) */受保护的$价格;/ * * *@ORM\列(type = " text ") * /受保护的$描述;}
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# src / Acme / StoreBundle /资源/配置/理论/ Product.orm.ymlAcme \ StoreBundle \实体\产品:类型:实体表:产品id:id:类型:整数发电机:{策略:汽车}字段:名称:类型:字符串长度:One hundred.价格:类型:小数规模:2描述:类型:文本
12 3 4 5 6 7 8 9 10 11 12 13 14 15
<!——src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.xml——> .xml<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 http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd”><实体的名字=“Acme \ StoreBundle \实体\产品”表格=“产品”><id的名字=“id”类型=“整数”列=“id”><发电机策略=“汽车”/>id><场的名字=“名称”列=“名称”类型=“字符串”长度=“100”/><场的名字=“价格”列=“价格”类型=“小数”规模=“2”/><场的名字=“描述”列=“描述”类型=“文本”/>实体>doctrine-mapping>
请注意
一个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/ /……使用Acme\StoreBundle\实体\产品;使用ob娱乐下载\组件\HttpFoundation\响应;公共函数createAction(){$产品=新产品();$产品->setName ('A Foo Bar');$产品->setPrice (“19.99”);$产品->setDescription (“Lorem ipsum dolor”);$新兴市场=$这->getDoctrine ()->getEntityManager ();$新兴市场->persist ($产品);$新兴市场->冲洗();返回新响应(“创建的产品id”.$产品->getId ());}
请注意
如果您遵循这个示例,您将需要创建一个指向此操作的路由,以查看它是否工作。
更详细地看看前面的例子:
- 行9 - 12方法的实例化和使用
美元的产品
对象,就像任何其他普通PHP对象一样。 - 第14行这一行是Doctrine的实体管理器对象,该对象负责处理持久化和从数据库获取对象的过程。
- 第15行的
persist ()
方法告诉Doctrine“管理”美元的产品
对象。这实际上不会导致对数据库的查询。 - 线16当
冲洗()
方法时,Doctrine会查看它所管理的所有对象,以确定它们是否需要持久化到数据库中。在本例中,美元的产品
对象尚未持久化,因此实体管理器将执行插入
查询中创建一行产品
表格
请注意
实际上,由于Doctrine知道您的所有托管实体,当您调用冲洗()
方法,它计算总体更改集并执行最有效的查询/查询。例如,如果您总共持久化了100产品
对象,然后调用冲洗()
教条会造就一个单预处理语句,并为每个插入重用它。这个模式叫做工作单元使用它是因为它快速有效。
当创建或更新对象时,工作流总是相同的。在下一节中,您将看到Doctrine是如何智能地自动发出一个更新
查询该记录是否已经存在于数据库中。
提示
Doctrine提供了一个库,允许您以编程的方式将测试数据加载到您的项目中(例如。“固定数据”)。有关信息,请参见DoctrineFixturesBundle/index。
从数据库中获取对象
从数据库中取回对象更加简单。例如,假设您配置了一条路由来显示特定的路由产品
基于它id
值:
12 3 4 5 6 7 8 9 10 11 12 13 14
公共函数showAction($id){$产品=$这->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”)->找到($id);如果(!$产品) {扔$这->createNotFoundException (“没有为id找到产品”.$id);}/ /……做一些事情,比如将$product对象传递到模板中}
提示
方法可以实现与此等效的功能,而无需编写任何代码@ParamConverter
快捷方式。有关更多细节,请参阅frameworkextrabundance。
当你查询一个特定类型的对象时,你总是使用所谓的“存储库”。您可以将存储库视为一个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
//查询名称和价格匹配的产品$产品=$存储库->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
公共函数updateAction($id){$新兴市场=$这->getDoctrine ()->getEntityManager ();$产品=$新兴市场->getRepository (“AcmeStoreBundle:产品”)->找到($id);如果(!$产品) {扔$这->createNotFoundException (“没有为id找到产品”.$id);}$产品->setName (“新产品名称!”);$新兴市场->冲洗();返回$这->重定向($这->generateUrl (“主页”));}
更新对象只涉及三个步骤:
- 从Doctrine中获取对象;
- 修改对象;
- 调用
冲洗()
在实体管理器上
请注意(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。
使用DQL查询对象
假设您想查询产品,但只返回成本高于19.99
从最便宜的到最贵的。在控制器内部,执行以下操作:
1 2 3 4 5 6
$新兴市场=$这->getDoctrine ()->getEntityManager ();$查询=$新兴市场->createQuery (“SELECT p FROM AcmeStoreBundle:Product p WHERE p.price >:price ORDER BY p.price ASC”)->setParameter (“价格”,“19.99”);$产品=$查询->getResult ();
如果您熟悉SQL,那么DQL应该感觉非常自然。最大的区别是您需要考虑“对象”,而不是数据库中的行。因此,您选择从AcmeStoreBundle:产品
然后用别名表示p
.
的getResult ()
方法返回结果数组。如果只查询一个对象,可以使用getSingleResult ()
方法:
1
$产品=$查询->getSingleResult ();
谨慎
的getSingleResult ()
方法引发学说\ ORM \ NoResultException
如果没有返回结果,则异常学说\ ORM \ NonUniqueResultException
如果更多的返回一个以上的结果。如果你使用这个方法,你可能需要将它包装在一个try-catch块中,并确保只返回一个结果(如果你正在查询一些可能返回多个结果的东西):
1 2 3 4 5 6 7 8 9
$查询=$新兴市场->createQuery (“选择…”)->setMaxResults (1);试一试{$产品=$查询->getSingleResult ();}抓(\ \ Orm \ NoResultException教义$e) {$产品=零;}/ /……
DQL语法非常强大,允许您轻松地在实体之间连接(本文的主题是关系稍后将讨论)、组等。欲了解更多信息,请参阅官方教义Doctrine查询语言欧宝官网下载app文档。
设置参数
请注意setParameter ()
方法。当使用Doctrine时,将任何外部值设置为“占位符”总是一个好主意,这在上面的查询中完成:
1
...>:价格…
的值价格
方法的占位符setParameter ()
方法:
1
->setParameter (“价格”,“19.99”)
使用参数而不是直接在查询字符串中放置值是为了防止SQL注入攻击总是做的事情。方法一次性设置它们的值setParameters ()
方法:
1 2 3 4
->setParameters (数组(“价格”= >“19.99”,“名字”= >“Foo”,))
使用Doctrine的查询生成器
您可以选择使用Doctrine的查询,而不是直接编写查询QueryBuilder
使用一个漂亮的面向对象的接口来完成同样的工作。如果使用IDE,还可以在键入方法名时利用自动补全功能。在控制器内部:
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 ()
方法时,查询构建器将返回一个法线查询
对象,该对象与您在前一节中直接构建的对象相同。
有关Doctrine的查询生成器的更多信息,请参阅Doctrine的查询构建器欧宝官网下载app文档。
自定义存储库类
在前面的小节中,您开始从控制器内部构造和使用更复杂的查询。为了隔离、测试和重用这些查询,最好为您的实体创建一个自定义存储库类,并在其中添加带有查询逻辑的方法。
为此,将存储库类的名称添加到映射定义中。
- 注释
- YAML
- XML
12 3 4 5 6 7 8 9 10 11 12
/ / src / Acme / / Product.php StoreBundle /实体名称空间Acme\StoreBundle\实体;使用学说\ORM\映射作为ORM;/ * * *@ORMAcme \ StoreBundle \ \实体(repositoryClass = "实体\ ProductRepository ") * /类产品{/ /……}
1 2 3 4 5
# src / Acme / StoreBundle /资源/配置/理论/ Product.orm.ymlAcme \ StoreBundle \实体\产品:类型:实体repositoryClass:Acme \ StoreBundle \ \ ProductRepository实体#……
1 2 3 4 5 6 7 8 9 10
<!——src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.xml——> .xml<!——……--><doctrine-mapping><实体的名字=“Acme \ StoreBundle \实体\产品”repository-class=“Acme \ StoreBundle \ \ ProductRepository实体”><!——……-->实体>doctrine-mapping>
Doctrine可以通过运行前面用来生成缺少的getter和setter方法的相同命令为您生成存储库类:
1
$ php应用/控制台原则:生成:实体Acme
接下来,添加一个新方法-findAllOrderedByName ()
-到新生成的存储库类。该方法将查询所有的产品
实体,按字母顺序排列。
12 3 4 5 6 7 8 9 10 11 12 13 14
/ / src / Acme / / ProductRepository.php StoreBundle /实体名称空间Acme\StoreBundle\实体;使用学说\ORM\EntityRepository;类ProductRepository扩展EntityRepository{公共函数findAllOrderedByName(){返回$这->getEntityManager ()->createQuery (SELECT p FROM AcmeStoreBundle:Product p ORDER BY p.name ASC)->getResult ();}}
提示
实体管理器可以通过$ this - > getEntityManager ()
从存储库内部。
你可以像使用存储库的默认查找器方法一样使用这个新方法:
1 2 3
$新兴市场=$这->getDoctrine ()->getEntityManager ();$产品=$新兴市场->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 ();}}
1 2 3 4 5 6 7 8 9
# src / Acme / StoreBundle /资源/配置/理论/ Category.orm.ymlAcme \ \ StoreBundle \实体类别:类型:实体#……对:产品:targetEntity:产品的mappedBy:类别不要忘记在实体__construct()方法中初始化集合
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
<!——src/Acme/StoreBundle/Resources/config/doctrine/Category.orm.xml——><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 http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd”><实体的名字=“Acme \ \ StoreBundle \实体类别”><!——……--><一对多场=“产品”目标实体=“产品”所绘制=“类别”/><!——不要忘记在实体__construct()方法——>中初始化集合实体>doctrine-mapping>
首先,由于类别
对象会涉及到很多产品
对象,产品
添加数组属性来保存它们产品
对象。同样,这样做并不是因为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") */受保护的$类别;}
1 2 3 4 5 6 7 8 9 10 11
# src / Acme / StoreBundle /资源/配置/理论/ Product.orm.ymlAcme \ StoreBundle \实体\产品:类型:实体#……manyToOne:类别:targetEntity:类别inversedBy:产品joinColumn:名称:category_id添加referencedColumnName:id
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
<!——src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.xml——> .xml<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 http://doctrine-project.org/schemas/orm/doctrine-mapping.xsd”><实体的名字=“Acme \ StoreBundle \实体\产品”><!——……--><多对一场=“类别”目标实体=“产品”连接列=“类别”><连接列的名字=“category_id添加”referenced-column-name=“id”/>多对一>实体>doctrine-mapping>
最后,现在您已经向两个类别
而且产品
类,告诉Doctrine为你生成缺少的getter和setter方法:
1
$ php应用/控制台原则:生成:实体Acme
暂时忽略Doctrine元数据。你现在有两个类类别
而且产品
自然的一对多关系。的类别
类的数组产品
对象和产品
物体可以容纳一个类别
对象。换句话说,您已经以一种对您的需求有意义的方式构建了类。数据需要持久化到数据库始终是次要的。
现在,看看上面的元数据美元的类别
的属性产品
类。这里的信息告诉学说相关的类是类别
它应该存储id
的类别记录上category_id添加
的场产品
表格换句话说,是相关的类别
对象将存储在美元的类别
属性,但在幕后,Doctrine将通过将类别的id值存储在category_id添加
的列产品
表格
上面的元数据美元的产品
的属性类别
对象不那么重要,只是告诉主义看Product.category
属性来确定如何映射关系。
在继续之前,请务必告诉Doctrine添加新的类别
表,product.category_id
列和新外键:
1
$ PHP应用程序/控制台原则:schema:update——force
请注意
这个任务应该只在开发期间使用。有关系统地更新生产数据库的更健壮的方法,请阅读DoctrineMigrationsBundle。
保存相关实体
现在您可以看到这个新代码的实际运行!假设你在一个控制器中:
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
/ /……使用Acme\StoreBundle\实体\类别;使用Acme\StoreBundle\实体\产品;使用ob娱乐下载\组件\HttpFoundation\响应;类DefaultController扩展控制器{公共函数createProductAction(){$类别=新类别();$类别->setName (主要产品的);$产品=新产品();$产品->setName (“Foo”);$产品->setPrice (19.99);//将该产品与类别联系起来$产品->setCategory ($类别);$新兴市场=$这->getDoctrine ()->getEntityManager ();$新兴市场->persist ($类别);$新兴市场->persist ($产品);$新兴市场->冲洗();返回新响应('创建的产品id: '.$产品->getId()。和类别id: '.$类别->getId ());}}
现在,一行被添加到两个类别
而且产品
表。的product.category_id
新产品的列设置为id
属于新类别。教条为你管理这种关系的持久性。
获取相关对象
当您需要获取关联对象时,您的工作流看起来就像以前一样。首先,获取美元的产品
对象,然后访问其相关类别
:
1 2 3 4 5 6 7 8 9 10
公共函数showAction($id){$产品=$这->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”)->找到($id);$categoryName=$产品->getCategory ()->getName ();/ /……}
在本例中,首先查询a产品
对象基于产品的id
.的查询只是该产品的数据和补水美元的产品
对象使用该数据。等会儿,等你打电话的时候产品- > getCategory()——> getName ()
,教义默默地进行第二次查询,以找到类别
这和这个有关产品
.它准备美元的类别
对象并将它返回给你。
重要的是,您可以很容易地访问产品的相关类别,但直到您请求类别时,类别数据才会实际检索到(即它是“惰性加载”)。
您也可以向其他方向查询:
1 2 3 4 5 6 7 8 9 10
公共函数showProductAction($id){$类别=$这->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 /实体公共函数findOneByIdJoinedToCategory($id){$查询=$这->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
公共函数showAction($id){$产品=$这->getDoctrine ()->getRepository (“AcmeStoreBundle:产品”)->findOneByIdJoinedToCategory ($id);$类别=$产品->getCategory ();/ /……}
更多有关关联的资料
本节介绍了一种常见的实体关系类型,即一对多关系。对于如何使用其他类型的关系(例如;一对一的
,多对多
),请参阅Doctrine关联映射文档欧宝官网下载app.
请注意
如果使用注释,则需要在所有注释前加上ORM \
(如。ORM \ OneToMany
),并没有反映在Doctrine的文档中。欧宝官网下载app您还需要包括使用Doctrine\ORM\Mapping作为ORM;
声明,进口的ORM
注释前缀。
生命周期回调
有时,您需要在插入、更新或删除实体之前或之后执行某个操作。这些类型的操作被称为“生命周期”回调,因为它们是你需要在实体生命周期的不同阶段执行的回调方法(例如,实体被插入、更新、删除等)。
如果要对元数据使用注释,首先要启用生命周期回调。如果你使用YAML或XML进行映射,这是不必要的:
1 2 3 4 5 6 7 8
/ * * *@ORM() * \实体@ORM\ HasLifecycleCallbacks () * /类产品{/ /……}
现在,您可以告诉Doctrine在任何可用的生命周期事件上执行一个方法。例如,假设你想要设置一个创建
Date列到当前日期,仅当实体第一次被持久化(即插入)时:
- 注释
- YAML
- XML
1 2 3 4 5 6 7
/ * * *@ORM\ PrePersist * /公共函数setCreatedValue(){$这->创建了=新\ DateTime ();}
1 2 3 4 5 6
# src / Acme / StoreBundle /资源/配置/理论/ Product.orm.ymlAcme \ StoreBundle \实体\产品:类型:实体#……lifecycleCallbacks:prePersist:(setCreatedValue)
12 3 4 5 6 7 8 9 10 11 12
<!——src/Acme/StoreBundle/Resources/config/doctrine/Product.orm.xml——> .xml<!——……--><doctrine-mapping><实体的名字=“Acme \ StoreBundle \实体\产品”><!——……--><lifecycle-callbacks><生命周期回调类型=“prePersist”方法=“setCreatedValue”/>lifecycle-callbacks>实体>doctrine-mapping>
请注意
上面的示例假设您已经创建并映射了一个创建
属性(此处未显示)。
现在,在首次持久化实体之前,Doctrine将自动调用此方法和创建
字段将被设置为当前日期。
这可以对任何其他生命周期事件重复,包括:
preRemove
postRemove
prePersist
postPersist
preUpdate
postUpdate
postLoad
loadClassMetadata
有关这些生命周期事件的含义和生命周期回调的更多信息,请参阅Doctrine生命周期事件文档欧宝官网下载app
生命周期回调和事件监听器
注意setCreatedValue ()
方法不接收参数。生命周期回调总是如此,并且是有意为之的:生命周期回调应该是简单的方法,关注实体中的内部数据转换(例如,设置创建/更新字段,生成一个slug值)。
如果您需要做一些更重的提升——比如执行日志记录或发送电子邮件——您应该将一个外部类注册为事件侦听器或订阅者,并让它访问所需的任何资源。有关更多信息,请参见如何注册事件监听器和订阅者.
原则扩展:时间戳,可延迟等。
Doctrine非常灵活,并且有许多第三方扩展可用,允许您轻松地在实体上执行重复和常见的任务。这些包括诸如Sluggable,Timestampable,Loggable,可翻译,树.
有关如何查找和使用这些扩展的更多信息,请参阅有关的烹饪书文章使用通用原则的扩展.
理论领域类型
Doctrine提供了大量可用的字段类型。它们中的每一个都将PHP数据类型映射到您所使用的数据库中的特定列类型。Doctrine支持以下类型:
字符串
字符串
(用于较短的字符串)文本
(用于较大的字符串)
数字
整数
短整型
长整型数字
小数
浮动
日期和时间(使用一个DateTime在PHP中为这些字段创建对象)
日期
时间
datetime
其他类型
布尔
对象
(序列化并存储在CLOB
字段)数组
(序列化并存储在CLOB
字段)
要了解更多信息,请参阅Doctrine映射类型文档欧宝官网下载app.
场的选择
每个字段都可以应用一组选项。可用的选项包括类型
(默认为字符串
),的名字
,长度
,独特的
而且可以为空
.举几个例子:
- 注释
- YAML
- XML
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/** *一个长度为255的字符串字段,不能为null *(反映了"type", "length" *和" nullable " *选项的默认值)@ORM\列()* /受保护的$的名字;/** *长度为150的字符串字段,持续到“email_address”列*,并且有唯一的索引。* *@ORM\Column(name="email_address", unique=true, length=150) */受保护的$电子邮件;
12 3 4 5 6 7 8 9 10 11 12 13 14
字段:#长度为255的字符串,不能为空#(反映了"length"和*nullable*选项的默认值)# type属性在yaml定义中是必要的名称:类型:字符串#长度为150的字符串字段,持续到“email_address”列并且有一个唯一的索引。电子邮件:类型:字符串专栏:email_address长度:150独特:真正的
12 3 4 5 6 7 8 9 10 11 12
<!——字符串字段长度255不能为null(反映了"length"和*nullable*选项的默认值)类型属性在yaml定义中是必要的——><场的名字=“名称”类型=“字符串”/><场的名字=“电子邮件”类型=“字符串”列=“email_address”长度=“150”独特的=“真正的”/>
请注意
这里还有一些没有列出的选项。要了解更多细节,请参阅Doctrine属性映射文档欧宝官网下载app
控制台命令
Doctrine2 ORM集成在学说
名称空间。要查看命令列表,可以不带参数地运行控制台:
1
$ PHP应用/控制台
将打印出可用命令的列表,其中许多以原则:
前缀。命令可以找到关于这些命令(或任何Symfony命令)的更多信息ob娱乐下载帮助
命令。的详细信息原则:数据库:创建
任务,运行:
1
$ PHP应用/控制台帮助原则:数据库:创建
一些值得注意或有趣的任务包括:
原则:ensure-production-settings
-检查当前环境是否为生产环境有效配置。这应该总是在刺激
环境:1
$ PHP应用/控制台原则:ensure-production-settings——no-debug——env=prod
谨慎
别忘了加上
——没有调试
开关,因为调试标志总是设置为true,即使环境设置为刺激
.原则:映射:导入
允许Doctrine反省现有的数据库并创建映射信息。有关更多信息,请参见如何从现有的数据库生成实体.原则:映射信息
-告诉你所有的实体,Doctrine是知道的,以及是否有任何基本的错误与映射。原则:查询:dql
而且原则:查询:sql
-允许您直接从命令行执行DQL或SQL查询。
请注意
要将数据fixture加载到数据库中,需要具有DoctrineFixturesBundle
包安装。要了解如何做到这一点,请阅读文档中的“DoctrineFixturesBundle”条目。欧宝官网下载app
提示
本页展示了如何在控制器中使用Doctrine。您可能还希望在应用程序的其他地方使用Doctrine。的getDoctrine ()方法返回学说
服务,您可以在其他地方以同样的方式使用它,将其注入到您自己的服务中。看到服务容器有关创建自己的服务的更多信息。