数据库和ORM原则

编辑本页

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

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

数据库和ORM原则

截屏视频

你更喜欢视频教程吗?请查看教义短片系列

ob娱乐下载Symfony提供了在应用程序中使用数据库所需的所有工具学说,是使用数据库的最佳PHP库集。这些工具支持关系数据库,如MySQL和PostgreSQL,也支持NoSQL数据库,如MongoDB。

数据库是一个广泛的主题,因此文档分为三篇文章:欧宝官网下载app

  • 本文解释了推荐的工作方式关系数据库在Syob娱乐下载mfony应用中;
  • 这是另一篇文章如果你需要低级访问对关系数据库执行原始SQL查询(类似于PHP)PDO);
  • DoctrineMongoDBBundle文档如果你在和MongoDB数据库

安装原则

首先,通过ormob娱乐下载Symfony包,以及MakerBundle,它将帮助生成一些代码:

1 2
作曲家需要symfony/orm-ob娱乐下载packComposer require——开发symob娱乐下载fony/maker-bundle

配置数据库

数据库连接信息存储为名为DATABASE_URL.对于开发,您可以在其中找到并定制它.env

12 3 4 5 6 7 8 9 10 11 12 13
# .env(或在.env中覆盖DATABASE_URL)。本地以避免提交您的更改)#自定义这一行!DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=5.7" # to use mariadb: DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name?serverVersion=mariadb-10.5.8" # to use sqlite: # DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db" # to use postgresql: # DATABASE_URL="postgresql://db_user:db_password@127.0.0.1:5432/db_name?serverVersion = 11 charset = utf8 "

谨慎

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

连接参数设置完成后,Doctrine可以创建db_name数据库为您:

1
PHP bin/控制台原则:数据库:创建

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

提示

还有许多其他的教义命令。运行PHP bin/控制台列表原则查看完整列表。

创建实体类

假设您正在构建一个需要显示产品的应用程序。甚至不考虑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
$ php bin/console make:entity要创建或更新的实体的类名称:> Product新属性名称(按<返回>停止添加字段):> name字段类型(输入?[string]: > string字段长度[255]:> 255该字段可以为空吗the database (nullable) (yes/no) [no]: > no返回>停止添加字段):> price字段类型(输入?查看所有类型)[string]: >整数这个字段可以为空吗the database (nullable) (yes/no) [no]: > no返回>停止添加字段):>(再次按enter键完成)

1.3

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

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

12 34 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\Column(type="string", length=255) */私人的名字/ * * *@ORM\列(类型=“整数”)* /私人价格公共函数getId(): ?int返回->id;}/ /……Getter和setter方法

请注意

为什么价格是整数?别担心:这只是一个例子。但是,将价格存储为整数(例如100 = $1 USD)可以避免舍入问题。

请注意

如果你使用的是SQLite数据库,你会看到以下错误:PDOException: SQLSTATE[HY000]:一般错误:1不能添加默认值为NULL的NOT NULL列.添加一个nullable = true选项。描述属性来修复问题。

谨慎

有一个索引键前缀的限制为767字节在MySQL 5.6及更早版本中使用InnoDB表时。字符串列,长度为255个字符utf8mb4编码超过了这个限制。这意味着任何类型的列字符串而且独特= true必须设置其最大值长度190.否则,你会看到这个错误:"[PDOException] SQLSTATE[42000]:语法错误或访问违规:1071指定的键太长;最大密钥长度为767字节

这个类称为“实体”。很快,您将能够将Product对象保存和查询到产品表。中的每个属性产品实体可以映射到该表中的列。这通常是通过注释完成的@ORM \…你在每个属性上面看到的注释:

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

Doctrine支持各种各样的字段类型,每种类型都有自己的选项。要查看完整的列表,请查看Doctrine的映射类型文档欧宝官网下载app.如果希望使用XML而不是注释,请添加类型:xml而且dir:“% kernel.project_dir % / config /学说”中的实体映射配置/包/ doctrine.yaml文件。

谨慎

注意不要使用保留的SQL关键字作为表或列的名称(例如。集团用户).看到学说的保留SQL关键字文档欧宝官网下载app有关如何逃脱这些的详细信息。或者,用更改表名@ORM \表(name = "集团")属性配置列名name = " group_name "选择。

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

