GVKun编程网logo

可怕的Evercookie和CakePHP

8

对于想了解可怕的Evercookie和CakePHP的读者,本文将提供新的信息,并且为您提供关于CakePHP2.xCookBook中文版第一章欢迎、CakePHP2.xCookBook中文版第七章模

对于想了解可怕的Evercookie和CakePHP的读者,本文将提供新的信息,并且为您提供关于CakePHP 2.x CookBook 中文版 第一章 欢迎、CakePHP 2.x CookBook 中文版 第七章 模型、CakePHP 2.x CookBook 中文版 第七章 模型 之 保存数据_PHP教程、CakePHP 2.x CookBook 中文版 第七章 模型 之 关联(一)的有价值信息。

本文目录一览:

可怕的Evercookie和CakePHP

可怕的Evercookie和CakePHP

所以我试图在cake PHP网站上实现 evercookie,但是我得到了一些非常非常奇怪的结果.我刚刚将代码文件复制并粘贴到我的app / webroot目录中,我似乎没有得到任何404错误,但我的cookie没有保存 – 他们每次都被重写.甚至更奇怪的是,当我加载页面时,它向Google.com发送至少90个GET请求,并在Google Chrome中存储4-5个sqlite数据库; evercookie网站只存储一个.

我生成的HTML页面中的代码是这样的:

var ec = new evercookie();

// set a cookie "id" to a random 10 character string
// usage: ec.set(key,value)
ec.set("id","vm5m172dyg");


// retrieve a cookie called "id" (simply)
ec.get("id",function(value) { alert("Cookie value is " + value) });



// or use a more advanced callback function for getting our cookie
// the cookie value is the first param
// an object containing the different storage methods
// and returned cookie values is the second parameter

function getCookie(best_candidate,all_candidates)

    {

    alert("The retrieved cookie is: " + best_candidate + "\n" +
        "You can see what each storage mechanism returned " +
        "by looping through the all_candidates object.");

         for (var item in all_candidates){

        document.write("Storage mechanism " + item +
                  " returned: " + all_candidates[item] + "<br>");

     }

}

    ec.get("id",getCookie);



// we look for "candidates" based off the number of "cookies" that
// come back matching since it's possible for mismatching cookies.
// the best candidate is most likely the correct one

这部分代码写入我的文档,这是输出(对我来说很好):

Storage mechanism userData returned: undefined
Storage mechanism cookieData returned: d9g6mfoo4y
Storage mechanism localData returned: d9g6mfoo4y
Storage mechanism globalData returned: undefined
Storage mechanism sessionData returned: d9g6mfoo4y
Storage mechanism windowData returned: d9g6mfoo4y
Storage mechanism historyData returned: undefined
Storage mechanism pngData returned: d9g6mfoo4y
Storage mechanism etagData returned: d9g6mfoo4y
Storage mechanism cacheData returned: d9g6mfoo4y
Storage mechanism dbData returned: d9g6mfoo4y
Storage mechanism lsoData returned: d9g6mfoo4y
Storage mechanism slData returned: d9g6mfoo4y

我的问题是如何阻止发送给Google的90个请求?我不知道它为什么这样做.如果我在网站上有十个用户(这并不令人难以置信),那就超过了900(0).你有没有想过为什么每次刷新页面时cookie都会重置?这正是我想要阻止的.

解决方法

好吧,我不觉得傻!事实证明,代码开头的ec.set()调用是在每个页面加载开始时设置cookie.所以,我调整了一些东西,呃,它现在正在运作.而且我不再向Google发送90个请求.

// retrieve a cookie called "id" (simply)
ec.get("id",function(value) { 
    if(value == undefined){
        // set a cookie "id" to a random 10 character string
        // usage: ec.set(key,value)
        ec.set("id","<?PHP echo $hash ?>");
    }
    else
    {
        // do nothing
    }
});

CakePHP 2.x CookBook 中文版 第一章 欢迎

CakePHP 2.x CookBook 中文版 第一章 欢迎

 欢迎

CakePHP cookbook 是一个开放的编程社区可编辑文档项目。我们希望保持一个高质量的、有效的、准确的文档。

随处可读

差不多在任何地方都可以享受 CakePHP cookbook. PDF和EPUB格式现在都可用,你可以在更多的设备上离线阅读本文档。

  • PDF
  • EPUB
  • 源代码

入门

创建一个博客

从 博客介绍 开始。 你能够学到 CakePHP 的基本用法,并能在学习过程中创建一个基本的博客系统。

约定

学到 CakePHP 中典型的 CakePHP 请求 和 约定

控制器

控制器处理请求,帮助协调和准备给客户端的应答。详情请参阅 控制器

视图

视图是 CakePHP 的表现层。它们将来自模型的数据转换成客户端请求的输出格式。详情请参阅 视图

模型

