第八章-模型层内部(原则)
到目前为止,大部分讨论都集中在构建页面以及处理请求和响应上。但是web应用程序的业务逻辑主要依赖于它的数据模型。ob娱乐下载Symfony的默认模型组件基于对象/关系映射层。ob娱乐下载Symfony附带了两个最流行的PHP orm:推动而且学说.在symfob娱乐下载ony应用程序中,您可以访问存储在数据库中的数据并通过对象对其进行修改;您从未显式地寻址数据库。这保持了高度的抽象和可移植性。
本章解释如何创建对象数据模型,以及在Doctrine中访问和修改数据的方法。同时也体现了“主义”在交响乐中的整合。ob娱乐下载
提示
如果你想使用驱动而不是教义,请阅读附录A,因为它包含完全相同的信息,但用于驱动。
为什么使用ORM和抽象层?
数据库是关系型的。PHP 5和symfob娱乐下载ony是面向对象的。为了在面向对象上下文中最有效地访问数据库,需要一个将对象逻辑转换为关系逻辑的接口。正如在第1章中所解释的,该接口称为对象-关系映射(ORM),它由提供数据访问权并将业务规则保留在自身中的对象组成。
ORM的主要好处是可重用性,允许从应用程序的不同部分,甚至从不同的应用程序调用数据对象的方法。ORM层还封装了数据逻辑——例如,根据贡献的数量和这些贡献的受欢迎程度计算论坛用户评级。当一个页面需要显示这样的用户评分时,它只需调用数据模型的一个方法,而不需要考虑计算的细节。如果之后计算发生变化,您只需要修改模型中的评级方法,而应用程序的其余部分保持不变。
使用对象而不是记录,使用类而不是表,还有另一个好处:它们允许您向对象添加不一定与表中的列匹配的新访问器。例如,如果您有一个名为客户端
其中有两个字段first_name
而且last_name
,你可能只需要一个的名字
.在面向对象的世界中,只需向对象中添加新的访问器方法即可客户端
如清单8-1所示。从应用的角度来看,两者并无区别FirstName
,姓
,的名字
的属性客户端
类。只有类本身可以确定哪些属性对应于数据库列。
清单8-1 -访问器屏蔽了模型类中的实际表结构
公共函数getName(){返回这个美元->getFirstName().' '.这个美元->getLastName();}
所有重复的数据访问函数和数据本身的业务逻辑都可以保存在这样的对象中。假设你有一个ShoppingCart
你所在的班级项目
(都是对象)。要获得购物车中用于结帐的全部金额,可以编写一个自定义方法来封装实际计算,如清单8-2所示。
清单8-2 - Accessors屏蔽数据逻辑
公共函数getTotal(){总美元=0;foreach(这个美元->getItems()作为美元的项目){总美元+ =美元的项目->getPrice()*美元的项目->getQuantity();}返回总美元;}
在构建数据访问过程时,还需要考虑另一个重要问题:数据库供应商使用不同的SQL语法变体。切换到另一个数据库管理系统(DBMS)将迫使您重写为前一个数据库管理系统设计的部分SQL查询。如果您使用独立于数据库的语法构建查询,并将实际的SQL转换留给第三方组件,那么您可以轻松地切换数据库系统。这是数据库抽象层的目标。它迫使您使用特定的语法进行查询,并执行符合DBMS细节和优化SQL代码的肮脏工作。
抽象层的主要好处是可移植性,因为它使得切换到另一个数据库成为可能,即使是在项目中间。假设您需要为一个应用程序编写一个快速原型,但是客户还没有决定哪个数据库系统最适合他的需求。例如,您可以开始使用SQLite构建应用程序,然后在客户端准备决定时切换到MySQL、PostgreSQL或Oracle。只需更改配置文件中的一行,它就可以工作。
ob娱乐下载Symfony使用Propel或Doctrine作为ORM,他们使用PHP数据对象进行数据库抽象。这两个第三方组件都是由Propel和Doctrine团队开发的,它们被无缝集成到symfony中,您可以将它们视为框架的一部分。ob娱乐下载本章所述的它们的句法和惯例都经过了调整,使它们尽可能少地区别于symfony。ob娱乐下载
请注意
在symfob娱乐下载ony项目中,所有应用程序共享相同的模型。这就是项目级别的全部意义:重新组合依赖公共业务规则的应用程序。这就是模型独立于应用程序的原因,并且模型文件存储在lib /模型/
目录在项目的根目录。
ob娱乐下载Symfony的数据库模式
为了创建symfony将使用的数据对象模型,您需要将数据库拥有的任何关系模型转换为对象数据模ob娱乐下载型。ORM需要关系模型的描述来进行映射,这称为模式。在模式中,定义表、表之间的关系以及表列的特征。
ob娱乐下载Symfony的模式语法使用YAML格式。的schema.yml
文件必须位于myproject / config /学说
目录中。
模式的例子
如何将数据库结构转换为模式?举个例子是理解它的最好方式。假设你有一个博客数据库,有两个表:blog_article
而且blog_comment
,结构如图8-1所示。
图8-1博客数据库表结构
相关的schema.yml
文件应该类似于清单8-3。
清单8-3 -示例schema.yml
文章:actAs: [Timestampable] tableName: blog_article columns: id: type: integer primary: true自动增量:true title: string(255) content: clob Comment: actAs: [Timestampable] tableName: blog_comment columns: id: type: integer primary: true自动增量:true article_id: integer author: string(255) content: clob relations:文章:onDelete: CASCADE foreignAlias: Comments
注意,数据库本身的名称(博客
)没有出现在schema.yml
文件。相反,数据库是在连接名(学说
在这个例子中)。这是因为实际的连接设置可能取决于应用程序运行的环境。例如,当您在开发环境中运行应用程序时,您将访问开发数据库(可能会访问)blog_dev
),但是使用与生产数据库相同的模式。连接设置将在databases.yml
在本章后面的“数据库连接”一节中描述。该模式不包含任何到设置的详细连接,仅包含一个连接名称,以维护数据库抽象。
基本模式语法
在一个schema.yml
文件中,第一个键表示模型名称。您可以指定多个模型,每个模型都有一组列。根据YAML语法,键以冒号结束,结构通过缩进(一个或多个空格,但没有表)显示。
模型可以具有特殊的属性,包括的表
(模型数据库表的名称)。如果你不提的表
对于模型,Doctrine根据模型名称的下划线版本创建模型。
提示
下划线约定在单词之间添加下划线,并将所有内容都小写。的默认下划线版本文章
而且评论
是文章
而且评论
.
模型包含列。列值可以用两种不同的方式定义:
如果只定义了一个属性,那么它就是列类型。ob娱乐下载Symfony理解常见的列类型:
布尔
,整数
,浮动
,日期
,字符串(大小)
,clob
(例如,转换为文本
在MySQL中),等等。如果需要定义其他列属性(如默认值、required等),则应该将列属性编写为一组
键:值
.这个扩展的模式语法将在本章后面描述。
模型还可以包含显式的外键和索引。请参阅本章后面的“扩展模式语法”一节了解更多信息。
模型类
该模式用于构建ORM层的模型类。为了节省执行时间,生成这些类的命令行任务为原则:建造模式
.
$ PHP ob娱乐下载symfony原则:build-model
提示
在构建模型之后,必须记得清除symfony的内部缓存ob娱乐下载PHP ob娱乐下载symfony cc
因此sob娱乐下载ymfony可以找到您新创建的模型。
类型中的基本数据模型类的生成将启动模式分析lib /模型/理论/基地
项目目录:
BaseArticle.php
BaseComment.php
此外,将在中创建实际数据模型类lib /模型/教义
:
Article.php
ArticleTable.php
Comment.php
CommentTable.php
您只定义了两个模型,最终得到了六个文件。没有什么不对,但它值得一些解释。
基类和自定义类
为什么将数据对象模型的两个版本保存在两个不同的目录中?
您可能需要向模型对象添加自定义方法和属性getName ()
方法)。但是随着项目的发展,您还将添加表或列。每当你改变schema.yml
文件中,您需要通过对doctrine:build-model进行新的调用来重新生成对象模型类。如果您的自定义方法是在实际生成的类中编写的,那么它们将在每次生成后被删除。
的基地
保存在/ lib /模型/理论/基地
目录是直接从模式生成的。您不应该修改它们,因为模型的每次新构建都会完全删除这些文件。
另一方面,自定义对象类,保存在lib /模型/教义
目录,实际上继承自基地
的人。当原则:建造模式
任务在现有模型上调用时,这些类不会被修改。这就是你可以添加自定义方法的地方。
类的第一次调用所创建的自定义模型类的示例,如清单8-4所示原则:建造模式
的任务。
清单8-4 -样本模型类文件,在lib /模型/理论/ Article.php
类文章扩展BaseArticle{}
它继承了世界的一切BaseArticle
类,但模式中的修改不会影响它。
自定义类扩展基类的机制允许您开始编码,即使不知道数据库的最终关系模型。相关的文件结构使得模型既可定制又可进化。
对象和表类
文章
而且评论
是表示数据库中记录的对象类。它们提供对记录的列和相关记录的访问权。这意味着您可以通过调用article对象的方法来知道文章的标题,如清单8-5所示的示例所示。
清单8-5 -对象类中有用于记录列的getter
美元的文章=新文章();/ /……美元的标题=美元的文章->getTitle();
ArticleTable
而且CommentTable
是表类;也就是说,包含对表进行操作的公共方法的类。它们提供了从表中检索记录的方法。它们的方法通常返回一个对象或相关对象类的对象集合,如清单8-6所示。
清单8-6 -表类中有用于检索记录的公共方法
// $article是类article的实例美元的文章= Doctrine_Core::可以获得的(“文章”)->找到(123);
访问数据
在syob娱乐下载mfony中,数据是通过对象访问的。如果您习惯于关系模型并使用SQL来检索和修改数据,那么对象模型方法可能看起来很复杂。但是,一旦您体验过面向对象的数据访问功能,您可能会非常喜欢它。
但首先,让我们确保我们拥有相同的词汇。关系数据模型和对象数据模型使用类似的概念,但它们各自有自己的命名法:
关系 | 面向对象的 |
---|---|
表格 | 类 |
行,记录 | 对象 |
场,列 | 财产 |
检索列值
类中定义的ob娱乐下载每个模型创建一个基对象类schema.yml
.这些类中的每一个都带有基于列定义的默认访问器和突变器新
,getXXX ()
,setXXX ()
方法可以帮助创建对象并访问对象属性,如清单8-7所示。
清单8-7 -生成的对象类方法
美元的文章=新文章();美元的文章->setTitle(“我的第一篇文章”);美元的文章->setContent(“这是我的第一篇文章。\ n希望你喜欢!”);美元的标题=美元的文章->getTitle();美元的内容=美元的文章->getContent();
请注意
调用生成的对象类文章
但是存储在一个名为blog_article
在数据库中。如果的表
如果没有在模式中定义,类会被调用吗文章
.访问器和突变器使用列名的驼峰式变体,因此getTitle ()
方法的值标题
列。
要同时设置多个字段,可以使用fromArray ()
方法,也可用于每个对象类,如清单8-8所示。
清单8-8 - ThefromArray ()
方法是一个多重Setter
美元的文章->fromArray(数组(“标题”= >“我的第一篇文章”,“内容”= >“这是我的第一篇文章。\ n希望你喜欢!”,));
检索相关记录
的article_id
中的列。blog_comment
属性的外键blog_article
表格每条评论都与一篇文章相关,一篇文章可以有很多评论。生成的类包含五个以面向对象的方式转换这种关系的方法,如下所示:
评论- > getArticle ()
:获取相关信息文章
对象评论- > getArticleId ()
:获取相关的ID文章
对象评论- > setArticle ($)
:定义相关的文章
对象评论- > setArticleId ($ id)
:定义相关的文章
从ID中获取对象文章- > getComments ()
:获取相关信息评论
对象
的getArticleId ()
而且setArticleId ()
方法表明,您可以考虑article_id
列作为常规列,并手动设置关系,但它们不是很有趣。面向对象方法的好处在其他三种方法中更为明显。清单8-9显示了如何使用生成的setter。
清单8-9 -外键被翻译成一个特殊Setter
美元的评论=新评论();美元的评论->setAuthor(“史蒂夫。”);美元的评论->setContent(“天哪,伙计,你太棒了:有史以来最好的文章!”);//将此注释附加到前面的$article对象美元的评论->setArticle(美元的文章);//其他语法//只有当对象已经保存在数据库中时才有意义美元的评论->setArticleId(美元的文章->getId());
清单8-10显示了如何使用生成的getter。它还演示了如何链接模型对象上的方法调用。
清单8-10 -外键被转换为特殊getter
//多对一关系回声美元的评论->getArticle()->getTitle();我的第一篇文章回声美元的评论->getArticle()->getContent();这是我的第一篇文章。希望你喜欢!//一对多关系美元的评论=美元的文章->getComments();
的getArticle ()
方法返回类的对象文章
,这得益于getTitle ()
访问器。这比自己进行连接要好得多,后者可能需要几行代码(从评论- > getArticleId ()
调用)。
的美元的评论
清单8-10中的variable包含class对象的数组评论
.你可以用评论[0]美元
或对集合进行迭代Foreach ($comments为$comment)
.
保存和删除数据
通过调用新
构造函数中创建了一个新对象,但没有创建实际记录blog_article
表格修改对象对数据库也没有影响。为了将数据保存到数据库中,需要调用save ()
对象的方法。
美元的文章->保存();
ORM足够智能,可以检测对象之间的关系,因此可以保存美元的文章
对象还保存相关的美元的评论
对象。它还知道保存的对象在数据库中是否有一个现有的对应对象,因此调用save ()
在SQL中有时由插入
,有时由一个更新
.方法自动设置主键save ()
方法,以便保存后可以使用文章- > getId ()
.
提示
你可以通过调用来检查一个对象是否是新的isNew ()
.如果您想知道一个对象是否已被修改并值得保存,请调用它isModified ()
方法。
如果你读到你文章的评论,你可能会改变在互联网上发表文章的兴趣。如果您不喜欢文章评论者的讽刺意味,可以使用delete ()
方法,如清单8-11所示。
清单8-11 -使用delete ()
方法
foreach(美元的文章->getComments()作为美元的评论){美元的评论->删除();}
按主键检索记录
如果知道特定记录的主键,请使用find ()
类的方法来获取相关对象。
美元的文章= Doctrine_Core::可以获得的(“文章”)->找到(7);
的schema.yml
文件定义id
属性的主键blog_article
表,所以这个语句实际上会返回条目id
7.由于使用了主键,您知道将只返回一条记录;的美元的文章
变量包含class的对象文章
.
在某些情况下,一个主键可能包含多个列。在这些情况下,find ()
方法接受多个参数,每个主键列对应一个参数。
使用Doctrine_Query检索记录
方法检索多条记录时,需要调用createQuery ()
与要检索的对象对应的表类的方法。例如,检索类的对象文章
,叫Doctrine_Core::可以获得的(“文章”)——> createQuery() - >执行()
.
的第一个参数execute ()
方法是一个参数数组,该数组是用来替换查询中找到的任何占位符的值数组。
一个空Doctrine_Query
返回类的所有对象。例如,清单8-12所示的代码检索所有文章。
清单8-12 -通过Doctrine_Query检索记录createQuery ()
——空查询
美元的问= Doctrine_Core::可以获得的(“文章”)->createQuery();美元的文章=美元的问->执行();//将导致以下SQL查询选择b.id作为b__id, b.title作为b__title, b.content作为b__content, b.created_at作为b__created_at, b.updated_at作为b . FROM blog_article
侧边栏
保湿
呼唤- > execute ()
实际上比简单的SQL查询强大得多。首先,SQL针对您选择的DBMS进行了优化。对象传递的任何值Doctrine_Query
在集成到SQL代码之前进行转义,以防止SQL注入风险。第三,该方法返回一个对象数组,而不是结果集。ORM根据数据库结果集自动创建和填充对象。这个过程被称为水化。
对于更复杂的对象选择,需要等价于WHERE、ORDER BY、GROUP BY和其他SQL语句。的Doctrine_Query
对象具有用于所有这些条件的方法和参数。例如,要获取Steve编写的所有注释(按日期排序),可以构建一个Doctrine_Query
如清单8-13所示。
清单8-13 -通过a检索记录Doctrine_Query
与createQuery ()
——带条件的Doctrine_Query
美元的问= Doctrine_Core::可以获得的(“评论”)->createQuery(“c”)->在哪里('c.author = ?',“史蒂夫。”)->orderBy(“c.created_at ASC”);美元的评论=美元的问->执行();//将导致以下SQL查询选择b.id作为b__id, b.article_id作为b__article_id, b.author作为b__author, b.content作为b__content, b.created_at作为b__created_at, b.updated_at作为b__updated_at FROM blog_comment(B.author = ?)ORDER BY b.created_at ASC
的SQL语法比较如表8-1所示Doctrine_Query
对象的语法。
表8-1 - SQL和标准对象语法
SQL | 标准 |
---|---|
WHERE列=值 |
->where('acolumn = ?', 'value') |
其他SQL关键字 | |
按列ASC订购 |
- > orderBy (acolumn ASC) |
按列描述顺序 |
- > addOrderBy (acolumn DESC) |
限制限制 |
- >限制(限制) |
抵消抵消 |
- >抵消(抵消) |
从table1左连接table2到table1。Col1 = table2.col2 |
- > leftJoin('。Model2 m”) |
从table1内部连接table2到table1。Col1 = table2.col2 |
- > innerJoin('。Model2 m”) |
清单8-14显示了另一个示例Doctrine_Query
有多种条件。它检索Steve对包含单词“enjoy”的文章的所有评论,按日期排序。
清单8-14 -通过Doctrine_Query获取记录的另一个示例createQuery ()
——带条件的Doctrine_Query
美元的问= Doctrine_Core::可以获得的(“评论”)->createQuery(“c”)->在哪里('c.author = ?',“史蒂夫。”)->leftJoin(“c.Article“)->引入(“a。content LIKE ?”,“%享受%”)->orderBy(“c.created_at ASC”);美元的评论=美元的问->执行();//将导致以下SQL查询选择b.id作为b__id, b.article_id作为b__article_id, b.author作为b__author, b.content作为b__content, b.created_at作为b__created_at, b.updated_at作为b__updated_at, b2.id作为b2__id, b2.title作为b2__title, b2.content作为b2__content, b2.created_at作为b2__created_at, b2.updated_at作为b2__updated_at FROM blog_comment加入blog_article b2 ON b.article_id = b2。身份证的地方(B.author = ?和b2。内容喜欢?)ORDER BY b.created_at ASC
就像SQL是一种允许您构建非常复杂查询的简单语言一样,Doctrine_Query对象可以处理任何复杂级别的条件。但是,由于许多开发人员在将条件转换为面向对象逻辑之前首先考虑SQL,因此Doctrine_Query
物体最初可能很难理解。理解它的最好方法是从示例和示例应用程序中学习。例如,sob娱乐下载ymfony项目的网站上充满了Doctrine_Query
建立例子,在很多方面给你启发。
每一个Doctrine_Query
实例具有一个count ()
方法,该方法仅计算查询的记录数量并返回一个整数。由于没有对象返回,在这种情况下,水化过程不会发生count ()
方法的速度比execute ()
.
表类还提供findAll ()
,findBy * ()
,findOneBy * ()
方法,它们是构造的快捷方式Doctrine_Query
实例,执行它们并返回结果。
最后,如果您只想返回第一个对象,则替换execute ()
与一个fetchOne ()
调用。当你知道aDoctrine_Query
将只返回一个结果,优点是该方法返回一个对象,而不是一个对象数组。
提示
当一个execute ()
查询返回大量结果,您可能希望在响应中只显示其中的一个子集。ob娱乐下载Symfony提供了一个名为sfDoctrinePager
,可以自动对结果进行分页。
使用原始SQL查询
有时,您不想检索对象,而只想获得由数据库计算的综合结果。例如,要获得所有文章的最新创建日期,检索所有文章并在数组上循环是没有意义的。您更倾向于要求数据库只返回结果,因为它将跳过对象水合过程。
另一方面,您不希望直接调用PHP命令进行数据库管理,因为这样会失去数据库抽象的好处。这意味着您需要绕过ORM(原则),而不是数据库抽象(PDO)。
使用PHP数据对象查询数据库需要执行以下操作:
- 获取数据库连接。
- 构建查询字符串。
- 用它创建一个语句。
- 迭代语句执行产生的结果集。
如果这看起来像胡言乱语,那么清单8-15中的代码可能更明确。
清单8-15 -使用PDO自定义SQL查询
美元的连接= Doctrine_Manager::连接();美元的查询=SELECT MAX(created_at) AS MAX FROM blog_article;美元的声明=美元的连接->执行(美元的查询);美元的声明->执行();$ resultset=美元的声明->获取(PDO::FETCH_OBJ);美元最大=$ resultset->马克斯;
就像Doctrine选择一样,当您第一次开始使用PDO查询时,它们也很棘手。同样,来自现有应用程序和教程的示例将向您展示正确的方法。
谨慎
如果您想绕过这个过程,直接访问数据库,就有可能失去Doctrine提供的安全性和抽象性。使用Doctrine的方法比较长,但它迫使您使用良好的实践来保证应用程序的性能、可移植性和安全性。对于包含来自不可信源(例如Internet用户)的参数的查询尤其如此。Doctrine执行所有必要的转义并保护您的数据库。直接访问数据库会使您面临sql注入攻击的风险。
使用特殊的日期列
通常,当一个表中有一个名为created_at
,它用于存储记录创建日期的时间戳。这同样适用于updated_at列,每次更新记录本身时,都要将这些列更新为当前时间的值。
好消息是教条主义有欧宝平台是合法的吗一个Timestampable
为您处理它们的更新的行为。您不需要手动设置created_at
而且updated_at
列;它们将自动更新,如清单8-16所示。
清单8-16 -created_at
而且updated_at
自动处理列
美元的评论=新评论();美元的评论->setAuthor(“史蒂夫。”);美元的评论->保存();//显示创建日期回声美元的评论->getCreatedAt();= >[日期数据库的INSERT操作]
侧边栏
重构到数据层
在开发symfony项目时,通常从在ob娱乐下载操作中编写域逻辑代码开始。但是数据库查询和模型操作不应该存储在控制器层中。因此,所有与数据相关的逻辑都应该移动到模型层。每当您需要在操作中的多个地方执行相同的请求时,请考虑将相关代码传输到模型。它有助于保持动作的简短和可读性。
例如,假设在一个博客中检索给定标记(作为请求参数传递)的10篇最受欢迎的文章所需的代码。这些代码不应该在操作中,而应该在模型中。事实上,如果你需要在模板中显示这个列表,动作应该简单地像这样:
公共函数executeShowPopularArticlesForTag(美元的请求){美元的标记= Doctrine_Core::可以获得的(“标签”)->findOneByName(美元的请求->getParameter(“标签”));这个美元->forward404Unless(美元的标记);这个美元->文章=美元的标记->getPopularArticles(10);}
该操作创建了一个class对象标签
从请求参数。然后查询数据库所需的所有代码都位于getPopularArticles ()
类的方法。它使操作更具可读性,并且模型代码可以很容易地在另一个操作中重用。
将代码移动到更合适的位置是重构技术之一。如果您经常这样做,您的代码将易于维护,并易于其他开发人员理解。关于何时对数据层进行重构的一条经验法则是,动作的代码应该很少包含超过10行PHP代码。
数据库连接
数据模型独立于所使用的数据库,但您肯定会使用数据库。symfony向项目数据库发送请求所需的最少信息是名称、凭据和数据库类型ob娱乐下载。对象的数据源名称(DSN)可以配置这些连接设置配置:数据库
任务:
mysql:ob娱乐下载host=localhost;dbname=blog
连接设置与环境有关。属性的不同设置刺激
,dev
,测验
环境,或应用程序中的任何其他环境env
选择:
——env=ob娱乐下载dev "mysql:host=localhost;dbname=blog_dev
每个应用程序也可以重写此配置。例如,您可以使用这种方法为前端应用程序和后端应用程序设置不同的安全策略,并在数据库中定义几个具有不同权限的数据库用户来处理此问题:
mysql:ob娱乐下载host=localhost;dbname=blog
对于每个环境,您都可以定义许多连接。使用的默认连接名是学说
.的的名字
选项允许您创建另一个连接:
$ php ob娱乐下载symfony配置:数据库——name=main "mysql:host=localhost
中手动输入这些连接设置databases.yml
文件位于配置/
目录中。清单8-17显示了这样一个文件的示例,清单8-18显示了带有扩展表示法的相同示例。
清单8-17 -简写数据库连接设置
all: doctrine: class: sfDoctrineDatabase param: dsn: mysql://login:passwd@localhost/blog
清单8-18 -数据库连接设置示例,在myproject / config / databases.yml
Prod: doctrine: param: dsn: mysql:dbname=blog;host=localhost username:登录密码:passwd attributes: quote_identifier: false use_native_enum: false validate: all idxname_format: %s_idx seqname_format: %s_seq tblname_format: %s
要覆盖每个应用程序的配置,需要编辑特定于应用程序的文件,例如应用程序/前端/ config / databases.yml
.
如果要使用SQLite数据库,请使用dsn
参数必须设置为数据库文件的路径。例如,如果你保持你的博客数据库数据/ blog.db
,databases.yml
文件将如清单8-19所示。
清单8-19 - SQLite的数据库连接设置使用文件路径作为主机
all: doctrine: class: sfDoctrineDatabase param: dsn: sqlite:///%SF_DATA_DIR%/blog.db
扩展模型
生成的模型方法很棒,但通常还不够。一旦实现了自己的业务逻辑,就需要通过添加新方法或覆盖现有方法来扩展它。
添加新方法
方法中生成的空模型类可以添加新方法lib /模型/教义
目录中。使用这个美元
调用当前对象的方法,并使用自我::
调用当前类的静态方法。类的方法继承基地
类位于lib /模型/理论/基地
目录中。
例如,对于文章
对象生成基于清单8-3,您可以添加一个魔术__toString ()
方法,以便回显类的对象文章
显示标题,如清单8-20所示。
清单8-20 -定制模型,在lib /模型/理论/ Article.php
类文章扩展BaseArticle{公共函数__toString(){返回这个美元->getTitle();// getTitle()继承自BaseArticle}}
您还可以扩展表类——例如,添加一个方法来检索按创建日期排序的所有文章,如清单8-21所示。
清单8-21 -自定义模型,在lib /模型/理论/ ArticleTable.php
类ArticleTable扩展BaseArticleTable{公共函数getAllOrderedByDate(){美元的问=这个美元->createQuery(“一个”)->orderBy(“a.created_at ASC”);返回美元的问->执行();}}
新方法可用的方式与生成的方法相同,如清单8-22所示。
清单8-22 -使用自定义模型方法就像使用生成的方法
美元的文章= Doctrine_Core::可以获得的(“文章”)->getAllOrderedByDate();foreach(美元的文章作为美元的文章){回声美元的文章;//调用神奇的__toString()方法}
重写现有方法
中的一些生成的方法基地
类不符合您的要求,您仍然可以在自定义类中覆盖它们。只需确保使用相同的方法签名(即相同数量的参数)。
例如,文章- > getComments ()
方法返回的集合评论
对象,没有特别的顺序。如果希望结果按创建日期排序,最新的注释放在前面,则可以创建getComments ()
方法,如清单8-23所示。
清单8-23 -覆盖现有模型方法,在lib /模型/理论/ Article.php
公共函数getComments(){美元的问= Doctrine_Core::可以获得的(“评论”)->createQuery(“c”)->在哪里('c.article_id = ?',这个美元->getId())->orderBy(“c.created_at ASC”);返回美元的问->执行();}
使用模型行为
一些模型修改是通用的,可以重用。例如,使模型对象可排序的方法和防止并发对象保存之间冲突的乐观锁都是可以添加到许多类中的通用扩展。
ob娱乐下载Symfony将这些扩展打包成行为。行为是外部类,为类建模提供了额外的方法。模型类已经包含钩子,symfony知道如何扩展它们。ob娱乐下载
要在模型类中启用行为,必须修改模式并使用找
选择:
文章:actAs: [Timestampable,可延迟]tableName: blog_article columns: id: type: integer primary: true自动增量:true title:字符串(255)内容:clob
在重建模型之后文章
模型有一个slug列,根据标题自动设置为url友好的字符串。
伴随着教义的一些行为是:
- Timestampable
- Sluggable
- SoftDelete
- 可搜索的
- I18n
- 版本管理
- NestedSet
扩展模式语法
一个schema.yml
文件可以很简单,如清单8-3所示。但是关系模型通常很复杂。这就是为什么模式具有广泛的语法,能够处理几乎所有的情况。
属性
连接和表可以具有特定的属性,如清单8-24所示。它们被放置在_attributes
关键。
清单8-24 -模型设置的属性
Article: attributes: export: tables validate: none
的出口
属性控制在为该模型创建表时导出到数据库的SQL。使用表
值将只导出表结构,而不导出外键、索引等。
包含本地化内容的表(也就是说,在一个相关的表中有几个版本的内容,用于国际化)应该使用I18n行为(详情请参阅第13章),如清单8-25所示。
清单8-25 - I18n行为
文章:actAs: I18n:字段:[标题,内容]
侧边栏
处理多个模式
每个应用程序可以有多个模式。ob娱乐下载Symfony将考虑以。结尾的每个文件.yml
在配置/学说
文件夹中。如果您的应用程序有许多模型,或者一些模型不共享相同的连接,您会发现这种方法非常有用。
考虑以下两种模式:
//在config/doctrine/business-schema中。yml条目:id: type: integer primary: true自动递增:true title: string(50) //在config/doctrine/stats-schema中。yml Hit: actAs: [Timestampable] columns: id: type: integer primary: true autoincrement: true resource: string(100)
两个模式共享相同的连接(学说
),以及文章
而且打击
类将在相同的目录下生成lib /模型/教义
目录中。就好像只编写了一个模式一样。
您还可以使用不同的连接使用不同的模式(例如,学说
而且doctrine_bis
的定义databases.yml
),并将其与上述联系联系起来:
//在config/doctrine/business-schema中。yml Article: connection: doctrine id: type: integer primary: true autoincrement: true title: string(50) //在config/doctrine/stats-schema中。yml Hit: connection: doctrine_bis actAs: [Timestampable] columns: id: type: integer primary: true autoincrement: true resource: string(100)
许多应用程序使用多个模式。特别是,一些插件有自己的模式,以避免混淆你自己的类(详情请参阅第17章)。
列的细节
基本语法允许您使用类型关键字之一定义类型。清单8-26演示了这些选择。
清单8-26 -基本列属性
Article: columns: title: string(50) #指定类型和长度
但是您可以为一列定义更多内容。如果这样做,您将需要将列设置定义为一个关联数组,如清单8-27所示。
清单8-27 -复杂列属性
文章:列:id: {type: integer, nottnull: true, primary: true, autoincrement: true} name: {type: string(50), default: foobar} group_id: {type: integer}
列参数如下:
类型
:列类型。选项是布尔
,整数
,双
,浮动
,小数
,字符串(大小)
,日期
,时间
,时间戳
,团
,clob
.notnull
:布尔。设置为真正的
如果您希望该列是必需的。长度
:支持该类型的字段的大小或长度规模
:用于十进制数据类型的小数位数(还必须指定大小)默认的
:默认值。主要的
:布尔。设置为真正的
对于主键。自动增量
:布尔。设置为真正的
对于类型的列整数
需要取一个自动递增的值。序列
:使用序列的数据库的序列名自动增量
列(例如PostgreSQL和Oracle)。独特的
:布尔。设置为真正的
如果你想让列是唯一的。
的关系
属性下的外键关系关系
输入一个模型。清单8-28中的模式将在user_id
列,与id
中的列。blog_user
表格
清单8-28 -外键替代语法
Article: actAs: [Timestampable] tableName: blog_article columns: id: type: integer primary: true autoincrement: true title: string(255) content: clob user_id: integer relations: User: onDelete: CASCADE foreignAlias: Articles
索引
属性下添加索引索引数量:
输入一个模型。方法定义惟一索引,必须使用类型:独特的
语法。对于需要大小的列,因为它们是文本列,所以索引的大小与使用括号指定列的长度相同。清单8-30显示了索引的替代语法。
清单8-30 -索引和惟一索引
Article: actAs: [Timestampable] tableName: blog_article columns: id: type: integer primary: true autoincrement: true title: string(255) content: clob user_id: integer relations: User: onDelete: CASCADE foreignAlias: Articles indexes: my_index: fields: title: length: 10 user_id: [] my_other_index: type: unique fields: created_at . columns: id: type: integer primary: true autoincrement: true title: string(255) content: clob user_id: integer relations: User: onDelete: CASCADE foreignAlias: Articles indexes: my_index: fields: title: length: 10 user_id: [] my_other_index: type: unique fields: created_at . properties
I18n表
ob娱乐下载Symfony支持相关表中的内容国际化。这意味着,当您拥有国际化的内容时,它将存储在两个单独的表中:一个表具有不变列,另一个表具有国际化列。
清单8-33 -显式i18n机制
DbGroup: actAs: I18n: fields: [name] columns: name: string(50)
行为
行为是插件提供的模型修饰符,它向Doctrine类添加新功能。第十七章解释了更多关于行为的内容。属性下为每个表列出行为及其参数,可以在模式中定义行为找
关键。清单8-34给出了一个扩展文章
类的Sluggable
的行为。
清单8-34 -行为声明
文章:actAs: #…
不要创建两次模型
使用ORM的代价是必须定义两次数据结构:一次用于数据库,一次用于对象模型。幸运的是,symfony提ob娱乐下载供了命令行工具来基于一个生成另一个,因此可以避免重复的工作。
基于现有模式构建SQL数据库结构
方法启动应用程序schema.yml
文件,symob娱乐下载fony可以生成一个SQL查询,直接从YAML数据模型创建表。要使用查询,请转到您的根项目目录并键入以下内容:
$ PHP ob娱乐下载symfony原则:build-sql
一个schema.sql
文件将在myproject /数据/ sql /
.方法中定义的数据库系统将优化生成的SQL代码databases.yml
.
您可以使用schema.sql
文件直接构建表。例如,在MySQL中,键入:
$ mysql -u root -p create blog < data/sql/schema.sql
生成的SQL还有助于在另一个环境中重新构建数据库,或更改到另一个DBMS。
提示
命令行还提供了一个任务,用基于文本文件的数据填充数据库。有关的更多信息,请参阅第16章原则:数据加载
任务和YAML fixture文件。
从现有数据库生成YAML数据模型
ob娱乐下载Symfony可以使用Doctrine生成一个schema.yml
来自现有数据库的文件,这要归功于自省(数据库确定其操作的表的结构的能力)。当您进行逆向工程时,或者如果您喜欢在处理对象模型之前先处理数据库,这可能特别有用。
为了做到这一点,您需要确保项目databases.yml
文件指向正确的数据库并包含所有连接设置,然后调用原则:建立模式
命令:
$ PHP ob娱乐下载symfony原则:build-schema
一个全新的schema.yml
从数据库结构构建的文件将在配置/理论/
目录中。您可以基于这个模式构建您的模型。
总结
ob娱乐下载Symfony使用Doctrine作为ORM,使用PHP数据对象作为数据库抽象层。这意味着在生成对象模型类之前,必须首先在YAML中描述数据库的关系模式。然后,在运行时,使用对象类和表类的方法检索关于一条记录或一组记录的信息。您可以通过向自定义类添加方法来覆盖它们并轻松地扩展模型。连接设置定义在databases.yml
文件,它可以支持多个连接。命令行包含特殊任务,以避免重复的结构定义。
模型层是symfony框架中最复杂的。ob娱乐下载造成这种复杂性的一个原因是数据操作是一件复杂的事情。相关的安全问题对于一个网站来说是至关重要的,不应该被忽视。另一个原因是symfony更适合企业上下文中的ob娱乐下载中大型应用程序。在这样的应用程序中,symfony模型提供的自动化实际上意味着时间的增加,值得投资学习其内部知识。ob娱乐下载
因此,不要犹豫,花一些时间测试模型对象和方法,以完全理解它们。应用程序的稳定性和可伸缩性将是一个巨大的回报。
本作品在GFDL许可下获得许可。