产品类已完全配置,并准备保存到产品表格如果你只定义了这个类,你的数据库实际上没有产品表。要添加它,可以利用DoctrineMigrationsBundle,已安装:

1
PHP bin/控制台make:迁移

如果一切正常,你会看到如下内容:

成功!

接下来:查看新的迁移“migrations/Version20180207231217.php”,然后:使用php bin/console运行迁移原则:迁移:迁移

如果您打开此文件,它将包含更新数据库所需的SQL !要运行SQL,请执行迁移:

1
PHP bin/控制台原则:迁移:迁移

此命令执行尚未在数据库上运行的所有迁移文件。在进行部署时,应该在生产环境中运行此命令,以保持生产数据库的最新状态。

迁移&添加更多字段

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

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
$ php bin/console make:entity创建或更新实体的类名> Product新属性名(按<返回>停止添加字段):> description字段类型(输入?to see all types) [string]: > text该字段可以为空吗the database (nullable) (yes/no) [no]: > no返回>停止添加字段):>(再次按enter键完成)

这将添加新的描述财产和getDescription ()而且setDescription ()方法:

12 3 4 5 6 7 8 9 10 11 12 13 14
// src/Entity/Product.php…class Product{//…+ / * *+ * @ORM\Column(type="text")+ * /+ private $description;// getDescription() & setDescription()也被添加}

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

1
PHP bin/控制台make:迁移

这一次,生成的文件中的SQL将是这样的:

1
改变表格产品添加描述量变

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

1
PHP bin/控制台原则:迁移:迁移

这将只执行一个新的迁移文件,因为DoctrineMigrationsBundle知道第一次迁移已经在前面执行了。在幕后,它管理着一个migration_versions表来跟踪这个。

每次对模式进行更改时,运行这两个命令生成迁移,然后执行它。确保提交迁移文件并在部署时执行它们。

提示

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

1
PHP bin/控制台生成实体

如果你做了一些改变,想要重生所有Getter /setter方法也通过——覆盖

持久化对象到数据库

是时候节省了产品对象的数据库!让我们创建一个新的控制器来进行实验:

1
ProductController . php bin/控制台

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

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

试试吧!

http://localhost:8000/product

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

1 2 3 4
PHP bin/console原则:query:sqlSELECT * FROM product在没有使用Powershell的Windows系统上,执行以下命令:# php bin/console doctrine:query:sql "SELECT * FROM product"

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

  • 第18行$ this - > getDoctrine()——> getManager ()方法得到Doctrine的实体管理器客体,是《主义》中最重要的客体。它负责将对象保存到数据库,并从数据库中获取对象。
  • 行20方法的实例化和使用美元的产品对象,就像任何其他普通PHP对象一样。
  • 第26行坚持(产品)调用告诉Doctrine“管理”美元的产品对象。这并导致对数据库进行查询。
  • 第29行冲洗()方法时,Doctrine会查看它所管理的所有对象,以确定它们是否需要持久化到数据库中。在本例中,美元的产品对象的数据在数据库中不存在,因此实体管理器执行插入对象中创建新行产品表格

请注意

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

无论您是创建还是更新对象,工作流程总是相同的:Doctrine足够智能,可以知道它是应该INSERT还是UPDATE您的实体。

验证对象

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

虽然产品实体没有定义任何显式的验证配置, ob娱乐下载Symfony内省Doctrine映射配置来推断一些验证规则。例如,假设的名字财产不能是在数据库中,aNotNull约束自动添加到属性(如果它尚未包含该约束)。

下表总结了Doctrine元数据和Symfony自动添加的相应验证约束之间的映射:ob娱乐下载

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

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

这种自动验证是一个很好的特性,可以提高您的工作效率,但它不能完全取代验证配置。你还需要添加一些验证约束确保用户提供的数据是正确的。

从数据库中获取对象