模型是应用程序的心脏,它们处理校验、存储和获取数据。详情请参阅 模型

获得帮助

如果你遇到阻碍,到这里寻找帮助: 获取帮助

CakePHP 2.x CookBook 中文版 第七章 模型

CakePHP 2.x CookBook 中文版 第七章 模型

模型

模型是应用程序中业务层的类。

这意味着它们负责管理工作域中几乎所有的与数据有关的东西:数据校验、交互和信息流演化。

通常模型类代理数据,用于 CakePHP 应用程序的数据访问,多数时候它们代理数据库表,但并不限于此,它也可以访问操纵数据的任何事物,如文件、外部 web service、iCal 事件或者一个 CSV 文件的行。

一个模型可以与其它模型相关联。例如,一个 Recipe 能够与食谱的 Author 及 食谱的 Ingredient 相关联。

这一节将说明模型的哪些特性可以是自动化的,如何覆盖这些特性,以及模型都有哪些方法和属性。还说明了关联数据的不同方法。描述了如果查找、保存和删除数据。最后,讲解了数据源。

理解模型

模型描述了你的数据模型。在面向对象编程中,一个数据模型是描述一件事的对象,比如一辆汽车、一个人、一幢房子。举例来说,一个博客可能有很多博客文章,每篇文章可能有很多评论。博客、文章和评论是全部示例模型,每一个都与其它的相关联。

下面是在 CakePHP 中定义的模型的简单示例:

class Ingredient extends AppModel { public $name = ''Ingredient''; }

只 需这样一个简单的声明,Ingredient 模型就被赋予了建立保存和删除数据的查询所需的全部功能。这些魔术方法继承自 CakePHP 的 Model 类。Ingredient 模型继承了应用程序模型 AppModel(一个扩展自内部 Model 类的模型类)。它是向 Ingredient 模型赋予功能的核心模型类。

介 于内部 Model 类和最终的模型类之间的 AppModel 类,在你创建专属于自己的 AppModel 类之前是空的,它位于 CakePHP 内核文件夹。覆写 AppModel 类允许你定义对应用程序中所有的模型类都可用的功能。要做到这一点,只需要在 Model 文件夹中建立你自己的 AppModel.php ,和应用程序中的其它模型类放在一起。使用 Bake 建立项目时,会自动为你生成这个文件。

关于如何向多个模型添加相同的逻辑的更多信息,请参见 Behaviors 。

回到我们的 Ingredient 模型,为了在其上工作,需要在 /app/Model 文件夹创建 PHP 文件。按照约定它的名字应该与类相同,对于本例,就是 Ingredient.php 。

注解

如 果 CakePHP 没有在 /app/Model 文件夹中找到符合条件的文件,它将动态创建一个模型对象。这意味关如果你的模型文件命名错误(例如 ingredient.php 或者 Ingredients.php),CakePHP 将使用出乎你意料的 AppModel 的实例(按照 CakePHP 自己的思维方式)。如果你尝试使用你已经定义在你的模型听方法,或者附加到你的模型上的行为,你会收到一个以你调用的方法名标识的 SQL 错误信息 - 它是 CakePHP 找不到你的模型的明确提示,你需要检查文件名、应用程序缓存,或者两者都检查一下。

注解

一些类名不能用于模型名。举例来说,’‘File’’ 不能使用,因为 ‘’File’’ 是 CakePHP 内核中已经存在的类。

定义过的模型就可以在 控制器 中访问了。CakePHP 会自动使与当前控制器名字相同的模型可用。例如,如果一个控制器的名字是 IngredientsController,将自动初始化 Ingredient 模型并将其赋予此控制器的 $this->Ingredient 变量:

class IngredientsController extends AppController { public function index() { //抓取所有的 ingredients 并将其传递给视图: $ingredients = $this->Ingredient->find(''all''); $this->set(''ingredients'', $ingredients); } }

关联模型通过主模型访问。在上面的例子中,Recipe 与 Ingredient 模型关联:

class Recipe extends AppModel { public function steakRecipes() { $ingredient = $this->Ingredient->findByName(''Steak''); return $this->findAllByMainIngredient($ingredient[''Ingredient''][''id'']); } }

上面的代码显示了如何使用已经连接的模型。想了解如何定义关联请移步至 Associations section

关于模型的更多...

