你对Doctrine ORM有什么经验?

32

你对doctrine有什么经验?我不太喜欢ORM,通常只使用基本的db抽象层,例如adodb。

但我理解所有概念和好处。所以当需要ORM的项目出现时,我想试试其中一个ORM框架。

我必须在doctrine和propel之间做出决定,所以我选择了doctrine,因为我不想处理phing要求。

我不知道我做错了什么。我以正确的心态进入。而且我绝不是“初级”php小子。但我一直在与系统作斗争。有很多文档,但感觉都有点杂乱。像YAML到db表创建这样的简单东西就无法工作,甚至没有错误或任何提示。许多其他东西需要调整才能正常工作。

也许我在这里犯了一些愚蠢的新手假设,一旦我找出来它是什么,我就会有“啊哈”时刻。但现在我完全讨厌这个系统。

是否有人可以给出一些提示,或者指向有关此主题的良好资源,或者一些权威的网站/人士?或者只是推荐另一个ORM框架,可以“只需工作”?


13
哇塞!一个古老而发人深省的问题,有成千上万的浏览量和数十个回答,却没有被“无生产力问题”群体关闭!满分! - Theodore R. Smith
他们最终还是追上了它,只是花了一些时间。 - Dennis
@TheodoreR.Smith提出的“发人深省的问题”和标题中所述的“你的经验”都完全描述了根据Stack Overflow模型所不希望的东西。我同意这很有趣,也很好玩,但是如果没有主观性,就无法回答,因此可能会给出潜在的不良建议。 - James
对我来说,这往往是不直观/违反常理的,过于复杂,并且解释不清。我发现文档只提供了一些关于简单用法的基本解释,比如在函数使用中指定变量而没有描述这些变量或其可行格式可能是什么,然后让我猜测我可以做什么,即使它可能非常强大和灵活。这意味着我不得不进行试错或跟踪供应商代码。文档不应该让我做这两件事情,它们应该是完全可选和补充的。即便如此,它可能描述不足,这就是我现在的困境。 - SteveExdia
12个回答

48

我有复杂的感受。我只是因为SQL非常容易验证而成为了SQL方面的专家。你可以快速测试SELECT语句,直到得到正确的结果。而且重构也很简单。

在Doctrine或任何ORM中,抽象层次如此之多,以至于几乎看起来像强迫症(OCD)。在我的最新项目中,我尝试使用Doctrine时遇到了几个难题。花了我好几天时间才找到一个解决方案,而这是我知道在SQL中只需要几分钟就能写出来的东西。真是太令人沮丧了。

我有些不满。SQL社区非常庞大,但Doctrine的社区/支持却微不足道。当然,你可以查看源代码并尝试弄清楚...那些问题需要花费数天时间才能解决。

底线是:如果没有计划长时间自学,请勿尝试使用Doctrine或任何ORM。


1
如果你能够在SQL中更快地完成某个特定任务,那么你可以使用Doctrine的原生SQL命令http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/native-sql.html,并将Doctrine保留用于简单任务。 - Craig
我使用Doctrine的经验在多个主要版本中都是一样的,但它比我使用过的大多数其他ORM更加困难。它本可以写得直观一些,附带足够的格式规范和示例,但它似乎经常被写成好像我和作者处于同样的思维框架和参考状态。 - SteveExdia

15
我认为 mtbikemike 很好地总结了这个问题:“我花了几天时间才找到一个解决方案,而其实我知道我可以在几分钟内用 SQL 写出来。” 这也是我的经历。慢应用程序开发(SAD)是肯定的。更不用说丑陋的代码和无处不在的限制了。本应只需要几分钟完成的事情需要花费数天,通常更复杂的任务需要数小时或数天才能完成,但却根本无法做到(或者不值得花费时间)。产生的代码更加冗长和晦涩难懂(因为我们真的需要另一种查询语言 DQL 来使事情变得更不可读)。奇怪的错误无处不在,大部分时间都花在了寻找错误、克服限制和解决问题上。Doctrine(我只使用过 v2.x 版本)就像是在做徒劳的练习,没有任何好处。它是我当前系统中最受人厌恶的组件,也是唯一存在巨大问题的组件。在进入一个新系统时,我总是在数据库和实体类之间反复搜索,试图弄清楚代码中不同位置的名称是否正确。这是一场彻底的噩梦。
我看不到 Doctrine 的任何优点,只有缺点。我不知道它为什么存在,每天都希望它不要存在(至少在我的项目中)。

