热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

CakePHP2.xCookBook中文版第七章模型之保存数据

CakePHP会为保存模型数据制作快照。准备保存的数据使用如下基本格式传递给模型的save()方法:

保存数据

CakePHP 会为保存模型数据制作快照。准备保存的数据使用如下基本格式传递给模型的 save() 方法:

1 Array 2 ( 3 [ModelName] => Array 4  ( 5 [fieldname1] => 'value' 6 [fieldname2] => 'value' 7  ) 8 )

多数时候你无需担心这种格式: CakePHP 的 FormHelper 和模型的 find 方法都用这种格式打包所有数据。如果使用其它的助手,数据也能方便地以 $this->request->data 形式使用。

下面是使用 CakePHP 模型向数据库表存入数据的控制器动作的示例:

 1 public function edit($id) {  2 // 有表单数据被 POST?  3 if ($this->request->is('post')) {  4 // 如果表单数据能够通过校验并保存...  5 if ($this->Recipe->save($this->request->data)) {  6 // 设置 session 跳转信息并跳转  7 $this->Session->setFlash('Recipe Saved!');  8 $this->redirect('/recipes');  9  } 10  } 11 12 // 如果没有表单数据,查找被编辑的 recipe 并将其赋值给视图。 13 $this->set('recipe', $this->Recipe->findById($id)); 14 }

在 save 方法被调用时,在第一个参数中传递给它的数据,被 CakePHP 校验机制校验(更多信息请参见 数据校验 一节)。 如果因为某些原因,数据没有被保存,检查一下是不是没有符合某些校验规则。 可以通过输出Model::$validationErrors 来 debug 这种情况。

1 if ($this->Recipe->save($this->request->data)) { 2 // "保存" 成功后的处理逻辑 3 } 4 debug($this->Recipe->validationErrors);

其它一些与保存相关的有用的模型方法:

Model::set($one, $two = null)

Model::set() 能够用于将数据的一个或多个列放入模型的 data 数组。当使用带有由 Model 提供的 ActiveRecord 特性的模型时很有用:

1 $this->Post->read(null, 1); 2 $this->Post->set('title', 'New title for the article'); 3 $this->Post->save();

此例展示了如何使用 ActiveRecord 的 set() 方法更新和保存单个列。还可以使用 set() 给多个列赋新值:

1 $this->Post->read(null, 1); 2 $this->Post->set(array( 3 'title' => 'New title', 4 'published' => false 5 )); 6 $this->Post->save();

上例将更新 thitle 和 published 列并保存到数据库中。

Model::save(array $data = null, boolean $validate =true, array $fieldList = array())

这个方法保存数组格式的数据。第二个参数允许跳过校验,第三个参数允许提供要保存的模型的列的列表。为了提高安全性,可以使用 $fieldList 限制要保存的列。

注解

如果不提供 $fieldList,恶意用户能够向表单数据中添加附加的列(在你没有使用 SecurityComponent 的情况下),并通过这种方法来改变原本不可以被改变的列。

save 方法还有一个替代语法:

1 save(array $data = null, array $params = array())

$params 数组可以用如下选项作为其键:

  • validate 设置为 true/false 能够 允许/禁止 校验。
  • fieldList 允许保存的列构成的数组。
  • callbacks 设置为 false 将禁止回调。使用 ‘before’ 或 ‘after’ 将仅允许指定的回调。

关于模型回调的更多信息请参见 这里

小技巧

如果你不想更新列在保存某些数据时被更新,在 $data 数组中添加 'updated' => false

一旦保存完成,可以使用模型对象的 $id 属性获得对象的 ID - 在创建新对象时可能会非常有用。

1 $this->Ingredient->save($newData); 2 $newIngredientId = $this->Ingredient->id;

创建或更新是通过模型的 id 列来控制的。如果设置了 $Model->id,带有这个主键的记录将被更新。 其它情况下,一条新记录被创建:

1 // 创建新记录: id 没有设置或设置为 null 2 $this->Recipe->create(); 3 $this->Recipe->save($this->request->data); 4 5 // 更新记录: id 被设置为一个数字值 6 $this->Recipe->id = 2; 7 $this->Recipe->save($this->request->data);

小技巧

在循环中调用 save 时,不要忘记调用 create() 。

如果想更新一个值,而不是创建一条新记录,必须确保向数据数组传递了主键列:

1 $data = array('id' => 10, 'title' => 'My new title'); 2 // 将更新 id 为 10 的 Recipe 记录 3 $this->Recipe->save($data);
Model::create(array $data = array())

这个方法为保存新信息重置模型的状态。 实际上它并不在数据库中创建新记录,而是清除预先设置的 Model::$id,并在 Model::$data 中设置基于数据库列默认的默认值。

如果传递了 $data 参数(使用上面描述的数组格式),模型实例将准备保存这些数据(使用 $this->data)。

如果用 false 代替一个数组传递给此方法,模型实际将不根据之前没有设置的模型结构来初始化列,而仅仅重置已经设置的列, 并且保留未设置的列。 这么做是为了避免更新数据库中已经设置的列的值。

小技巧

如果想要用插入一个新行来代替更新已经存在的一行,必须先调用 create()。这样能够避免与回调或者其它位置中曾调用过的 save 发生冲突。

Model::saveField(string $fieldName, string$fieldValue, $validate = false)

用于保存单个列的值。在使用 saveField() 之前要先设置模型的 ID ($this->ModelName->id = $id)。在使用这个方法时,$fieldName 仅需要包含列名,不需要模型名和列。

例如,更新一条博客的标题,可以用如下方式在控制器中调用 saveField

1 $this->Post->saveField('title', 'A New Title for a New Day');

警告

在使用这个方法更新时不能停止更新列,你需要使用 save() 方法。

saveField 方法也有一个替代语法:

1 saveField(string $fieldName, string $fieldValue, array $params = array())

$params 数组可以用如下选项作为其键:

  • validate 设置为 true/false 能够 允许/禁止 校验。
  • callbacks 设置为 false 将禁止回调。使用 ‘before’ 或 ‘after’ 将仅允许指定的回调。

Model::updateAll(array $fields, array $conditions)

一次调用更新一条或多条记录。被更新的记录通过 $conditions 数组标识,$fields 参数指定的列和值被更新。

例如,批准所有成为会员超过一年的面包师,调用如下的更新语句:

1 $this_year = date('Y-m-d h:i:s', strtotime('-1 year')); 2 3 $this->Baker->updateAll( 4 array('Baker.approved' => true), 5 array('Baker.created <=' => $this_year) 6 );

小技巧

$fields 数组接受 SQL 表达式。字面值使用 Sanitize::escape() 手动引用。

注解

即使列中存在的编辑列被更新,它也不会通过 ORM 自动更新。必须手动将其加入到你想更新的数组中。

例如,关闭所有属于指定客户的所有门票:

1 $this->Ticket->updateAll( 2 array('Ticket.status' => "'closed'"), 3 array('Ticket.customer_id' => 453) 4 );

默认情况下,updateAll() 将自动连接支持 join 的数据库的 belongsTo 关联。通过临时绑定关联能够防止这种连接。

Model::saveMany(array $data = null, array $options= array())

此方法用于同时保存同一模型的多行。可以带有如下选项:

  • validate: 设置为 false 将禁止校验,设置为 true 将在保存前校验每条记录,设置为 ‘first’(此为默认值) 将在任意一条被保存前检查 全部 记录。
  • atomic: 如果为 true(默认),将在单个指令中保存所有记录,如果 数据库/表 不支持单指令需要设置为 false。
  • fieldList: 与 Model::save() 方法的 $fieldList 参数相同。
  • deep: (自 2.1 版开始)如果设置为 true,关联数据也被保存,参见 saveAssociated。

为单个模型保存多条记录,$data 需要是数字索引的记录数组:

1 $data = array( 2 array('title' => 'title 1'), 3 array('title' => 'title 2'), 4 );

注解

我们传递了数字索引代替了通常情况下 $data 包含的 Article 键。在保存同一模型的多条记录时,记录数组需要使用数字索引,而不是模型的键。

它也可以接受如下格式的数据:

1 $data = array( 2 array('Article' => array('title' => 'title 1')), 3 array('Article' => array('title' => 'title 2')), 4 );

如果还要保存带有 $options['deep'] = true 的关联数据,上面的两个例子将类似于下面的代码:

1 $data = array( 2 array('title' => 'title 1', 'Assoc' => array('field' => 'value')), 3 array('title' => 'title 2'), 4 ); 5 $data = array( 6 array('Article' => array('title' => 'title 1'), 'Assoc' => array('field' => 'value')), 7 array('Article' => array('title' => 'title 2')), 8 ); 9 $Model->saveMany($data, array('deep' => true));

切记,如果想用更新记录代替创建新记录,需要向数据行添加主键索引:

1 $data = array( 2 array('Article' => array('title' => 'New article')), // 创建新记录 3 array('Article' => array('id' => 2, 'title' => 'title 2')), // 更新存在的记录 4 );

Model::saveAssociated(array $data = null, array$options = array())


此方法用于一次保存多个模型关联。可以带有如下选项:

  • validate: 设置为 false 将禁止校验,设置为 true 将在保存前校验每条记录,设置为 ‘first’(此为默认值) 将在任意一条被保存前检查 全部 记录。
  • atomic: 如果为 true(默认),将在单个指令中保存所有记录,如果 数据库/表 不支持单指令需要设置为 false。
  • fieldList: 与 Model::save() 方法的 $fieldList 参数相同。
  • deep:(自 2.1 版开始)如果设置为 true,关联数据也被保存,参见 saveAssociated。

为了保存记录的同时保存与其有着 hasOne 或者 belongsTo 关联的记录,data 数组看起来就像下面这样:

1 $data = array( 2 'User' => array('username' => 'billy'), 3 'Profile' => array('sex' => 'Male', 'occupation' => 'Programmer'), 4 );

为了保存记录的同时,保存与其有着 hasMany 关联的记录,data 数组看起来就像下面这样:

1 $data = array( 2 'Article' => array('title' => 'My first article'), 3 'Comment' => array( 4 array('body' => 'Comment 1', 'user_id' => 1), 5 array('body' => 'Comment 2', 'user_id' => 12), 6 array('body' => 'Comment 3', 'user_id' => 40), 7 ), 8 );

为了保存记录的同时保存与其有着超过两层深度的 hasMany 关联的记录,data 数组看起来就像下面这样:

 1 $data = array(  2 'User' => array('email' => 'john-doe@cakephp.org'),  3 'Cart' => array(  4 array(  5 'payment_status_id' => 2,  6 'total_cost' => 250,  7 'CartItem' => array(  8 array(  9 'cart_product_id' => 3, 10 'quantity' => 1, 11 'cost' => 100, 12 ), 13 array( 14 'cart_product_id' => 5, 15 'quantity' => 1, 16 'cost' => 150, 17  ) 18  ) 19  ) 20  ) 21 );

注解

如果保存成功,主模型的外键将被存储在相关模型的 id 列中,例如 $this->RelatedModel->id

警告

在调用 atomic 选项设置为 false 的 saveAssociated 方法时要小心的进行检查,它返回的是一个数组,而不是逻辑值。

在 2.1 版更改: 现在你可以保存深层关联的数据(用 $options['deep'] = true 设置)。

为了保存记录的同时,保存与其有 hasMany 关联的相关记录及深层关联的 Comment belongsTo User 数据,data 数组看起来就像下面这样::

1 $data = array( 2 'Article' => array('title' => 'My first article'), 3 'Comment' => array( 4 array('body' => 'Comment 1', 'user_id' => 1), 5 array('body' => 'Save a new user as well', 'User' => array('first' => 'mad', 'last' => 'coder')), 6 ), 7 );

并用如下语句进行保存:

1 $Article->saveAssociated($data, array('deep' => true));

在 2.1 版更改: Model::saveAll() 和同族方法现在支持为多个模型传递 fieldList。

为多个模型传递 fieldList 的例子:

1 $this->SomeModel->saveAll($data, array( 2 'fieldList' => array( 3 'SomeModel' => array('field_1'), 4 'AssociatedModel' => array('field_2', 'field_3') 5  ) 6 ));

fieldList 是一个以模型别名为键,以列构成的数组作为值的数组。 模型名如同在被保存的数据中那样,不能嵌套。

Model::saveAll(array $data = null, array $options =array())

saveAll 函数只是 savaMany 和 saveAssociated 方法的包装器。它检查数据并且决定执行哪种数据保存类型。它查看数据并决定执行哪种类型的保存。如果数据是数字索引数组,saveMany 被调用,否则 saveAssociated 被调用。

此函数的选项与前面的两个函数相同,并向后兼容。推荐根据实际情况使用 saveMany 或 saveAssociated

保存相关模型的数据(hasOne, hasMany, belongsTo)

在与关联模型一起工作时,When working with associated models, 一定要意识到模型数据的保存总是由相应有 CakePHP 模型来完成。如果保存一条新的 Post 和它关联的 Comment,就需要在保存操作的过程中同时使用 Post 和 Comment 模型。

如果系统中还不存在关联模型记录(例如,想要保存新的 User,同时保存相关的 Profile 记录),需要先保存主模型或者父模型。

为了了解这是如何工作的,想像一下我们在处理保存新用 User 和相关 Profile 的控制器中有一个动作。下面的示例动作假设已经为创建单个 User 和单个 Profile,POST 了足够的数据(使用 FormHelper):

 1 public function add() {  2 if (!empty($this->request->data)) {  3 // 我们能保存 User 数据:  4  // 它放在 $this->request->data['User'] 中  5  6 $user = $this->User->save($this->request->data);  7  8 // 如果用户被保存,添加这条信息到数据并保存 Profile。  9 10 if (!empty($user)) { 11 // 新创建的 User ID 已经被赋值给 $this->User->id. 12 $this->request->data['Profile']['user_id'] = $this->User->id; 13 14 // 由于 User hasOne Profile,因此可以通过 User 模型访问 Profile 模型: 15 $this->User->Profile->save($this->request->data); 16  } 17  } 18 }

作为一条规则,当带有 hasOne、hasMany、belongsTo 关联时,全部与键有关。基本思路是从一个模型中获取键,并将其放入另一个模型的外键列中。有时需要涉及使用保存后的模型类的 $id 属性,但是其它情况下只涉及从 POST 给控制器动作的表单的隐藏域(hidden input)中得到的 ID。

作为上述基本方法的补充,CakePHP 还提供了一个非常有用的方法 saveAssociated(),它允许你用一个简短的方式校验和保存多个模型。另外,saveAssociated() 还提供了事务支持以确保数据库中的数据的完整(例如,一个模型保存失败,另一个模型也就不保存了)。

注解

为使事务工作在 MySQL 中正常工作,表必须使用 InnoDB 引擎。记住,MyISAM 表不支持事务。

来看看如何使用 saveAssociated() 同时保存 Company 和 Account 模型吧。

首先,需要同时为 Company 和 Account 创建表单(假设 Company hasMany Account):

 1 echo $this->Form->create('Company', array('action' => 'add'));  2 echo $this->Form->input('Company.name', array('label' => 'Company name'));  3 echo $this->Form->input('Company.description');  4 echo $this->Form->input('Company.location');  5  6 echo $this->Form->input('Account.0.name', array('label' => 'Account name'));  7 echo $this->Form->input('Account.0.username');  8 echo $this->Form->input('Account.0.email');  9 10 echo $this->Form->end('Add');

看看为 Acount 模型命名表单列的方法。如果 Company 是主模型,saveAssociated() 期望相关模型(Account)数据以指定的格式放进数组。并且拥有我们需要的 Account.0.fieldName

注解

上面的列命名对于 hasMany 关联是必须的。如果关联是 hasOne,你就得为关联模型使用 ModelName.fieldName 了。

现在,可以在 CompaniesController 中创建 add() 动作了:

1 public function add() { 2 if (!empty($this->request->data)) { 3 // 使用如下方式避免校验错误: 4 unset($this->Company->Account->validate['company_id']); 5 $this->Company->saveAssociated($this->request->data); 6  } 7 }

这就是全部的步骤了。现在 Company 和 Account 模型将同时被校验和保存。默认情况下,saveAssociated 将检验传递过来的全部值,然后尝试执行每一个保存。

通过数据保存 hasMany

让我们来看看存在在 join 表里的两个模型的数据是如何保存的。就像 hasMany 贯穿 (连接模型) 一节展示的那样,join 表是用 hasMany 类型的关系关联到每个模型的。 我们的例子包括 Cake 学校的负责人要求我们写一个程序允许它记录一个学生在某门课上出勤的天数和等级。下面是示例代码:

 1 // Controller/CourseMembershipController.php  2 class CourseMembershipsController extends AppController {  3 public $uses = array('CourseMembership');  4  5 public function index() {  6 $this->set('courseMembershipsList', $this->CourseMembership->find('all'));  7  }  8  9 public function add() { 
            var cpro_id = "u6885494";

        
        
    
推荐阅读
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • Java序列化对象传给PHP的方法及原理解析
    本文介绍了Java序列化对象传给PHP的方法及原理,包括Java对象传递的方式、序列化的方式、PHP中的序列化用法介绍、Java是否能反序列化PHP的数据、Java序列化的原理以及解决Java序列化中的问题。同时还解释了序列化的概念和作用,以及代码执行序列化所需要的权限。最后指出,序列化会将对象实例的所有字段都进行序列化,使得数据能够被表示为实例的序列化数据,但只有能够解释该格式的代码才能够确定数据的内容。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了adg架构设置在企业数据治理中的应用。随着信息技术的发展,企业IT系统的快速发展使得数据成为企业业务增长的新动力,但同时也带来了数据冗余、数据难发现、效率低下、资源消耗等问题。本文讨论了企业面临的几类尖锐问题,并提出了解决方案,包括确保库表结构与系统测试版本一致、避免数据冗余、快速定位问题等。此外,本文还探讨了adg架构在大版本升级、上云服务和微服务治理方面的应用。通过本文的介绍,读者可以了解到adg架构设置的重要性及其在企业数据治理中的应用。 ... [详细]
  • 本文详细介绍了云服务器API接口的概念和作用,以及如何使用API接口管理云上资源和开发应用程序。通过创建实例API、调整实例配置API、关闭实例API和退还实例API等功能,可以实现云服务器的创建、配置修改和销毁等操作。对于想要学习云服务器API接口的人来说,本文提供了详细的入门指南和使用方法。如果想进一步了解相关知识或阅读更多相关文章,请关注编程笔记行业资讯频道。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • 信息安全等级保护是指对国家秘密信息、法人和其他组织及公民的专有信息以及公开信息和存储、传输、处理这些信息的信息系统分等级实行安全保护,对信息系统中使用的信息安全产品实 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • t-io 2.0.0发布-法网天眼第一版的回顾和更新说明
    本文回顾了t-io 1.x版本的工程结构和性能数据,并介绍了t-io在码云上的成绩和用户反馈。同时,还提到了@openSeLi同学发布的t-io 30W长连接并发压力测试报告。最后,详细介绍了t-io 2.0.0版本的更新内容,包括更简洁的使用方式和内置的httpsession功能。 ... [详细]
  • 本文详细介绍了相机防抖的设置方法和使用技巧,包括索尼防抖设置、VR和Stabilizer档位的选择、机身菜单设置等。同时解释了相机防抖的原理,包括电子防抖和光学防抖的区别,以及它们对画质细节的影响。此外,还提到了一些运动相机的防抖方法,如大疆的Osmo Action的Rock Steady技术。通过本文,你将更好地理解相机防抖的重要性和使用技巧,提高拍摄体验。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • 本文详细介绍了华为4GLTE路由器B310的外置天线安装和设置方法。通过连接电源和网线,输入路由器的IP并登陆设置页面,选择手动设置和手动因特网设置,输入ISP提供商的用户名和密码,并设置MTU值。同时,还介绍了无线加密的设置方法。最后,将外网线连在路由器的WAN口即可使用。 ... [详细]
  • 本文讨论了前端工程化的准备工作,主要包括性能优化、安全防护和监控等方面需要注意的事项。通过系统的答案,帮助前端开发者更好地进行工程化的准备工作,提升网站的性能、安全性和监控能力。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • MyBatis错题分析解析及注意事项
    本文对MyBatis的错题进行了分析和解析,同时介绍了使用MyBatis时需要注意的一些事项,如resultMap的使用、SqlSession和SqlSessionFactory的获取方式、动态SQL中的else元素和when元素的使用、resource属性和url属性的配置方式、typeAliases的使用方法等。同时还指出了在属性名与查询字段名不一致时需要使用resultMap进行结果映射,而不能使用resultType。 ... [详细]
author-avatar
手机用户2502857341
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有