  • 关联:将模型连接在一起
    • 关系类型
    • hasOne
    • belongsTo
    • hasMany
    • counterCache - 缓存你的 count()
    • hasAndBelongsToMany (HABTM)
    • hasMany 贯穿 (连接模型)
    • 在运行期间创建和销毁关联
    • 同一模型上的多个关系
    • 连接表
  • 检索数据
    • find
    • find(‘first’)
    • find(‘count’)
    • find(‘all’)
    • find(‘list’)
    • find(‘threaded’)
    • find(‘neighbors’)
    • 创建自定义 find 类型
    • 魔术查找类型
      • findAllBy
      • findBy
    • Model::query()
    • Model::field()
    • Model::read()
    • 复杂的查找条件
      • 子查询
      • 预处理语句
  • 保存数据
    • Model::set($one, $two = null)
    • Model::save(array $data = null, boolean $validate = true, array $fieldList = array())
    • Model::create(array $data = array())
    • Model::saveField(string $fieldName, string $fieldValue, $validate = false)
    • Model::updateAll(array $fields, array $conditions)
    • Model::saveMany(array $data = null, array $options = array())
    • Model::saveAssociated(array $data = null, array $options = array())
    • Model::saveAll(array $data = null, array $options = array())
    • 保存相关模型的数据(hasOne, hasMany, belongsTo)
    • 通过数据保存 hasMany
      • 保存相关模型数据 (HABTM)
        • 当 HABTM 变得复杂时怎么办?
    • 数据表
      • 使用 created 和 modified 列
  • 删除数据
    • delete
    • deleteAll
  • 数据校验
    • Simple Rules
    • One Rule Per Field
      • rule
      • required
      • allowEmpty
      • on
      • message
    • Multiple Rules per Field
      • last
    • Custom Validation Rules
      • Custom Regular Expression Validation
      • Adding your own Validation Methods
    • Dynamically change validation rules
      • Adding new validation rules
      • Modifying current validation rules
      • Removing rules from the set
    • Core Validation Rules
    • Localized Validation
      • Validating Data from the Controller
  • 回调方法
    • beforeFind
    • afterFind
    • beforeValidate
    • beforeSave
    • afterSave
    • beforeDelete
    • afterDelete
    • onError
  • 行为
    • Using Behaviors
    • Creating Behaviors
    • Creating behavior methods
      • Mapped methods
    • Behavior callbacks
      • Creating a behavior callback
  • 数据源
    • Basic API For DataSources
    • An Example
    • Plugin DataSources
  • 模型属性
    • useDbConfig
    • useTable
    • tablePrefix
    • 主键
    • displayField
    • recursive
    • order
    • data
    • _schema
    • validate
    • virtualFields
    • name
    • cacheQueries
  • 附加的方法和附属
    • Model::associations()
    • Model::buildQuery(string $type = ''first'', array $query = array())
    • Model::deconstruct(string $field, mixed $data)
    • Model::escapeField(string $field = null, string $alias = null)
    • Model::exists($id)
    • Model::getAffectedRows()
    • Model::getAssociated(string $type = null)
    • Model::getColumnType(string $column)
    • Model::getColumnTypes()
    • Model::getID(integer $list = 0)
    • Model::getInsertID()
    • Model::getLastInsertID()
  • 虚拟列
    • Creating virtual fields
    • Using virtual fields
      • Model::hasField()
      • Model::isVirtualField()
      • Model::getVirtualField()
      • Model::find() and virtual fields
      • Pagination and virtual fields
    • Virtual fields and model aliases
    • Virtual fields in SQL queries
    • Limitations of virtualFields
  • 事务
    • Nested Transactions

CakePHP 2.x CookBook 中文版 第七章 模型 之 保存数据_PHP教程

CakePHP 2.x CookBook 中文版 第七章 模型 之 保存数据_PHP教程

保存数据

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);
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''));
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() {
10         if ($this->request->is(''post'')) {
11             if ($this->CourseMembership->saveAssociated($this->request->data)) {
12                 $this->redirect(array(''action'' => ''index''));
13             }
14         }
15     }
16 }
17 
18 // View/CourseMemberships/add.ctp
19 
20 Form->create(''CourseMembership''); ?>
21 Form->input(''Student.first_name''); ?>
22 Form->input(''Student.last_name''); ?>
23 Form->input(''Course.name''); ?>
24 Form->input(''CourseMembership.days_attended''); ?>
25 Form->input(''CourseMembership.grade''); ?>
26    
27 Form->end(); ?>
提交的数据数组如下:
 
 1 Array
 2 (
 3     [Student] => Array
 4     (
 5         [first_name] => Joe
 6         [last_name] => Bloggs
 7     )
 8 
 9     [Course] => Array
10     (
11         [name] => Cake
12     )
13 
14     [CourseMembership] => Array
15     (
16         [days_attended] => 5
17         [grade] => A
18     )
19 
20 )
Cake 会很乐意使用一个带有这种数据结构的 saveAssociated 调用就能同时保存很多,并将 Student 和 Course 的外键赋予 CouseMembership. 如果我们运行 CourseMembershipsController 上的 index 动作,从 find(‘all’) 中获取的数据结构如下:
 
 1 Array
 2 (
 3     [0] => Array
 4     (
 5         [CourseMembership] => Array
 6         (
 7             [id] => 1
 8             [student_id] => 1
 9             [course_id] => 1
10             [days_attended] => 5
11             [grade] => A
12         )
13 
14         [Student] => Array
15         (
16             [id] => 1
17             [first_name] => Joe
18             [last_name] => Bloggs
19         )
20 
21         [Course] => Array
22         (
23             [id] => 1
24             [name] => Cake
25         )
26     )
27 )
当然,还有很多带有连接模型的工作的方法。上面的版本假定你想要立刻保存每样东西。 还有这样的情况:你想独立地创建 Student 和 Course,稍后再指定两者与 CourseMembership 的关联。 因此你可能有一个允许利用列表或ID选择存在的学生和课程及两个 CourseMembership 元列的表单,例如:
 
