第四章-推动整合
在Web项目中,大多数表单都用于创建或修改模型对象。由于ORM,这些对象通常在数据库中序列化。ob娱乐下载Symfony的表单系统为与Propel (Symfony的内置ORM)的接口提供了一个额外的层,使得基于这些模型对象的表单实现更加容易。
本章将详细介绍如何将表单与Propel对象模型集成。强烈建议您已经熟悉Propel及其在symfony中的集成。ob娱乐下载如果不是这样,请参考本章模型层内部摘自《交响乐权威指南》一书。ob娱乐下载
开始之前
在本章中,我们将创建一个文章管理系统。让我们从数据库模式开始。它由五张桌子组成:文章
,作者
,类别
,标签
,article_tag
,如清单4-1所示。
清单4-1 -数据库模式
/ /配置/模式。yml propel: article: id: ~ title: {type: varchar(255), required: true} slug: {type: varchar(255), required: true} content: longvarchar状态:varchar(255) author_id: {type: integer, required: true, foreignTable: author, foreignReference: id, OnDelete: cascade} category_id: {type: integer, required: false, foreignTable: category, foreignReference: id, OnDelete: setnull} publhed_at: timestamp created_at: ~ updated_at: ~ _uniques: unique_slug: [slug] author: id: ~ first_name:varchar(20) last_name: varchar(20) email: {type: varchar(255), required: true} active: boolean类别:id: ~ name: {type: varchar(255), required: true} tag: id: ~ name: {type: varchar(255), required: true} article_tag: article_id: {type: integer, foreignTable: article, foreignReference: id, primaryKey: true, onDelete: cascade} tag_id: {type: integer, foreignTable: tag, foreignReference: id, primaryKey: true, onDelete: cascade}
以下是表之间的关系:
- 之间的1-n关系
文章
表和作者
表格:一篇文章由且仅由一个作者撰写 - 之间的1-n关系
文章
表和类别
表:一篇文章属于一个或零类别 - 之间的N-n关系
文章
而且标签
表
生成表单类
的信息文章
,作者
,类别
,标签
表。为此,我们需要创建链接到每个表的表单,并配置与数据库模式相关的小部件和验证器。即使可以手动创建这些表单,这也是一项漫长而乏味的任务,而且总的来说,它迫使在几个文件中重复相同类型的信息(列和字段名、列和字段的最大大小,……)。此外,每当我们更改模型时,我们也必须更改相关的表单类。幸运的是,Propel插件有一个内置的任务推动:构建表单
自动生成与对象模型相关的表单的过程:
$ ./ob娱乐下载symfony驱动:构建形式
在表单生成过程中,该任务使用模型内省并考虑表之间的关系,为每个列创建带有验证器和小部件的每个表类。
请注意
的推动:构建所有
而且推动:build-all-load
还更新表单类,自动调用推动:构建表单
的任务。
执行这些任务后,将在lib /形式/
目录中。下面是为我们的示例模式创建的文件:
lib/ form/ baseformprol .class.php ArticleForm.class.php articleagform .class.php AuthorForm.class.php TagForm.class.php base/ BaseArticleForm.class.php BaseCategoryForm.class.php
的推动:构建表单
任务为模式的每个表生成两个类,在lib /形式/基地
目录和一个在lib /形式/
目录中。例如,作者
表,由BaseAuthorForm
而且AuthorForm
在文件中生成的类lib /形式/基地/ BaseAuthorForm.class.php
而且lib / / AuthorForm.class.php形式
.
侧边栏
表单生成目录
的推动:构建表单
task以类似于Propel结构的结构生成这些文件。的包
属性的驱动模式允许逻辑地把表的子集放在一起。默认包为lib.model
的文件中生成这些文件lib /模型/
目录中生成的表单lib /形式
目录中。使用lib.model.cms
包,如下例所示,驱动类将在lib /模型/ cms /
目录中的表单类/ cms / lib /形式
目录中。
推动:_attributes:{noXsd:假, defaultIdMethod: none, package: lib.model.cms}#……
包对于拆分数据库模式和在插件中传递表单非常有用,我们将在第5章中看到这一点。
有关软件包的进一步资料,请参阅模型层内部《交响乐权威指南》的章节。ob娱乐下载
下表总结了涉及的不同类之间的层次结构AuthorForm
形式的定义。
类 | 包 | 为 | 描述 |
---|---|---|---|
AuthorForm | 项目 | 开发人员 | 覆盖生成的表单 |
BaseAuthorForm | 项目 | ob娱乐下载 | 方法的每次执行时重写推动:构建表单 任务 |
BaseFormPropel | 项目 | 开发人员 | 允许全球自定义驱动形式 |
sfFormPropel | 推动插件 | ob娱乐下载 | 推进形式的基础 |
sfForm | ob娱乐下载 | ob娱乐下载 | 交响乐形式的基础ob娱乐下载 |
对象中创建或编辑对象作者
类,我们将使用AuthorForm
类,如清单4-2所示。类继承的方法中不包含任何方法BaseAuthorForm
它是通过配置生成的。的AuthorForm
class是我们将用于自定义和覆盖表单配置的类。
清单4-2 -AuthorForm
类
类AuthorForm扩展BaseAuthorForm{公共函数配置(){}}
清单4-3显示了BaseAuthorForm
类生成的验证器和小部件作者
表格
清单4-3 -BaseAuthorForm
类的窗体作者
表格
类BaseAuthorForm扩展BaseFormPropel{公共函数设置(){这个美元->setWidgets(数组(“id”= >新sfWidgetFormInputHidden(),“first_name”= >新sfWidgetFormInputText(),“last_name”= >新sfWidgetFormInputText(),“电子邮件”= >新sfWidgetFormInputText(),));这个美元->setValidators(数组(“id”= >新sfValidatorPropelChoice(数组(“模型”= >“作者”,“列”= >“id”,“要求”= >假)),“first_name”= >新sfValidatorString(数组(“max_length”= >20.,“要求”= >假)),“last_name”= >新sfValidatorString(数组(“max_length”= >20.,“要求”= >假)),“电子邮件”= >新sfValidatorString(数组(“max_length”= >255)),));这个美元->widgetSchema->setNameFormat(“作者(% s)”);这个美元->errorSchema=新sfValidatorErrorSchema(这个美元->validatorSchema);父::设置();}公共函数getModelName(){返回“作者”;}}
生成的类看起来非常类似于我们在前面章节中已经创建的表单,除了一些事情:
- 基类为
BaseFormPropel
而不是标识
- 验证器和小部件配置在
设置()
方法,而不是在配置()
方法 - 的
getModelName ()
方法返回与此表单相关的Propel类
侧边栏
驱动形式的全球定制
除了为每个表生成的类之外,推动:构建表单
也生成BaseFormPropel
类。类中所有其他生成类的基类/ lib /形式/基地
目录,并允许配置每个驱动表单的行为全局。例如,可以轻松地更改所有Propel表单的默认格式化器:
摘要类BaseFormPropel扩展sfFormPropel{公共函数设置(){sfWidgetFormSchema::setDefaultFormFormatterName(“div”);}}
你会注意到BaseFormPropel
类继承自sfFormPropel
类,它包含了特定于Propel的功能,以及处理从表单中提交的值到数据库中的对象序列化。
提示基类使用设置()
方法进行配置,而不是配置()
方法。这允许开发人员重写生成的空类的配置,而无需处理父::配置()
调用。
表单字段名与我们在模式中设置的列名相同:id
,first_name
,last_name
,电子邮件
.
的每一列作者
表,推动:构建表单
Task根据模式定义生成小部件和验证器。该任务总是生成尽可能安全的验证器。让我们考虑id
字段。我们可以检查这个值是否是一个有效的整数。相反,这里生成的验证器还允许我们验证标识符是否实际存在(以编辑现有对象)或标识符是否为空(以便我们可以创建一个新对象)。这是一个更强的验证。
生成的表单可以立即使用。添加一个<?PHP echo $form ?>
语句,这将允许创建带有验证的函数式表单不用写一行代码.
除了能够快速创建原型之外,生成的表单很容易扩展,而无需修改生成的类。这要归功于基类和表单类的继承机制。
最后,在数据库模式的每次演进中,该任务允许再次生成表单,以考虑模式修改,而不会覆盖您可能已经进行的Customization。
CRUD生成器
现在有了生成的表单类,让我们看看创建一个symfony模块来处理来自浏览器的对象是多么容易。ob娱乐下载对象中创建、修改和删除对象文章
,作者
,类别
,标签
类。的模块创建开始作者
类。即使我们可以手动创建一个模块,Propel插件也提供了推动:generate-module
基于Propel对象模型类生成CRUD模块的任务。使用我们在前一节中生成的表单:
$ ./ob娱乐下载symfony propel:生成模块前端作者作者
的推动:generate-module
有三个参数:
前端
:要在其中创建模块的应用程序的名称作者
:需要创建的模块名作者
:要为其创建模块的模型类的名称
请注意
CRUD代表创建/检索/更新/删除,并总结了我们可以对模型数据执行的四个基本操作。
在清单4-4中,我们看到任务生成了5个操作,允许我们列出(指数
),创建(创建
),修改(编辑
),保存(更新
),并删除(删除
)的对象作者
类。
清单4-4 - TheauthorActions
由Task生成的类
/ /应用程序/前端/模块/作者/行动/ actions.class.php类authorActions扩展sfActions{公共函数executeIndex(){这个美元->authorList= AuthorPeer::doSelect(新标准());}公共函数executeCreate(){这个美元->形式=新AuthorForm();这个美元->setTemplate(“编辑”);}公共函数executeEdit(美元的请求){这个美元->形式=新AuthorForm(AuthorPeer::retrieveByPk(美元的请求->getParameter(“id”)));}公共函数executeUpdate(美元的请求){这个美元->forward404Unless(美元的请求->isMethod(“职位”));这个美元->形式=新AuthorForm(AuthorPeer::retrieveByPk(美元的请求->getParameter(“id”)));这个美元->形式->绑定(美元的请求->getParameter(“作者”));如果(这个美元->形式->isValid()){美元的作者=这个美元->形式->保存();这个美元->重定向(的作者/编辑?id = '.美元的作者->getId());}这个美元->setTemplate(“编辑”);}公共函数executeDelete(美元的请求){这个美元->forward404Unless(美元的作者= AuthorPeer::retrieveByPk(美元的请求->getParameter(“id”)));美元的作者->删除();这个美元->重定向(“作者/指数”);}}
在这个模块中,表单生命周期由三个方法处理:创建
,编辑
而且,更新
.也可以问推动:generate-module
任务生成一个覆盖前面三个方法功能的方法,并使用选项——non-atomic-actions
:
$ ./ob娱乐下载symfony propel:生成模块前端作者作者-非原子动作
生成的代码使用——non-atomic-actions
(清单4-5)更简洁,更简洁。
清单4-5 -authorActions
类生成的——non-atomic-actions
选项
类authorActions扩展sfActions{公共函数executeIndex(){这个美元->authorList= AuthorPeer::doSelect(新标准());}公共函数executeEdit(美元的请求){这个美元->形式=新AuthorForm(AuthorPeer::retrieveByPk(美元的请求->getParameter(“id”)));如果(美元的请求->isMethod(“职位”)){这个美元->形式->绑定(美元的请求->getParameter(“作者”));如果(这个美元->形式->isValid()){美元的作者=这个美元->形式->保存();这个美元->重定向(的作者/编辑?id = '.美元的作者->getId());}}}公共函数executeDelete(美元的请求){这个美元->forward404Unless(美元的作者= AuthorPeer::retrieveByPk(美元的请求->getParameter(“id”)));美元的作者->删除();这个美元->重定向(“作者/指数”);}}
该任务还生成了两个模板,indexSuccess
而且editSuccess
.的editSuccess
模板未使用<?PHP echo $form ?>
声明。属性可以修改这种行为——non-verbose-templates
:
$ ./ob娱乐下载symfony驱动:生成模块前端作者作者——non-verbose-templates
这个选项在原型阶段很有帮助,如清单4-6所示。
清单4-6 -editSuccess
模板
/ /应用程序/前端/模块/作者/模板/ editSuccess.php<?php美元的作者=美元的形式->getObject()? ><标题> < ?php回声美元的作者->isNew()?“新”:“编辑”? >Author
提示
的——用指
选项让我们生成一个操作和一个模板,我们可以使用它来查看对象(只读)。
现在可以打开URL了/ frontend_dev.php /作者
在浏览器中查看已生成的模块(如图4-1和图4-2所示)。花点时间玩一下界面。由于生成的模块,您可以列出作者,添加一个新的,编辑,修改,甚至删除。您还将注意到验证规则也在工作。
图4-1 -作者列表
图4-2 -编辑带有验证错误的Author
我们现在可以重复操作文章
类:
$ ./ob娱乐下载symfony驱动:生成模块前端文章文章——非冗长模板——非原子动作
生成的代码非常类似于作者
类。但是,如果您尝试创建一个新的文章,代码会抛出一个致命错误,如图4-3所示。
图4-3 -链接表必须定义__toString ()
方法
的ArticleForm
表单使用sfWidgetFormPropelSelect
控件之间的关系文章
对象和作者
对象。这个小部件创建了一个包含作者的下拉列表。方法将作者对象转换为字符串__toString ()
魔术方法,其中必须定义作者
类,如清单4-7所示。
清单4-7 -实现__toString ()
方法。作者
类
类作者扩展BaseAuthor{公共函数__toString(){返回这个美元->getFirstName().' '.这个美元->getLastName();}}
就像作者
类,您可以创建__toString ()
模型中其他类的方法:文章
,类别
,标签
.
提示
的方法
选项sfWidgetFormPropelSelect
小部件更改用于以文本格式表示对象的方法。
实现后,如何创建项目,如图4-4所示__toString ()
方法。
图4-4 -创建文章
定制生成的表单
的推动:构建表单
而且推动:generate-module
任务让我们创建功能symfony模块来列出、创建、编辑和删除模ob娱乐下载型对象。这些模块不仅考虑了模型的验证规则,还考虑了表之间的关系。所有这些都无需编写一行代码!
现在是定制生成代码的时候了。如果表单类已经考虑了许多元素,则需要自定义某些方面。
配置验证器和小部件
让我们从配置默认生成的验证器和小部件开始。
的ArticleForm
表单有一个鼻涕虫
字段。段码是一串字符,在URL中唯一地表示文章。例如,标题为“使用symfony优化开发”的文章的分段是ob娱乐下载12-optimize-the-developments-with-ob娱乐下载symfony
,12
成为文章id
.该字段通常在保存对象时自动计算,这取决于标题
,但它有可能被用户显式地覆盖。即使该字段在模式中是必需的,它也不能是表单的强制性字段。这就是我们修改验证器并使其成为可选的原因,如清单4-8所示。我们还将自定义内容
字段增加其大小并迫使用户输入至少五个字符。
清单4-8 -自定义验证器和小部件
类ArticleForm扩展BaseArticleForm{公共函数配置(){/ /……这个美元->validatorSchema[“鼻涕虫”]->setOption(“要求”,假);这个美元->validatorSchema[“内容”]->setOption(“min_length”,5);这个美元->widgetSchema[“内容”]->setAttributes(数组(“行”= >10,“关口”= >40));}}
这里我们使用validatorSchema
而且widgetSchema
对象作为PHP数组。这些数组将字段的名称作为键,并分别返回验证器对象和相关的小部件对象。然后,我们可以自定义单独的字段和小部件。
请注意
为了允许将对象用作PHP数组,使用sfValidatorSchema
而且sfWidgetFormSchema
类实现ArrayAccess
接口,自版本5起在PHP中可用。
确保两篇文章不能有相同之处鼻涕虫
,在模式定义中添加了唯一性约束。数据库级别上的这种约束反映在ArticleForm
使用sfValidatorPropelUnique
验证器。这个验证器可以检查任何表单字段的唯一性。例如,检查登录的电子邮件地址的唯一性是很有帮助的。清单4-9显示了如何在ArticleForm
的形式。
清单4-9 -使用sfValidatorPropelUnique
验证器来检查字段的唯一性
类BaseArticleForm扩展BaseFormPropel{公共函数设置(){/ /……这个美元->validatorSchema->setPostValidator(新sfValidatorPropelUnique(数组(“模型”= >“文章”,“列”= >数组(“鼻涕虫”))));}}
的sfValidatorPropelUnique
验证器是一个postValidator
在每个字段单独验证后运行整个数据。为了验证鼻涕虫
唯一性,验证器必须能够访问,而不仅仅是鼻涕虫
值,也包括主键的值。验证规则在整个创建和版本过程中确实是不同的,因为在文章更新期间,段代码可以保持不变。
让我们现在自定义活跃的
字段作者
表,用于了解作者是否处于活动状态。列表4-10显示了如何从ArticleForm
表单,修改标准
选项sfWidgetPropelSelect
连接到author_id
字段。的标准
option接受一个Propel Criteria对象,允许缩小滚动列表中可用选项的列表。
清单4-10 -自定义sfWidgetPropelSelect
小部件
类ArticleForm扩展BaseArticleForm{公共函数配置(){/ /……authorCriteria美元=新标准();authorCriteria美元->添加(AuthorPeer::活跃的,真正的);这个美元->widgetSchema[“author_id”]->setOption(“标准”,authorCriteria美元);}}
即使小部件定制可以使我们缩小可用选项的列表,我们也不能忘记在验证器级别上考虑这种缩小,如清单4-11所示。就像sfWidgetProperSelect
小部件,sfValidatorPropelChoice
验证器接受标准
选项,以缩小对字段有效的选项。
清单4-11 -自定义sfValidatorPropelChoice
验证器
类ArticleForm扩展BaseArticleForm{公共函数配置(){/ /……authorCriteria美元=新标准();authorCriteria美元->添加(AuthorPeer::活跃的,真正的);这个美元->widgetSchema[“author_id”]->setOption(“标准”,authorCriteria美元);这个美元->validatorSchema[“author_id”]->setOption(“标准”,authorCriteria美元);}}
在前面的示例中,我们定义了标准
对象的配置()
方法。在我们的项目中,这个标准在其他情况下肯定会有所帮助,因此最好创建一个getActiveAuthorsCriteria ()
方法中的AuthorPeer
类,并从中调用此方法ArticleForm
如清单4-12所示。
清单4-12 -重构标准
在模型中
类AuthorPeer扩展BaseAuthorPeer{静态公共函数getActiveAuthorsCriteria(){美元标准=新标准();美元标准->添加(AuthorPeer::活跃的,真正的);返回美元标准;}}类ArticleForm扩展BaseArticleForm{公共函数配置(){authorCriteria美元= AuthorPeer::getActiveAuthorsCriteria();这个美元->widgetSchema[“author_id”]->setOption(“标准”,authorCriteria美元);这个美元->validatorSchema[“author_id”]->setOption(“标准”,authorCriteria美元);}}
更改验证器
的电子邮件
被定义为varchar (255)
在模式中,symfony创建了ob娱乐下载一个sfValidatorString ()
验证器限制最大长度为255个字符。该字段还应该接收有效的电子邮件,清单4-14将生成的验证器替换为sfValidatorEmail
验证器。
清单4-13 -修改电子邮件
字段的验证器AuthorForm
类
类AuthorForm扩展BaseAuthorForm{公共函数配置(){这个美元->validatorSchema[“电子邮件”]=新sfValidatorEmail();}}
添加验证器
在前一章中,我们观察了如何修改生成的验证器。但是在这个例子中电子邮件
字段时,保留最大长度验证将很有用。在清单4-14中,我们使用sfValidatorAnd
验证器来保证电子邮件的有效性,并检查字段允许的最大长度。
清单4-14 -使用多个Validator
类AuthorForm扩展BaseAuthorForm{公共函数配置(){这个美元->validatorSchema[“电子邮件”]=新sfValidatorAnd(数组(新sfValidatorString(数组(“max_length”= >255)),新sfValidatorEmail(),));}}
前面的例子并不完美,因为如果我们决定以后修改的长度电子邮件
字段在数据库模式中,我们将不得不考虑在表单中也这样做。与其替换生成的验证器,不如添加一个,如清单4-15所示。
清单4-15 -添加Validator
类AuthorForm扩展BaseAuthorForm{公共函数配置(){这个美元->validatorSchema[“电子邮件”]=新sfValidatorAnd(数组(这个美元->validatorSchema[“电子邮件”],新sfValidatorEmail(),));}}
改变小部件
在数据库模式中,状态
字段文章
表将项目状态存储为字符串。中定义了可能的值ArticePeer
类,如清单4-16所示。
清单4-16 -定义可用状态ArticlePeer
类
类ArticlePeer扩展BaseArticlePeer{静态受保护的美元的状态=数组(“草案”,“在线”,“离线”);静态公共函数的getstatus(){返回自我::美元的状态;}/ /……}
在编辑一篇文章时状态
字段必须表示为下拉列表,而不是文本字段。为此,让我们更改我们使用的小部件,如清单4-17所示。
清单4-17 -为状态
场
类ArticleForm扩展BaseArticleForm{公共函数配置(){这个美元->widgetSchema[“状态”]=新sfWidgetFormSelect(数组(“选择”= > ArticlePeer::的getstatus()));}}
为了更彻底,我们还必须更改验证器,以确保所选择的状态实际上属于可能的选项列表(清单4-18)。
清单4-18 -修改状态
字段验证器
类ArticleForm扩展BaseArticleForm{公共函数配置(){美元的状态= ArticlePeer::的getstatus();这个美元->widgetSchema[“状态”]=新sfWidgetFormSelect(数组(“选择”= >美元的状态));这个美元->validatorSchema[“状态”]=新sfValidatorChoice(数组(“选择”= >中的(美元的状态)));}}
删除字段
的文章
表格有两个特殊的列,created_at
而且updated_at
,其更新由Propel自动处理。然后我们必须从表单中删除它们,如清单4-19所示,以防止用户修改它们。
清单4-19 -删除字段
类ArticleForm扩展BaseArticleForm{公共函数配置(){设置(这个美元->validatorSchema[“created_at”]);设置(这个美元->widgetSchema[“created_at”]);设置(这个美元->validatorSchema[“updated_at”]);设置(这个美元->widgetSchema[“updated_at”]);}}
为了删除字段,需要删除它的验证器和小部件。清单4-20展示了如何使用表单作为PHP数组,在一个操作中同时删除两者。
清单4-20 -删除字段使用表单作为PHP数组
类ArticleForm扩展BaseArticleForm{公共函数配置(){设置(这个美元[“created_at”],这个美元[“updated_at”]);}}
总结
总而言之,清单4-21和清单4-22显示了ArticleForm
而且AuthorForm
我们定制的表单。
清单4-21 -ArticleForm
形式
类ArticleForm扩展BaseArticleForm{公共函数配置(){authorCriteria美元= AuthorPeer::getActiveAuthorsCriteria();/ /部件这个美元->widgetSchema[“内容”]->setAttributes(数组(“行”= >10,“关口”= >40));这个美元->widgetSchema[“状态”]=新sfWidgetFormSelect(数组(“选择”= > ArticlePeer::的getstatus()));这个美元->widgetSchema[“author_id”]->setOption(“标准”,authorCriteria美元);/ /验证器这个美元->validatorSchema[“鼻涕虫”]->setOption(“要求”,假);这个美元->validatorSchema[“内容”]->setOption(“min_length”,5);这个美元->validatorSchema[“状态”]=新sfValidatorChoice(数组(“选择”= >中的(ArticlePeer::的getstatus())));这个美元->validatorSchema[“author_id”]->setOption(“标准”,authorCriteria美元);设置(这个美元[“created_at”]);设置(这个美元[“updated_at”]);}}
清单4-22 -AuthorForm
形式
类AuthorForm扩展BaseAuthorForm{公共函数配置(){这个美元->validatorSchema[“电子邮件”]=新sfValidatorAnd(数组(这个美元->validatorSchema[“电子邮件”],新sfValidatorEmail(),));}}
使用推动:构建表单
允许自动生成大部分元素,让表单自省对象模型。这种自动化是有帮助的,原因如下:
它使开发人员的生活更轻松,将他们从重复和冗余的工作中拯救出来。然后,他可以根据项目的特定业务规则专注于验证器和小部件定制。
此外,当数据库模式更新时,生成的表单也会自动更新。开发人员只需要调整他们所做的自定义。
类生成的操作和模板的自定义推动:generate-module
的任务。
形式序列化
上一节向我们展示了如何自定义由任务生成的表单推动:构建表单
.类生成的代码开始自定义表单的生命周期推动:generate-module
的任务。
默认值
一个Propel表单实例总是连接到一个Propel对象.类返回的类getModelName ()
方法。例如,AuthorForm
形式只能链接到属于作者
类。对象的空实例作者
类),或者作为第一个参数发送给构造函数的对象。“average”表单的构造函数以一组值作为第一个参数,而Propel表单的构造函数以一个Propel对象作为第一个参数。该节点用于定义每个表单字段的默认值。的getObject ()
方法返回与当前实例相关的对象isNew ()
方法允许知道对象是否通过构造函数发送:
//创建一个新对象authorForm美元=新AuthorForm();打印authorForm美元->getObject()->getId();//输出null打印authorForm美元->isNew();//输出true//修改现有对象美元的作者= AuthorPeer::retrieveByPk(1);authorForm美元=新AuthorForm(美元的作者);打印authorForm美元->getObject()->getId();//输出1打印authorForm美元->isNew();//输出false
处理生命周期
正如我们在本章开头所观察到的编辑
action(如清单4-23所示)处理表单生命周期。
清单4-23 - TheexecuteEdit
方法作者
模块
/ /应用程序/前端/模块/作者/行动/ actions.class.php类authorActions扩展sfActions{/ /……公共函数executeEdit(美元的请求){美元的作者= AuthorPeer::retrieveByPk(美元的请求->getParameter(“id”));这个美元->形式=新AuthorForm(美元的作者);如果(美元的请求->isMethod(“职位”)){这个美元->形式->绑定(美元的请求->getParameter(“作者”));如果(这个美元->形式->isValid()){美元的作者=这个美元->形式->保存();这个美元->重定向(的作者/编辑?id = '.美元的作者->getId());}}}}
即使编辑
Action看起来像我们在前面章节中描述的动作,我们可以指出一些不同之处:
的推进对象
作者
类作为第一个参数发送给表单构造函数:美元的作者= AuthorPeer::retrieveByPk(美元的请求->getParameter(“id”));这个美元->形式=新AuthorForm(美元的作者);
小部件
的名字
属性格式是自动自定义的,以允许检索以相关表(作者
):这个美元->形式->绑定(美元的请求->getParameter(“作者”));
当表单有效时,只需调用
save ()
方法创建或更新与表单相关的Propel对象:美元的作者=这个美元->形式->保存();
创建和修改一个驱动对象
清单4-23代码用一个方法处理对象的创建和修改作者
类:
创建一个新的
作者
对象:的
编辑
行动叫没有id
参数(请求- > getParameter (id)
是零
)调用
retrieveByPk ()
因此发送零
的
形式
对象链接到一个空对象作者
推动物体的
$ this - >形式- >保存()
调用因此创建一个new作者
当提交有效的表单时
修改现有的
作者
对象:的
编辑
Action被调用id
参数(请求- > getParameter (id)
表示主键作者
对象为修改)调用
retriveByPk ()
方法返回作者
与主键相关的对象的
形式
对象因此链接到先前找到的对象的
$ this - >形式- >保存()
调用更新作者
当提交有效的表单时
的save ()
方法
当Propel表单有效时,save ()
方法更新相关对象并将其存储在数据库中。这个方法实际上不仅存储主对象,还存储潜在相关的对象。例如,ArticleForm
表单更新连接到文章的标签。两者之间的关系文章
表和标签
表是n-n关系,与文章相关的标记保存在article_tag
表(使用saveArticleTagList ()
生成的方法)。
请注意
我们将在第9章看到save ()
方法也会自动更新国际化的表。
为了验证一致的序列化,save ()
方法包括一个事务中的所有更新。
侧边栏
使用bindAndSave ()
方法
的bindAndSave ()
方法将用户提交的输入数据绑定到表单,验证该表单并更新数据库中的相关对象,所有这些操作都在一个操作中完成:
类articleActions扩展sfActions{公共函数executeCreate(sfWebRequest美元的请求){这个美元->形式=新ArticleForm();如果(美元的请求->isMethod(“职位”)& &这个美元->形式->bindAndSave(美元的请求->getParameter(“文章”))){这个美元->重定向(“文章/创建”);}}}
处理文件上传
的save ()
方法自动更新Propel对象,但不能像管理文件上传一样处理侧元素。
让我们看看如何将文件附加到每篇文章。文件存储在web /上传
目录和对文件路径的引用保存在文件
字段文章
如清单4-24所示。
清单4-24 -文章
表与相关文件
/ /配置/模式。Yml推进:文章://…文件:varchar (255)
在每次模式更新之后,你都需要更新对象模型、数据库和相关的表单:
$ ./ob娱乐下载symfony propel:build-all
谨慎
请注意推动:构建所有
任务删除每个模式表以重新创建它们。因此,表中的数据将被覆盖。这就是为什么创建测试数据(固定装置
),可在每次模型修改处重新下载。
列表4-25显示了如何修改ArticleForm
类,以便将小部件和验证器链接到文件
字段。
清单4-25 -修改文件
字段ArticleForm
的形式。
类ArticleForm扩展BaseArticleForm{公共函数配置(){/ /……这个美元->widgetSchema[“文件”]=新sfWidgetFormInputFile();这个美元->validatorSchema[“文件”]=新sfValidatorFile();}}
至于每一个允许上传文件的表单,也不要忘记添加enctype
属性的形式
(有关文件上传管理的进一步信息,请参阅第2章)。
清单4-26显示了保存表单时应用的修改,以便将文件上传到服务器并将其路径存储在文章
对象。
清单4-26 -保存文章
对象和动作中上传的文件
公共函数executeEdit(美元的请求){美元的作者= ArticlePeer::retrieveByPk(美元的请求->getParameter(“id”));这个美元->形式=新ArticleForm(美元的作者);如果(美元的请求->isMethod(“职位”)){这个美元->形式->绑定(美元的请求->getParameter(“文章”),美元的请求->getfile(“文章”));如果(这个美元->形式->isValid()){美元的文件=这个美元->形式->getValue(“文件”);美元的文件名=sha1(美元的文件->getOriginalName()).美元的文件->getExtension(美元的文件->getOriginalExtension());美元的文件->保存(sfConfig::得到(“sf_upload_dir”).' / '.美元的文件名);美元的文章=这个美元->形式->保存();这个美元->重定向(“文章/编辑?id = '.美元的文章->getId());}}}
将上传的文件保存在文件系统上允许sfValidatedFile
对象以了解文件的绝对路径。的调用期间save ()
方法时,字段值用于更新相关对象和文件
场,sfValidatedFile
对象被转换为字符串,这得益于__toString ()
方法,返回文件的绝对路径。的文件
的列文章
表将存储这个绝对路径。
提示
对象的相对路径sfConfig: get(“sf_upload_dir”)
目录中,可以创建继承的类sfValidatedFile
并使用validated_file_class
选项发送到sfValidatorFile
验证新类的名称。然后,验证器将返回类的一个实例。在本章的剩余部分,我们将看到另一种方法,包括修改文件
列,然后在数据库中保存对象。
自定义save ()
方法
在上一节中,我们观察了如何将上传的文件保存在编辑
行动。面向对象编程的原则之一是代码的可重用性,这要归功于类中的封装。方法,而不是在每个操作中复制用于保存文件的代码ArticleForm
形式,最好是在移动它ArticleForm
类。清单4-27显示了如何重写save ()
方法,以同时保存文件并可能删除现有文件。
清单4-27 -重写save ()
方法ArticleForm
类
类ArticleForm扩展BaseFormPropel{/ /……公共函数保存(PropelPDO反对美元=零){如果(file_exists(这个美元->getObject()->getFile())){拆开(这个美元->getObject()->getFile());}美元的文件=这个美元->getValue(“文件”);美元的文件名=sha1(美元的文件->getOriginalName()).美元的文件->getExtension(美元的文件->getOriginalExtension());美元的文件->保存(sfConfig::得到(“sf_upload_dir”).' / '.美元的文件名);返回父::保存(反对美元);}}
将代码移动到表单后,将编辑
方法最初生成的代码与操作相同推动:generate-module
的任务。
侧边栏
重构表单模型中的代码
类生成的操作推动:generate-module
任务通常不应该被修改。
逻辑可以添加到编辑
动作,尤其是在表单序列化期间,通常必须在模型类或表单类中移动。
我们刚刚讨论了表单类中重构的示例,以便考虑上传文件的存储。让我们再举一个与该模型相关的例子。的ArticleForm
表单有一个鼻涕虫
字段。我们观察到,该字段应该从标题
可能被用户覆盖的字段名。这种逻辑不依赖于形式。因此,它属于模型,如下所示的代码:
类文章扩展BaseArticle{公共函数保存(PropelPDO反对美元=零){如果(!这个美元->getSlug()){这个美元->setSlugFromTitle();}返回父::保存(反对美元);}受保护的函数setSlugFromTitle(){/ /……}}
这些重构的主要目标是尊重应用程序层的分离,特别是开发的可重用性。
自定义doSave ()
方法
我们观察到,保存对象是在事务中进行的,以保证与保存相关的每个操作都被正确处理。当重写save ()
方法,就像我们在前一节中为保存上传的文件所做的那样,执行的代码独立于此事务。
清单4-28显示了如何使用doSave ()
方法在全局事务中插入保存上载文件的代码。
清单4-28 -重写doSave ()
方法中的ArticleForm
形式
类ArticleForm扩展BaseFormPropel{/ /……受保护的函数doSave(反对美元=零){如果(file_exists(这个美元->getObject()->getFile())){拆开(这个美元->getObject()->getFile());}美元的文件=这个美元->getValue(“文件”);美元的文件名=sha1(美元的文件->getOriginalName()).美元的文件->getExtension(美元的文件->getOriginalExtension());美元的文件->保存(sfConfig::得到(“sf_upload_dir”).' / '.美元的文件名);返回父::doSave(反对美元);}}
的doSave ()
方法在创建的事务中调用save ()
方法时,如果调用save ()
方法文件()
对象抛出异常,则该对象将不会被保存。
自定义updateObject ()
方法
在更新到保存到数据库之间,有时需要修改连接到表单的对象。
在我们的文件上载示例中,将上载文件的绝对路径存储在文件
列时,我们希望存储相对于sfConfig: get(“sf_upload_dir”)
目录中。
清单4-29显示了如何重写updateObject ()
方法ArticleForm
形式,以改变的值文件
自动更新对象之后但保存之前的列。
清单4-29 -重写updateObject ()
方法和ArticleForm
类
类ArticleForm扩展BaseFormPropel{/ /……公共函数updateObject(美元的价值=零){美元的对象=父::updateObject(美元的价值);美元的对象->setFile(str_replace(sfConfig::得到(“sf_upload_dir”).' / ',”,美元的对象->getFile()));返回美元的对象;}}
的updateObject ()
方法调用doSave ()
方法,然后将对象保存到数据库中。
本作品在创作共用署名相似共享3.0未移植许可许可下获得许可。