目录
Cake Naming Conventions
表格清单
范例清单
目录
这份手册是为了那些想更快速地建立 Web 应用的人所写的。CakePHP 旨在帮助各种 PHP 水平的用户,简单快速的创建健壮的、可维护的程序。
这份手册需要一些 PHP 和 HTML 的基础知识。如果对 MVC 模式熟悉的话,对阅读会很有帮助,但我们会为那些初学 MVC 的人继续介绍这些知识。本文会尝试帮助读者简单配置服务器和对服务器进行排错,但对这些问题的全面讲解已经超出了本手册的范围。
急救、基本的生存技能、约会和关系处理方面的基本知识也推荐学习,不过也不在文本的范围之内,呵呵。
CakePHP 是自由的。你不需要对他付费,你可以用任何方式使用它。CakePHP 是按照MIT许可证开发的。CakePHP是一个开放源代码的项目,这意味着你有访问全部源代码的权利。获取最新的版本的最佳去处是在CakePHP的官方网站(http://www.cakephp.org)。你可以可以在这里看到最新最棒的代码。
CakePHP 是由一群辛勤劳动的人们所开发的。他们来自全世界不同的国家,为了使广大群众受益,集合到了一起来共同创建 CakePHP 框架。要了解Cake目前活跃的开发人员和用户社区的信息,请访问:http://www.cakephp.org。
CakePHP 是一个免费的开放源代码的,针对PHP的快速开发框架: 受到Ruby on Rails框架启发形成了有结构的库、类和运行时基础。 我们的主要目的是让您可以按照一种结构化和快速的方式来进行开发——而同时又不丧失弹性。
CakePHP 拥有一些特性让他成为迅速开发应用(而又不用反复论证)的首选:
兼容 PHP4 和 PHP5
提供集成的用于数据库交互和简单的查询 CRUD(包括快速脚手架功能)
可美化、自定义URL的请求分配器。
快速的有弹性的模版引擎(PHP 语法,使用助手类)
还有许多有用的核心特点(访问控制列表以及 AJAX 集成等等)
在网站的任何子目录下面都能正常工作,涉及到的Apache配置很少。
在2005年,Michal Tatrynowicz用PHP写了一个很小的快速应用开发框架,他发现这是一个好的框架的起点。Michal 以 MIT 许可证发布了这个框架,并称之为 Cake,并逐渐形成了一个开发人员的社区,正是他们现在在CakePHP的名下维护Cake。
本章是将简短地介绍 MVC 的概念以及他们如何在Cake中实现的。如果你是初学 MVC (模型-视图-控制器),那么你一定要看这一章。我们将从一个通用的 MVC 概念的讨论开始,并逐渐进入特定的 CakePHP 中的 MVC 应用,最后使用MVC模式演示一些简单的 CakePHP 的例子。
模型-视图-控制器(Model-View-Controller)是一种软件设计模式,它可帮助你有逻辑地划分代码,使代码可服用、可维护,一般来说会更好。模型-视图-控制器最先由Gang of Four的作者小组描述出来。Dean Helman 写到(以下节选自Objective Toolkit Pro白皮书):
“MVC 范例是一种分解应用的方法,或者甚至仅仅是一个应用接口的一小部分,它将应用分解成三个部分:模型、视图和控制器。开发 MVC 最先的目的,是用来将原有的输入、处理、输出的角色映射到 GUI 的领域中”
输入 → 处理 → 输出
控制器 → 模型 → 视图
“用户输入,外部世界的模型和给用户的视觉反馈是被模型,视图端口和控制器对象所分开并处理的。控制器解释用户的鼠标、键盘输入,并将这些用户动作映射到发送到模型和/或视图端口的命令,来实现合适的更改。模型管理一个或多个数据元素,响应对它的状态的查询,并响应更改状态的指令。视图端口管理显示的一个矩形的区域,并负责通过图形和文字给用户展示数据。”
在 Cake 的术语中,模型代表了一个特定的数据库表/记录,以及它和其他表和记录之间的关系。模型也可以包含校验规则,它会在插入或者更新模型数据时应用。视图代表了 Cake 的视图文件,这些是一些普通的嵌入了 PHP 代码的 HTML 文件。Cake 的控制器处理服务器的请求。它获取用户输入(URL 和 POST 数据),应用业务逻辑,使用模型来从数据库和其他数据源读取数据或者写入数据,最后,将数据输出到合适和视图文件。
为了能尽可能更容易地组织应用程序,Cake使用了这个模式不仅仅来管理对象如何在应用之中进行交互,还管理了如何存放文件,这些会在下面详细讲解。
当你在服务器上解压开 Cake 安装包,你会发现有四个文件夹—— app
, cake
, tmp
, 和 vendors
。其中cake
文件夹是 Cake 的核心库所放的地方,
基本上你不需要动它。
其实它并非一直这样—— 在CakePHP的0.10.0版本发布之前,所有东西都是放在在一个文件夹cake下面的,这种结构被证明有一些问题。
如果你想部署超过一个的Web应用程序,你需要下载并多次安装Cake,这样核心库就重复了,这不仅对空间是一种浪费,同时也违背了CakePHP试图遵循的DRY原则。
当 CakePHP 新版本发布时,你必须非常小心以免 app 文件夹下面的文件被新版本中的默认文件所覆盖。
app
文件夹是你的程序和文件所放的地方。核心库和应用程序文件夹的分离使得你可以有许多程序目录共享同一个
Cake 库。这也使得更新 CakePHP
变得更加容易:你只需要下载最新版本的Cake并覆盖当前的核心库,而不需要担心是否会覆盖掉你为你的应用所写的东西。
tmp
目录可以用于不同的 Cake
操作,比如baking(自动创建新的php文件),缓存和日志记录。
你可以使用vendors
目录文件夹来存放第三方库文件。稍后你将更多地了解 vendors。
下面的列表显示了主要的文件夹和它们的基本目的:
cake
(根目录)
app
(这里存放你的应用程序逻辑)
config
(特定于应用的配置文件,比如ACL,core, database connection,routes,
paths, 和tags)
controllers
(这里放置控制器)
components
(组件,也就可以协助控制逻辑的类,存放的地方)
index.php
(cake中有三个该文件:它令用户可以用不同的方法部署 Cake 应用。)
models
(放置模型)
plugins
(放置插件或者第三方应用)
tmp
(用于日志、baking等等)
views
(与视图相关文件)
elements
(小的经常重复使用的布局零件)
errors
(存放错误页面)
helpers
(自定义的助手类文件)
layouts
(页面布局)
pages
(PagesController管理的静态内容)
webroot
(这个目录用作网站的根目录,可以并将公共的文件放在这里)
css
files
img
js
cake
(库文件,最好不要动。)
index.php
vendors
(放第三方库文件)
目录
如果你对配置服务器很熟悉,可以跳过这一部分,直接开始。无论如何,你都应该亲自动手尝试一下。
这一章将描述哪些东西必须安装在服务器上,以及配置服务器的几种不同方法,下载和安装 CakePHP,显示出默认的 CakePHP 页面,以及对于一些可能出现的错误的提示。
要使用CakePHP,你必须首先有一个具备基本的程序和库服务器来运行CakePHP:
下面是配置 Linux 服务器来运行 CakePHP 所必需的条件:
一个 HTTP 服务器(如 Apache ),且启用下面的选项:sessions, mod_rewrite(虽然不是绝对必须,但是有更好)
PHP 4.3.2 或者更高版本,CakePHP 在 PHP4 和 5 下都能很好工作。
数据库引擎(目前已经支持MySQL,PostgreSQL ,还包括 ADODB 的包装器)
要安装Cake PHP 你首先需要从CakePHP网站(在www.cakephp.org)下载最新的稳定版本,打开网站后点击 Downloads > Release。
如果你喜欢冒险或者是好奇,你可以从nightly目录中下载一个每夜的CakePHP项目的快照,不过这个代码仍然在开发中,所以没有任何稳定性上的担保。
对于更加勇敢的人,可以使用SVN直接检出最新的版本。在 svn://svn.cakephp.org 检出一份副本。
现在你已经下载了最新的发行版,将压缩包放入你的 Web 服务器的 Web 根目录中。现在你需要解压缩 CakePHP 的包。这里有两种方式,使用开发模式安装(可以让你在单个域名下面察看多个 CakePHP 应用),或者生产方式安装(在一个域名下面只能有一个CakePHP应用)。
第一种安装 CakePHP 的方法一般只推荐开发环境使用,因为它安全性低一些。第二种方法要较为安全,在生产环境中应该使用它。
如果你要使用Cake的日志和缓存功能,你还需要将应用程序中的 tmp
目录设置为可以被 Web
服务器写。这个目录在 /app/tmp
。
一些共享主机的用户可能没有权限通过修改 http.conf 来改变 DocumnentRoot 来指向他们的产品安装。在这种情况下,用户可以安装下面的方式,修改 CakePHP的结构。Cake安装在 /path_to_cake_install,文件目录(不可以修改)指向/public_html
针对开发方式,我们可以将整个 Cake 的安装目录放到指定的 DocumentRoot 下,如下:
/wwwroot/ /cake /.htaccess /app/ /cake/ /index.php /tmp/ /vendors
这种安装中,wwwroot 会扮演整个网站的根目录,这样你的 URL 将像这样:
www.example.com/cake/index.php
如果使用了mod_rewrite, URL 会遵循 http://www.example.com/cake/<控制器名称>/<动作名称>/<参数1>/<参数2>
这种模式.
为了实现生产方式的安装,你必须有修改服务器上文档根目录(DocumentRoot)的权限.这样做,可以让整个域名作为一个 CakePHP 应用运行。
生产方式安装使用以下文件布局:
../path_to_cake_install/ /.htaccess /app/ /.htaccess /config/ /conftrollers /index.php /models /plugins /tmp /vendors /views /webroot <-- 这里是你的新的 DocumentRoot /cake/ /index.php /vendors
在这种配置下,webroot 目录作为网站的根目录,这时你的URL可能像这样:
http://www.example.com/
如果你使用了mod_rewrite,你的URL就会按照 http://www.example.com/controller_name/action_name/param1/param2
的模式。
某些情况下,你可能要将 Cake 的目录放在磁盘的其他地方。这可能是由于共享主机的一些限制,或者是你想要让几个应用同时共享同一个 Cake 库。
一个 Cake 应用有三个主要的部分:
核心 CakePHP 库
在 /cake
应用程序代码(即控制器、模型、布局和视图)
在 /app
应用程序Web根目录(即图像、JavaScript和CSS)。
在 /app/webroot
这些目录中的每一个都可以放在文件系统的任何位置,除了 webroot,它必须能被你的 Web 服务器读取。你甚至可以将 webroot
目录移出 app
目录,只要告诉 Cake
你把它放在哪里了。
如果要配置你的Cake安装,需要对 /app/webroot/index.php
进行一些修改
。你需要编辑的是三个主要的常量定义:ROOT、APP_DIR和CAKE_CORE_INCLUDE_PATH。
ROOT 应被设置为包含 app
目录的路径。
APP_DIR 应被设为你的 app
目录的绝对路径。
CAKE_CORE_INCLUDE_PATH 应该被设为你的Cake库的文件夹。
使用一个例子就可以更好地说明它。假设我要让 Cake 按照以下设置运行:
我想让我的 Cake 库和其他应用共享,放在 /usr/lib/cake
中。
我的 Cake 的 webroot 目录是 /var/www/mysite/
。
我的应用程序文件放在 /home/me/mysite
中。
文件布局如下: /home /me /mysite <-- 原来是 /cake_install/app /config /controllers /index.php /models /plugins /tmp /vendors /views /var /www /mysite <-- 原来是 /cake_install/app/webroot /.htaccess /css /css.php /favicon.ico /files /img /index.php /js /usr /lib /cake <-- 原来是 /cake_install/cake /cake /app_controller.php /app_model.php /basics.php /bootstrap.php /config /dispatcher.php /docs /libs /scripts /vendors
给出了这个安装形式,要修改webroot中的index.php文件(这个例子中,它应该在 /var/www/mysite/index.php)使之如下:
推荐使用“DS”常量而不是斜杠来分隔文件路径。这防止当使用了错误的分隔符会出现的“missing file”(缺少文件)错误,同时也令你的代码更加有移植性。
if (!defined('ROOT')) { define('ROOT', DS.'home'.DS.'me'); } if (!defined('APP_DIR')) { define ('APP_DIR', 'mysite'); } if (!defined('CAKE_CORE_INCLUDE_PATH')) { define('CAKE_CORE_INCLUDE_PATH', DS.'usr'.DS.'lib'.DS.'cake'); }
虽然 CakePHP 是按照不管有没有 mod_rewrite 来构建的,我们发现有些用户始终不能很理想地运行全部的东西。 下面的一些事情,可能要试一下就可以使之正确运行:
确保 .htaccess 的配置覆盖(Override)是允许的:在你的 httpd.conf 中,
应该包含了定义服务器上目录的一节(Directory)。确保相应的目录设置中的 AllowOverride
已经设成了 All
。
确保修改的是系统的 httpd.conf,而不是特定于用户或者站点的httpd.conf。
由于某些原因,你可能得到一份缺少 .htaccess 文件的 CakePHP。 有时候这是因为某些系统把 .开头的文件当作隐藏文件,就不拷贝它们。所以确保你的 CakePHP 的副本是从网站的下载页面或者是我们的 SVN 仓库中获取的。
确保正确载入了mod_rewrite!你应该可以在 httpd.conf 中看到这样的文字: LoadModule
rewrite_module libexec/httpd/mod_rewrite.so
和 AddModule
mod_rewrite.c
(这行对Apache2是没有的——译注)。
好吧,让我们马上看看我们的成果。根据你选择不同的安装方式,要将你的浏览器指向 http://www.example.com 或者 http://www.example.com/cake。这时,你会看到CakePHP的默认首页,以及一条显示当前数据库连接状态的消息。
恭喜!你现在已经可以创建你的第一个基于Cake的程序了。如果你现在什么都没有看到或者收到一条错误信息,可以到 http://wiki.cakephp.org 或者 irc.freenode.net 上的 #cakephp 频道寻找更多的帮助。
app/config/database.php
文件是数据库配置文件。默认安装后并没有database.php
文件,所以你需要复制database.php.default
到database.php
。之后,你可以看到如下:
根据你应用程序的数据库连接信息,替换默认提供的配置。
CakePHP支持下面的数据库驱动:
mysql |
postgres |
sqlite |
pear-驱动名 (例如,你可以输入pear-mysql) |
adodb-驱动名 |
$default 连接中的 connect 键允许你指定是否需要连接数据库连接作为持久连接,请仔细阅读database.php.default文件中的注释,以配置你的数据库连接类型。
数据库中的表应遵循下面的命名规则:
cake使用的表名应该由英文的复数形式组成,如 users, authors, articles。注意,对应的模型使用是单数形式的名称。
所有的表都必须有一个叫做 “id” 的主键。
如果你需要关联表,使用类似“article_id”的外键 。表名是单数,跟下划线,然后是“id”。,必须是小写。
另外,也可以遵从下面的命名规则来使用某些功能:
包含一个 'created' 列
包含一个 'modified' 或者 'updated' 列
你可能注意到了 database.php 文件中还有一个 $test 的连接,填上这个配置(或者添加其他格式类似的配置),然后在你使用的时候将下面的代码放入某个模型中:
var $useDbConfig = 'test';
你可以按照这种方式加入任何数量的额外连接设置。
CakePHP的全局配置在/app/config/core.php
文件中。尽管我们不喜欢配置文件,但是没有配置确实很难做。在这个文件中你可以进行一些修改,每一项设置的说明都可以在core.php
的注释中找到。
DEBUG: 将它设成不同的值可以以不同方式帮你在构建应用程序时进行调试。将其指定为一个非零的数值将强制 Cake 输出 pr( ) 和 debug( )
函数调用的结果,而且会停止消息页面(flash)的自动转向。将其设为2或更高时会将SQL语句打印在页面的地步。同时,当处于调试模式的时候(也就是 DEBUG
被设为 1 或更高),Cake 会渲染某些生成的错误页面,也就是“Missing Controller”(缺少控制器)、“Missing
Action”(缺少动作)等。然而,在生产模式下(DEBUG 被设为0),Cake 将渲染 “Not Found”页面,可以通过 app/views/errors/error404.thtml
来覆盖这个默认页面。
CAKE_SESSION_COOKIE:将它改成你想要的会话(Session)数据所使用的 Cookie 的名称。
CAKE_SECURITY:将这个值改成表示你想要的会话检查级别的值。根据你在这里提供的设置,Cake 会令会话超时,生成新的会话ID,并删除旧的会话数据。可能的值是:
high: 会话在10分钟无动作后超时,同时会话ID在每一次请求时都会重新生成。
medium: 会话在20分钟无动作后超时
low: 会话在30分钟无动作后超时
CAKE_SESSION_SAVE: 指定了如何保存会话数据。可能的值是:
cake: 会话数据保存在你的 Cake 安装位置下的 tmp/ 目录
php: 会话数据按照 php.ini 所定义的进行保存。
database: 会话数据保存到由“default”键定义的数据库链接中。
“路由”(Route)‘Routing’是类似于mod_rewrite的 pared-down pure-PHP(机制),可以帮助将URL影射到 controller/action/params. Cake 添加这个可以帮助我们更好的实现URL转化并使得我们可以脱离mod_rewrite的要求。然而使用mod_rewrite,使得我们的address bar显得更加整洁。 "Routing" is a pared-down pure-PHP mod_rewrite-alike that can map URLs to controller/action/params and back. It was added to Cake to make pretty URLs more configurable and to divorce us from the mod_rewrite requirement. Using mod_rewrite, however, will make your address bar look much more tidy.
Routes是映射URLs到特定的controllers和actions的独立规则。Routes被配置在app/config/routes.php文件中,设置形式如下:
Routes are individual rules that map matching URLs to specific controllers and
actions. Routes are configured in the app/config/routes.php
file. They are set-up like this:
在这里 Where:
URL是Cake的URL你想要映射的(URL is the Cake URL you wish to map) URL is the regular expression Cake URL you wish to map, |
controllername 是你想要调用的controller的名字 controllername is the name of the controller you wish to invoke, |
actionname 是你想要调用controller的action的名字 actionname is the name of the controller's action you wish to invoke, |
firstparam是特定的action的第一个参数 and firstparam is the value of the first parameter of the action you've specified. |
Any parameters following firstparam will also be passed as parameters to the controller action.
下面的这个例子将/blog下面的所有URL连接到了BlogController. 默认的action是BlogController::index() The following example joins all the urls in /blog to the BlogController. The default action will be BlogController::index().
一个URL 比如 /blog/history/05/june 处理如下: A URL like /blog/history/05/june can then be handled like this:
URL中的’history’被匹配到 Blog route中的:action.( The 'history' from the URL was matched by :action from the Blog's route.) URL中被*匹配的元素被传递到活动的controller的处理方法,这里是$year和$month。比如这个URL /blog/history/05, 仅仅传递一个参数05给history() The 'history' from the URL was matched by :action from the Blog's route. URL elements matched by * are passed to the active controller's handling method as parameters, hence the $year and $month. Called with URL /blog/history/05, history() would only be passed one parameter, 05.
接下来的例子是默认的CakePHP route,指向
PagesController::display(‘home’).其中home是一个view,位于/app/views/pages/home.thtml The
following example is a default CakePHP route used to set up a route for
PagesController::display('home'). Home is a view which can be overridden by
creating the file /app/views/pages/home.thtml
.
Scaffolding是一个很棒的途径,使得早期开发的部分web应用能够运行起来。早期的数据库模式是不稳定的,很容易变化。Scaffolding有个下降趋势:web程序员憎恨创建以后可能根本用不到的forms。为了减少程序员的这种重复劳动, Cake中包含了Scaffolding。Scaffolding分析数据库,创建一些标准的使用add、delete、和edit按钮的lists,创建输入的forms,以及查看数据库中一个item的标准views。为了在程序中的controller中添加Scaffolding,需要添加$scaffold变量: Cake's scaffolding is pretty cool. So cool that you'll want to use it in production apps. Now, we think its cool, too, but please realize that scaffolding is... well... just scaffolding. Its a bunch of stuff you throw up real quick during the beginning of a project in order to get started. It isn't meant to be completely flexible. So, if you find yourself really wanting to customize your logic and your views, its time to pull your scaffolding down in order to write some code.
Scaffolding is a great way of getting the early parts of developing a web
application started. Early database schemas are volatile and subject to change,
which is perfectly normal in the early part of the design process. This has a
downside: a web developer hates creating forms that never will see real use. To
reduce the strain on the developer, scaffolding has been included in Cake.
Scaffolding analyzes your database tables and creates standard lists with add,
delete and edit buttons, standard forms for editing and standard views for
inspecting a single item in the database. To add scaffolding for your
application, in the controller, add the $scaffold
variable:
class CategoriesController extends AppController { var $scaffold; }
有关Scaffold,要注意一个重要的问题: Scaffold期望每个以_id结尾的filed
name是一个外键并且指向一个table,table的名称和_id前方的一样(只不过是小写的)。所以,举个例子来说,如果你嵌套了分类,你最好有个列叫做parent_id。在这个版本中,最好能够命名为parentid.同样,在表中有一个外键(比如,titles
table有个category_id),并且你已经合适的联结到models(查看6.2理解联结),在show/edit/newd的views中,选择的表将会和外键的表(category)一起自动的表现出来(原文:a
select box will be automatically populated with the rows from the foreign table
(category) in the show/edit/new views.)。在foreign
model中设置$displayField来决定foreign中哪些field会被显示。继续我们的例子,category有个标题One important
thing to note about scaffold: it expects that any field name that ends with
_id
is a foreign key to a table which has a name that
precedes the underscore. So, for example, if you have nested categories, you'd
probably have a column called parent_id
. With this
release, it would be best to call this parentid. Also, when you have a foreign
key in your table (e.g. titles table has category_id
),
and you have associated your models appropriately (see Understanding
Associations, 6.2), a select box will be automatically populated with the rows
from the foreign table (category) in the show/edit/new views. To set which field
in the foreign table is shown, set the $displayField
variable in the foreign model. To continue our example of a category having a
title:
class Title extends AppModel { var $name = 'Title'; var $displayField = 'title'; }
目录
模型是什么?它其实就是 MVC 模式中的 M。
模型做些什么。它将领域逻辑和表现形式分离,并独立了应用逻辑。
一个模型一般来说就是数据库的一个访问点,更具体地说,就是数据库中特定的表。默认情况下,每个模型都使用以自身名字的复数形式为名称的表,比如,User 模型使用 users 表。模型也可以包含数据校验的规则,关联信息以及特定于它使用的表的方法。下面是一个简单的用户模型的例子:
从PHP的观点看,模型都是扩展了 AppModel 类的类。类 AppModel 最初是定义在 cake/ 目录下,但如果你要创建自己的,可以将其放在
app/app_model.php
。它应该包含会在两个或多个模型之间共享的方法。它本身是扩展了 Model
类,这时一个标准的 Cake 库,定义在 libs/model.php
中。
本节介绍的是 Cake 的 Model 中常用的方法,最好使用 http://api.cakephp.org 来查看全部的参考。
模型中表特定的方法的例子,是用于隐藏/显示 Blog 中帖子的一组方法。
下面是使用模型来得到数据的一些标准的途径:
findAll ($conditions, $fields, $order, $limit, $page, $recursive)
返回指定的字段最多 $limit (默认是50)条的满足 $conditions(如果有)的记录,列出从第 $page(默认为第一页)页开始。$conditions 内容应该像 SQL 语句中的一样:例如,$conditions = "race = 'wookie' AND thermal_detonators > 3"。
当 $recursive 选项被设置成 1 到 3 之间的整数时,findAll() 操作将会返回在联结到该模型中发现的所有相关的记录。递归寻找可以最深 3 层。 When the $recursive option is set to a integer value between 1 and 3, the findAll() operation will make an effort to return the models associated to the ones found by the findAll(). The recursive find can go up to three levels deep. If your property has many owners who in turn have many contracts, a recursive findAll() on your Property model will return up to three levels deep of associated models.
find ($conditions, $fields, $order, $recursive)
返回匹配 $conditions 的第一条记录中指定的字段(如果没有指定则返回全部)。
$recursive 作用同上When the $recursive option is set to a integer value between 1 and 3, the find() operation will make an effort to return the models associated to the ones found by the find(). The recursive find can go up to three levels deep. If your property has many owners who in turn have many contracts, a recursive find() on your Property model will return up to three levels deep of associated models.
findAllBy<FieldName>($value) and findBy<fieldName>($value)
这些魔法函数可以用来根据给定字段和特定的值可以快速查找表中的某一行。,奇妙的方法可以用于指定特定的field和特定的value快速查找行,你要做的就是把你的field用驼峰表达法添加在后面。举个例子(用于controller中)These magic functions can be used as a shortcut to search your tables for a row given a certain field, and a certain value. Just tack on the name of the field you wish to search, and CamelCase it. Examples (as used in a Controller) might be:
$this->Post->findByTitle('My First Blog Post'); $this->Author->findByLastName('Rogers'); $this->Property->findAllByState('AZ'); $this->Specimen->findAllByKingdom('Animalia');
返回的结果是一个数组,它和 find() 以及 findAll() 返回的结果的格式是一样的。
findNeighbours($conditions = null, $field, $value)
返回包含相邻记录(由 $field 和 $value 指定中间的记录)的数组(只包含特定字段),通过 $conditions 作为 SQL 条件进行筛选。
当你要生成“前一条”和“后一条”链接来帮助用户按照某种顺序在条目中逐个查看的时候,它就十分有用。它只能按照数值型或者日期字段。
class ImagesController extends AppController { function view($id) { // 我们要展示图像…… $this->set('image', $this->Image->find("id = $id"); // 不过我们还要前一个和后一个图像…… $this->set('neighbours', $this->Image->findNeighbours(null, 'id', $id); } }
这将给我们的视图传递一格完整的 $image['Image'] 数组,同时还有 $neighbours['prev']['Image']['id'] 和 $neighbours['next']['Image']['id'] 。
field($name, $conditions, $order)
从第一个按照 $order 排序满足条件 $conditions 的第一条记录中的字段$name 的值。
findCount($conditions)
返回匹配给定条件 $conditions 的记录的个数。
generateList ($conditions=null, $order=null, $limit=null, $keyPath=null, $valuePath=null)
这个函数是从模型的记录中获取“键-值”对的数组的一个快捷方式——尤其是对从模型记录中创建一个 <select> 列表十分有用。$conditions、$order 和 $limit参数使用方法和 findAll() 请求相同。$keyPath 和 $valuePath 是你告诉模型要从哪里去找所生成的列表的键和值。举个例子,如果你想根据 Role 模型生成一个角色的列表,列表由角色的数值ID作为键,那么完整的调用应该类似:
$this->set( 'Roles', $this->Role->generateList(null, 'role_name ASC', null, 'id', 'role_name') ); // 这会返回类似以下的东西: array( '1' => 'Account Manager', '2' => 'Account Viewer', '3' => 'System Manager', '4' => 'Site Visitor' );
read($fields=null, $id=null)
使用这个方法从当前已载入的记录,或者由 $id 指定的记录中获取指定字段和值。
请注意,不管模型中的 $recursive 值是多少,read() 操作都只抓取关联模型的第一层。如果要取得额外的级别,请使用 find() 或者 findAll()。
自定义的的 SQL 调用可以通过模型的 findBySql() 方法来完成。例如,在一个 blog 应用中,假设我要存储一个发贴者的姓到一个不是我的 Cake 策略(简化的例子)中的表时。我可以在模型中放一个自定义的函数,然后来获取数据。返回值是一个多位数组。
当然也有 query()
方法,如果执行成功返回true,失败返回false。因为查询语句并不是都返回结果集,像“DELETE FROM
problems WHERE solved = true
”。
要将数据保存到模型中,必须向模型提供要保存的数据。这些提交给 save() 方法的数据应该按照下面的形式:
Array ( [modelname] => Array ( [fieldname1] => value [fieldname2] => value ) )
为了能让你的数据按照这种形式 POST 发送到控制器中,只要使用 HTML 助手类就可以很方便地完成,因为它创建的表单元素的命名是 Cake
所期望的。如果你不要使用它:只要确保元素的名称类似 data[模型名][字段名]
。
从表单提交的数据会自动按照这种格式并放在控制器中的 $this->params['data']
,这样通过web的表单直接保存数据就很快。一个特性控制器的编辑方法可能会像下面这样显示:
function edit($id) { //注意:特性模型已经自动为我们载入了,在 $this->Property 中。 // 检查我们是否已经获取了表单数据…… if (isset($this->params['form']['data']['property'])) { // 这里我们尝试保存数据…… if ($this->property->save($this->params['data'])) { // 告诉用户他的数据已经被保存了…… $this->flash('Your information has been saved.', '/properties/edit/'.$this->params['data']['property']['id'], 2); exit(); } else { // 如果数据不能通过验证,显示验证错误 // 并重新将表单字段填上被提交的数据…… $this->set('form', $this->params['data']); $this->validateErrors($this->property); $this->render(); } } // 如果没有提交表单数据,那么就渲染编辑视图…… $this->render(); }
注意 save
操作是如何放置在一个条件语句中:如果你试图保存数据到model中,cake自动尝试确数据正确(根据你提供的规则)。可以查看第十章了解更多关于正确规则(validation).如果不想查看正确性直接保存数据,使用save($data,false);
Notice how the save operation is placed inside a conditional: when you try to
save data to your model, Cake automatically attempts to validate your data using
the rules you've provided. To learn more about data validation, see Chapter 10.
If you do not want save() to try to validate your data, use save($data, false)
.
其他有用的保存方法Other useful save functions:
del($id = null, $cascade = true)
删除模型中由$id指定的记录,或者当前的记录。
如果这个模型还与其他模型关联,同时在关系数组中设置了“dependant”键,那么当 $cascade 为 true 时,这个方法还会删除相关的模型。
当成功的时候返回 true。
saveField($name, $value)
保存一个单独的字段的值。
getLastInsertID()
返回最后创建记录的ID。
在我们接近0.10.x最终版的时候,我们添加了一些模型回调函数,它们允许用户能够逻辑上在模型的操作前或操作后执行。要在应用中使用这种功能,使用提供的参数并在你的 Cake 模型中覆盖这些函数。
beforeFind($conditions)
beforeFind()
回调函数在一个查询操作开始之前执行。将查询前的操作逻辑放到这个方法里面。当你在自己的模型中覆盖了这个方法后,如果你要继续执行 find
操作的话,返回true
,若要中止查询继续,返回false
。
afterFind($results)
使用这个回调函数可以修改从 find 操作中返回的结果,或者进行其他任何的 find 后逻辑。该函数的参数是模型的 find 操作的结果,返回的是修改后的结果。
beforeValidate()
使用这个回调函数可以在模型数据被验证之前,对数据进行修改。通过使用Model::invalidate()
,它可以用于额外的、更加复杂的验证规则。在这个环境中,模型数据是通过$this->data
来访问的。这个函数返回true
,save( )操作才会继续执行,否则会中断。
beforeSave()
将任何保存前的逻辑放在这个函数中。在模型数据通过验证之后,保存数据之前就立即执行这个函数(如果没有通过验证,那么save(
)调用就会终止,这个回调函数也不会被执行)。如果你要继续保存操作,那么也要返回true
,如果要终止,那么返回false
。
一种 beforeSave 的用法是格式化时间数据来存储到数据库中:
// 通过 HTML 帮助类来创建 日期/时间 字段: // 这段代码要放在视图中 $html->dayOptionTag('Event/start'); $html->monthOptionTag('Event/start'); $html->yearOptionTag('Event/start'); $html->hourOptionTag('Event/start'); $html->minuteOptionTag('Event/start'); /*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ // 用于重新组合日期数据用于数据存储的模型回调函数 // 这段代码应该在 Event 模型中: function beforeSave() { $this->data['Event']['start'] = $this->_getDate('Event', 'start'); return true; } function _getDate($model, $field) { return date('Y-m-d H:i:s', mktime( intval($this->data[$model][$field . '_hour']), intval($this->data[$model][$field . '_min']), null, intval($this->data[$model][$field . '_month']), intval($this->data[$model][$field . '_day']), intval($this->data[$model][$field . '_year']))); }
afterSave()
将你想要在保存后执行的代码放在这个回调函数中。
beforeDelete()
将删除前的逻辑代码放在这个函数中。如果你要让删除操作继续,那么返回true
,否则如果要终止,返回false
。
afterDelete()
将你想要在删除后执行的代码放在这个回调函数中。
当创建模型的时候,你可以设置一些特殊的变量,来获得 Cake 的一些功能:
$primaryKey
如果这个模型对应到一个数据库的表,但表的主键并不是叫“id”,那么可以使用这个变量来告诉 Cake 主键的名字。
$recursive
它可以设置你想让 Cake 在 find() 和 findAll() 操作中抓取的关联模型数据的层次数量。
假设你有一个组模型 Group,它可以对应多个用户模型 User,而 User 又可以对应多个文章 Article。
表 6.1. Model::recursive 选项
$recursive = 0 | Cake 仅抓取 Group 的数据 |
$recursive = 1 | Cake 抓取一个 Group 以及与它相关的 User |
$recursive = 2 | Cake 抓取一个 Group 和与它关联的 User,以及与 User 关联的 Article。 |
$transactional
告诉 Cake 该模型是否要启用事务处理(例如 begin/commit/rolback)。它是一个 boolean 值。只有对支持它的数据库才有用。
$useTable
如果你想使用的数据库表的名称并不是模型名称的复数形式(而且你也不想改变表的名字),那么可以将这个变量设置为你要让这个模型使用的表名。
$validate
用于验证传递给模型的数据的数组。请见第十章。
$useDbConfig
还记得你可以在 /app/config/database.php
文件中配置的数据库设置么?使用这个变量可以切换数据库配置――只要在数据库配置文件中创建的数据库链接的名字。默认的当然是“default”。
CakePHP 的一个最强大的功能是由模型提供的关系映射。在 CakePHP 中,表之间的链接是通过关联(association)进行处理的。关联是两个相关的逻辑单元之间的凝胶。
在 CakePHP 中存在四种类型的联结:
hasOne
hasMany
belongsTo
hasAndBelongsToMany
当定义了模型之间的关系之后,Cake 将自动抓取和你目前正在使用的模型相关的模型。例如,如果一个 Post 模型和一个 Author 模型使用一个 hasMany 关联相联系,那么在控制器中调用 $this->Post->findAll() 会抓取 Post 的记录,同时所有和这些记录相关的 Author 记录。
要正确使用关联,最好遵守 CakePHP 的命名约定(见附录 B)。如果你使用了 CakePHP 的命名约定的话,你就可以使用脚手架功能来立刻使应用的数据从页面上可见,因为脚手架功能可以探测和使用模型之间的关联。当然你也可以自定义模型之间的关联来绕开 CakePHP 的命名约定,但这些技巧我们以后再提。现在,还是让我们坚持这些约定。在这里我们关心的命名约定是外键、模型名称和表格名称。
下面列出了对上面提到的三种东西,Cake 所期望的名称:(关于命名的更多信息参见附录 B)
外键:
[模型名称单数形式]_id。例如,在“authors”表中有一个外键指向指定 Author 所属的 Post,则这个外键应命名为“post_id”。
表名称:
[对象名称复数形式]。由于我们要存储关于日志帖子和作者的信息,所以相应要有“posts”和“authors”两张表。
模型名称:
[表格名称的骆驼写法、单数形式]。对应“posts”表的模型的名称应为“Post”,同时对应“authors”表的模型的名称是“Post”。
为了进一步举例说明这些关联是如何用的,我们还继续使用 Blog 应用为例。假设我们要为 Blog 创建一个简单的用户管理系统。我们要跟踪用户的信息,要让每个用户与一个档案(Profile)关联(一个用户有一个档案——User hasOne Profile)。用户也要能创建评论,同时与它们保持关联(用户有多个评论——User hasMany Cooments)。我们让这个用户系统运行起来之后,我们就可以让帖子和标签(Tag)对象使用 hasAndBelongsToMany 来建立关系(Post hasAndBelongsToMany Tags)。
为了描述这个关联,假设你已经创建了 User 和 Profile 模型。要定义他们之间的 hasOne 关联,我们要给模型添加一个数组来告诉 Cake 他们是什么关系。如下:
Cake 使用 $hasOne 数组来在 User 和 Profile 模型之间构建关联。数组中的每一个建都可以让你进一步配置这个关联:
className (必须):要关联的模型的类名
针对我们的例子,我们要指定“Profile”模型的类名。
conditions: 定义关系的SQL 条件的片断
比如,我们可以使用它来告诉 Cake 只能关联有一个绿色抬头的 Profile。要这么做的话,你只要指定一个SQL条件部分作为这个键的值:"Profile.header_color = 'green'".
order: 相关联的模型的排序方式
如果你想令被关联的模型按照某个特定的顺序进行排列,将该键的值设成一个SQL顺序的谓词,例如:“Profile.name ASC”。
dependent:如果将其设置为 true,那么当某个记录被删除时,相关联的模型就会被销毁。
例如,如果“Cool Blue”档案是与“Bob”相关联的,且我删除了用户“Bob”,那么档案“Cool Blue”也将被删除。
foreignKey:指向相关联的模型的外键的名称。
万一你所处理的数据库并不遵守 Cake 的命名约定,那么可以修改它。
现在,当我们对 User 模型执行 find() 或者 findAll() 时,我们也将获得相关联的 Profile 模型:
$user = $this->User->findById('25'); print_r($user); //output: Array ( [User] => Array ( [id] => 25 [first_name] => John [last_name] => Anderson [username] => psychic [pasword] => c4k3roxx ) [Profile] => Array ( [id] => 4 [name] => Cool Blue [header_color] => aquamarine [user_id] = 25 ) )
现在一个User可以看到它对应的 Profile,我们还需要定义一个关联以便 Profile 可以看到它的 User。这可以通过使用 belongsTo 关联来完成。在 Profile 模型中,我们可以如下:
Cake 将使用 $belongsTo 数组来建立 User 和 Profile 模型之间的关联。数组中的每一个键都可以让你对这个关联进行进一步的配置:
className (必须):要关联的模型的类名
针对我们的例子,我们要指定“User”模型的类名。 class name.
conditions:定义关系的SQL 条件的片断
比如,我们可以使用它来告诉 Cake 只能关联活动着的用户。要这么做的话,你只要指定一个SQL条件部分作为这个键的值:"User.active = '1'"。
order: 相关联的模型的排序方式
如果你想令被关联的模型按照某个特定的顺序进行排列,将该键的值设成一个SQL顺序的谓词,例如:“User.last_name ASC”。
foreignKey: 指向相关联的模型的外键的名称。
万一你所处理的数据库并不遵守 Cake 的命名约定,那么可以修改它。
现在,当我们对 Profile 模型执行 find() 或者 findAll() 时,我们也将获得相关联的 User 模型:
$profile = $this->Profile->findById('4'); print_r($profile); //output: Array ( [Profile] => Array ( [id] => 4 [name] => Cool Blue [header_color] => aquamarine [user_id] = 25 ) [User] => Array ( [id] => 25 [first_name] => John [last_name] => Anderson [username] => psychic [pasword] => c4k3roxx ) )
现在 User 和 Profile 已经相互关联了并且运行正常,我们继续构建系统,让用户记录可以和多条评论相关。可以在 User 模型中这样做:
Cake 使用 $hasMany 数组来构建 User 和 Comment 模型之间的关联。数组中的每一个键都可以让你对这个关联进行进一步的配置:
className (必须):要关联的模型的类名
针对我们的例子,我们指定“Comment”模型的类名。
conditions:定义关系的SQL 条件的片断
比如,我们可以使用它来告诉 Cake 只能关联那些被审批的的评论。要这么做的话,你只要指定一个SQL条件部分作为这个键的值:"Comment.moderated = 1"。
order: 相关联的模型的排序方式
如果你想令被关联的模型按照某个特定的顺序进行排列,将该键的值设成一个SQL顺序的谓词,例如:“Comment.created DESC”。
limit: 你要让 Cake 获取的关联模型的数量上限。
对于这个例子,我们并不想抓取用户所有的评论,只要头五条。
foreignKey: 指向相关联的模型的外键的名称。
万一你所处理的数据库并不遵守 Cake 的命名约定,那么可以修改它。
dependent:如果将其设置为 true,那么当某个记录被删除时,相关联的模型就会被销毁。
例如,如果评论“Cool Blue”是与“Bob”相关联的,且我删除了用户“Bob”,那么评论“Cool Blue”也将被删除。
exclusive: 如果将其设置为 true,那么所有相关联的对象都只用一句 SQL 语句来删除,而不执行它们的 beforeDestroy 回调函数。
对于简单的关联十分有效,因为它更快。
finderSql: 指定完整的用于获取关联的 SQL 语句。
对于需要依赖多个表的复杂关联,这是一个很好的办法。如果 Cake 的自动生成的关联不能正常工作,那么,你可以在这里进行自定义。
现在,当我们对 User 模型执行 find() 或者 findAll() 的话,我们应该能看到相关联的 Comment 模型了:
$user = $this->User->findById('25'); print_r($user); //output: Array ( [User] => Array ( [id] => 25 [first_name] => John [last_name] => Anderson [username] => psychic [pasword] => c4k3roxx ) [Profile] => Array ( [id] => 4 [name] => Cool Blue [header_color] => aquamarine [user_id] = 25 ) [Comment] => Array ( [0] => Array ( [id] => 247 [user_id] => 25 [body] => The hasMany assocation is nice to have. ) [1] => Array ( [id] => 256 [user_id] => 25 [body] => The hasMany assocation is really nice to have. ) [2] => Array ( [id] => 269 [user_id] => 25 [body] => The hasMany assocation is really, really nice to have. ) [3] => Array ( [id] => 285 [user_id] => 25 [body] => The hasMany assocation is extremely nice to have. ) [4] => Array ( [id] => 286 [user_id] => 25 [body] => The hasMany assocation is super nice to have. ) ) )
最好同样定义一下“Comment belongsTo User”关联,不过在此我们就不再赘述。这么做可以让两个模型都能够看到对象。否则,在进行搭建脚手架功能的时候,常常会有一些麻烦。
现在你已经掌握了简单的关联,让我们继续学会最后一种关联:hasAndBelongsToMany(简称 HABTM)。这是最后一个也是最难理解的一个,但它也是最有用的。当你想让两个模型通过一个中间表来建立链接,那么 HABTM 关联是很有用的。中间表中保存了和两个模型有关系的例。
hasMany 和 hasAndBelongsToMany 之间的区别是,使用了 hasMany,被关联的模型就不能被共享。如果一个用户有很多评论(User hasMany Comments),那么它是唯一和这些评论相关联的用户。但使用HABTM,被关联的模型可以被共享。这对我们接下去要做的东西是意义巨大的:将 Post 模型和 Tag 模型相关联。当The difference between hasMany and hasAndBelongsToMany is that with hasMany, the associated model is not shared. If a User hasMany Comments, it is the *only* user associated to those comments. With HABTM, the associated models are shared. This is great for what we're about to do next: associate Post models to Tag models. When a Tag belongs to a Post, we don't want it to be 'used up', we want to continue to associate it to other Posts as well.
In order to do this, we'll need to set up the correct tables for this association. Of course you'll need a "tags" table for you Tag model, and a "posts" table for your posts, but you'll also need to create a join table for this association. The naming convention for HABTM join tables is [plural model name1]_[plural model name2], where the model names are in alphabetical order:
HABTM 中间表需要至少包含两个外键。例如,“post_id”和“tag_id”。join tables need to at least consist of the two foreign keys of the models they link. For our example, "post_id" and "tag_id" is all we'll need.
下面是Post HABTM Tag 例子的 SQL 导出:
-- -- Table structure for table `posts` -- CREATE TABLE `posts` ( `id` int(10) unsigned NOT NULL auto_increment, `user_id` int(10) default NULL, `title` varchar(50) default NULL, `body` text, `created` datetime default NULL, `modified` datetime default NULL, `status` tinyint(1) NOT NULL default '0', PRIMARY KEY (`id`) ) TYPE=MyISAM; -- -------------------------------------------------------- -- -- Table structure for table `posts_tags` -- CREATE TABLE `posts_tags` ( `post_id` int(10) unsigned NOT NULL default '0', `tag_id` int(10) unsigned NOT NULL default '0', PRIMARY KEY (`post_id`,`tag_id`) ) TYPE=MyISAM; -- -------------------------------------------------------- -- -- Table structure for table `tags` -- CREATE TABLE `tags` ( `id` int(10) unsigned NOT NULL auto_increment, `tag` varchar(100) default NULL, PRIMARY KEY (`id`) ) TYPE=MyISAM;
设置好了表格之后,我们就要在 Post 模型中定义关联:
The $hasAndBelongsToMany array is what Cake uses to build the association between the Post and Tag models. Each key in the array allows you to further configure the association:
className (required): the classname of the model you'd like to associate
For our example, we want to specify the 'User' model class name.
joinTable: this is here for a database that doesn't adhere to Cake's naming conventions. If your table doesn't look like [plural model1]_[plural model2] in lexical order, you can specify the name of your table here.
foreignKey: the name of the foreign key that points to the associated model.
This is here in case you're working with a database that doesn't follow Cake's naming conventions.
associationForeignKey: the name of the foreign key in the join table that points to the current model.
conditions: SQL condition fragments that define the relationship
We could use this to tell Cake to only associate a Comment that has been moderated. You would do this by setting the value of the key to be "Comment.moderated = 1", or something similar.
order: the ordering of the associated models
IF you'd like your associated models in a specific order, set the value for this key using an SQL order predicate: "Comment.created DESC", for example.
limit: the maximum number of associated models you'd like Cake to fetch.
For this example, we didn't want to fetch *all* of the user's comments, just five.
uniq: If set to true, duplicate associated objects will be ignored by accessors and query methods.
Basically, if the associations are distinct, set this to true. That way the Tag "Awesomeness" can only be assigned to the Post "Cake Model Associations" once, and will only show up once in result arrays.
finderSql: Specify a complete SQL statement to fetch the association.
This is a good way to go for complex associations that depends on multiple tables. If Cake's automatic assocations aren't working for you, here's where you customize it.
deleteQuery: A complete SQL statement to be used to remove assocations between HABTM models.
If you don't like the way Cake is performing deletes, or your setup is customized in some way, you can change the way deletion works by supplying your own query here.
Now, when we execute find() or findAll() calls using the Post model, we should see our associated Tag models there as well:
$post = $this->Post->findById('2'); print_r($user); //output: Array ( [Post] => Array ( [id] => 2 [user_id] => 25 [title] => Cake Model Associations [body] => Time saving, easy, and powerful. [created] => 2006-04-15 09:33:24 [modified] => 2006-04-15 09:33:24 [status] => 1 ) [Tag] => Array ( [0] => Array ( [id] => 247 [tag] => CakePHP ) [1] => Array ( [id] => 256 [tag] => Powerful Software ) ) )
Saving models that are associated by hasOne, belongsTo, and hasMany is pretty simple: you just populate the foreign key field with the ID of the associated model. Once that's done, you just call the save() method on the model, and everything gets linked up correctly.
With hasAndBelongsToMany, its a bit trickier, but we've gone out of our way to make it as simple as possible. In keeping along with our example, we'll need to make some sort of form that relates Tags to Posts. Let's now create a form that creates posts, and associates them to an existing list of Tags.
You might actually like to create a form that creates new tags and associates them on the fly - but for simplicity's sake, we'll just show you how to associate them and let you take it from there.
When you're saving a model on its own in Cake, the tag name (if you're using the Html Helper) looks like 'Model/field_name'. Let's just start out with the part of the form that creates our post:
The form as it stands now will just create Post records. Let's add some code to allow us to bind a given Post to one or many Tags:
In order for a call to $this->Post->save() in the controller to save the links between this new Post and its associated Tags, the name of the field must be in the form "Tag/Tag". The submitted data must be a single ID, or an array of IDs of linked records. Because we're using a multiple select here, the submitted data for Tag/Tag will be an array of IDs.
The $tags variable here is just an array where the keys are the IDs of the possible Tags, and the values are the displayed names of the Tags in the multi-select element.
目录
一个Controller是用来管理你应用某个方面的逻辑。大多数来说,controllers用来管理一个model的逻辑。比如,你正在建设一个站点来管理一个video的collection,你可能有一个VideoController以及一个RentalControlle来管理你的视频和租金。分别的,cake中每个controller的名字通常是复数。 A controller is used to manage the logic for a certain section of your application. Most commonly, controllers are used to manage the logic for a single model. For example, if you were building a site that manages a video collection, you might have a VideoController and a RentalController managing your videos and rentals, respectively. In Cake, controller names are always plural.
程序的Controllers都是从AppController类继承来的类,AppController又是从核心的类Controller继承来的。Controller类可以包含人以数量的action:各种显示Views的方法 Your application's controllers are classes that extend the Cake AppController class, which in turn extends a core Controller class. Controllers can include any number of actions: functions used in your web application to display views.
AppController类可以在/app/app_controller.php中第一,它里面包含了许多可以被更多controllers共享的方法,它本身是从核心类Controller继承来的。
AppController class can be defined in /app/app_controller.php
and it should contain methods that
are shared between two or more controllers. It itself extends the Controller
class which is a standard Cake library.
一个Action是controller中的一个单独的方法,这个方法可以被Dispathcher自动的运行,运行的根据是从routes配置解析的页面请求。回到我们的 video collection的例子,VideoController类可以包含actions比如view(), rent(), search()等等,这个controller可以在/app/controllers/video_controller.php找到,并且文件内容为: An action is a single functionality of a controller. It is run automatically by the Dispatcher if an incoming page request specifies it in routes configuration. Returning to our video collection example, our VideoController might contain the view(), rent(), and search() actions. The controller would be found in /app/controllers/videos_controller.php and contain:
class VideosController extends AppController { function view($id) { //action logic goes here.. } function rent($customer_id, $video_id) { //action logic goes here.. } function search($query) { //action logic goes here.. } }
你可以通过使用下面的URLs来调用这些方法: You would be able to access these actions using the following example URLs:
http://www.example.com/videos/view/253 http://www.example.com/videos/rent/5124/0-235253 http://www.example.com/videos/search/hudsucker+proxy
但是这些页面将如何显示?你需要为每个action定义一个view,你可以在下一章查看具体内容。但是本章中,你会掌握Cake的controller,并且能够更好的使用。具体的说,你将学到如何用controller将你的数据传递给view,使用户转向以及其他更多功能。 But how would these pages look? You would need to define a view for each of these actions - check it out in the next chapter, but stay with me: the following sections will show you how to harness the power of the Cake controller and use it to your advantage. Specifically, you'll learn how to have your controller hand data to the view, redirect the user, and much more.
尽管本章中介绍了许多Cake中常用的方法,更多的手册内容请参考http://api.cake.org While this section will treat most of the oft-used functions in Cake's Model, its important to remember to use http://api.cakephp.org for a full reference.
set($var, $value)
这个方法是将controller中的数据传递给view的主要方法。你可以使用这个方法控制任何值:single values, 整个数组等等。一旦你使用了set(),view中也可以使用这个变量:在controller中使用set(‘color’,’blue’)可以使得$color变量在view中使用This function is the main way to get data from your controller to your view. You can use it to hand over anything: single values,whole arrays, etc. Once you've used set(), the variable can be accessed in your view: doing set('color', 'blue') in your controller makes $color available in the view.
validate()
通过收集错误的保存,返回错误的个数Returns the number of errors generated by an unsuccessful save.
validateErrors()
根据model中第一的正确性规则,检查一个model的数据是否正确,更多信息参考第十章Validates a model data according to the model's defined validation rules. For more on validation, see Chapter 10.
render($action = null, $layout = null, $file = null)
你并非时刻需要这个方法,因为在每个controller方法的最后,render都被自动调用,并且调用后定义(或者使用)相应的viewYou may not often need this function, because render is automatically called for you at the end of each controller action, and the view named after your action is rendered. Alternatively, you can call this function to render the view at any point in the controller logic.
redirect($url)
使用这个方法告诉用户将跳转到哪里。这里的URL可以使Cake规则的URL或者使一个完整定义的URL(http://...)Tell your users where to go using this function. The URL passed here can be a Cake interal URL, or a fully qualified URL (http://...)
flash($message, $url, $pause = 1)
这个方法在你的flash
layout(app/views/layouts/flash.thtml)中显示$message信息$pause秒钟,之后跳转到指定的URLThis
function shows $message for $pause seconds inside of your flash layout
(found in app/views/layouts/flash.thtml
) then
redirects the user to the specified
URL.
Cake Controller定义了一系列回调函数,你可以使用他们在重要的controller function之前或者之后插入功能逻辑。实现这些功能,适用下面介绍的参数和返回值声明这些方法 Cake controllers feature a number of callbacks you can use to insert logic before or after important controller functions. To utilize this functionality, declare these functions in your controller using the parameters and return values detailed here.
beforeFilter()
在每个controller action之前调用,非常有用的地方去检查sessions是否有效,并检查相应的权限Called before every controller action. A useful place to check for active sessions and check roles.
afterFilter()
在每个controlleraction之后调用Called after every controller action.
beforeRender()
在controller逻辑之后,view显示之前调用Called after controller logic, and just before a view is rendered.
尽管这些方法是在Cake的Object类中,他们在Controller中也可以使用While these are functions part of Cake's Object class, they are also available inside the Controller:
generateFieldNames($data = null, $doCreateOptions = true)
requestAction($url, $extra = array())
这个方法根据任何地方(location)调用controller的action,然后返回已经显示的view.$url是Cake规范的URL(/controllername/actionname/params),如果$extra数组包含了一个键‘render’,controller action将自动设置AutoRenderThis function calls a controller's action from any location and returns the rendered view. The $url is a Cake URL (/controllername/actionname/params). If the $extra array includes a 'return' key, AutoRender is automatically set to true for the controller action.
You can use requestAction to get data from another controller action, or get a fully rendered view from a controller.
First, getting data from a controller is simple. You just use request action in the view you need the data.
// Here is our simple controller: class UsersController extends AppController { function getUserList() { $this->User->findAll(); } }
Imagine that we needed to create a simple table showing the users in the system. Instead of duplicating code in another controller, we can get the data from UsersController::getUserList() instead by using requestAction().
class ProductsController extends AppController { function showUserProducts() { $this->set('users', $this->requestAction('/users/getUserList')); // Now the $users variable in the view will have the data from // UsersController::getUserList(). } }
If you have an oft used element in your application that is not static, you might want to use requestAction() to inject it into your views. Let's say that rather than just passing the data from UsersController::getUserList, we actually wanted to render that action's view (which might consist of a table), inside another controller. This saves you from duplicating view code.
class ProgramsController extends AppController { function viewAll() { $this->set('userTable', $this->requestAction('/users/getUserList', array('return'))); // Now, we can echo out $userTable in this action's view to // see the rendered view that is also available at /users/getUserList. } }
Please note that actions called using requestAction() are rendered using an empty layout - this way you don't have to worry about layouts getting rendered inside of layouts.
The requestAction() function is also useful in AJAX situations where a small element of a view needs to be populated before or during and AJAX update.
log($msg, $type = LOG_ERROR)
你可以使用这个方法在你的程序应中记录发生的任何事件。记录文件Logs放在/tmp文件目录下You can use this function to
log different events that happen within your web application. Logs can be
found inside Cake's /tmp
directory.
If the $type is equal to the PHP constant LOG_DEBUG, the message is written to the log as a debug message. Any other type is written to the log as an error.
// Inside a controller, you can use log() to write entries: $this->log('Mayday! Mayday!'); //Log entry: 06-03-28 08:06:22 Error: Mayday! Mayday! $this->log("Look like {$_SESSION['user']} just logged in.", LOG_DEBUG); //Log entry: 06-03-28 08:06:22 Debug: Looks like Bobby just logged in.
postConditions($dtaa)
A method to which you can pass $this->params['data'], and it will pass back an array formatted as a model conditions array.
For example, if I have a person search form:
// app/views/people/search.thtml: <?php echo $html->input('Person/last_name'); ?>
Submitting the form with this element would result in the following $this->params['data'] array:
Array ( [Person] => Array ( [last_name] => Anderson ) )
At this point, we can use postConditions() to format this data to use in model:
// app/controllers/people_controller.php: $conditions = $this->postConditions($this->params['data']); // Yields an array looking like this: Array ( [Person.last_name] => Anderson ) // Which can be used in model find operations: $this->Person->findAll($conditions);
数量使用controller中定义的变量可以使你更好的使用cake的某些附加功能Manipulating a few special variables inside of your controller allows you to take advantage of some extra Cake functionality:
$name
PHP4 并不能以驼峰表示法将当前类的名称提供给我们。如果你遇到问题,使用这个变量将你的类名设置成正确的驼峰表示法PHP 4 doesn't like to give us the name of the current class in CamelCase. Use this variable to set the correct CamelCased name of your class if you're running into problems.
$uses
你的controller是否使用了很多的model?比如你的Fragglescontroller会自动调用$this->fraggle,但是你也想同时能够调用$this->smurf,那就将下面的语句加入到你的controller中Does your controller use more than one model? Your FragglesController will automatically load $this->fraggle, but if you want $this->smurf as well, try adding something like the following to your controller:
var $uses = array('Fraggle','Smurf');
$helpers
使用这个变量使得你的controller装在helpers到它的views. 这个HTML helper会自动装载,但是你可以使用这个变量来指定其他的Use this variable to have your controller load helpers into its views. The HTML helper is automatically loaded, but you can use this variable to specify a few others:
var $helpers = array('html','ajax','javascript');
$layout
将这个变量设置成为你在这个controller中想使用的layoutSet this variable to the name of the layout you would like to use for this controller.
$autoRender
将这个变量设置成为false将会使得actions停止自动显示(rendering)Setting this to false will stop your actions from automatically rendering.
$beforeFilter
如果你需要在某一个或者任何一个action调用之前都要运行一小段代码,使用$beforefilter.这个函数处理access control十分有用,你可以在任何action处理之前检查用户的权限。你需要做的就是将这个变量设置成为一个数组,数组中包含了你想运行的controller的action,如下所示 If you'd like a bit of code run every time an action is called (and before any of that action code runs), use $beforeFilter. This functionality is really nice for access control - you can check to see a user's permissions before any action takes place. Just set this variable using an array containing the controller action(s) you'd like to run:
class ProductsController extends AppController { var $beforeFilter = array('checkAccess'); function checkAccess() { //Logic to check user identity and access would go here.... } function index() { //When this action is called, checkAccess() is called first. } }
$components
和$helpers和$uses类似,这个变量用来装载你需要的components var $components = array('acl');Just like $helpers and $uses, this variable is used to load up components you will need:
var $components = array('acl');
Controller parameters are available at $this->params
in your Cake controller. This variable is
used to get data into the controller and provide access to information about the
current request. The most common usage of $this->params is to get access to
information that has been handed to the controller via POST or GET
operations.
$this->params['data']
Used to handle POST data sent from HTML Helper forms to the controller.
// A HTML Helper is used to create a form element $html->input('User/first_name'); // When rendered in the HTML would look something like: <input name="data[User][first_name]" value="" type="text" /> // And when submitted to the controller via POST, // shows up in $this->params['data']['User']['first_name'] Array ( [data] => Array ( [User] => Array ( [username] => mrrogers [password] => myn3ighb0r [first_name] => Mister [last_name] => Rogers ) ) )
$this->params['form']
Any POST data from any form is stored here, including information also found in $_FILES.
$this->params['bare']
Stores '1' if the current layout is bare, '0' if not.
$this->params['ajax']
Stores '1' if the current layout is ajax, '0' if not.
$this->params['controller']
Stores the name of the current controller handling the request. For example, if the URL /posts/view/1 was called, $this->params['controller'] would equal "posts".
$this->params['action']
Stores the name of the current action handling the request. For example, if the URL /posts/view/1 was called, $this->params['action'] would equal "view".
$this->params['pass']
Stores the GET query string passed with the current request. For example, if the URL /posts/view/?var1=3&var2=4 was called, $this->params['pass'] would equal "?var1=3&var2=4".
$this->params['url']
Stores the current URL requested, along with key-value pairs of get variables. For example, if the URL /posts/view/?var1=3&var2=4 was called, $this->params['pass'] would look like this:
[url] => Array ( [url] => posts/view [var1] => 3 [var2] => 4 )
一个View就是一个页面的模版,经常以action的名字来命名。举例来说,Postscontroller::add()方法的view存放在/app/views/posts/add.thtml。Cake的views文件都是简单的PHP文件,所以你可以在里面使用任何PHP代码,几乎所有的view文件都包含HTML,一个view可以使特定数据集的任何体现,包括XML,图片等等A
view is a page template, usually named after an action. For example, the view
for PostsController::add()
would be found at /app/views/posts/add.thtml
. Cake views are quite simply
PHP files, so you can use any PHP code inside them. Although most of your view
files will contain HTML, a view could be any perspective on a certain set of
data, be it XML, and image, etc.
在view的模版文件中,你可以从对应的Model中使用数据,这个数据以数组$data的形式传递过来,你在controller中使用set()传递过来的任何数据在view中都可以使用In
the view template file, you can use the data from the corresponding Model. This
data is passed as an array called $data
. Any data that
you've handed to the view using set() in the controller is also now available in
your view.
HTML helper默认情况下在任何一个view都是可用的。而且是views中使用最多的。它创建forms包括scripts和media、链接以及数据正确性检查中都非常有用。参考第九章第1.1节可以了解HTML helper更多的内容The HTML helper is available in every view by default, and is by far the most commonly used helper in views. It is very helpful in creating forms, including scripts and media, linking and aiding in data validation. Please see section 1.1 in Chapter 9 for a discussion on the HTML helper.
大部分视图中可用的函数都是由助手类(Helper)提供的。Cake提供了很多的助手类(参考第九章),你也可以引用自己定义的。因为views不应该包含过多的逻辑,所以views类中并没有很多public的方法。其中一个有用的方法就是renderElement(),这个方法我们在1.2节中讨论 Most of the functions available in the views are provided by Helpers. Cake comes with a great set of helpers (discussed in Chapter 9), and you can also include your own. Because views shouldn't contain much logic, there aren't many well used public functions in the view class. One that is helpful is renderElement(), which will be discussed in section 1.2.
一个布局(layout)包括了围绕view的所有代码。你想在view中见到的所有东西都应该被放在你的layout中。 布局文件位于/app/views/layouts。A layout contains all the presentational code that wraps around a view. Anything you want to see in all of your views should be placed in your layout.
Cake的默认layout可以在/app/views/layouts/default.thtml被新的默认layout重写。一旦一个新的默认layout创建,controller
view的代码将在页面被显示的时候替换到默认的layout Layout files are placed in /app/views/layouts
. Cake's default layout can be
overridden by placing a new default layout at /app/views/layouts/default.thtml
. Once a new default
layout has been created, controller view code is placed inside of the default
layout when the page is rendered.
当你常见一个layout的时候,你需要告诉Cake你的controller view的代码位置:为了
达到这个目的,一定要确保layout包含$content_for_layout(有$title_for_layout更好)下面是一 个默认的layout的例子
When you create a layout, you need to tell Cake where to place your controller
view code: to do so, make sure your layout includes a place for $content_for_layout
(and optionally, $title_for_layout
). Here's an example of what a default layout
might look like:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title><?php echo $title_for_layout?></title> <link rel="shortcut icon" href="favicon.ico" type="image/x-icon"> <body> <!-- IF you'd like some sort of menu to show up on all of your views, include it here --> <div id="header"> <div id="menu">...</div> </div> <!-- Here's where I want my views to be displayed --> <?php echo $content_for_layout ?> <!-- Add a footer to each displayed page --> <div id="footer">...</div> </body> </html>
To set the title for the layout, its easiest to do so in the controller, using the $pageTitle controller variable.
class UsersController extends AppController { function viewActive() { $this->pageTitle = 'View Active Users'; } }
You can create as many layouts as you wish for your Cake site, just place
them in the app/views/layouts directory, and switch between them inside of your
controller actions using the controller's $layout
variable, or setLayout() function.
For example, if a section of my site included a smaller ad banner space, I might create a new layout with the smaller advertising space and specify it as the layout for all controller's actions using something like:
var $layout = 'default_small_ad';
Many applications have small blocks of presentational code that needs to be repeated from page to page, sometimes in different places in the layout. Cake can help you repeat parts of your website that need to be reused. These reusable parts are called Elements. Ads, help boxes, navigational controls, extra menus, and callouts are often implemented in Cake as elements. An Element is basically a mini-view that can be included in other Views.
Elements live in the /app/views/elements/
folder,
and have the .thtml
filename extension.
The Element by default has no access to any data. To give it access to data, you send it in as a named parameter in an array.
Inside the Element file, all the passed variables are available as the names
of the keys of the passed array (much like how set() in the controller works
with the views). In the above example, the /app/views/elements/helpbox.thtml
file can use the $helptext
variable. Of course, it would be more useful to pass
an array to the Element.
Elements can be used to make a View more readable, placing the rendering of repeating elements in its own file. They can also help you re-use content fragments in your website.
目录
Helpers are meant to provide functions that are commonly needed in views to format and present data in useful ways.
The HTML helper is one of Cake's ways of making development less monotonous and more rapid. The HTML helper has two main goals: to aid in the insertion of oft-repeated sections of HTML code, and to aid in the quick-and-easy creation of web forms. The following sections will walk you through the highlight functions in the helper, but remember http://api.cakephp.org should always be used as a final reference.
Many of the functions in the HTML helper make use of a HTML tag definition
file called tags.ini.php
. Cake's core configuration
contains a tags.ini.php, but if you'd like to make some changes, create a copy
of /cake/config/tags.ini.php
and place it in your
/app/config/
folder. The HTML helper uses the tag
definitions in this file to generate tags you request. Using the HTML helper to
create some of your view code can be helpful, as a change to the tags.ini.php
file will result in a site-wide cascading change.
Additionally, if AUTO_OUTPUT is set to true in your core config file for your
application (/app/config/core.php
), the helper will
automatically output the the tag, rather than returning the value. This has been
done in an effort to assuage those who dislike short tags (<?= ?>) or lots
of echo() calls in their view code. Functions that include the $return parameter
allow you to force-override the settings in your core config. Set $return to
true if you'd like the HTML helper to return the HTML code regardless of any
AUTO_OUTPUT settings.
HTML helper functions also include a $htmlAttributes parameter, that allow you to tack on any extra attributes on your tags. For example, if you had a tag you'd like to add a class attribute to, you'd pass this as the $htmlAttribute value:
array('class'=>'someClass')
If you'd like to use Cake to insert well-formed and oft-repeated elements in your HTML code, the HTML helper is great at doing that. There are functions in this helper that insert media, aid with tables, and there's even guiListTree which creates an unordered list based on a PHP array.
charset ($charset, $return)
This is used to generate a charset META-tag.
css ($path, $rel= 'stylesheet', $htmlAttributes=null, $return=false)
Creates a link to a CSS stylesheet. The $rel parameter allows you to provide a rel= value for the tag.
image ($path, $htmlAttributes=null, $return=false)
Renders an image tag. The code returned from this function can be used as an input for the link() function to automatically create linked images.
link ($title, $url=null, $htmlAttributes=null, $confirmMessage=false, $escapeTitle=true, $return=false)
Use this function to create links in your view. $confirmMessage is used when you need a JavaScript confirmation message to appear once the link is clicked. For example, a link that deletes an object should probably have a "Are you sure?" type message to confirm the action before the link is activated. Set $escapeTitle to true if you'd like to have the HTML helper escape the data you handed it in the $title variable.
tableHeaders ($names, $tr_options=null, $th_options=null)
Used to create a formatted table header.
tableCells ($data, $odd_tr_options=null, $even_tr_options=null)
Used to create a formatted set of table cells.
guiListTree ($data, $htmlAttributes=null, $bodyKey= 'body', $childrenKey='children', $return=false)
Generates a nested unordered list tree from an array.
The HTML helper really shines when it comes to quickening your form code in your views. It generates all your form tags, automatically fills values back in during error situations, and spits out error messages. To help illustrate, let's walk through a quick example. Imagine for a moment that your application has a Note model, and you want to create controller logic and a view to add and edit Note objects. In your NotesController, you would have an edit action that might look something like the following:
Once we've got our controller set up, lets look at the view code (which would
be found in app/views/notes/edit.thtml
). Our Note
model is pretty simple at this point 鈥?it only contains an id, a submitter's id
and a body. This view code is meant to display Note data and allow the user to
enter new values and save that data to the model.
The HTML helper is available in all views by default, and can be accessed
using $html.
Specifically, let's just look at the table where the guts of the form are found:
Most of the form tag generating functions (along with tagErrorMsg) require you to supply a $fieldName. This $fieldName lets Cake know what data you are passing so that it can save and validate the data correclty. The string passed in the $fieldName parameter is in the form "modelname/fieldname." If you were going to add a new title field to our Note, you might add something to the view that looked like this:
<?php echo $html->input('note/title', $note['note']['title']) ?> <?php echo $html->tagErrorMsg('note/title', 'Please supply a title for this note.')?>
Error messages displayed by the tagErrorMsg() function are wrapped in <div class="error_message"></div> for easy CSS styling.
Here are the form tags the HTML helper can generate (most of them are straightforward):
formTag ($target=null, $type='post', $htmlAttributes=null)
Generates an opening form tag. Use $target to specify the form's action. Remember, if your form is submitting a file, you need to supply the $htmlAttributes parameter with the proper enctype information.
submit ($caption= 'Submit', $htmlAttributes=null, $return=false)
Supply an alternate button title using $caption.
password ($fieldName, $htmlAttributes=null, $return=false)
textarea ($fieldName, $htmlAttributes=null, $return=false)
checkbox ($fieldName, $title=null, $htmlAttributes=null, $return=false)
file ($fieldName, $htmlAttributes=null, $return=false)
hidden ($fieldName, $htmlAttributes=null, $return=false)
input ($fieldName, $htmlAttributes=null, $return=false)
radio ($fieldName, $options, $inbetween=null, $htmlAttributes=null, $return=false)
tagErrorMsg ($field, $text)
The HTML helper also includes a set of functions that aid in creating date-related option tags. The $tagName parameter should be handled in the same way that the $fieldName parameter; Just provide the name of the field this date option tag is relevant to. Once the data is processed, you'll see it in your controller with the part of the date it handles concatenated to the end of the field name. For example, if my Note had a deadline field that was a date, and my dayOptionTag $tagName parameter was set to 'note/deadline', the day data would show up in the $params variable once the form has been submitted to a controller action:
$this->params['form']['data']['note']['deadline_day']
You can then use this information to concatenate the time data in a format that is friendly to your current database configuration. This code would be placed just before you attempt to save the data, and saved in the $data array used to save the information to the model.
dayOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)
yearOptionTag ($tagName, $value=null, $minYear=null, $maxYear=null, $selected=null, $optionAttr=null)
monthOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)
hourOptionTag ($tagName, $value=null, $format24Hours=false, $selected=null, $optionAttr=null)
minuteOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)
meridianOptionTag ($tagName, $value=null, $selected=null, $optionAttr=null)
dateTimeOptionTag ($tagName, $dateFormat= 'DMY', $timeFormat= '12', $selected=null, $optionAttr=null)
The Cake Ajax helper utilizes the ever-popular Prototype and script.aculo.us
libraries for Ajax operations and client side effects. In order to use this
helper, you must have a current version of the JavaScript libraries from
http://script.aculo.us placed in /app/webroot/js/
.
In addition, any views that plan to use the Ajax Helper will need to include
those libraries.
Most of the functions in this helper expect a special $options array as a parameter. This array is used to specify different things about your Ajax operation. Here are the different values you can specify:
Here are the helper's functions for making Ajax in Cake quick and easy:
link ($title, $href, $options=null, $confirm = null, $escapeTitle = true)
Displays linked text $title, which retrieves the remote document at $options['url'] and updates the DOM element $options['update']. Callbacks can be used with this function.
remoteFunction ($options=null)
This function creates the JavaScript needed to make a remote call it is primarily used as a helper for linkToRemote. This isn't often used unless you need to generate some custom scripting.
remoteTimer ($options=null)
Periodically calls the specified action at $options['url'], every $options['frequency'] seconds (default is 10). Usually used to update a specified div (specified by $options['update']) with the results of the remote call. Callbacks can be used with this function.
form ($params = null, $type = 'post', $options = array()))
Returns a form tag that will submit to the action at $params using XMLHttpRequest in the background instead of the regular reload-required POST submission. The form data from this form will act just as a normal form data would (i.e. it will be available in $this->params['form']). The DOM element specified by $options['update'] will be updated with the resulting remote document. Callbacks can be used with this function.
observeField ($field_id, $options=null)
Observes the field with the DOM ID specified by $field_id (every $options['frequency'] seconds) and calls the action at $options['url'] when its contents have changed. You can update a DOM element with ID $options['update'] or specify a form element using $options['with'] as well. Callbacks can be used with this function.
observeForm ($form_id, $options=array())
Works the same as observeField(), only this observes all the elements in a given form.
autoComplete ($field, $url="", $options=array())
Renders a text field with ID $field with autocomplete. The action at $url should be able to return the autocomplete terms: basically, your action needs to spit out a unordered list (<ul></ul>) with list items that are the auto complete terms. If you wanted an autocomplete field that retrieved the subjects of your blog posts, your controller action might look something like:
function autocomplete () { $this->set('posts', $this->Post->findAll( "subject LIKE '{$this->params['form']['autocomplete_field_name_goes_here']}'") ); $this->layout = "ajax"; }
And your view for the autocomplete() action above would look something like:
<ul> <?php foreach($posts as $post): ?> <li><?php echo $post['Post']['subject']; ?></li> <?php endforeach; ?> </ul>
The actual auto-complete field as it would look in a view would look like this:
<form action="/users/index" method="POST"> <?=$ajax->autoComplete('Post/subject', '/posts/autoComplete')?> <?=$html->submit('View Post')?> </form>
The autoComplete() function will use this information to render a text field, and some divs that will be used to show the autocomplete terms supplied by your action. You might also want to style the view with something like the following:
<style type="text/css"> div.auto_complete { position :absolute; width :250px; background-color :white; border :1px solid #888; margin :0px; padding :0px; } li.selected { background-color: #ffb; } </style>
drag ($id, $options=array())
Makes the DOM element with ID $id draggable. There are some additional things you can specify using $options:
// (The version numbers refer to script.aculo.us versions) $options['handle'] // (v1.0) Sets whether the element should only be // draggable by an embedded handle. The value must be // an element reference or element id. $options['handle'] // (V1.5) As above, except now the value may be a // string referencing a CSS class value. The first // child/grandchild/etc. element found within the // element that has this CSS class value will be used // as the handle. $options['revert'] // (V1.0) If set to true, the element returns to its // original position when the drags ends. $options['revert'] // (V1.5) Revert can also be an arbitrary function // reference, called when the drag ends. $options['constraint'] // If set to 鈥榟orizontal鈥?or 鈥榲ertical鈥?the drag will // be constrained to take place only horizontally or // vertically.
drop ($id, $options=array())
Makes the DOM element with ID $id drop-able. There are some additional things you can specify using $options:
$options['accept'] // Set accept to a string or a JavaScript array of // strings describing CSS classes. The Droppable will // only accept Draggables that have one or more of // these CSS classes. $options['containment'] // The droppable element will only accept the // draggable element if it is contained in the given // elements (or element ids). Can be a single element // or a JS array of elements. $options['overlap'] //If set to 鈥榟orizontal鈥?or 鈥榲ertical鈥?the droppable // will only react to a draggable element if its //overlapping by more than 50% in the given direction.
dropRemote ($id, $options=array(), $ajaxOptions=array())
Used to create a drop target that initiates a XMLHttpRequest when a draggable element is dropped on it. The $options are the same as in drop(), and the $ajaxOptions are the same as in link().
sortable ($id, $options=array())
Makes a list or group of floated objects (specified by DOM element ID $id) sortable. The $options array can configure your sorting as follows:
$options['tag'] // Sets the kind of tag (of the child elements of the // container) that will be made sortable. For UL and // OL containers, this is 鈥楲I鈥? you have to provide // the tag kind for other sorts of child tags. // Defaults to 'li'. $options['only'] // Further restricts the selection of child elements // to only encompass elements with the given CSS class // (or, if you provide an array of strings, on any of // the classes). $options['overlap'] // Either 鈥榲ertical鈥?(default) or 鈥榟orizontal鈥? For // floating sortables or horizontal lists, choose // 鈥榟orizontal鈥? Vertical lists should use 鈥榲ertical鈥? $options['constraint'] // Restricts the movement of draggable elements, // 'vertical' or 'horizontal'. $options['containment'] // Enables dragging and dropping between Sortables. // Takes an array of elements or element-ids (of the // containers). $options['handle'] // Makes the created draggable elemetns use handles, // see the handle option on drag().
The JavaScript helper is used to aid the developer in outputting well-formatted Javascript-related tags and data.
codeBlock ($script)
Used to return $script placed within JavaScript <script> tags.
link ($url)
Returns a JavaScript include tag pointing to the script referenced by $url.
linkOut ($url)
Same as link(), only the include tag assumes that the script referenced by $url is not hosted on the same domain.
escapeScript ($script)
Escapes carriage returns and single and double quotes for JavaScript code segments.
event ($object, $event, $observer, $useCapture=true)
Attaches an event to an element. Used with the Prototype library.
cacheEvents ()
Caches JavaScript events created with event().
writeEvents ()
Writes cached events cached with cacheEvents().
includeScript ($script="")
This tag returns JavaScript include tags for all of the scripts currently
residing in Cake's JavaScript directory (/app/webroot/js/
).
The Number helper includes a few nice functions for formatting numerical data in your views.
precision ($number, $precision=3)
Returns $number formatted to the level of precision specified by $precision.
toReadableSize ($size)
Returns a human readable size, given the $size supplied in bytes. Basically, you pass a number of bytes in, and this function returns the appropriate human-readable value in KB, MB, GB, or TB.
toPercentage ($number, $precision=2)
Returns the given number formatted as a percentage, limited to the precision specified in $precision.
The Text Helper provides methods that a developer may need for outputting well formatted text to the browser.
highlight ($text, $phrase, $highlighter= '< span class="highlight">\1</span >')
Returns $text, with every occurance or $phrase wrapped with the tags specified in $highlighter.
stripLinks ($text)
Returns $text, with all HTML links (<a href= ...) removed.
autoLinkUrls ($text, $htmlOptions=array())
Returns $text with URLs wrapped in corresponding <a> tags.
autoLinkEmails ($text, $htmlOptions=array())
Returns $text with email addresses wrapped in corresponding <a> tags.
autoLink ($text, $htmlOptions=array())
Returns $text with URLs and emails wrapped in corresponding <a> tags.
truncate ($text, $length, $ending='...')
Returns the first $length number of characters of $text followed by $ending ('...' by default).
trim ()
Alias for truncate().
excerpt ($text, $phrase, $radius=100, $ending="...")
Extracts an excerpt from the $text, grabbing the $phrase with a number of characters on each side determined by $radius.
flay($text, $allowHtml=false)
Text-to-html parser, similar to Textile or RedCloth, only with a little different syntax.
The Time Helper provides methods that a developer may need for outputting Unix timestamps and/or datetime strings into more understandable phrases to the browser.
Dates can be provided to all functions as either valid PHP datetime strings or Unix timestamps.
fromString ($date_string)
Returns a UNIX timestamp, given either a UNIX timestamp or a valid strtotime() date string.
nice ($date_string=null, $return=false)
Returns a nicely formatted date string. Dates are formatted as "D, M jS Y, H:i", or 'Mon, Jan 1st 2005, 12:00'.
niceShort ($date_string=null, $return=false)
Formats date strings as specified in nice(), but ouputs "Today, 12:00" if the date string is today, or "Yesterday, 12:00" if the date string was yesterday.
isToday ($date_string)
Returns true if given datetime string is today.
daysAsSql ($begin, $end, $field_name, $return=false)
Returns a partial SQL string to search for all records between two dates.
dayAsSql ($date_string, $field_name, $return=false)
Returns a partial SQL string to search for all records between two times occurring on the same day.
isThisYear ($date_string, $return=false)
Returns true if given datetime string is within current year.
wasYesterday ($date_string, $return=false)
Returns true if given datetime string was yesterday.
isTomorrow ($date_string, $return=false)
Returns true if given datetime string is tomorrow.
toUnix ($date_string, $return=false)
Returns a UNIX timestamp from a textual datetime description. Wrapper for PHP function strtotime().
toAtom ($date_string, $return=false)
Returns a date formatted for Atom RSS feeds.
toRSS ($date_string, $return=false)
Formats date for RSS feeds
timeAgoInWords ($datetime_string, $return=false)
Returns either a relative date or a formatted date depending on the difference between the current time and given datetime. $datetime should be in a strtotime-parsable format like MySQL datetime.
relativeTime ($datetime_string, $return=false)
Works much like timeAgoInWords(), but includes the ability to create output for timestamps in the future as well (i.e. "Yesterday, 10:33", "Today, 9:42", and also "Tomorrow, 4:34").
wasWithinLast ($timeInterval, $date_string, $return=false)
Returns true if specified datetime was within the interval specified, else false. The time interval should be specifed with the number as well as the units: '6 hours', '2 days', etc.
Have the need for some help with your view code? If you find yourself needing a specific bit of view logic over and over, you can make your own view helper.
Let's say we wanted to create a helper that could be used to output a CSS styled link you needed in your application. In order to fit your logic in to Cake's existing Helper structure, you'll need to create a new class in /app/views/helpers. Let's call our helper LinkHelper. The actual php class file would look something like this:
There are a few functions included in Cake's helper class you might want to take advantage of:
output($str, $return = false)
Decides whether to output or return a string based on AUTO_OUTPUT (see
/app/config/core.php
) and $return's value. You
should use this function to hand any data back to your
view.
loadConfig()
Returns your application's current core configuration and tag definitions.
Let's use output() to format our link title and URL and hand it back to the view.
You may wish to use some functionality already existing in another helper. To take advantage of that, you can specify helpers you wish to use with a $helpers array, formatted just as you would in a controller.
Once you've created your helper and placed it in /app/views/helpers/, you'll be able to include it in your controllers using the special variable $helpers.
class ThingsController { var $helpers = array('html', 'link'); }
Remember to include the HTML helper in the array if you plan to use it elsewhere. The naming conventions are similar to that of models.
LinkHelper = class name
link = key in helpers array
link.php = name of php file in /app/views/helpers
.
Please consider giving your code back to Cake - contact one of the developers using our Trac system or the mailing list, or open a new CakeForge project to share your new helper with others.
Here are some globally available constants and functions that you might find useful as you build your application with Cake.
Here are Cake's globally available functions. Many of them are convenience wrappers for long-named PHP functions, but some of them (like vendor() and uses()) can be used to include code, or perform other useful functions. Chances are if you're wanting a nice little function to do something annoying over and over, it's here.
config()
Loads Cake's core configuration file. Returns true on success.
uses($lib1, $lib2, $lib3...)
Used to load Cake's core libaries (found in cake/libs/
). Supply the name of the lib filename
without the '.php' extension.
uses('sanitize', 'security');
vendor($lib1, $lib2, $lib3...)
Used to load external libraries found in the /vendors directory. Supply the name of the lib filename without the '.php' extension.
vendor('myWebService', 'nusoap');
debug($var, $showHTML = false)
If the application's DEBUG level is non-zero, the $var is printed out. If $showHTML is true, the data is rendered to be browser-friendly.
a()
Returns an array of the parameters used to call the wrapping function.
function someFunction() { echo print_r(a('foo', 'bar')); } someFunction(); // output: array( [0] => 'foo', [1] => 'bar' )
aa()
Used to create associative arrays formed from the parameters used to call the wrapping function.
echo aa('a','b'); // output: array( 'a' => 'b' )
e($text)
Convenience wrapper for echo().
low()
Convenience wrapper for strtolower().
up()
Convenience wrapper for strtoupper().
r($search, $replace, $subject)
Convenience wrapper for str_replace().
pr($data)
Convenience function equivalent to:
echo "<pre>" . print_r($data) . "</pre>";
Only prints out information if DEBUG is non-zero.
am($array1, $array2, $array3...)
Merges and returns the arrays supplied in the parameters.
env($key)
Gets an environment variable from available sources. Used as a backup if $_SERVER or $_ENV are disabled.
This function also emulates PHP_SELF and DOCUMENT_ROOT on unsupporting servers. In fact, it's a good idea to always use env() instead of $_SERVER or getenv() (especially if you plan to distribute the code), since it's a full emulation wrapper.
cache($path, $data, $expires, $target = 'cache')
Writes the data in $data to the path in /app/tmp specified by $path as a cache. The expiration time specified by $expires must be a valid strtotime() string. The $target of the cached data can either be 'cache' or 'public'.
clearCache($search, $path, $ext)
Used to delete files in the cache directories, or clear contents of cache directories.
If $search is a string, matching cache directory or file names will be removed from the cache. The $search parameter can also be passed as an array of names of files/directories to be cleared. If empty, all files in /app/tmp/cache/views will be cleared.
The $path parameter can be used to specify which directory inside of /tmp/cache is to be cleared. Defaults to 'views'.
Teh $ext param is used to specify files with a certain file extention you wish to clear.
stripslashes_deep($array)
Recursively strips slashes from all values in an array.
countdim($array)
Returns the number of dimensions in the supplied array.
fileExistsInPath($file)
Searches the current include path for a given filename. Returns the path to that file if found, false if not found.
convertSlash($string)
Converts forward slashes to underscores and removes first and last underscores in a string.
Creating custom validation rules can help to make sure the data in a Model conforms to the business rules of the application, such as passwords can only be eight characters long, user names can only have letters, etc.
The first step to data validation is creating the validation rules in the Model. To do that, use the Model::validate array in the Model definition, for example:
Validations are defined using Perl-compatibile regular expressions, some of which are pre-defined in /libs/validators.php. These are:
VALID_NOT_EMPTY |
VALID_NUMBER |
VALID_EMAIL |
VALID_YEAR |
If there are any validations present in the model definition (i.e. in the $validate array), they will be parsed and checked during saves (i.e. in the Model::save() method). To validate the data directly use the Model::validates() (returns false if data is incorrect) and Model::invalidFields() (which returns an array of error messages).
But usually the data is implicit in the controller code. The following example demonstrates how to create a form-handling action:
The view used by this action can look like this:
The Controller::validates($model[, $model...]) is used to check any custom validation added in the model. The Controller::validationErrors() method returns any error messages thrown in the model so they can be displayed by tagErrorMsg() in the view.
If you'd like to perform some custom validation apart from the regex based Cake validation, you can use the invalidate() function of your model to flag a field as erroneous. Imagine that you wanted to show an error on a form when a user tries to create a username that already exists in the system. Because you can't just ask Cake to find that out using regex, you'll need to do your own validation, and flag the field as invalid to invoke Cake's normal form invalidation process.
The controller might look something like this:
<?php class UsersController extends AppController { function create() { // Check to see if form data has been submitted if ($this->params['data']['User']) { //See if a user with that username exists $user = $this->User->findByUsername($this->params['data']['User']['username']); // Invalidate the field to trigger the HTML Helper's error messages if (!empty($user['User']['username'])) { $this->User->invalidate('username'); } //Try to save as normal, shouldn't work if the field was invalidated. if($this->User->save($this->params['data'])) { $this->redirect('/users/index/saved'); } else { $this->render(); } } } } ?>
目录
Most important, powerful things require some sort of access control. Access control lists are a way to manage application permissions in a fine-grained, yet easily maintainable and manageable way. Access control lists, or ACL, handle two main things: things that want stuff, and things that are wanted. In ACL lingo, things (most often users) that want to use stuff are called access request objects, or AROs. Things in the system that are wanted (most often actions or data) are called access control objects, or ACOs. The entities are called 'objects' because sometimes the requesting object isn't a person - sometimes you might want to limit the access certain Cake controllers have to initiate logic in other parts of your application. ACOs could be anything you want to control, from a controller action, to a web service, to a line on your grandma's online diary.
To use all the acronyms at once: ACL is what is used to decide when an ARO can have access to an ACO.
Now, in order to help you understand this, let's use a practial example. Imagine, for a moment, a computer system used by a group of adventurers. The leader of the group wants to forge ahead on their quest while maintaining a healthy amount of privacy and security for the other members of the party. The AROs involved are as following:
Gandalf Aragorn Bilbo Frodo Gollum Legolas Gimli Pippin Merry
These are the entities in the system that will be requesting things (the ACOs) from the system. It should be noted that ACL is *not* a system that is meant to authenticate users. You should already have a way to store user information and be able to verify that user's identity when they enter the system. Once you know who a user is, that's where ACL really shines. Okay - back to our adventure.
The next thing Gandalf needs to do is make an initial list of things, or ACOs, the system will handle. His list might look something like:
Weapons The One Ring Salted Pork Diplomacy Ale
Traditionally, systems were managed using a sort of matrix, that showed a basic set of users and permissions relating to objects. If this information were stored in a table, it might look like the following, with X's indicating denied access, and O's indicating allowed access.
Weapons The One Ring Salted Pork Diplomacy Ale Gandalf X X O O O Aragorn O X O O O Bilbo X X X X O Frodo X O X X O Gollum X X O X X Legolas O X O O O Gimli O X O X X Pippin X X X O O Merry X X X X O
At first glance, it seems that this sort of system could work rather well. Assignments can be made to protect security (only Frodo can access the ring) and protect against accidents (keeping the hobbits out of the salted pork). It seems fine grained enough, and easy enough to read, right?
For a small system like this, maybe a matrix setup would work. But for a growing system, or a system with a large amount of resources (ACOs) and users (AROs), a table can become unwieldy rather quickly. Imagine trying to control access to the hundreds of war encampments and trying to manage them by unit, for example. Another drawback to matrices is that you can't really logically group sections of users, or make cascading permissions changes to groups of users based on those logical groupings. For example, it would sure be nice to automatically allow the hobbits access to the ale and pork once the battle is over: Doing it on an indivudual user basis would be tedious and error prone, while making a cascading permissions change to all 'hobbits' would be easy.
ACL is most usually implemented in a tree structure. There is usually a tree of AROs and a tree of ACOs. By organizing your objects in trees, permissions can still be dealt out in a granular fashion, while still maintaining a good grip on the big picture. Being the wise leader he is, Gandalf elects to use ACL in his new system, and organizes his objects along the following lines:
Fellowship of the Ring: Warriors Aragorn Legolas Gimli Wizards Gandalf Hobbits Frodo Bilbo Merry Pippin Vistors Gollum
By structuring our party this way, we can define access controls to the tree, and apply those permissions to any children. The default permission is to deny access to everything. As you work your way down the tree, you pick up permissions and apply them. The last permission applied (that applies to the ACO you're wondering about) is the one you keep. So, using our ARO tree, Gandalf can hang on a few permissions:
Fellowship of the Ring: [Deny: ALL] Warriors [Allow: Weapons, Ale, Elven Rations, Salted Pork] Aragorn Legolas Gimli Wizards [Allow: Salted Pork, Diplomacy, Ale] Gandalf Hobbits [Allow: Ale] Frodo Bilbo Merry Pippin Vistors [Allow: Salted Pork] Gollum
If we wanted to use ACL to see if the Pippin was allowed to access the ale, we'd first get his path in the tree, which is Fellowship->Hobbits->Pippin. Then we see the different permissions that reside at each of those points, and use the most specific permission relating to Pippin and the Ale.
Fellowship = DENY Ale, so deny (because it is set to deny all ACOs)
Hobbits = ALLOW Ale, so allow
Pippin = ?; There really isn't any ale-specific information so we stick with ALLOW.
Final Result: allow the ale.
The tree also allows us to make finer adjustments for more granular control - while still keeping the ability to make sweeping changes to groups of AROs:
Fellowship of the Ring: [Deny: ALL] Warriors [Allow: Weapons, Ale, Elven Rations, Salted Pork] Aragorn [Allow: Diplomacy] Legolas Gimli Wizards [Allow: Salted Pork, Diplomacy, Ale] Gandalf Hobbits [Allow: Ale] Frodo [Allow: Ring] Bilbo Merry [Deny: Ale] Pippin [Allow: Diplomacy] Vistors [Allow: Salted Pork] Gollum
You can see this because the Aragorn ARO maintains is permissions just like others in the Warriors ARO group, but you can still make fine-tuned adjustments and special cases when you see fit. Again, permissions default to DENY, and only change as the traversal down the tree forces an ALLOW. To see if Merry can access the Ale, we'd find his path in the tree: Fellowship->Hobbits->Merry and work our way down, keeping track of ale-related permissions:
Fellowship = DENY (because it is set to deny all), so deny the ale.
Hobbits = ALLOW: ale, so allow the ale
Merry = DENY ale, so deny the ale
Final Result: deny the ale.
Cake's first ACL implementation was based off of INI files stored in the Cake installation. While its useful and stable, we recommend that you use the database backed ACL solution, mostly because of its ability to create new ACOs and AROs on the fly. We meant it for usage in simple applications - and especially for those folks who might not be using a database for some reason.
ARO/ACO permissions are specified in /app/config/acl.ini.php
. Instructions on specifying access
can be found at the beginning of acl.ini.php:
; acl.ini.php - Cake ACL Configuration ; --------------------------------------------------------------------- ; Use this file to specify user permissions. ; aco = access control object (something in your application) ; aro = access request object (something requesting access) ; ; User records are added as follows: ; ; [uid] ; groups = group1, group2, group3 ; allow = aco1, aco2, aco3 ; deny = aco4, aco5, aco6 ; ; Group records are added in a similar manner: ; ; [gid] ; allow = aco1, aco2, aco3 ; deny = aco4, aco5, aco6 ; ; The allow, deny, and groups sections are all optional. ; NOTE: groups names *cannot* ever be the same as usernames!
Using the INI file, you can specify users (AROs), the group(s) they belong to, and their own personal permissions. You can also specify groups along with their permissions. To learn how to use Cake's ACL component to check permissions using this INI file, see section 11.4.
The default ACL permissions implementation is database stored. Database ACL, or dbACL consists of a set of core models, and a command-line script that comes with your Cake installation. The models are used by Cake to interact with your database in order to store and retrieve nodes the ACL trees. The command-line script is used to help you get started and be able to interact with your trees.
To get started, first you'll need to make sure your /app/config/database.php
is present and correctly
configured. For a new Cake installation, the easiest way to tell that this is so
is to bring up the installation directory using a web browser. Near the top of
the page, you should see the messages "Your database configuration file is
present." and "Cake is able to connect to the database." if you've done it
correctly. See section 4.1 for more information on database configuration.
Next, use the the ACL command-line script to initialize your database to
store ACL information. The script found at /cake/scripts/acl.php will help you
accomplish this. Initialize the your database for ACL by executing the following
command (from your /cake/scripts/
directory):
At this point, you should be able to check your project's database to see the new tables. If you're curious about how Cake stores tree information in these tables, read up on modified database tree traversal. Basically, it stores nodes, and their place in the tree. The acos and aros tables store the nodes for their respective trees, and the aros_acos table is used to link your AROs to the ACOs they can access.
Now, you should be able to start creating your ARO and ACO trees.
There are two ways of referring to AROs/ACOs. One is by giving them an numeric id, which is usually just the primary key of the table they belong to. The other way is by giving them a string alias. The two are not mutually exclusive.
The way to create a new ARO is by using the methods defined the the Aro Cake model. The create() method of the Aro class takes three parameters: $link_id, $parent_id, and $alias. This method creates a new ACL object under the parent specified by a parent_id - or as a root object if the $parent_id passed is null. The $link_id allows you to link a current user object to Cake's ACL structures. The alias parameter allows you address your object using a non-integer ID.
Before we can create our ACOs and AROs, we'll need to load up those classes. The easiest way to do this is to include Cake's ACL Component in your controller using the $components array:
var $components = array('Acl');
Once we've got that done, let's see what some examples of creating these objects might look like. The following code could be placed in a controller action somewhere:
$aro = new Aro(); // First, set up a few AROs. // These objects will have no parent initially. $aro->create( 1, null, 'Bob Marley' ); $aro->create( 2, null, 'Jimi Hendrix'); $aro->create( 3, null, 'George Washington'); $aro->create( 4, null, 'Abraham Lincoln'); // Now, we can make groups to organize these users: // Notice that the IDs for these objects are 0, because // they will never tie to users in our system $aro->create(0, null, 'Presidents'); $aro->create(0, null, 'Artists'); //Now, hook AROs to their respective groups: $aro->setParent('Presidents', 'George Washington'); $aro->setParent('Presidents', 'Abraham Lincoln'); $aro->setParent('Artists', 'Jimi Hendrix'); $aro->setParent('Artists', 'Bob Marley'); //In short, here is how to create an ARO: $aro = new Aro(); $aro->create($user_id, $parent_id, $alias);
You can also create AROs using the command line script using $acl.php create aro <link_id> <parent_id> <alias>.
Creating an ACO is done in a similar manner:
$aco = new Aco(); //Create some access control objects: $aco->create(1, null, 'Electric Guitar'); $aco->create(2, null, 'United States Army'); $aco->create(3, null, 'Fans'); // I suppose we could create groups for these // objects using setParent(), but we'll skip that // for this particular example //So, to create an ACO: $aco = new Aco(); $aco->create($id, $parent, $alias);
The corresponding command line script command would be: $acl.php create aco <link_id> <parent_id> <alias>.
After creating our ACOs and AROs, we can finally assign permission between the two groups. This is done using Cake's core Acl component. Let's continue on with our example:
// First, in a controller, we'll need access // to Cake's ACL component: class SomethingsController extends AppController { // You might want to place this in the AppController // instead, but here works great too. var $components = array('Acl'); // Remember: ACL will always deny something // it doesn't have information on. If any // checks were made on anything, it would // be denied. Let's allow an ARO access to an ACO. function someAction() { //ALLOW // Here is how you grant an ARO full access to an ACO $this->Acl->allow('Jimi Hendrix', 'Electric Guitar'); $this->Acl->allow('Bob Marley', 'Electric Guitar'); // We can also assign permissions to groups, remember? $this->Acl->Allow('Presidents', 'United States Army'); // The allow() method has a third parameter, $action. // You can specify partial access using this parameter. // $action can be set to create, read, update or delete. // If no action is specified, full access is assumed. // Look, don't touch, gentlemen: $this->Acl->allow('George Washington', 'Electric Guitar', 'read'); $this->Acl->allow('Abraham Lincoln', 'Electric Guitar', 'read'); //DENY //Denies work in the same manner: //When his term is up... $this->Acl->deny('Abraham Lincoln', 'United States Army'); } }
This particular controller isn't especially useful, but it is mostly meant to show you how the process works. Using the Acl component in connection with your user management controller would be the best usage. Once a user has been created on the system, her ARO could be created and placed at the right point of the tree, and permissions could be assigned to specific ACO or ACO groups based on her identity.
Permissions can also be assigned using the command line script packaged with Cake. The syntax is similar to the model functions, and can be viewed by executing $php acl.php help.
Checking permissions is the easiest part of using Cake's ACL: it consists of using a single method in the Acl component: check(). A good way to implement ACL in your application might be to place an action in your AppController that performs ACL checks. Once placed there, you can access the Acl component and perform permissions checks application-wide. Here's an example implementation:
class AppController extends Controller { // Get our component var $components = array('Acl'); function checkAccess($aco) { // Check access using the component: $access = $this->Acl->check($_SESSION['user_alias'], $aco, $action = "*"); //access denied if ($access === false) { echo "access denied"; exit; } //access allowed else { echo "access allowed"; exit; } } }
Basically, by making the Acl component available in the AppController, it will be visible for use in any controller in your application.
// Here's the basic format: $this->Acl->Check($aro, $aco, $action = '*');
Cake comes with Sanitize, a class you can use to rid user-submitted data of malicious attacks and other unwanted data. Sanitize is a core library, so it can be used anywhere inside of your code, but is probably best used in controllers or models.
// First, include the core library: uses('sanitize'); // Next, create a new Sanitize object: $mrClean = new Sanitize(); // From here, you can use Sanitize to clean your data // (These methods explained in the next section)
This section explains how to use some of the functions that Sanitize offers.
paranoid($string, $allowed = array())
This function strips anything out of the target $string that is not a plain-jane alphanumeric character. You can, however, let it overlook certain characters by passing them along inside the $allowed array.
$badString = ";:<script><html>< // >@@#"; echo $mrClean->paranoid($badString); // output: scripthtml echo $mrClean->paranoid($badString, array(' ', '@')); // output: scripthtml @@
html($string, $remove = false)
This method helps you get user submitted data ready for display inside an existing HTML layout. This is especially useful if you don't want users to be able to break your layouts or insert images or scripts inside of blog comments, forum posts, and the like. If the $remove option is set to true, any HTML is removed rather than rendered as HTML entities.
$badString = '<font size="99" color="#FF0000">HEY</font><script>...</script>'; echo $mrClean->html($badString); // output: <font size="99" color="#FF0000">HEY</font><script>...</script> echo $mrClean->html($badString, true); // output: font size=99 color=#FF0000 HEY fontscript...script
sql($string)
Used to escape SQL statements by adding slashes, depending on the system's current magic_quotes_gpc setting.
cleanArray(&$toClean)
This function is an industrial strength, multi-purpose cleaner, meant to be used on entire arrays (like $this->params['form'], for example). The function takes an array and cleans it: nothing is returned because the array is passed by reference. The following cleaning operations are performed on each element in the array (recursively):
Odd spaces (including 0xCA) are replaced with regular spaces.
HTML is replaced by its corresponding HTML entities (including \n to <br>).
Double-check special chars and remove carriage returns for increased SQL security.
Add slashes for SQL (just calls the sql function outlined above).
Swap user-inputted backslashes with trusted backslashes.
Cake comes preset to save session data in three ways: as temporary files
inside of your Cake installation, using the default PHP mechanism, or serialized
in a database. By default, Cake uses PHP's default settings. To override this in
order to use temp files or a database, edit your core configuration file at
/app/config/core.php
. Change the CAKE_SESSION_SAVE
constant to 'cake', 'php', or 'database', depending on your application's
needs.
In order to use the database session storage, you'll need to create a table
in your database. The schema for that table can be found in /app/config/sql/sessions.sql
.
The Cake session component is used to interact with session information. It includes basic session reading and writing, but also contains features for using sessions for error and reciept messages (i.e. "Your data has been saved"). The Session Component is available in all Cake controllers by default.
Here are some of the functions you'll use most:
check($name)
Checks to see if the current key specified by $name has been set in the session.
del($name) and delete($name)
Deletes the session variable specified by $name.
error()
Returns the last error created by the CakeSession component. Mostly used for debugging.
flash($key = 'flash')
Returns the last message set in the session using setFlash(). If $key has been set, the message returned is the most recent stored under that key.
read($name = null)
Returns the session variable specified by $name.
renew()
Renews the currently active session by creating a new session ID, deleting the old one, and passing the old session data to the new one.
setFlash($flashMessage, $layout = 'default', $params = array(), $key = 'flash')
Writes the message specified by $flashMessage into the session (to be later retrieved by flash()).
If $layout is set to 'default', the message is stored as '<div class="message">'.$flashMessage.'</div>'. If $default is set to '', the message is stored just as it has been passed. If any other value is passed, the message is stored inside the Cake view specified by $layout.
Params has been placed in this function for future usage. Check back for more info.
The $key variable allows you to store flash messages under keys. See flash() for retreiving a flash message based off of a key.
valid()
Returns true if the session is valid. Best used before read() operations to make sure that the session data you are trying to access is in fact valid.
write($name, $value)
Writes the variable specified by $name and $value into the active session.
The Request Handler component is used in Cake to determine information about the incoming HTTP request. You can use it to better inform your controller about AJAX requests, get information about the remote client's IP address and request type, or strip unwanted data from output. To use the Request Handler component, you'll need to make sure it is specified in your controller's $components array.
class ThingsController extends AppController { var $components = array('RequestHandler'); // ... }
Let's just dive in:
accepts($type)
Returns information about the content-types that the client accepts, depending on the value of $type. If null or no value is passed, it will return an array of content-types the client accepts. If a string is passed, it returns true if the client accepts the given type, by checking $type against the content-type map (see setContent()). If $type is an array, each string is evaluated individually, and accepts() will return true if just one of them matches an accepted content-type. For example:
class PostsController extends AppController { var $components = array('RequestHandler'); function beforeFilter () { if ($this->RequestHandler->accepts('html')) { // Execute code only if client accepts an HTML (text/html) response } elseif ($this->RequestHandler->accepts('rss')) { // Execute RSS-only code } elseif ($this->RequestHandler->accepts('atom')) { // Execute Atom-only code } elseif ($this->RequestHandler->accepts('xml')) { // Execute XML-only code } if ($this->RequestHandler->accepts(array('xml', 'rss', 'atom'))) { // Executes if the client accepts any of the above: XML, RSS or Atom } } }
getAjaxVersion()
If you are using the Prototype JS libraries, you can fetch a special header it sets on AJAX requests. This function returns the Prototype version used.
getClientIP ()
Returns the remote client's IP address.
getReferrer ()
Returns the server name from which the request originated.
isAjax()
Returns true if the current request was an XMLHttpRequest.
isAtom()
Returns true if the client accepts Atom feed content (application/atom+xml).
isDelete()
Returns true if the current request was via DELETE.
isGet()
Returns true if the current request was via GET.
isMobile()
Returns true if the user agent string matches a mobile web browser.
isPost()
Returns true if the current request was via POST.
isPut()
Returns true if the current request was via PUT.
isRss()
Returns true if the clients accepts RSS feed content (application/rss+xml).
isXml()
Returns true if the client accepts XML content (application/xml or text/xml).
setContent($name, $type)
Adds a content-type alias mapping, for use with accepts() and prefers(), where $name is the name of the mapping (string), and $type is either a string or an array of strings, each of which is a MIME type. The built-in type mappings are as follows:
// Name => Type 'js' => 'text/javascript', 'css' => 'text/css', 'html' => 'text/html', 'form' => 'application/x-www-form-urlencoded', 'file' => 'multipart/form-data', 'xhtml' => array('application/xhtml+xml', 'application/xhtml', 'text/xhtml'), 'xml' => array('application/xml', 'text/xml'), 'rss' => 'application/rss+xml', 'atom' => 'application/atom+xml'
Occasionally you will want to remove data from a request or output. Use the following Request Handler functions to perform these sorts of operations.
stripAll($str)
Strips the white space, images, and scripts from $str (using stripWhitespace(), stripImages(), and stripScripts()).
stripImages($str)
Strips any HTML embedded images from $str.
stripScripts($str)
Strips any <script> and <style> related tags from $str.
stripTags($str, $tag1, $tag2, $tag3...)
Removes the tags specified by $tag1, $tag2, etc. from $str.
$someString = '<font color="#FF0000"><bold>Foo</bold></font> <em>Bar</em>'; echo $this->RequestHandler->stripTags($someString, 'font', 'bold'); // output: Foo <em>Bar</em>
stripWhitespace($str)
Strips whitespace from $str.
The Request Handler component is especially useful when your application includes AJAX requests. The setAjax() function is used to automatically detect AJAX requests, and set the controller's layout to an AJAX layout for that request. The benefit here is that you can make small modular views that can also double as AJAX views.
// list.thtml <ul> <? foreach ($things as $thing):?> <li><?php echo $thing;?></li> <?endforeach;?> </ul> //------------------------------------------------------------- //The list action of my ThingsController: function list() { $this->RequestHandler->setAjax($this); $this->set('things', $this->Thing->findAll()); }
When a normal browser request is made to /things/list, the unordered list is rendered inside of the default layout for the application. If the URL is requested as part of an AJAX operation, the list is automatically rendered in the bare AJAX layout.
目录
The Security component is used to secure your controller actions against malicious or errant requests. It allows you to set up the conditions under which an action can be requested, and optionally specify how to deal with requests that don't meet those requirements. Again, before using the Security component, you must make sure that 'Security' is listed in your controllers' $components array.
The Security component contains two primary methods for restricting access to controller actions:
requirePost($action1, $action2, ...$actionN);
In order for the specified actions to execute, they must be requested via POST.
requireAuth($action1, $action2, ...$actionN);
Ensures that a request is coming from within the application by checking an authentication key in the POST'ed form data against an authentication key stored in the user's session. If they match, the action is allowed to execute. Be aware, however, that for reasons of flexibility, this check is only run if form data has actually been posted. If the action is called with a regular GET request, requireAuth() will do nothing. For maximum security, you should use requirePost() and requireAuth() on actions that you want fully protected. Learn more about how the authentication key is generated, and how it ends up where it should in Section 4 below.
But first, let's take a look at a simple example:
class ThingsController extends AppController { var $components = array('Security'); function beforeFilter() { $this->Security->requirePost('delete'); } function delete($id) { // This will only happen if the action is called via an HTTP POST request $this->Thing->del($id); } }
Here, we're telling the Security component that the 'delete' action requires a POST request. The beforeFilter() method is usually where you'll want to tell Security (and most other components) what to do with themselves. It will then go do what it's told right after beforeFilter() is called, but right before the action itself is called.
And that's about all there is to it. You can test this by typing the URL for the action into your browser and seeing what happens.
So if a request doesn't meet the security requirements that we define, what happens to it? By default, the request is black-holed, which means that the client is sent a 404 header, and the application immediately exits. However, the Security component has a $blackHoleCallback property, which you can set to the name of a custom callback function defined in your controller.
Rather than simply give a 404 header and then nothing, this property allows you to perform some additional checking on the request, redirect the request to another location, or even log the IP address of the offending client. However, if you choose to use a custom callback, it is your responsibility to exit the application if the request is invalid. If your callback returns true, then the Security component will continue to check the request against other defined requirements. Otherwise, it stops checking, and your application continues uninhibited.
The requireAuth() method allows you to be very detailed in specifying how and from where an action can be accessed, but it comes with certain usage stipulations, which become clear when you understand how this method of authentication works. As stated above, requireAuth() works by comparing an authentication key in the POST data to the key stored in the user's session data. Therefore, the Security component must be included in both the controller recieveing the request, as well as the controller making the request.
For example, if I have an action in PostsController with a view containing a form that POSTs to an action in CommentsController, then the Security component must be included in both CommentsController (which is receiving the request, and actually protecting the action), as well as PostsController (from which the request will be made).
Every time the Security component is loaded, even if it is not being used to
protect an action, it does the following things: First, it generates an
authentication key using the core Security class. Then, it writes this key to
the session, along with an expiration date and some additional information (the
expiration date is determined by your session security setting in /app/config/core.php
). Next, it sets the key in your
controller, to be referenced later.
Then in your view files, any form tag you generate using $html->formTag() will also contain a hidden input field with the authentication key. That way, when the form is POSTed, the Security component can compare that value to the value in the session on the receiving end of the request. After that, the authentication key is regenerated, and the session is updated for the next request.
目录
Welcome to Cake! You're probably checking out this tutorial because you want to learn more about how Cake works. Its our aim to increase productivity and make coding more enjoyable: we hope you'll see this as you dive into the code.
This tutorial will walk you through the creation of a simple blog application. We'll be getting and installing Cake, creating and configuring a database, and creating enough application logic to list, add, edit, and delete blog posts.
Here's what you'll need:
A running web server. We're going to assume you're using Apache, though the instructions for using other servers should be very similar. We might have to play a little with the server configuration, but most folks can get Cake up and running without any configuration at all.
A database server. We're going to be using mySQL in this tutorial. You'll need to know enough about SQL in order to create a database: Cake will be taking the reigns from there.
Basic PHP knowledge. The more object-oriented programming you've done, the better: but fear not if you're a procedural fan.
Finally, you'll need a basic knowledge of the MVC programming pattern. A quick overview can be found in Chapter 2, Section 1: The MVC Pattern. Don't worry: its only a half a page or so.
Let's get started!
First, let's get a copy of fresh Cake code. The most recent release as of this writing is RC6 (0.10.8.2195, to be exact.
To get a fresh download, visit the CakePHP project at Cakeforge: http://cakeforge.org/projects/cakephp/ and download a fresh release or release candidate.
You can also checkout/export a fresh copy of our trunk code at: https://svn.cakephp.org/repo/trunk/cake/.
Regardless of how you downloaded it, place the code inside of your DocumentRoot. Once finished, your directory setup should look something like the following:
/path_to_document_root /.htaccess /app /cake /index.php /vendors /VERSION.txt
Now might be a good time to learn a bit about how Cake's directory structure works: check out Chapter 2, Section 2: Overview of the Cake File Layout.
Next, lets set up the underlying database for our blog. Right now, we'll just create a single table to store our posts. We'll also throw in a few posts right now to use for testing purposes. Execute the following SQL statements into your database:
/* First, create our posts table: */ CREATE TABLE posts ( id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, title VARCHAR(50), body TEXT, created DATETIME DEFAULT NULL, modified DATETIME DEFAULT NULL ); /* Then insert some posts for testing: */ INSERT INTO posts (title,body,created) VALUES ('The title', 'This is the post body.', NOW()); INSERT INTO posts (title,body,created) VALUES ('A title once again', 'And the post body follows.', NOW()); INSERT INTO posts (title,body,created) VALUES ('Title strikes back', 'This is really exciting! Not.', NOW());
The choices on table and column names are not arbitrary. If you follow Cake's database naming conventions (outlined in Chapter 1, Section 4), and Cake's class naming conventions (outlined in Appendix B), you'll be able to take advantage of a lot of free functionality and avoid configuration. Cake is flexible enough to accomodate even the worst legacy database schema, but adhering to convention will save you time.
Check out Chapter 1, Section 4 and Appendix B for more information, but suffice it to say that naming our table 'posts' automatically hooks it to our Post model, and having fields called 'modified' and 'created' will be automagically managed by Cake.
Onward and upward: let's tell Cake where our database is and how to connect to it. This will be the first and last time you configure anything.
A copy of Cake's database configuration file is found in /app/config/database.php.default
. Make a copy of this file
in the same directory, but name it database.php
.
The config file should be pretty straightforward: just replace the values in the $default array with those that apply to your setup. A sample completed configuration array might look something like the following:
var $default = array('driver' => 'mysql', 'connect' => 'mysql_pconnect', 'host' => 'localhost', 'login' => 'cakeBlog', 'password' => 'c4k3-rUl3Z', 'database' => 'cake_blog_tutorial' );
Once you've saved your new database.php file, you should be able to open your browser and see the Cake welcome page. It should also tell you that your database connection file was found, and that Cake can successfully connect to the database.
Occasionally a new user will run in to mod_rewrite issues, so I'll mention them marginally here. If the Cake welcome page looks a little funny (no images or css styles), it probably means mod_rewrite isn't functioning on your system. Here are some tips to help get you up and running:
Make sure that an .htaccess override is allowed: in your httpd.conf, you
should have a section that defines a section for each Directory on your
server. Make sure the AllowOverride
is set to All
for the correct Directory.
Make sure you are editing the system httpd.conf rather than a user- or site-specific httpd.conf.
For some reason or another, you might have obtained a copy of CakePHP without the needed .htaccess files. This sometimes happens because some operating systems treat files that start with '.' as hidden, and don't copy them. Make sure your copy of CakePHP is from the downloads section of the site or our SVN repository.
Make sure you are loading up mod_rewrite correctly! You should see
something like LoadModule rewrite_module
libexec/httpd/mod_rewrite.so
and AddModule
mod_rewrite.c
in your httpd.conf.
If you don't want or can't get mod_rewrite (or some other compatible module)
up and running on your server, you'll need to use Cake's built in pretty URLs.
In /app/config/core.php
, uncomment the line that
looks like:
define ('BASE_URL', env('SCRIPT_NAME'));
This will make your URLs look like www.example.com/index.php/controllername/actionname/param rather than www.example.com/controllername/actionname/param.
The model class is the bread and butter of CakePHP applications. By creating a Cake model that will interact with our database, we'll have the foundation in place needed to do our view, add, edit, and delete operations later.
Cake's model class files go in /app/models
, and
the file we will be creating will be saved to /app/models/post.php
. The completed file should look like
this:
Because of the way the class and file are named, this tells Cake that you want a Post model available in your PostsController that is tied to a table in your default database called 'posts'.
The $name variable is always a good idea to add, and is used to overcome some class name oddness in PHP4.
For more on models, such as table prefixes, callbacks, and validation, check out Chapter 6.
Next we'll create a controller for our posts. The controller is where all the
logic for post interaction will happen, and its also where all the actions for
this model will be found. You should place this new controller in a file called
posts_controller.php
inside your /app/controllers
directory. Here's what the basic
controller should look like:
Now, lets add an action to our controller. When users request www.example.com/posts, this is the same as requesting www.example.com/posts/index. Since we want our readers to view a list of posts when they access that URL, the index action would look something like this:
Now that we have our database connected using our model, and our application logic and flow defined by our controller, let's create a view for the index action we defined above.
Cake views are just HTML and PHP flavored fragments that fit inside an application's layout. Layouts can be defined and switched between, but for now, let's just use the default.
Remember in the last section how we assigned the 'posts' variable to the view using the set() method? That would hand down data to the view that would look something like this:
// print_r($posts) output: Array ( [0] => Array ( [Post] => Array ( [id] => 1 [title] => The title [body] => This is the post body. [created] => 2006-03-08 14:42:22 [modified] => ) ) [1] => Array ( [Post] => Array ( [id] => 2 [title] => A title once again [body] => And the post body follows. [created] => 2006-03-08 14:42:23 [modified] => ) ) [2] => Array ( [Post] => Array ( [id] => 3 [title] => Title strikes back [body] => This is really exciting! Not. [created] => 2006-03-08 14:42:24 [modified] => ) ) )
Cake's view files are stored in /app/views
inside
a folder named after the controller they correspond to (we'll have to create a
folder named 'posts' in this case). To format this post data in a nice table,
our view code might look something like this:
Hopefully this should look somewhat simple.
You might have noticed the use of an object called $html
. This is an instance of the HtmlHelper
class. Cake comes with a set of view 'helpers'
that make things like linking, form output, JavaScript and Ajax a snap. You can
learn more about how to use them in Chapter 9, but what's important to note here
is that the link()
method will generate an HTML link
with the given title (the first parameter) and URL (the second parameter).
When specifying URL's in Cake, you simply give a path relative to the base of
the application, and Cake fills in the rest. As such, your URL's will typically
take the form of /controller/action/id
.
Now you should be able to point your browser to http://www.example.com/posts/index. You should see your view, correctly formatted with the title and table listing of the posts.
If you happened to have clicked on one of the links we created in this view
(that link a post's title to a URL /posts/view/some_id
), you were probably informed by Cake
that the action hasn't yet been defined. If you were not so informed, either
something has gone wrong, or you actually did define it already, in which case
you are very sneaky. Otherwise, we'll create it now:
The set() call should look familiar. Notice we're using read() rather than findAll() because we only really want a single post's information.
Notice that our view action takes a parameter. This parameter is handed to the action by the URL called. If a user requests /posts/view/3, then the value '3' is passed as $id.
Now let's create the view for our new 'view' action and place it in /app/views/posts/view.thtml.
Verify that this is working by trying the links at /posts/index or manually requesting a post by accessing /posts/view/1.
reading from the database and showing us the posts is fine and dandy, but let's allow for the adding of new posts.
First, start with the add() action in the PostsController:
Cake goes a long way in taking the monotany out of form input validation. Everyone hates coding up endless forms and their validation routines, and Cake makes it easier and faster.
To take advantage of the validation features, you'll need to use Cake's HtmlHelper in your views. The HtmlHelper is available by default to all views at $html.
Here's our add view:
As with $html->link()
, $html->formTag()
will generate a proper URL from the
controller and action we have given it. By default, it prints out a POST form
tag, but this can be modified by the second parameter. The $html->input()
and $html->textarea()
functions spit out form elements of
the same name. The first parameter tells Cake which model/field they correspond
to, and the second param is for extra HTML attributes (like the size of the
input field). Again, refer to Chapter 9 for more on helpers.
The tagErrorMsg()
function calls will output the
error messages in case there is a validation problem.
If you'd like, you can update your /app/views/posts/index.thtml
view to include a new "Add
Post" link that points to www.example.com/posts/add.
That seems cool enough, but how do I tell Cake about my validation requirements? This is where we come back to the model.
The $validate
array tells Cake how to validate
your data when the save()
method is called. The
values for those keys are just constants set by Cake that translate to regex
matches (see /cake/libs/validators.php
). Right now
Cake's validation is regex based, but look for a more robust solution in the
future.
Now that you have your validation in place, use the app to try to add a post without a title or body to see how it works.
Next, let's make a way for users to delete posts. Start with a delete() action in the PostsController:
This logic deletes the post specified by $id, and uses flash() to show the user a confirmation message before redirecting them on to /posts.
Because we're just executing some logic and redirecting, this action has no view. You might want to update your index view to allow users to delete posts, however.
This view code also uses the HtmlHelper to prompt the user with a JavaScript confirmation dialog before they attempt to delete a post.
So... post editing: here we go. You're a Cake pro by now, so you should have picked up a pattern. Make the action, then the view. Here's what the edit action of the Posts Controller would look like:
This checks for submitted form data. If nothing was submitted, go find the Post and hand it to the view. If some data has been submitted, try to save the Post model (or kick back and show the user the validation errors).
The edit view might look something like this:
This view ouputs the edit form (with the values populated), and the necessary error messages (if present). One thing to note here: Cake will assume that you are edititing a model if the 'id' field is present and exists in a currently stored model. If no 'id' is present (look back at our add view), Cake will assume that you are inserting a new model when save() is called.
You can now update your index view with links to edit specific posts:
This part is optional, but helpful in understanding how URLs map to specific function calls in Cake. We're only going to make a quick change to routes in this tutorial. For more information, see Chapter 4, Section 3: Routes Configuration.
Cake's default route will take a person visiting the root of your site (i.e. http://www.example.com) to the PagesController, and render a view called home. Rather than do that, we'll want users of our blog application to go to our soon-to-be-created PostsController.
Cake's routing is found in /app/config/routes.php
. You'll want to comment out or
remove the line that looks like this:
$Route->connect ('/', array('controller'=>'pages', 'action'=>'display', 'home'));
This line connects the URL / with the default Cake home page. We want it to connect with our own controller, so add a line that looks like this:
$Route->connect ('/', array('controller'=>'posts', 'action'=>'index'));
This should connect users requesting '/' to the index() action of our soon-to-be-created PostsController.
Creating applications this way will win you peace, honor, women, and money beyond even your wildest fantasies. Simple, isn't it? Keep in mind that this tutorial was very basic. Cake has many more features to offer, and is flexible in ways we didn't wish to cover here. Use the rest of this manual as a guide for building more feature-rich applications.
Now that you've created a basic Cake application you're ready for the real thing. Start your own project, read the rest of the Manual and API.
If you need help, come see us in #cakephp. Welcome to Cake!
Model class names are singular.
Model class names are Capitalized for single-word models, and UpperCamelCased for multi-word models.
Examples: Person, Monkey, GlassDoor, LineItem, ReallyNiftyThing
Model filenames use a lower-case underscored syntax.
Examples: person.php, monkey.php, glass_door.php, line_item.php, really_nifty_thing.php
Database tables related to models also use a lower-case underscored syntax - but they are plural.
Examples: people, monkeys, glass_doors, line_items, really_nifty_things
CakePHP naming conventions are meant to streamline code creation and make code more readable. If you find it getting in your way, you can override it.
Model name: Set var $name
in your model
definition.
Model-related database tables: Set var $useTable
in
your model definition.
Controller class names are plural.
Controller class names are Capitalized for single-word controllers, and UpperCamelCased for multi-word controllers. Controller class names also end with 'Controller'.
Examples: PeopleController, MonkeysController, GlassDoorsController, LineItemsController, ReallyNiftyThingsController
Controller file names use a lower-case underscored syntax. Controller file names also end with '_controller'
Examples: people_controller.php, monkeys_controller.php, glass_doors_controller.php, line_items_controller.php, really_nifty_things_controller.php
Views are named after actions they display.
Name the view file after action name, in lowercase.
Examples: PeopleController::worldPeace() expects a view in /app/views/people/worldpeace.thtml
;
MonkeysController::banana() expects a view in /app/views/monkeys/banana.thtml
.
You can force an action to render a specific view by calling $this->render('name_of_view_file_without_dot_php'); at the end of your action.