1 // View/CourseMemberships/add.ctp
3 Form->create(''CourseMembership''); ?>
4 Form->input(''Student.id'', array(''type'' => ''text'', ''label'' => ''Student ID'', ''default'' => 1)); ?>
5 Form->input(''Course.id'', array(''type'' => ''text'', ''label'' => ''Course ID'', ''default'' => 1)); ?>
6 Form->input(''CourseMembership.days_attended''); ?>
7 Form->input(''CourseMembership.grade''); ?>
8    
9 Form->end(); ?>
所得到的 POST 数据:
 
 1 Array
 2 (
 3     [Student] => Array
 4     (
 5         [id] => 1
 6     )
 7 
 8     [Course] => Array
 9     (
10         [id] => 1
11     )
12 
13     [CourseMembership] => Array
14     (
15         [days_attended] => 10
16         [grade] => 5
17     )
18 )
Cake 利用 saveAssociated 将 Student id 和 Course id 推入 CourseMembership。
 
保存相关模型数据 (HABTM)
通过 hasOne、belongsTo、hasMany 保存有关联的模型是非常简单的: 只需要将关联模型的 ID 填入外键列。 填完之后,只要调用模型上的 save() 方法,一切就都被正确的串连起来了。 下面是准备传递给 Tag 模型的 save() 方法的数据数组格式的示例:
 
 1 Array
 2 (
 3     [Recipe] => Array
 4         (
 5             [id] => 42
 6         )
 7     [Tag] => Array
 8         (
 9             [name] => Italian
10         )
11 )
也可以在 saveAll() 中使用这种格式保存多条记录和与它们有 HABTM 关联的的模型,格式如下:
 
 1 Array
 2 (
 3     [0] => Array
 4         (
 5             [Recipe] => Array
 6                 (
 7                     [id] => 42
 8                 )
 9             [Tag] => Array
10                 (
11                     [name] => Italian
12                 )
13         )
14     [1] => Array
15         (
16             [Recipe] => Array
17                 (
18                     [id] => 42
19                 )
20             [Tag] => Array
21                 (
22                     [name] => Pasta
23                 )
24         )
25     [2] => Array
26         (
27             [Recipe] => Array
28                 (
29                     [id] => 51
30                 )
31             [Tag] => Array
32                 (
33                     [name] => Mexican
34                 )
35         )
36     [3] => Array
37         (
38             [Recipe] => Array
39                 (
40                     [id] => 17
41                 )
42             [Tag] => Array
43                 (
44                     [name] => American (new)
45                 )
46         )
47 )
将上面的数组传递给 saveAll() 将创建所包含的 tag ,每个都与它们各自的 recipe 关联。
 
作为示例,我们建立了创建新 tag 和运行期间生成与 recipe 关联的正确数据数组的表单。
 
这个简单的表单如下:(我们假定 $recipe_id 已经设置了):
 
1 Form->create(''Tag''); ?>
2 Form->input(
3         ''Recipe.id'',
4         array(''type'' => ''hidden'', ''value'' => $recipe_id)
5     ); ?>
6 Form->input(''Tag.name''); ?>
7 Form->end(''Add Tag''); ?>
在这个例子中,你能看到 Recipe.id hidden 域,其值被设置为我们的 tag 想要连接的 recipe 的 ID。
 
当在控制器中调用 save() 方法,它将自动将 HABTM 数据保存到数据库:
 
1 public function add() {
2     // 保存关联
3     if ($this->Tag->save($this->request->data)) {
4         // 保存成功后要做的事情
5     }
6 }
这段代码将创建一个新的 Tag 并与 Recipe 相关联,其 ID 由 $this->request->data[''Recipe''][''id''] 设置。
 
某些情况下,我们可能希望呈现的关联数据能够包含下拉 select 列表。数据可能使用 find(''list'') 从模型中取出并且赋给用模型名命名的视图变量。 同名的 input 将自动把数据放进
 
1 // 控制器中的代码:
2 $this->set(''tags'', $this->Recipe->Tag->find(''list''));
1 // 视图中的代码:
2 $this->Form->input(''tags'');
更可能的情形是一个 HABTM 关系包含一个允许多选的
 
