数据库和教义ORM

编辑该页面

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

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

数据库和教义ORM

截屏视频

你喜欢视频教程?检查教义视频系列

欧宝娱乐app下载地址Symfony提供了你需要的所有工具在您的应用程序由于使用数据库学说最好的一组PHP库与数据库工作。这些工具还支持关系数据库MySQL和PostgreSQL和NoSQL数据库如MongoDB。

数据库是一个广泛的话题,所以文档分为三篇文章:欧宝体育电话

  • 本文解释了推荐的方式关系数据库在Sy欧宝娱乐app下载地址mfony应用程序;
  • 其他的文章如果你需要低级访问执行原始SQL查询关系数据库(类似于PHP的PDO);
  • DoctrineMongoDBBundle文档如果你正在与MongoDB数据库

安装原则

首先,安装原则支持通过orm欧宝娱乐app下载地址Symfony包,以及MakerBundle,这将有助于生成一些代码:

1 2
美元作曲家需要symfony / or欧宝娱乐app下载地址m-pack美元作曲家要求- dev symfony / ma欧宝娱乐app下载地址ker-bundle

配置数据库

数据库连接信息存储为一个环境变量DATABASE_URL。发展,你会发现和定制这里面.env:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
# .env(或覆盖在.env DATABASE_URL。当地以避免提交您的更改)#定制这一行!DATABASE_URL = " mysql: / / db_user: db_password@127.0.0.1:3306 / db_name ? serverVersion = 5.7 " #使用mariadb: DATABASE_URL =“mysql: / / db_user: db_password@127.0.0.1:3306 / db_name ? serverVersion = mariadb-10.5.8 " #使用sqlite: # DATABASE_URL = " sqlite: / / / % kernel.project_dir % / var /应用程序。db " #使用postgresql: # DATABASE_URL = " postgresql: / / db_user: db_password@127.0.0.1:5432 / db_name吗?serverVersion = 11 charset = utf8 " #使用oracle: # DATABASE_URL = " oci8: / / db_user: db_password@127.0.0.1:1521 / db_name”

谨慎

