数据库和ORM原则

编辑本页

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

本页的更新版本用于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

1 2 3 4 5 6 7
# .env(或在.env中覆盖DATABASE_URL)。本地以避免提交您的更改)#自定义这一行!DATABASE_URL="mysql://db_user:db_password@127.0.0.1:3306/db_name" #使用sqlite: # DATABASE_URL="sqlite:///%kernel.project_dir%/var/app.db"

谨慎

如果用户名、密码、主机或数据库名在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
/ / src /实体/ Product.php名称空间应用程序实体使用学说ORM映射作为ORM/ * * *@ORM实体(repositoryClass = \ App \ Repository \ ProductRepository) * /产品/ * * *@ORM\ Id *@ORM\ GeneratedValue *@ORM\列(类型=“整数”)* /私人id/ * * *@ORM\Column(type="string", length=255) */私人的名字/ * * *@ORM\列(类型=“整数”)* /私人价格公共函数getId()返回->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:迁移

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

成功!

接下来:查看新的迁移“src/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也将自动受益于这些自动验证约束。

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

4.3

在Symfony 4.3中添加了自动验证功能。ob娱乐下载

从数据库中获取对象

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

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
/ / src /控制器/ ProductController.php/ /……/ * * *@Route("/product/{id}", name="product_show") */公共函数显示id产品->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
/ / src /控制器/ ProductController.php/ /……使用应用程序存储库ProductRepository/ * * *@Route("/product/{id}", name="product_show") */公共函数显示id, 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

现在,简化你的控制器:

1 2 3 4 5 6 7 8 9 10 11
/ / src /控制器/ ProductController.php使用应用程序实体产品/ * * *@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 16 17 18 19 20 21
/ * * *@Route(" /产品/编辑/ {id} ") * /公共函数更新identityManager->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价格数组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
/ / src /仓库/ ProductRepository.php/ /……公共函数findAllGreaterThanPrice价格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 15 16 17 18
/ / src /仓库/ ProductRepository.php/ /……公共函数findAllGreaterThanPrice价格数组康涅狄格州->getEntityManager ()->getConnection ();sql' SELECT * FROM product p WHERE p.price >:price ORDER BY p.price ASC '支撑康涅狄格州->准备(sql);支撑->执行([“价格”= >价格]);//返回数组的数组(即原始数据集)返回支撑->fetchAll ();}

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

关系和关联

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

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

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

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

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