1 // 控制器中的代码:
2 $this->set(''tags'', $this->Recipe->Tag->find(''list''));
1 // 视图中的代码:
2 $this->Form->input(''Tag'');
使用上面这段代码,将建立可多选的下拉列表(select),允许多选自动被保存到已添加或已保存到数据库中的 Recipe。
 
当 HABTM 变得复杂时怎么办?
默认情况下,Cake 在保存 HABTM 关系时,会先删除连接表中的所有行。 例如,有一个拥有10个 Children 关联的 Club。带着2个 children 更新 Club。Club 将只有2个 Children,而不是12个。
 
要注意,如果想要向带有 HABTM 的连接表添加更多的列(建立时间或者元数据)是可能的,重要的是要明白你有一个简单的选项。
 
两个模型间的 HasAndBelongsToMany 关联实际上是同时拥有 hasMany 和 belongsTo 关联的三个模型关系的简写。
 
考虑下面的例子:
 
Child hasAndBelongsToMany Club
另一个方法是添加一个 Membership 模型:
 
Child hasMany Membership
Membership belongsTo Child, Club
Club hasMany Membership.
这两个例子几乎是相同的。它们在数据库中使用了命名相同的 amount 列,模型中的 amount 也是相同的。最重要的不同是 “join” 表命名不同,并且其行为更具可预知性。
 
小技巧
 
当连接表包含外键以外的扩展列时,通过将数组的 ''unique'' 设置为 “‘keepExisting’”,能够防止丢失扩展列的值。同样,可以认为设置 ‘unique’ => true,在保存操作过程中不会丢失扩展列的数据。参见 HABTM association arrays。
 
不过,更多情况下,为连接表建立一个模型,并像上面的例子那样设置 hasMany、belongsTo 关联,代替使用 HABTM 关联,会更简单。
 
数据表
虽然 CakePHP 可以有非数据库驱动的数据源,但多数时候,都是有数据库驱动的。 CakePHP 被设计成可以与 MySQL、MSSQL、Oracle、PostgreSQL 和其它数据库一起工作。 你可以创建你平时所用的数据库系统的表。在创建模型类时,模型将自动映射到已经建立的表上。表名被转换为复数小写,多个单词的表名的单词用下划线间隔。例如,名为 Ingredient 的模型对应的表名为 ingredients。名为 EventRegistration 的模型对应的表名为 event_registrations。CakePHP 将检查表来决定每个列的数据类型,并使用这些信息自动化各种特性,比如视图中输出的表单域。列名被转换为小写并用下划线间隔。
 
使用 created 和 modified 列
通过在数据库表中定义 created 和 modified 列作为 datetime 列,CakePHP 能够识别这些域并自动在其中填入记录在数据库中创建的时间和保存的时间(除非被保存的数据中已经包含了这些域的值)。
 
在记录最初添加时,created 和 modified 列将被设置为当前日期和时间。当已经存在的记录被保存时,modified 列将被更新至当前日期和时间。
 
如果在 Model::save() 之前 $this->data 中包含了 updated、created、modified 数据(例如 Model::read 或者 Model::set),那么这些值将从 $this->data 中获取,并且不自动更新。 或者使用 unset($this->data[''Model''][''modified'']) 等方法。总是可以覆盖 Model::save() 方法来做这件事:
 
 1 class AppModel extends Model {
 2 
 3     public function save($data = null, $validate = true, $fieldList = array()) {
 4         // 在每个保存操作前清除 modified 域值:
 5         $this->set($data);
 6         if (isset($this->data[$this->alias][''modified''])) {
 7             unset($this->data[$this->alias][''modified'']);
 8         }
 9         return parent::save($this->data, $validate, $fieldList);
10     }
11 
12 }
 

www.bkjia.comtruehttp://www.bkjia.com/PHPjc/477776.htmlTechArticle保存数据 CakePHP 会为保存模型数据制作快照。准备保存的数据使用如下基本格式传递给模型的 save() 方法: 1 Array 2 ( 3 [ModelName] = Array 4 ( 5...

CakePHP 2.x CookBook 中文版 第七章 模型 之 关联(一)

CakePHP 2.x CookBook 中文版 第七章 模型 之 关联(一)

关联:将模型连接在一起

CakePHP 的一个非常强劲的特性就是由模型提供关系映射,通过关联来管理多个模型间的连接。

在应用程序的不同对象间定义关系是很自然的。例如:在食谱数据库,一个食谱可能有多个评论,每个评论有一个作者,每个作者可能有多个评论。 以定义这些关系的形式工作,将允许你以一种直观且强大的方式访问你的数据库。

本节的目的是展示如何在 CakePHP 中计划、定义以及利用模型间的关系。

虽然数据可能来自各种源,但在 web 应用程序中最常见的则是存储在关系数据库中。 本节将覆盖这方面的大部分内容。