1
这在2015年仍然是您使用Doctrine的经验吗? - Dennis
优点 - 自动将 SELECT 转换为您的 DomainObject;自动生成模式并进行验证。 - Dennis
@Dennis 我怀疑。如果他仍然为每个小任务/网站手写代码 - 从数据库转换为对象甚至只使用数组...我真的怀疑他会这样做。 - idchlife
@Dennis 在 Doctrine 中的验证?ò_O 我认为你是在混淆 Symfony? - Kamafeather
@Kamafeather,我的意思是以一种非官方的方式进行验证。我经常使用传统表格,并且有时使用手写的 SQL 进行表格修改。然后,我运行 Doctrine 模式工具来查看是否建议更改任何内容。如果没有建议更改,那么它会给我一些验证,证明我编码正确。或者,我在实体中进行修改,让 Doctrine 做 SQL 工作。这在某种程度上跳过了验证,但同时也让我相信 SQL 将以正确的方式完成。 - Dennis
啊,这让我想起了Doctrine在函数调用、参数和DQL格式化方面采取的“魔法”单词转换和“快捷方式”,这让我很恼火。如果它遵循一致+直观的格式,那么这是可以接受的,但不同的上下文处理过于不一致和不必要。幸运的是,聪明的IDE有所帮助,但仍然很难进行检查,特别是在与Symfony一起使用时,它也会陷入自己的类似上下文不一致的陷阱中。只是有太多的名称魔法转换...就像Fabien经常说的那样,保持简单。 - SteveExdia

9
We have been using Propel with Symfony for 2 years and Doctrine with Symfony for more than 1 year. I can say that moving to ORM with MVC framework was the best step we've made. I would recommend sticking with Doctrine even though it takes some time to learn how to work with it. In the end, you'll find your code more readable and flexible.
If you're searching for some place where to start, I would recommend Symfony Jobeet tutorial http://www.symfony-project.org/jobeet/1_4/Doctrine/en/ (chapters 3, 6 covers the basics) and of course Doctrine documentation.
As I wrote above we have been using Doctrine for some time now. To make our work more comfortable we developed a tool called ORM Designer (www.orm-designer.com) where you can define DB model in a graphical user interface (no more YAML files :-), which aren't btw bad at all). You can find there also some helpful tutorials.

ORM Designer非常棒。我希望它是免费的:D - knagode
6
这似乎是推销员的回答。“是的,使用Doctrine2,然后使用我们自己的ORM Designer”,完全没有偏见。 - AntonioCS
我还没有检查过你的工具,但我完全认为拥有一个数据库的图形用户界面并不强制要求你真正知道你在做什么。对于简单的任务,如在CMS上管理内容,UI是可以接受的。然而,对于像数据库这样的复杂事物,学习基础知识并应用它们是必要的。SQL非常纯净、简单和直观。为什么要使用中间件?在以下某一点上,它最终会比原始版本差:文档、功能、速度、社区。 - agoldev

6

Propel和Doctrine使用PDO。PDO在Oracle数据库中存在许多未解决的bug,所有这些bug都与CLOB字段有关。如果您正在使用Oracle,请在开始新项目之前请注意此问题。这些bug已经存在多年了。当使用Oracle和CLOBs时,Doctrine和PDO会崩溃。


6

我的经验与你类似。我刚开始使用Doctrine,从未使用过Propel。然而,我对Doctrine非常失望。它的文档很糟糕。组织不良,相当不完整。


1
阿门。这份文档让我想要抠出自己的眼睛。而 Freenode IRC 社区也不是很有帮助。我发现,在继承使用 Doctrine 的项目时,如果被错手操作,它可能会成为一种大规模破坏性武器。 - ficuscr

5

在2015年使用Doctrine 2.5。一切看起来都很顺利,直到我想要使用两个实体(在JOIN中)。[现在掌握了DQL之后情况更好了]

好处:

  • 为我生成SQL
  • 使用外键和引用完整性
  • 默认生成InnoDB
  • 使用doctrine命令行工具对SQL进行更新

可以接受的部分:

  • 需要超级注意实体名称和映射,以及如何将实体映射到实际表格