从数据库中取回对象更加简单。假设你想要能够/产品/ 1要查看您的新产品:

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
/ / src /控制器/ ProductController.php名称空间应用程序控制器使用应用程序实体产品使用ob娱乐下载组件HttpFoundation响应/ /……ProductController扩展AbstractController/ * * *@Route("/product/{id}", name="product_show") */公共函数显示(intid响应产品->getDoctrine ()->getRepository(产品::类)->找到(id);如果(!产品){->createNotFoundException (“没有为id找到产品”id);}返回响应(“看看这个很棒的产品吧:”产品->getName ());//或呈现一个模板//在模板中,使用{{product.name}}打印内容//返回$this->渲染('product/show.html. 'Twig ', ['product' => $product]);}}

另一种可能是使用ProductRepository使用Symfob娱乐下载ony的自动装配和依赖注入容器注入:

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

试试吧!

http://localhost:8000/product/1

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

一旦你有了一个存储库对象,你就有了很多帮助方法:

12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21
存储库->getDoctrine ()->getRepository(产品::类);//通过主键(通常是"id")查找单个Product产品存储库->找到(id);//通过名称查找单个产品产品存储库->findOneBy ([“名字”= >“键盘”]);//或按名称和价格查找产品存储库->findOneBy ([“名字”= >“键盘”“价格”= >1999]);//查找多个与名称匹配的Product对象,按价格排序产品存储库->findBy ([“名字”= >“键盘”]、[“价格”= >“ASC”]);//查找*所有* Product对象产品存储库->findAll ();

您还可以添加自定义用于更复杂查询的方法!稍后会详细介绍数据库和ORM原则部分。

提示

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

如果数据库查询的数量太高,图标将变为黄色,表示可能有错误。单击图标打开Symfony Profiler并查看执行的确切ob娱乐下载查询。如果看不到web调试工具栏,请安装分析器ob娱乐下载Symfony包运行此命令:开发symfony/profiler-packob娱乐下载

自动抓取对象(ParamConverter)

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

1
Composer需要sensio/framework-extra-bundle

现在,简化你的控制器:

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

就是这样!bundle使用{id}从路由中查询产品id列。如果没有找到,则生成404页面。

您可以使用的选项还有很多。阅读更多有关ParamConverter

更新对象

一旦你从Doctrine中获取了一个对象,你就可以像与任何PHP模型一样与它交互:

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

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

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

可以调用entityManager - >保存(产品),但这不是必要的:Doctrine已经在“观察”你的对象的变化。

删除对象

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

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

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

查询对象:存储库

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

1 2 3 4
//从控制器内部存储库->getDoctrine ()->getRepository(产品::类);产品存储库->找到(id);

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

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

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

假设您希望查询大于某个价格的所有Product对象。为你的存储库添加一个新方法:

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
/ / src /仓库/ ProductRepository.php/ /……ProductRepository扩展ServiceEntityRepository公共函数__construct(ManagerRegistry注册表::__construct (注册表、产品::类);}/ * * *@return产品[]* /公共函数findAllGreaterThanPrice(int价格数组entityManager->getEntityManager ();查询entityManager->createQuery (SELECT p FROM App\Entity\Product WHERE p.price >:price ORDER BY p.price ASC->setParameter (“价格”价格);//返回Product对象数组返回查询->getResult ();}}

传递给的字符串createQuery ()可能看起来像SQL,但它确实是Doctrine查询语言.这允许您使用常见的查询语言输入查询,但引用PHP对象代替(即在声明)。

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

1 2 3 4 5 6 7 8
//从控制器内部minPrice1000产品->getDoctrine ()->getRepository(产品::类)->findAllGreaterThanPrice (minPrice);/ /……

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

使用查询生成器进行查询

原则还提供了查询构建器,一种面向对象的方式来编写查询。建议在动态构建查询(即基于PHP条件)时使用:

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

使用SQL查询

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

12 3 4 5 6 7 8 9 10 11 12 13 14 16 17 18 19 20 21
/ / src /仓库/ ProductRepository.php/ /……ProductRepository扩展ServiceEntityRepository公共函数findAllGreaterThanPrice(int价格数组康涅狄格州->getEntityManager ()->getConnection ();sql' SELECT * FROM product p WHERE p.price >:price ORDER BY p.price ASC '支撑康涅狄格州->准备(sql);支撑->执行([“价格”= >价格]);//返回数组的数组(即原始数据集)返回支撑->fetchAllAssociative ();}}

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

关系和关联

Doctrine提供了管理数据库关系(也称为关联)所需的所有功能,包括多对一、多对多、一对一和多对多关系。

有关详细信息,请参见如何处理教义协会/关系

原则扩展(可时间戳、可翻译等)

Doctrine社欧宝体育平台怎么样区创建了一些扩展来实现共同的需求,例如在创建实体时自动设置createdAt属性的值.阅读更多有关可用的理论扩展并使用StofDoctrineExtensionsBundle将它们集成到应用程序中。

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