关于与插件模型一起的关联的信息,请参见 插件模型

关系类型

CakePHP 的关系类型有四种: hasOne、hasMany、belongsTo 和 hasAndBelongsToMany (HABTM)。

关系 关联类型 例子       一对多 hasMany 一个用户有多份食谱 多对一 belongsTo 多份食谱属于同一个用户 多对多 hasAndBelongsToMany 多份食谱有且属于多种成分

关联是通过创建一个由你定义的关联命名的类变量来定义的。 此变量有时候可能是简单的字符串,但也可能是用于定义关联细节的复杂的多维数组。

class User extends AppModel { public $hasOne = ''Profile''; public $hasMany = array( ''Recipe'' => array( ''className'' => ''Recipe'', ''conditions'' => array(''Recipe.approved'' => ''1''), ''order'' => ''Recipe.created DESC'' ) ); }

在上面的例子中,第一个实例的单词 ‘Recipe’ 是别名。它是关系的唯一标识,它可以是你选择的任何东西。通常你会选择与要引用的类相同的名字。然而,每个模型的别名在应用程序中必须唯一。合适的例子有:

class User extends AppModel { public $hasMany = array( ''MyRecipe'' => array( ''className'' => ''Recipe'', ) ); public $hasAndBelongsToMany => array( ''MemberOf'' => array( ''className'' => ''Group'', ) ); } class Group extends AppModel { public $hasMany = array( ''MyRecipe'' => array( ''className'' => ''Recipe'', ) ); public $hasAndBelongsToMany => array( ''Member'' => array( ''className'' => ''User'', ) ); }

但是在所有的情况下,以下代码都不工作:

class User extends AppModel { public $hasMany = array( ''MyRecipe'' => array( ''className'' => ''Recipe'', ) ); public $hasAndBelongsToMany => array( ''Member'' => array( ''className'' => ''Group'', ) ); } class Group extends AppModel { public $hasMany = array( ''MyRecipe'' => array( ''className'' => ''Recipe'', ) ); public $hasAndBelongsToMany => array( ''Member'' => array( ''className'' => ''User'', ) ); }

因为在 HABTM 关联中,别名 ‘Member’ 同时指向了 User 模型(在 Group 模型中)和 Group 模型(在 User 模型中)。 在不同的模型为某个模型起不唯一的别名,可能会带来未知的行为。

Cake 能自动在关联模型对象间建立连接。所以你可以在你的 User 模型中以如下方式访问 Recipe 模型:

$this->Recipe->someFunction();

同样的,你也能在控制器中循着模型关系访问关联模型:

$this->User->Recipe->someFunction();

注解

记住,关系定义是 ‘单向的’。如果你定义了 User hasMany Recipe,对 Recipe 模型是没有影响的。你需要定义 Recipe belongsTo User才能从 Recipe 模型访问 User 模型。

hasOne

让我们设置 User 模型以 hasOne 类型关联到 Profile 模型。

首先,数据库表需要有正确的主键。对于 hasOne 关系,一个表必须包含指向另一个表的记录的外键。在本例中,profiles 表将包含一个叫做 user_id 的列。基本模式是: :

hasOne: 另一个 模型包含外键。

关系 结构 Apple hasOne Banana bananas.apple_id User hasOne Profile profiles.user_id Doctor hasOne Mentor mentors.doctor_id

注解

关于这一点,并没有强制要求遵循 CakePHP 约定,你能够很容易地在关联定义中使用任何外键来覆盖它。虽然如此,遵守规则将使你的代码更简捷,更易于阅读和维护。

User 模型文件保存为 /app/Model/User.php。为了定义‘User hasOne Profile’ 关联,需要在模型类中添加 $hasOne属性。记得要在 /app/Model/Profile.php 文件中放一个 Profile 模型,否则关联将不工作:

class User extends AppModel { public $hasOne = ''Profile''; }

有两种途径在模型文件中描述此关系。简单的方法是设置一个包含要关联的模型的类名的字符串型属性 $hasOne,就像我们上面做的那样。

如果需要更全面的控制,可以使用数组语法定义关联。例如,你可能想要限制关联只包含某些记录。

class User extends AppModel { public $hasOne = array( ''Profile'' => array( ''className'' => ''Profile'', ''conditions'' => array(''Profile.published'' => ''1''), ''dependent'' => true ) ); }