如果用户名、密码,主机或数据库名称包含任何字符被认为是一个URI(如特殊+,@,美元,#,/,:,*,!),您必须对它们进行编码。看到RFC 3986保留字符或使用的完整列表urlencode函数进行编码。在这种情况下,你需要删除解决:前缀的配置/包/ doctrine.yaml为了避免错误:url: ' % env(解决:DATABASE_URL) % '

现在你的连接参数设置,教义可以创建db_name数据库:

1
美元php bin /控制台学说:数据库:创建

有更多的选择配置/包/ doctrine.yaml您可以配置,包括你server_version(如5.7如果你使用MySQL 5.7),这可能会影响到原则的功能。

提示

还有许多其他学说的命令。运行php bin /控制台列表教义看到一个完整的列表。

创建一个实体类

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

您可以使用:实体命令你需要创建这个类和任何字段。命令将会问你一些问题,回答像做如下:

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
美元php bin /控制台:实体类名的实体来创建或更新:>产品新属性名称(按<返回>停止添加字段):>名称字段类型(输入?查看所有类型)[string]:[255] >字符串字段长度:> 255这个字段可以为空数据库(可以为空)(yes / no)[不]:>没有新的属性名(按<返回>停止添加字段):>价格领域类型(输入?查看所有类型)[string]: >整数这一领域能空吗数据库(可以为空)(yes / no)[不]:>没有新的属性名(按<返回>停止添加字段):>(按回车键完成)

1.3

的互动行为:实体命令是在MakerBundle 1.3中引入的。

哇!你现在有一个新的src /实体/ Product.php文件:

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
/ / src /实体/ Product.php名称空间应用程序\实体;使用应用程序\存储库\ProductRepository;使用学说\ORM\映射作为ORM;/ * * *@ORM\实体(repositoryClass = ProductRepository::类)* /产品{/ * * *@ORM* \ Id ()@ORM* \ GeneratedValue ()@ORM\列(类型=“整数”)* /私人美元id;/ * * *@ORM\列(类型=“字符串”,长度= 255)* /私人美元的名字;/ * * *@ORM\列(类型=“整数”)* /私人美元价格;公共函数getId():哦?int{返回美元- >id;}/ /……getter和setter方法}

请注意

困惑为什么价格是整数?不要担心,这只是一个例子。但是,存储价格为整数(例如100 = 1美元)可以避免舍入的问题。

请注意

如果您正在使用一个SQLite数据库,您将看到以下错误:PDOException: SQLSTATE [HY000]:一般错误:1不能添加NOT NULL列默认值为空。添加一个nullable = true可以选择的描述属性来解决这个问题。

谨慎

有一个767字节的索引键限制前缀当使用InnoDB表在MySQL 5.6和更早的版本。255个字符长度和字符串列utf8mb4编码超过极限。这意味着任何类型的列字符串独特= true必须设置它的最大长度190年。否则,你将会看到这个错误:“PDOException SQLSTATE[42000]:语法错误或访问违例:1071指定的密钥太长;马克斯关键长度是767字节”

这类被称为一个“实体”。很快,你就可以保存和查询产品对象产品表在数据库中。每个属性的产品实体可以被映射到表的一列。这通常是用注释:@ORM \…你在上面看到的评论每个属性:

:实体命令是一个工具,使生活变得更加容易。但这是你的代码:添加/删除字段,添加/删除或更新配置方法。

学说支持各种各样的字段类型,每个都有自己的选择。看到一个完整的列表,请查看教义的映射类型文档欧宝体育电话。如果你想使用XML而不是注释,添加类型:xmldir:“% kernel.project_dir % / config /学说”在你的实体映射配置/包/ doctrine.yaml文件。

谨慎

小心不要使用SQL关键字保留作为表名或列名(如。集团用户)。看到学说的保留SQL关键字的文档欧宝体育电话如何摆脱这些细节。或者,改变表名@ORM \表(name = "集团")在类或配置的列名name = " group_name "选择。

迁移:创建数据库表/模式

产品类是配置完整,准备保存到产品表。如果你只是这个类定义,数据库实际上并不拥有产品表。添加它,您可以利用DoctrineMigrationsBundle已经安装了:

1
美元php bin /控制台:迁移

如果一切工作,您应当会看到类似这样的:

1 2 3 4
成功!下:审查新移民“迁移/ Version20211116204726。php”:用php bin /控制台运行迁移学说:迁移:迁移

如果你打开这个文件,它包含了SQL数据库需要更新!运行SQL,执行你的迁移:

1
美元php bin /控制台学说:迁移:迁移

这个命令执行所有迁移的文件还没有针对数据库运行。你应该上运行这个命令生产部署保持最新的生产数据库。

迁移和添加更多的字段

但是,如果您需要添加一个新的字段属性产品,就像一个描述吗?您可以编辑类来添加新属性。但是,您还可以使用:实体再次:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
美元php bin /控制台:实体类名的实体创建或更新>产品新的属性名(按<返回>停止添加字段):>描述字段类型(输入?查看所有类型)[string]: >文本这个字段可以为空数据库(可以为空)(yes / no)[不]:>没有新的属性名(按<返回>停止添加字段):>(按回车键完成)

这增加了新的描述财产和getDescription ()setDescription ()方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
/ / src /实体/ Product.php/ /……类产品{/ /……+ / * *+ * @ORM \列(type = "文本")+ * /+私人美元描述;/ / getDescription () & setDescription},还添加了()

新的属性映射,但它并不存在产品表。没问题!生成一个新的迁移:

1
美元php bin /控制台:迁移

这一次,生成的文件中的SQL会看起来像这样:

1
改变产品添加描述量变

迁移系统聪明的。比较你的所有实体的当前状态数据库并生成所需的SQL同步!像以前一样,执行你的迁移:

1
美元php bin /控制台学说:迁移:迁移

这样只会执行一个新移民文件,因为DoctrineMigrationsBundle知道第一个迁移已经执行。在幕后,它管理migration_versions表来跟踪。

每次你改变你的模式,运行这两个命令来生成迁移,然后执行它。一定要提交移民文件,当您部署执行它们。

提示

如果你喜欢手动添加新的属性,:实体命令可以生成getter和setter方法:

1
美元php bin /控制台:实体——再生

如果你做一些修改,并想再生所有getter / setter方法,还通过——覆盖

持久化对象到数据库

保存的时候了产品对象到数据库!让我们创建一个新的控制器实验:

1
美元php bin /控制台:控制器ProductController

控制器内部,您可以创建一个新的产品对象,设置数据,并将其保存:

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 /控制器/ ProductController.php名称空间应用程序\控制器;/ /……使用应用程序\实体\产品;使用学说\ORM\EntityManagerInterface;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;ProductController扩展AbstractController{/ * * *@Route(“/产品”,name = " create_product ") * /公共函数createProduct():响应{/ /可以通过$ this - >获取EntityManager getDoctrine ()/ /或者你可以添加一个参数行动:createProduct (EntityManagerInterface entityManager美元)美元entityManager=美元- >getDoctrine ()- >getManager ();美元产品=产品();美元产品- >setName (“键盘”);美元产品- >setPrice (1999年);美元产品- >setDescription (的人体工学和时尚!);/ /告诉学说你想(最终)保存产品(没有查询)美元entityManager- >persist (美元产品);/ /实际执行查询(即插入查询)美元entityManager- >冲洗();返回响应(“保存新产品id”美元产品- >getId ());}}

试一下!

http://localhost: 8000 /产品

恭喜你!你刚刚创建的第一行产品表。为了证明这一点,你可以直接查询数据库:

1 2 3 4
美元php bin /控制台学说:查询:sql“从产品选择*”#在Windows系统不使用Powershell,运行这个命令:# php bin /控制台学说:查询:sql SELECT *从产品”

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

  • 第18行$ this - > getDoctrine () - > getManager ()方法原则的实体管理器对象,该对象是最重要的原则。负责保存对象,获取对象从数据库中。
  • 行20在本节中,您实例化和工作美元的产品像其他普通PHP对象。
  • 第26行坚持(产品)打电话告诉学说“管理”美元的产品对象。这并导致一个查询的数据库。
  • 第29行冲洗()方法被调用,原则通过的所有对象的管理,看看他们是否需要被持久化到数据库中。在这个例子中,美元的产品对象的数据在数据库中不存在,所以实体管理器执行一个插入查询,创建一个新行产品表。

请注意

如果冲洗()调用失败,学说\ ORM \ ORMException异常。看到事务和并发性

不管你是创建或更新对象,工作流总是相同的:原则是足够聪明知道这应该插入或更新你的实体。

验证对象

Symf欧宝娱乐app下载地址ony的验证器重用原则元数据执行一些基本的验证任务:

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
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;使用应用程序\实体\产品;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;使用欧宝娱乐app下载地址\组件\验证器\验证器\ValidatorInterface;/ /……ProductController扩展AbstractController{/ * * *@Route(“/产品”,name = " create_product ") * /公共函数createProduct(ValidatorInterface美元验证器):响应{美元产品=产品();/ /这将引发一个错误:数据库中的列不可以为空美元产品- >setName ();/ /这将触发一个类型不匹配错误:一个整数美元产品- >setPrice (“1999”);/ /……美元错误=美元验证器- >validate (美元产品);如果(数(美元错误)>0){返回响应((字符串)美元错误,400年);}/ /……}}

虽然产品实体不定义任何显式的验证配置Sy欧宝娱乐app下载地址mfony将自查教义映射配置来推断一些验证规则。例如,考虑到的名字财产不能在数据库中,aNotNull约束自动添加到属性(如果它不包含约束已经)。

下表总结了教义之间的映射元数据和相应的验证约束Symfony自动添加:欧宝娱乐app下载地址

义属性 验证约束 笔记
nullable = false NotNull 需要安装PropertyInfo组件
类型 类型 需要安装PropertyInfo组件
独特= true UniqueEntity
长度 长度

因为表单组件以及API的平台内部使用验证器组件,所有的表单和web api也将自动受益于这些自动验证约束。

自动验证是一个不错的功能来提高你的生产率,但是它不能完全代替验证配置。你还需要添加一些验证约束以确保用户提供的数据是正确的。

从数据库中获取对象

获取一个对象的数据库更容易。假设你希望能够去/产品/ 1看到你的新产品:

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
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;使用应用程序\实体\产品;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;/ /……ProductController扩展AbstractController{/ * * *@Route(" /产品/ {id} " name = " product_show ") * /公共函数显示(int美元id):响应{美元产品=美元- >getDoctrine ()- >getRepository(产品::类)- >找到(美元id);如果(!美元产品){美元- >createNotFoundException (“没有发现产品id”美元id);}返回响应(“看看这个伟大的产品:”美元产品- >getName ());/ /或呈现模板/ /模板中,打印东西{{product.name}}/ /返回$ this - >渲染(产品/ show.html。嫩枝”,(“产品”= >产品美元));}}

另一种可能性是使用ProductRepository使用Symf欧宝娱乐app下载地址ony的自动装配和注入的依赖注入容器:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;使用应用程序\实体\产品;使用应用程序\存储库\ProductRepository;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;/ /……ProductController扩展AbstractController{/ * * *@Route(" /产品/ {id} " name = " product_show ") * /公共函数显示(int美元id,ProductRepository美元productRepository):响应{美元产品=美元productRepository- >找到(美元id);/ /……}}

试一下!

http://localhost: 8000 /产品/ 1

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

一旦你有一个存储库对象,你有许多辅助方法:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
美元存储库=美元- >getDoctrine ()- >getRepository(产品::类);/ /寻找单一产品的主键(通常是“id”)美元产品=美元存储库- >找到(美元id);/ /寻找一个产品的名字美元产品=美元存储库- >findOneBy ([“名字”= >“键盘”]);/ /或者找到的名字和价格美元产品=美元存储库- >findOneBy ([“名字”= >“键盘”,“价格”= >1999年]);/ /寻找匹配的多个产品对象名称、订购价格美元产品=美元存储库- >findBy ([“名字”= >“键盘”]、[“价格”= >“ASC”]);/ /寻找* *产品所有对象美元产品=美元存储库- >findAll ();

你也可以加入自定义方法更复杂的查询!更晚些时候数据库和教义ORM部分。

提示

呈现一个HTML页面时,web调试工具栏底部的页面将显示查询的数量和执行时间:

如果数据库查询的数量过高,图标会变黄,表明一些可能不是正确的。点击图标打开Symfony分析器,看看具体执行的查询。欧宝娱乐app下载地址如果您没有看到web调试工具栏,安装分析器欧宝娱乐app下载地址Symfony包通过运行这个命令:作曲家要求- dev symfony / pr欧宝娱乐app下载地址ofiler-pack

自动抓取对象(ParamConverter)

在许多情况下,您可以使用SensioFrameworkExtraBundle自动帮你查询!首先,安装包,以防你没有:

1
美元作曲家需要sensio赞助/ framework-extra-bundle

现在,简化你的控制器:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;使用应用程序\实体\产品;使用应用程序\存储库\ProductRepository;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;/ /……ProductController扩展AbstractController{/ * * *@Route(" /产品/ {id} " name = " product_show ") * /公共函数显示(产品美元产品):响应{/ /使用的产品!/ /……}}

就是这样!包使用{id}从路线查询产品id列。如果没有发现,生成一个404页的。

有很多选项可以使用。阅读更多关于ParamConverter

更新一个对象

一旦你获取一个对象从教条,你与它相同的与任何PHP交互模型:

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
/ / src /控制器/ ProductController.php名称空间应用程序\控制器;使用应用程序\实体\产品;使用应用程序\存储库\ProductRepository;使用欧宝娱乐app下载地址\组件\HttpFoundation\响应;/ /……ProductController扩展AbstractController{/ * * *@Route(" /产品/编辑/ {id} ") * /公共函数更新(int美元id):响应{美元entityManager=美元- >getDoctrine ()- >getManager ();美元产品=美元entityManager- >getRepository(产品::类)- >找到(美元id);如果(!美元产品){美元- >createNotFoundException (“没有发现产品id”美元id);}美元产品- >setName (“新产品的名字!”);美元entityManager- >冲洗();返回美元- >redirectToRoute (“product_show”,(“id”= >美元产品- >getId ()));}}

使用原则编辑现有产品包括三个步骤:

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

可以调用(entityManager - >保存美元产品),但它没有必要:学说已经“看”你的对象的变化。

删除一个对象

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

1 2
美元entityManager- >remove (美元产品);美元entityManager- >冲洗();

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

查询对象:存储库

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

1 2 3 4
/ /从一个控制器美元存储库=美元- >getDoctrine ()- >getRepository(产品::类);美元产品=美元存储库- >找到(美元id);

但是如果你需要一个更复杂的查询呢?当你生成实体:实体,命令生成一个ProductRepository类:

1 2 3 4 5 6 7 8 9 10 11 12 13 14
/ / src /仓库/ ProductRepository.php名称空间应用程序\存储库;使用应用程序\实体\产品;使用学说\\DoctrineBundle\存储库\ServiceEntityRepository;使用学说\持久性\ManagerRegistry;ProductRepository扩展ServiceEntityRepository{公共函数__construct(ManagerRegistry美元注册表){::__construct (美元注册表、产品::类);}}

当你获取存储库(即。- > getRepository(产品::类)),这是实际上的一个实例对象!这是由于repositoryClass在你的配置生成产品实体类。

假设您想查询所有产品对象大于一定的价格。添加一个新方法,这样您的存储库:

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
/ / src /仓库/ ProductRepository.php/ /……ProductRepository扩展ServiceEntityRepository{公共函数__construct(ManagerRegistry美元注册表){::__construct (美元注册表、产品::类);}/ * * *@return产品[]* /公共函数findAllGreaterThanPrice(int美元价格):数组{美元entityManager=美元- >getEntityManager ();美元查询=美元entityManager- >createQuery (“从App \实体\产品选择p p, p。价格>:价格按p。ASC的价格)- >setParameter (“价格”,美元价格);/ /返回一个产品对象数组返回美元查询- >getResult ();}}

字符串传递给createQuery ()可能看起来像SQL,但它是教义的查询语言。这允许您输入查询使用常见的查询语言,而是引用PHP对象(即中声明)。

现在,您可以在存储库中调用这个方法:

1 2 3 4 5 6 7 8
/ /从一个控制器美元minPrice=1000年;美元产品=美元- >getDoctrine ()- >getRepository(产品::类)- >findAllGreaterThanPrice (美元minPrice);/ /……

看到服务容器如何将存储库注入任何服务。

查询查询生成器

教义还提供了一个查询构建器,面向对象的方法来编写查询。建议使用此查询时动态构建基于PHP(即条件):

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日
/ / src /仓库/ ProductRepository.php/ /……ProductRepository扩展ServiceEntityRepository{公共函数findAllGreaterThanPrice(int美元价格bool,美元includeUnavailableProducts= false):数组{/ /自动知道选择产品/ /“p”是您将使用的别名查询美元qb=美元- >createQueryBuilder (“p”)- >(在哪里“p。价格>:价格的)- >setParameter (“价格”,美元价格)- >orderBy (“p.price”,“ASC”);如果(!美元includeUnavailableProducts){美元qb- >引入(“p。可用= TRUE”);}美元查询=美元qb- >getQuery ();返回美元查询- >execute ();/ /得到只有一个结果:/ /产品=美元查询- > setMaxResults (1) - > getOneOrNullResult ();}}

与SQL查询

此外,您可以直接使用SQL查询如果你需要:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/ / src /仓库/ ProductRepository.php/ /……ProductRepository扩展ServiceEntityRepository{公共函数findAllGreaterThanPrice(int美元价格):数组{美元康涅狄格州=美元- >getEntityManager ()- >getConnection ();美元sql=“从产品选择* p, p。价格>:价格按p。ASC的价格;美元支撑=美元康涅狄格州- >准备(美元sql);美元结果集=美元支撑- >executeQuery ([“价格”= >美元价格]);/ /返回一个数组的数组(即原始数据集)返回美元结果集- >fetchAllAssociative ();}}

使用SQL,您将回到原始数据,而不是对象(除非你使用NativeQuery功能)。

关系和联系

学说提供了所有你需要的功能来管理数据库(也称为协会)的关系,包括ManyToOne OneToMany, OneToOne和多关系。

信息,请参阅如何使用主义协会/关系

理论的扩展(Timestampable,可翻译等)。

主义社会创造了一些欧宝下载链接扩展来实现共同的需要等“createdAt属性的设置值时自动创建一个实体”。阅读更多关于可用原则扩展并使用StofDoctrineExtensionsBundle集成在您的应用程序。

这项工作,包括代码示例,许可下Creative Commons冲锋队3.0许可证。
欧宝娱乐app下载地址Symfony 5.3支持通过JoliCode