不足之处:

  • 需要花费大量时间-学习查询构建器的自定义API,或者弄清楚如何执行简单的JOIN,并且想知道是否有更好的技术可用。如果您想要执行面向对象的查询,似乎必须编写自定义函数以执行简单的JOIN。
  • [对以上第一印象的更新]-我选择使用DQL,因为它最类似于SQL

我认为这个工具在概念上非常棒,但是要适当地执行它需要开发人员花费很多时间。我想用它来生成实体SQL,但然后再使用PDO进行实际输入/输出。只是因为我还没有学会如何使用SQL进行外键和引用完整性。但是,学习这些似乎比学习Doctrine的内部细节要容易得多,即使只是像实体等效于JOIN这样的简单内容。

现有项目中的Doctrine

我(刚开始)使用Doctrine开发现有项目上的新功能。因此,不是为该功能添加新的mysql表格,而是添加实体(使用Doctrine模式生成创建了表格)。在我更好地了解它之前,我保留了不使用Doctrine的现有表格的选项。

如果我要在现有表格上使用它,我将首先……清理表格,包括:

  • 添加id列作为主/代理密钥
  • 使用InnoDb/支持事务的表格
  • 适当地将其映射到实体
  • 运行Doctrine验证工具(doctrine orm:validate-schema

这是因为Doctrine对您的表格做出了某些假设。并且因为您本质上将通过代码来驱动您的表格。因此,您的代码和表格必须尽可能地达成1:1的协议。因此,一般情况下,Doctrine不适用于任何“自由形式”的表格。

但在一些情况下,只有一些小细节不被考虑,比如你的实体中可能会存在额外的列(我认为除非你要求它,否则Doctrine不会进行检查)。因此,你必须构造查询语句时需要知道自己能够做什么。例如,当你请求一个“实体”时,Doctrine会按照列名具体请求实体的所有字段。如果你的实际数据库架构包含更多的列名,我认为Doctrine也不会介意(我已经通过在我的架构中创建额外的列进行验证)。
所以,使用Doctrine是可能的,但是我建议小心谨慎地开始。首先,你需要将表格转换为支持事务和具有代理索引(主键)。对于像外键和引用完整性这样的内容,你需要与Doctrine一起修改您的实体并使其完全匹配。你可能需要让Doctrine重建架构以使用自己的索引名称,以便它可以正确使用FK和RI。实质上,你正在放弃对数据库的一些控制权,并且我认为它必须以自己的方式了解架构(例如能够使用自己的索引名称等)。

你只是用它来构建新项目吗?我的意思是,首先使用Doctrine创建数据库。我是在询问关于在现有(“手工制作”的)数据库上使用Doctrine的意见,这些数据库不一定格式良好(没有主键的表,应该实际上是两个表的大表等)。Doctrine对这种情况有多适合? - Foo Bar
1
我已经在答案中添加了我的回复。 - Dennis
谢谢。我处于无法更改表的情况下(不幸的是,我必须将数据从一个给定的数据库迁移到另一个具有不同模式的给定数据库)。所以我认为Doctrine不是正确的工具。而仅使用Doctrine进行数据库连接,但执行原始SQL则违背了Doctrine的目的。 - Foo Bar
真实的。除了使用原始的SQL,您将使用原始的DQL(处理实体而不是列名的语言)。您还可以使用QueryBuilder来构建查询。但是,如果您的表不支持事务,则可能会遇到多个问题。我可以看到在使用启用事务的表时使用Doctrine,其中您使用DQL或QueryBuilder和Doctrine的各种设施,如参数化查询等。但是,在整个过程中,您可能会遇到各种小问题,例如无法首先控制数据库。 - Dennis
1
感谢您指出运行Doctrine验证工具(doctrine orm:validate-schema)的方法,这对我很有帮助。+1 - surfmuggle
显示剩余2条评论

5
我在一个中等规模的项目中使用Doctrine,需要从我不拥有的预先存在的数据库中工作。它提供了很多内置功能,但我有一个主要的抱怨。
由于我必须从数据库生成我的模型而不是反过来,因此我的模型与数据库非常接近:字段名称与数据库列非常相似,为了得到对象,您必须查询本质上是sql(我把那段代码放在哪里,以及如何测试它?)。
最终,我不得不编写一个复杂的Doctrine包装器,让我怀疑是否更容易只使用旧的dao/model方法并将Doctrine排除在外。对此仍有争议。祝好运!

我们有一个现有的数据库,我们中的一些人想在2020年在mysql 5.6和Windows上使用Doctrine。您今天的裁决是什么? - surfmuggle

4
在2016年使用Doctrine ORM,具有大约2-2.5年的经验。
固有的不一致性。
SELECT i, p            
FROM \Entity\Item i
JOIN i.product p
WHERE ...

假设实体是ItemProduct。它们通过Item.product_id连接到Product.id,而Product包含我们想要与Item一起显示的Product.model
以下是使用上述SQL检索相同“product.model”(产品型号)的数据库数据,但变化的是SQL参数:
//SELECT i, p
$ret[0]->getProduct()->getModel();          

//SELECT i as item, p as product
$ret[0]['item']->getProduct()->getModel();  

//SELECT i as item, p.model as model
$ret[0]['model'];                           

我要表达的观点是:
根据你如何编写DQL / ORM SELECT语句,输出ResultSet结构可能会发生很大变化。从对象数组到对象的关联数组,再到关联数组,取决于您想要的选择方式。想象一下,如果您必须更改SQL,然后想象一下必须回到代码并重新执行与读取结果集中的数据相关的所有代码。哎呀!即使只有几行代码,您也依赖于结果集的结构,并没有完全解耦/通用标准。
Doctrine擅长的地方:
在某些方面,它消除了处理SQL、制作和维护自己的表格的工作。它并不完美。有时候会失败,您不得不进入MySQL命令行并键入SQL来调整事情,使Doctrine和您感到满意,让Doctrine看到列类型是有效的,并且您对列类型感到满意。您不必定义自己的外键或索引,这是由它自动完成的。
Doctrine不擅长的地方:
每当您需要将任何高级SQL翻译为DQL / ORM时,您可能会遇到困难。除此以外,您还可能会处理像上面那样的不一致性。
最后的想法:
我喜欢Doctrine为我创建/修改表格并将表格数据转换为对象以及将其持久化回来,以及使用准备好的语句和其他检查和平衡,使我的数据更安全。我喜欢感觉Doctrine从PHP面向对象接口中处理持久存储,我得到了这种刺痛的感觉,认为我的数据是我的代码的一部分,并且ORM负责与数据库交互的脏工作。数据库感觉更像一个本地变量,我已经意识到,如果您照顾好自己的数据,它会回报您。
我讨厌Doctrine的不一致性和陡峭的学习曲线,以及在我知道如何编写SQL时必须查找专有的DQL语法。 SQL知识是随时可用的,DQL没有那么多的专家(与SQL相比)可以在遇到困难时帮助您积累知识体系。

1
很遗憾,我在2017年并没有经常使用Doctrine,尽管它仍然存在于代码库中。主要是因为我的基准测试表明MySQLi速度,而Doctrine速度。我的当前代码库大部分是使用约1200个纯SQL语句编写的,现在加上了约55个Doctrine语句。在某些用例中,Doctrine确实可以消除复杂性,例如当我更新实体时,不必跟踪更改的内容,当我使用flush时,Doctrine会处理这些内容。 - Dennis
1
我已经将MySQL访问方式制作得像Doctrine一样,因此几乎不管我使用哪个,语法都非常相似,我可以轻松地使用SQL。因此,我一直在使用mysqli并为其编写了自己的包装器。 - Dennis
1
我的大多数SQL语句都有2到6个JOIN子句,还有一些奇怪的边缘情况,我不想在Doctrine中处理它们。在Doctrine中处理它们需要“学习和维护的额外事项”,而且这并不容易。 - Dennis
2
在2019/2020年,大部分情况下避免使用Doctrine,改用MySQLi。我仍然在一些存储库类中使用与Doctrine相关的代码,但最近没有编写新的Doctrine代码。 - Dennis

4
有点晚了,但是让我也来谈一下我的看法。我将和 Laravel 建立联系,因为这是我使用的框架。

Active Record vs. Data Mapping vs. Proper OOP

Active Record 是 Laravel 和许多其他框架所喜欢的东西。它可能非常适合简单的应用程序,并且可以为琐碎的 DB 管理节省时间。然而,从面向对象编程的角度来看,它是一个纯粹的反模式。关注点分离(SoC)被破坏了。它在模型属性和 SQL 列名之间创建了耦合。对于扩展和未来的更新来说是可怕的。

随着您的项目的增长(是的,它会增长!),ActiveRecord 将越来越令人头疼。甚至不要想着轻松更新 SQL 结构。记住,在 PHP 代码中到处都是列名。
我曾经接手一个旨在成为相当大的项目。我看到了 ActiveRecord 的局限性。我静静地坐了三个星期,使用数据映射器重写了所有内容,将数据库与上层分开。
现在,回到 Data Mapper为什么我没有选择 DoctrineData Mapper 的主要思想是将数据库与代码分离。这是从面向对象编程的角度来看正确的方法。关注点分离规则!我详细审查了 Doctrine,并且立即不喜欢几个方面。
  • 映射。为什么在世界上有人会使用注释作为命令?我认为这是一种极其糟糕的做法。为什么不只使用一个 PHP 类来存储映射关系呢?
  • Yaml 或 XML 用于映射。再次,为什么?为什么浪费时间解析文本文件,而不是使用常规的 PHP 类。此外,类可以扩展、继承、包含方法,而不仅仅是数据。等等。
  • 如果我们有一个映射器和一个携带数据的模型,那么应该是映射器存储模型。像 $product->save() 这样的方法就不好。模型处理数据,它不应该关心将任何内容保存到 DB 中。这是非常紧密的耦合。如果我们花时间构建映射器,那么为什么不使用 $mapper->save($product)。根据定义,应该是映射器知道如何保存数据。
Doctrine 或 Eloquent 等工具在开始时可以节省时间,毫无疑问。但是这里有一个棘手的问题需要每个人自己回答。在 /开发时间/未来更新/价格/简单性/遵循面向对象编程原则/之间找到正确的平衡点是什么?最终,这取决于您恰当地回答和决定。 我自己的 DataMapper 而不是 Doctrine 最终,我开发了自己的 DataMapper,并已经将其用于我的几个小项目中。它非常好用,易于扩展和重用。大多数时候,我们只需设置参数,而不需要新代码。
以下是关键原则:
  • 模型(Model)保存数据,类似于Laravel的模型。以下示例变量$model适用于以下示例。
  • 模型映射(ModelMap)包含一个字段,将模型的属性映射到SQL数据库表的列上。ModelMap知道表名、id等信息。它知道哪些属性应该转换为JSON,哪些属性应该被隐藏(例如deleted_at)。该模型映射还包含具有相同名称的列的别名(连接的表)。以下示例变量:$modelMap
  • 模型数据映射器(ModelDataMapper)是一个类,在控制器中接受模型(Model)模型映射(ModelMap),并提供store/getById/deleteById功能。您只需调用$modelMapper->store($model)即可。
  • 基本数据映射器还处理分页、搜索能力、将数组转换为JSON,添加时间戳,检查软删除等等。对于简单的用法,基本数据映射器已足够。对于任何更复杂的情况,可以很容易地通过继承来扩展它。

2
我很喜欢你从抽象角度出发的详细回答,而不是抱怨一个使用案例。你愿意分享你的自定义数据映射器吗?我已经在使用Doctrine了,但我喜欢看看其他选择,你的解决方案听起来很有趣。 - Fodagus
1
@Fodagus 谢谢!我不介意分享它,但是我一直很忙,没有时间将其制作成独立的git包。我一直在从一个项目中复制到另一个项目中,这不是一个好的长期解决方案 :) 我计划在今年创建一个git包。查看我的个人资料并与我联系,我可以与您分享一些想法,也许还有一些代码。 - Peter Matisko
1
@PeterMatisko,你的数据映射器有什么更新吗?谢谢。 - reformed
@reformed,我已经将它作为一个独立的包,但是目前没有太多的文档。通过LinkedIn或电子邮件与我联系,我可以分享给你。我的网站上有我的联系方式。 - Peter Matisko

3
在对PHP的各种ORM库进行了一些研究后,我决定使用PHP ActiveRecord(请参见)。我的决策基于该库配置少或无需配置、轻量级特性以及缺乏代码生成。Doctrine对于我的需求来说过于强大;PHP ActiveRecord所不能做的事情可以在我的包装层中实现。我建议花点时间审查一下你在ORM方面的要求,看看像PHP ActiveRecord这样的简单库是否提供你需要的功能,或者自己编写一个活动记录实现是否更好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接