hasOne 关联数组可能包含的键有: :

  • className: 被关联到当前模型的模型类名。如果你定义了 ‘User hasOne Profile’关系,类名键将是 ‘Profile.’
  • foreignKey: 另一张表中的外键名。如果需要定义多个 hasOne 关系,这个键非常有用。其默认值为当前模型的单数模型名缀以 ‘_id’。在上面的例子中,就默认为 ‘user_id’。
  • conditions: 一个 find() 兼容条件的数组或者类似 array(‘Profile.approved’ => true) 的 SQL 字符串.
  • fields: 需要在匹配的关联模型数据中获取的列的列表。默认返回所有的列。
  • order: 一个 find() 兼容排序子句或者类似 array(‘Profile.last_name’ => ‘ASC’) 的 SQL 字符串。
  • dependent: 当 dependent 键被设置为 true,并且模型的 delete() 方法调用时的参数 cascade 被设置为 true,关联模型的记录同时被删除。在本例中,我们将其设置为 true 将导致删除一个 User 时同时删除与其相关的 Profile。

一旦定义了关系,User 模型上的 find 操作将匹配存在的关联 Profile 记录:

// 调用 $this->User->find() 的示例结果。 Array ( [User] => Array ( [id] => 121 [name] => Gwoo the Kungwoo [created] => 2007-05-01 10:31:01 ) [Profile] => Array ( [id] => 12 [user_id] => 121 [skill] => Baking Cakes [created] => 2007-05-01 10:31:01 ) )

belongsTo

现 在我们有了通过访问 User 模型获取相关 Profile 数据的办法,让我们在 Profile 模型中定义 belongsTo 关联以获取相关的 User 数据。belongsTo 关联是 hasOne 和 hasMany 关联的自然补充:它允许我们从其它途径查看数据。

在为 belongsTo 关系定义数据库表的键时,遵循如下约定:

belongsTo: 当前模型 包含外键。

关系 结构 Banana belongsTo Apple bananas.apple_id Profile belongsTo User profiles.user_id Mentor belongsTo Doctor mentors.doctor_id

小技巧

如果一个模型(表)包含一个外键,它 belongsTo 另一个模型(表)。

我们可以使用如下字符串语法,在 /app/Model/Profile.php 文件中的 Profile 模型中定义 belongsTo 关联:

class Profile extends AppModel { public $belongsTo = ''User''; }

我们还能使用数组语法定义特定的关系:

class Profile extends AppModel { public $belongsTo = array( ''User'' => array( ''className'' => ''User'', ''foreignKey'' => ''user_id'' ) ); }

belongsTo 关联数组可能包含的键有:

  • className: 被关联到当前模型的模型类名。如果你定义了 ‘Profile belongsTo User’关系,类名键的值将为 ‘User.’

  • foreignKey: 当前模型中需要的外键。用于需要定义多个 belongsTo 关系。其默认值为另一模型的单数模型名缀以 ‘_id’。

  • conditions: 一个 find() 兼容条件的数组或者类似 array(''User.active'' => true) 的 SQL 字符串。

  • type: SQL 查询的 join 类型,默认为 Left,这不可能在所有情况下都符合你的需求,在你想要从主模型和关联模型获取全部内容或者什么都不要时很有用!(仅在某些条件下有效)。 (注:类型值必须是小写,例如:left, inner)

  • fields: 需要在匹配的关联模型数据中获取的列的列表。默认返回所有的列。

  • order: 一个 find() 兼容排序子句或者类似 array(''User.username'' => ''ASC'') 的 SQL 字符串。

  • counterCache: 如果此键的值设置为 true,当你在做 “save()” 或者 “delete()” 操作时关联模型将自动递增或递减外键关联的表的 “[singular_model_name]_count” 列的值。如果它是一个字符串,则其将是计数用的列名。计数列的值表示关联行的数量。也可以通过使用数组指定多个计数缓存,键为列名,值为条件,例如:

    array( ''recipes_count'' => true, ''recipes_published'' => array(''Recipe.published'' => 1) )
  • counterScope: 用于更新计数缓存列的可选条件数组。

一旦定义了关联,Profile 模型上的 find 操作将同时获取相关的 User 记录(如果它存在的话):

//调用 $this->Profile->find() 的示例结果。 Array ( [Profile] => Array ( [id] => 12 [user_id] => 121 [skill] => Baking Cakes [created] => 2007-05-01 10:31:01 ) [User] => Array ( [id] => 121 [name] => Gwoo the Kungwoo [created] => 2007-05-01 10:31:01 ) )

hasMany

下一步:定义一个 “User hasMany Comment” 关联。一个 hasMany 关联将允许我们在获取 User 记录的同时获取用户的评论。

在为 hasMany 关系定义数据库表的键时,遵循如下约定:

hasMany: 其它 模型包含外键。

关系 结构 User hasMany Comment Comment.user_id Cake hasMany Virtue Virtue.cake_id Product hasMany Option Option.product_id

我们可以使用如下字符串语法,在 /app/Model/User.php 文件中的 User 模型中定义 hasMnay 关联:

class User extends AppModel { public $hasMany = ''Comment''; }

我们还能使用数组语法定义特定的关系:

class User extends AppModel { public $hasMany = array( ''Comment'' => array( ''className'' => ''Comment'', ''foreignKey'' => ''user_id'', ''conditions'' => array(''Comment.status'' => ''1''), ''order'' => ''Comment.created DESC'', ''limit'' => ''5'', ''dependent'' => true ) ); }

hasMany 关联数组可能包含的键有:

  • className: 被关联到当前模型的模型类名。如果你定义了 ‘User hasMany Comment’关系,类名键的值将为 ‘Comment.’。
  • foreignKey: 另一张表中的外键名。如果需要定义多个 hasMany 关系,这个键非常有用。其默认值为当前模型的单数模型名缀以 ‘_id’。
  • conditions: 一个 find() 兼容条件的数组或者类似 array(‘Comment.visible’ => true) 的 SQL 字符串。
  • order: 一个 find() 兼容排序子句或者类似 array(‘Profile.last_name’ => ‘ASC’) 的 SQL 字符串。
  • limit: 想返回的关联行的最大行数。
  • offset: 获取和关联前要跳过的行数(根据提供的条件 - 多数用于分页时的当前页的偏移量)。
  • dependent: 如果 dependent 设置为 true,就有可能进行模型的递归删除。在本例中,当 User 记录被删除后,关联的 Comment 记录将被删除。
  • exclusive: 当 exclusive 设置为 true,将用 deleteAll() 代替分别删除每个实体来来完成递归模型删除。这大大提高了性能,但可能不是所有情况下的理想选择。
  • finderQuery: CakePHP 中用于获取关联模型的记录的完整 SQL 查询。用在包含许多自定义结果的场合。 如果你建立的一个查询包含关联模型 ID 的引用,在查询中使用 $__cakeID__$} 标记它。例如,如果你的 Apple 模型 hasMany Orange,此查询看上去有点像这样: SELECT Orange.* from oranges as Orange WHEREOrange.apple_id = {$__cakeID__$};

一旦关联被建立,User 模型上的 find 操作也将获取相关的 Comment 数据(如果它存在的话):

//调用 $this->User->find() 获得的结果示例。 Array ( [User] => Array ( [id] => 121 [name] => Gwoo the Kungwoo [created] => 2007-05-01 10:31:01 ) [Comment] => Array ( [0] => Array ( [id] => 123 [user_id] => 121 [title] => On Gwoo the Kungwoo [body] => The Kungwooness is not so Gwooish [created] => 2006-05-01 10:31:01 ) [1] => Array ( [id] => 124 [user_id] => 121 [title] => More on Gwoo [body] => But what of the Nut? [created] => 2006-05-01 10:41:01 ) ) )

有 件事需要记住:你还需要定义 Comment belongsTo User 关联,用于从两个方向获取数据。 我们在这一节概述了能够使你从 User 模型获取 Comment 数据的方法。在 Comment 模型中添加 Comment belongsTo User 关系将使你能够从 Comment 模型中获取 User 数据 - 这样的链接关系才是完整的且允许从两个模型的角度获取信息流。

counterCache - 缓存你的 count()

这个功能帮助你缓存相关数据的计数。模型通过自己追踪指向关联 $hasMany 模型的所有的添加/删除并递增/递减父模型表的专用整数列,替代手工调用 find(''count'') 计算记录的计数。

这个列的名称由列的单数名后缀以下划线和单词 “count” 构成:

my_model_count

如果你有一个叫 ImageComment 的模型和一个叫 Image 的模型,你需要添加一个指向 images 表的新的整数列并命名为image_comment_count

下面是更多的示例:

模型 关联模型 示例 User Image users.image_count Image ImageComment images.image_comment_count BlogEntry BlogEntryComment blog_entries.blog_entry_comment_count

一旦你添加了计数列,就可以使用它了。通过在你的关联中添加 counterCache 键并将其值设置为 true,可以激活 counter-cache:

class ImageComment extends AppModel { public $belongsTo = array( ''Image'' => array( ''counterCache'' => true, ) ); }

自此,你每次添加或删除一个关联到 Image 的 ImageCommentimage_comment_count 字段的数字都会自动调整。

你还可以指定 counterScope。它允许你指定一个简单的条件,通知模型什么时候更新(不更新)计数值,这依赖于你如何查看。

在我们的 Image 模型示例中,我们可以象下面这样指定:

class ImageComment extends AppModel { public $belongsTo = array( ''Image'' => array( ''counterCache'' => true, ''counterScope'' => array(''Image.active'' => 1) // only count if "Image" is active = 1 ) ); }

关于可怕的Evercookie和CakePHP的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于CakePHP 2.x CookBook 中文版 第一章 欢迎、CakePHP 2.x CookBook 中文版 第七章 模型、CakePHP 2.x CookBook 中文版 第七章 模型 之 保存数据_PHP教程、CakePHP 2.x CookBook 中文版 第七章 模型 之 关联(一)等相关内容,可以在本站寻找。

本文标签: