CodeIgnite2-秘籍-全-
CodeIgnite2 秘籍(全)
原文:
zh.annas-archive.org/md5/20614a184b90575a1967d1d8b00719c4译者:飞龙
前言
CodeIgniter 2 烹饪秘籍提供了许多易于使用、易于集成和易于适应的配方,包括使用 HTTPS、图像处理、cookie 接受、表单验证等。它是解决凌晨 2 点问题的绝佳资源。
本书涵盖的内容
第一章, CodeIgniter 基础,带您了解 CodeIgniter 的下载和安装、基本配置等。
第二章, 用户管理,专注于构建一个基本的 CRUD 界面来管理用户。
第三章, 创建电子商务功能,探讨了使用 CodeIgniter 购物车类创建简单店面,允许客户将商品添加到购物车并结账。
第四章, 电子邮件、HTML 表格和文本库,专注于使用 CodeIgniter 电子邮件库发送电子邮件、创建交互式表格,以及使用一些实用的 HTML 函数。
第五章, 管理数据输入和输出,处理表单验证、将文件写入磁盘、确认用户接受 cookie,等等。
第六章, 与数据库协同工作,涵盖了使用基本 Active Record 函数、从数据库查询绑定中导出数据以及您可能需要与数据库协同工作的几乎所有内容。
第七章, 创建安全用户环境,涵盖了转义用户输入、在 CodeIgniter 中切换到和从 HTTPS,等等。
第八章, 日历、正确的时间和地点,处理创建一个交互式日历,您可以使用模糊日期添加预约,并计算一个人的出生日期(用于年龄验证)。
第九章, 扩展核心,专注于使用语言类和动态切换语言,创建钩子,使用 FTP 上传文件,以及使用 MY_Controller 扩展控制器。
第十章, 与图像协同工作,处理使用 CodeIgniter 图像处理库裁剪、旋转图像并添加水印,以及向表单添加 CAPTCHA 验证。
第十一章, SEO、缓存和日志,处理从数据库缓存数据、使用 CodeIgniter 路由方法更改和修改浏览器地址栏中显示的 URL,以及在整个应用程序中记录错误和其他活动。
您需要这本书的内容
您需要以下软件:
-
一个 *AMP 环境(LAMP、MAMP、WAMP 等)
-
一份 CodeIgniter 框架的副本
这本书面向的对象
CodeIgniter 是一个易于学习的 PHP 框架;因此,熟悉 PHP 和 CodeIgniter 对您来说是有利的。但是,即使没有 CodeIgniter 的经验,也不应该成为阅读这本书的障碍。如果你不确定,最好的做法就是买下来,直接开始阅读。话虽如此,如果你熟悉 CodeIgniter,这本书可以提供即时的代码片段和食谱,你可以用于各种日常的 CodeIgniter 相关任务。
规范
在这本书中,你会发现许多不同风格的文本,以区分不同类型的信息。以下是一些这些风格的示例,以及它们含义的解释。
文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名如下所示:“public function index() 函数将我们重定向到 public function send_mail() 函数。”
代码块设置如下:
$this->email->from('from@domain.com', 'Your Name');
$this->email->to('to@domain.com');
$this->email->subject('This is a text email');
$this->email->message('And this is some content for the text email.');
当我们希望将你的注意力引到代码块的一个特定部分时,相关的行或项目将以粗体显示:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->library('email');
}
任何命令行输入或输出都如下所示:
C:\path\to\CodeIgniter\file.htaccess" .htaccess
新术语和重要词汇以粗体显示。你在屏幕上看到的单词,例如在菜单或对话框中,在文本中如下所示:“用户点击 查看购物车 来查看他们希望订购的产品”。
注意
警告或重要注意事项以如下框的形式出现。
小贴士
技巧和窍门以如下形式出现。
读者反馈
我们欢迎读者的反馈。告诉我们你对这本书的看法——你喜欢什么或可能不喜欢什么。读者反馈对我们来说非常重要,以便我们开发出真正能让你受益的标题。
要向我们发送一般反馈,只需发送一封电子邮件到 <feedback@packtpub.com>,并在邮件的主题中提及书名。
如果你在一个你擅长的主题上感兴趣,无论是写作还是为书籍做出贡献,请参阅我们的作者指南 www.packtpub.com/authors。
客户支持
现在你已经是 Packt 书籍的骄傲拥有者,我们有一些可以帮助你从购买中获得最大收益的东西。
下载示例代码
你可以从你购买的所有 Packt 书籍的账户中下载示例代码文件。www.packtpub.com。如果你在其他地方购买了这本书,你可以访问 www.packtpub.com/support 并注册,以便直接将文件通过电子邮件发送给你。
错误清单
尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在我们的书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以避免其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问 www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情来报告。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站,或添加到该标题的勘误部分下的现有勘误列表中。您可以通过选择您的标题从 www.packtpub.com/support 查看任何现有勘误。
盗版
在互联网上对版权材料的盗版是所有媒体中持续存在的问题。在 Packt,我们非常重视保护我们的版权和许可证。如果您在互联网上发现我们作品的任何非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
请通过 <copyright@packtpub.com> 联系我们,并提供涉嫌盗版材料的链接。
我们感谢您在保护我们作者和提供有价值内容方面的帮助。
问题
如果你在本书的任何方面遇到问题,可以通过 <questions@packtpub.com> 联系我们,我们将尽力解决。
第一章. CodeIgniter 基础知识
在本章中,我们将涵盖:
-
下载和安装 CodeIgniter
-
基本配置选项
-
在不同环境中管理 CodeIgniter
-
在不同环境中管理数据库设置
-
保护系统文件
-
使用 .htaccess 移除地址栏中的 index.php
-
安装和使用 Sparks
简介
CodeIgniter 是一个易于使用、易于设置的基于 PHP 的框架,您可以使用它构建您能想到的几乎所有基于 Web 的应用程序。在我们开始使用 CodeIgniter 之前,需要做一些配置;然而,本章将指导您下载、安装和理解 CodeIgniter 的基本配置,以帮助您快速启动并运行。
下载和安装 CodeIgniter
首先,您需要一份 CodeIgniter 的副本才能开始使用。有几个选择:您可以下载夜间构建版本、旧版本或当前稳定版本。然而,建议您选择最新的稳定版本。
如何操作...
您可以通过以下链接获取 CodeIgniter 的最新稳定版本:
CodeIgniter 将以压缩存档文件的形式提供。一旦下载了 CodeIgniter,将其包复制到您的 Web 文件夹中,并像通常在系统上解压缩存档一样解压缩它。完成此操作后,您需要设置一些配置选项,我们将在下一节中讨论。
基本配置选项
配置 CodeIgniter 比许多其他可用的 Web 框架要容易得多,并且不需要您求助于使用命令行。您只需要快速启动并运行,就需要访问application/config/文件夹中的几个文件。以下是一些建议的设置,这些设置将使您的 CodeIgniter 安装准备就绪而无需太多麻烦。
如何操作...
在您的本地主机或开发环境中的/path/to/codeigniter/application/config/config.php文件中打开文件,并找到以下行:
$config["base_url"]:
该值应该是到 CodeIgniter 安装的完整网络地址(在浏览器地址栏中写入的地址)。因此,如果您在本地主机上工作,该值应该是:http://localhost/path/to/codeigniter/。
小贴士
请记住以http://开始,并且始终放置尾随的斜杠/。
如果您已修改主机文件以使用域名而不是 localhost,那么请确保将 localhost 替换为该域名:
$config["encryption_key"]
如果您想在您的应用程序中使用Session或Encryption类,则必须设置加密密钥。加密密钥是 CodeIgniter 用于加密各种通信的字符字符串:
$config["global_xss_filtering"]
上一行代码指定是否应将跨站脚本过滤应用于Get、Post或Cookie变量。出于安全考虑,这应设置为TRUE,尤其是在生产环境中:
$config["csrf_protection"]
上一行代码指定是否设置 cookie 令牌,如果设置为 TRUE,则每次从客户端提交表单时都会进行检查。在实时环境中,它应该设置为 TRUE:
$config["log_threshold"]
上一行代码指定是否要将信息写入日志,如果是,则写入日志的信息类型。例如:
-
0: 不将错误写入日志,因为日志已禁用 -
1: 仅错误消息(这将包括 PHP 错误) -
2: 仅调试消息 -
3: 仅信息性消息 -
4: 所有类型的消息
以下代码行是您希望保存日志文件的文件夹路径:
$config["log_path"] = "/path/to/log/file"
它是如何工作的...
CodeIgniter 现在将根据提供的设置做出响应并运行。
在不同环境中管理 CodeIgniter
在某些情况下,可能需要调整您的配置文件,以便它们可以在多个服务器或环境中运行,而无需每次移动时都编辑或维护。例如,您在本地主机上可能有的配置设置与在实时或生产服务器上的设置很可能不同。正确设置配置文件将为您节省大量时间,而不是手动在两者之间切换。
如何操作...
打开 /path/to/codeigniter/application/config/config.php 文件,并将 $config["base_url"] 行替换为以下内容:
switch($_SERVER["SERVER_NAME"]) {
case "localhost":
$config["base_url"] = "http://localhost/path/to/codeigniter/";
break;
case "mydomain.com":
$config["base_url"] = "http://www.mydomain.com/";
break;
}
它是如何工作的...
这是一个简单的 case/switch 语句,带有 SERVER_NAME 检查。base_url 值根据 CodeIgniter 应用程序或项目运行的服务器设置。
在不同环境中管理数据库设置
如果您计划为您的 CodeIgniter 应用程序使用数据库,那么您需要维护正确的连接设置。CodeIgniter 将这些设置保存在 database.php 配置文件中。
如何操作...
-
打开
/path/to/codeigniter/application/config/database.php文件。可能需要更改的唯一值是数据库服务器的标准主机名、用户名、密码和数据库名称。 -
找到定义
$active_group的行,它指定了特定托管环境要使用的特定数据库设置。您可以通过类似于之前使用的case/switch测试来切换设置,例如,以下代码测试特定服务器并加载适当的设置:switch($_SERVER["SERVER_NAME"]) { case "localhost": $active_group = "testing"; break; case "mydomain.com": $active_group = "default" break; } $db["default"]["hostname"] = "localhost"; $db["default"]["username"] = "root"; $db["default"]["password"] = ""; $db["default"]["database"] = "database_name"; $db["default"]["dbdriver"] = "mysql"; $db["default"]["dbprefix"] = ""; $db["default"]["pconnect"] = TRUE; $db["default"]["db_debug"] = FALSE; $db["default"]["cache_on"] = FALSE; $db["default"]["cachedir"] = ""; $db["default"]["char_set"] = "utf8"; $db["default"]["dbcollat"] = "utf8_general_ci"; $db["default"]["swap_pre"] = ""; $db["default"]["autoinit"] = TRUE; $db["default"]["stricton"] = FALSE; $db["testing"]["hostname"] = "localhost"; $db["testing"]["username"] = "root"; $db["testing"]["password"] = ""; $db["testing"]["database"] = "database_name"; $db["testing"]["dbdriver"] = "mysql"; $db["testing"]["dbprefix"] = ""; $db["testing"]["pconnect"] = TRUE; $db["testing"]["db_debug"] = TRUE; $db["testing"]["cache_on"] = FALSE; $db["testing"]["cachedir"] = ""; $db["testing"]["char_set"] = "utf8"; $db["testing"]["dbcollat"] = "utf8_general_ci"; $db["testing"]["swap_pre"] = ""; $db["testing"]["autoinit"] = TRUE; $db["testing"]["stricton"] = FALSE; $active_record – Specifies if you require active record support. By default it is set to TRUE.
它是如何工作的...
我们所做的一切只是定义网站运行的环境。在上面的示例中,我们指定了两个环境:default 或 testing,并为它们应用了特定的设置。因此,让我们看看一些变量定义。
常见值
以下表格显示了标准的数据库访问选项:
| 选项名称 | 有效选项 | 描述 |
|---|---|---|
| $db["default"]["hostname"] | 通常为 localhost | 这是数据库所在的服务器 |
| $db["default"]["username"] | 数据库访问用户名 | |
| $db["default"]["password"] | 数据库的密码 | |
| $db["default"]["database"] | 数据库的名称 |
其他值
以下表格显示了通常保持默认设置不变但在此处提供以便您更改的选项:
| 选项名称 | 有效选项 | 描述 |
|---|---|---|
| $db["default"]["dbdriver"] | mysql | 这是您使用的 DBMS 类型——本书中的配方使用 MySQL。值必须全部小写。 |
| $db["default"]["dbprefix"] | 默认:mysql,但也可能是 postgre、odbc 等 | 有时您可能希望向数据库表名添加前缀,例如,一个博客应用可能会将表名前缀为“blog”,这样帖子表就会变成 blog_posts。 |
| $db["default"]["pconnect"] | TRUE/FALSE | 指定您是否希望保持与数据库的持久连接。 |
| $db["default"]["db_debug"] | TRUE/FALSE | 指定您是否希望在屏幕上显示数据库错误。默认为空白,但出于安全考虑,在实际环境中应设置为FALSE,在开发时应设置为TRUE。 |
| $db["default"]["cache_on"] | TRUE/FALSE | 指定您是否希望启用数据库查询缓存。 |
| $db["default"]["cachedir"] | 指定数据库查询缓存的绝对文件路径。 | |
| $db["default"]["char_set"] | utf8 | 指定 CodeIgniter 将用于数据库的字符集。 |
| $db["default"]["dbcollat"] | utf8_general_ci | 指定 CodeIgniter 将用于数据库的字符排序。 |
| $db["default"]["port"] | 3306 | 默认 MySQL 端口。此选项默认不包含,如果您希望为数据库连接使用特定端口,您需要手动写入此行并设置值。 |
我们将在第六章与数据库一起工作中更详细地探讨从数据库访问数据。
保护系统文件
在实际环境中,强烈建议您将系统文件夹移出网站根目录,以防止恶意访问。
如何操作...
-
通过命令行或使用计算机的 GUI 将系统文件夹移动到公开可访问的网站文件夹之外。执行此操作的方法将取决于您使用的系统,但我相信您知道如何移动文件夹,所以我们在这里不讨论这一点。
-
在您移动系统文件夹后,您需要更新
path/to/codeigniter/index.php文件中的$system_path变量。查找并找到以下行:$system_path = "path/to/system/folder";修改该行以反映系统文件夹的新位置。例如,如果您将系统文件夹从网站根目录向上移动一个级别,您应该写下以下行:
$system_path = "../system";
它是如何工作的...
通过将系统文件夹移出 Web 根目录,你可以尽可能保护它免受通过互联网的访问。系统文件夹在 Web 根目录外被访问的可能性比在内部要小得多。
使用.htaccess从地址栏中删除 index.php
当 CodeIgniter 运行时,有可能从网络浏览器地址栏中删除index.php文件。
如何操作...
创建或打开一个.htaccess文件。如果还没有.htaccess文件,你可以按照以下步骤创建一个:
Linux/Mac
- 打开一个终端窗口并输入:
touch/path/to/CodeIgniter/.htaccess。
Windows
-
在你的 CodeIgniter 根目录中创建一个文本文件,命名为
file.htaccess。 -
按Windows + R打开运行对话框。
-
输入以下命令并点击确定:
ren "C:\path\to\CodeIgniter\file.htaccess" .htaccess -
一旦你的
.htaccess文件被打开,在文件顶部写下以下行:<IfModule mod_rewrite.c> RewriteEngine on RewriteCond $1 !^(index\.php|images|robots\.txt) RewriteRule ^(.*)$ index.php/$1 [L] </IfModule>
它是如何工作的...
.htaccess文件中的此规则将从浏览器的地址栏中删除index.php文件。CodeIgniter 的index.php文件仍然被调用,但它不会在浏览器的地址栏中显示给用户。
安装和使用 Sparks
很长一段时间以来,为了找到和使用 CodeIgniter 的扩展、库和其他有用的代码片段,你必须在网上搜索,并从博客、代码仓库等各种地方下载代码。有用的 CodeIgniter 安装散布在互联网上,因此可能很难找到。Sparks 作为 CodeIgniter 扩展的单一点参考。它简单易安装和使用,包含数千个有用的 CodeIgniter 附加组件。
如何操作...
如果你正在使用 MAC 或 Linux,那么命令行界面对你来说是开放的。
-
使用系统上的终端应用程序,导航到你的 CodeIgniter 应用程序的根目录,并输入以下行:
php -r "$(curl -fsSL http://getsparks.org/go-sparks)"如果你的安装成功,你应该会看到类似以下的内容:
user@server:/path/to/codeigniter$ php -r "$(curl -fsSL http://getsparks.org/go-sparks)" Pulling down spark manager from http://getsparks.org/static/install/spark-manager-0.0.9.zip ... Pulling down Loader class core extension from http://getsparks.org/static/install/MY_Loader.php.txt ... Extracting zip package ... Cleaning up ... Spark Manager has been installed successfully! Try: `php tools/spark help`如果你正在使用 Windows,那么你需要手动下载 Sparks 并解压。为此,执行以下说明或查看 GetSparks 网站上的最新版本说明:
-
在你的 CodeIgniter 目录的最高层(根目录)中创建一个名为
tools的文件夹。 -
访问以下 URL:
getsparks.org/install。 -
前往正常安装部分并下载 Sparks 包。
-
将下载解压到你在步骤 1 中创建的
tools文件夹中。 -
从
getsparks.org/static/install/MY_Loader.php.txt下载Loader类扩展。 -
将
MY_Loader.php.txt文件重命名为MY_Loader.php,并将其移动到你的 CodeIgniter 实例中的application/core/MY_Loader.php目录。 -
现在 Sparks 已经安装在你的 CodeIgniter 实例中,你可以开始安装扩展和包了。
要从 Sparks 安装一个包,请在命令行中输入以下内容:
php tools/spark install [Package Version] Spark Name在这里,
Package Version是你希望安装的 Spark 的具体版本。你不需要声明版本,如果不指定,Spark 将默认下载最新版本。Spark Name是你希望安装的 Spark 的名称,例如,要安装默认安装中包含的example-spark(版本 1.0.0),请在命令行中输入以下命令:php tools/spark install -v1.0.0 example-spark如果安装成功,你应该会看到类似以下内容:
user@server:/path/to/codeigniter$ php tools/spark install -v1.0.0 example-spark [ SPARK ] Retrieving spark detail from getsparks.org [ SPARK ] From Downtown! Retrieving spark from Mercurial repository at https://url/of/the/spark/repo [ SPARK ] Spark installed to ./sparks/example-spark/1.0.0 - You're on fire!
它是如何工作的...
你现在应该准备好开始使用你的 Spark 了。请确保阅读随 Spark 一起提供的Readme文件或文档,以了解其正确使用方法。
第二章:用户管理
在本章中,我们将涵盖以下内容:
-
查看用户
-
创建用户
-
编辑用户
-
删除用户
-
使用 CodeIgniter 生成密码
-
使用 CodeIgniter 生成密码 – 基础
-
忘记密码? – 使用 CodeIgniter 重置密码
简介
很有可能,你用 CodeIgniter 构建的许多网站和应用都需要用户,并且需要直接管理他们及其详细信息,也就是说创建、更新、编辑和删除他们。
在本章中,我们将探讨基本用户管理,并构建一个简单的 CRUD 界面来管理和维护数据库中的这些用户。稍后,在 第七章,创建安全用户环境 中,我们将探讨使用登录和会话功能来保护用户信息,但就目前而言,我们将专注于构建用户管理界面。
在开始之前,我们需要在 application/config 文件夹中的几个配置文件中更改一些设置。我们将编辑以下文件:
-
path/to/codeigniter/application/config/config.php -
path/to/codeigniter/application/config/database.php
在 path/to/codeigniter/application/config/config.php 文件中找到以下配置值,并修改它们以反映以下内容:
| 配置项 | 更改为 | 描述 |
|---|---|---|
$config['sess_cookie_name'] |
ci_session |
这应该是写入用户计算机的 cookie 的名称。 |
$config['sess_expiration'] |
7200 |
这是在会话在一段时间内没有活动后变为无效之前应保持活跃的秒数。 |
$config['sess_expire_on_close'] |
TRUE |
这指定了如果用户关闭浏览器,会话将变为无效。 |
$config['sess_encrypt_cookie'] |
TRUE |
这指定了如果 cookie 应在用户的计算机上加密;出于安全考虑,应将其设置为 TRUE。 |
$config['sess_use_database'] |
TRUE |
这指定了是否将会话存储在数据库中。出于安全考虑,应将其设置为 TRUE。你还需要创建会话表,该表可以在 数据库模式 部分找到。 |
$config['sess_table_name'] |
sessions |
这指定了用于存储会话数据的数据库表名称。 |
$config['sess_match_ip'] |
TRUE |
这指定了 CodeIgniter 应该监控请求的 IP 地址与 session_id 的对比。如果传入请求的 IP 地址与之前的值不匹配,则不允许会话。 |
$config['sess_match_useragent'] |
TRUE |
这指定了 CodeIgniter 应该监控请求的用户代理地址与 session_id 的对比。如果传入请求的用户代理地址与之前的值不匹配,则不允许会话。 |
在 path/to/codeigniter/application/config/database.php 文件中找到以下配置值,并修改它们以反映以下内容:
| 配置项 | 更改为值 | 描述 |
|---|---|---|
$db['default']['hostname'] |
localhost |
数据库的主机名;这通常是 localhost 或 IP 地址 |
$db['default']['username'] |
? | 您希望用于连接数据库的用户名 |
$db['default']['password'] |
? | 连接到数据库使用的密码 |
$db['default']['database'] |
? | 您希望连接到的数据库名称,例如,users |
数据库模式
使用您选择的方法(命令行、phpmyadmin 等)将以下代码输入到您的数据库中:
CREATE TABLE IF NOT EXISTS `sessions` (
`session_id` varchar(40) COLLATE utf8_bin NOT NULL DEFAULT '0',
`ip_address` varchar(16) COLLATE utf8_bin NOT NULL DEFAULT '0',
`user_agent` varchar(120) COLLATE utf8_bin DEFAULT NULL,
`last_activity` int(10) unsigned NOT NULL DEFAULT '0',
`user_data` text COLLATE utf8_bin NOT NULL,
PRIMARY KEY (`session_id`),
KEY `last_activity_idx` (`last_activity`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(125) NOT NULL,
`last_name` varchar(125) NOT NULL,
`email` varchar(255) NOT NULL,
`created_date` int(11) NOT NULL COMMENT 'unix timestamp',
`is_active` varchar(3) NOT NULL COMMENT 'yes or no',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1;
INSERT INTO `users` (`id`, `first_name`, `last_name`, `email`, `created_date`, `is_active`) VALUES
(5, 'First Name', 'Last name', 'first@last.com', 0, '0');
列是什么以及我们将存储什么类型的数据?以下表格是先前数据库模式的指南:
| 项目名称 | 属性 | 描述 |
|---|---|---|
user_id |
INTEGER(11) |
表的主键。 |
user_first_name |
VARCHAR(125) |
用户的第一个名字。 |
user_last_name |
VARCHAR(125) |
用户的姓氏。 |
user_email |
VARCHAR(255) |
用户的电子邮件地址,例如,<name@example.org>。 |
user_created_date |
INTEGER(11) |
用户在数据库中创建的日期的 Unix 时间戳。 |
user_is_active |
INTEGER(1) |
以 0 或 1 表示的布尔值,如果用户是活跃的。此变量指定用户是否在系统中活跃。活跃用户可以登录,而不活跃用户则不能。 |
小贴士
如果您已经创建了会话表,则可以省略该表。
查看用户
我们开始的好地方是显示我们用户的列表。我们将创建一个模型、视图和控制台,以提供执行此操作的功能。
如何操作...
我们将创建以下三个文件:
-
path/to/codeigniter/application/models/users_model.php: 此文件为我们提供数据库的 CRUD 支持 -
path/to/codeigniter/application/views/users/view_all_users.php: 此文件包含一个foreach循环,遍历结果数组,将所有用户写入表格 -
path/to/codeigniter/application/controllers/users.php: 此文件包含处理 CRUD 功能所需的代码
-
将以下代码复制到
path/to/codeigniter/application/controllers/users.php文件中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Users extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('security'); $this->load->model('Users_model'); $this->load->database(); } public function index() { redirect('users/view_users'); } public function view_users() { $data['query'] = $this->Users_model->get_all_users(); $this->load->view('users/view_all_users', $data); } } -
将以下代码复制到
path/to/codeigniter/application/models/users_model.php文件中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Users_model extends CI_Model { function __construct() { parent::__construct(); } public function get_all_users() { return $this->db->get('users'); } } -
将以下代码复制到
path/to/codeigniter/application/views/users/view_all_users.php文件中:<?php if ($query->num_rows() > 0 ) : ?> <table border="0"> <tr> <td>ID</td> <td>First Name</td> <td>Last Name</td> <td>Created Date</td> <td>Is Active</td> <td colspan="2">Actions</td> </tr> <?php foreach ($query->result() as $row) : ?> <tr> <td><?php echo $row->id; ?></td> <td><?php echo $row->first_name; ?></td> <td><?php echo $row->last_name; ?></td> <td><?php echo date("d-m-Y", $row->created_date); ?></td> <td><?php echo ($row->is_active ? 'Yes' : 'No'); ?></td> <td><?php echo anchor('users/edit_user/'.$row->id, 'Edit') ; ?></td> <td><?php echo anchor('users/delete_user/'.$row->id, 'Delete') ; ?></td> </tr> <?php endforeach ; ?> </table> <?php endif ; ?>
它是如何工作的...
这相当标准,没有复杂的事情发生。我们有一个控制器正在运行显示,它加载了一些有用的辅助函数来支持诸如redirect()、其他安全函数以及构造函数中的Users_model等功能。public function index()将重定向到public function view_users(),然后通过$this->Users_model->get_all_users()语法连接到Users_model模型中的get_all_users()函数,以返回一个活动记录结果集。然后,这个结果集被传递到users/view_all_users视图,在其中通过foreach循环在表格中显示。看...我告诉过你这是多么简单!
创建用户
你总是需要一种方法从应用程序内部创建用户,并且需要手动输入他们的数据,而不是让用户自己输入数据。我们将构建允许你逐个创建用户的功能。
如何做...
我们需要创建一个文件:
path/to/codeigniter/application/views/users/new_user.php
并且修改以下两个文件:
-
path/to/codeigniter/application/controllers/users.php -
path/to/codeigniter/application/models/users_model.php
-
将以下代码复制到
path/to/codeigniter/application/views/users/new_user.php文件中:<?php echo form_open('users/new_user') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <table border="0"> <tr> <td>User First Name</td> <td><?php echo form_input($first_name); ?></td> </tr> <tr> <td>User Last Name</td> <td><?php echo form_input($last_name); ?></td> </tr> <tr> <td>User Email</td> <td><?php echo form_input($email); ?></td> </tr> <tr> <td>User Is Active?</td> <td><?php echo form_checkbox($is_active); ?></td> </tr> </table> <?php echo form_submit('submit', 'Create'); ?> or <?php echo anchor('users/index', 'cancel'); ?> <?php echo form_close(); ?> -
修改
path/to/codeigniter/application/controllers/users.php文件,添加以下代码:public function new_user() { // Load support assets $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); // Set validation rules $this->form_validation->set_rules('first_name', 'First Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('last_name', 'Last Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('email', 'Email', 'required|min_length[1]|max_length[255]|valid_email'); $this->form_validation->set_rules('is_active', 'Is Active', 'min_length[1]|max_length[1]|integer|is_natural'); // Begin validation if ($this->form_validation->run() == FALSE) { // First load, or problem with form $data['first_name'] = array('name' => 'first_name', 'id' => 'first_name', 'value' => set_value('first_name', ''), 'maxlength' => '100', 'size' => '35'); $data['last_name'] = array('name' => 'last_name', 'id' => 'last_name', 'value' => set_value('last_name', ''), 'maxlength' => '100', 'size' => '35'); $data['email'] = array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '35'); $data['is_active'] = array('name' => 'is_active', 'id' => 'is_active', 'value' => set_value('is_active', '')); $this->load->view('users/new_user',$data); } else { // Validation passed, now escape the data $data = array( 'first_name' => $this->input->post('first_name'), 'last_name' => $this->input->post('last_name'), 'email' => $this->input->post('email'), 'is_active' => $this->input->post('is_active'), ); if ($this->Users_model->process_create_user($data)) { redirect('users'); } } } -
修改
path/to/codeigniter/application/models/users_model.php文件,添加以下代码:public function process_create_user($data) { if ($this->db->insert('users', $data)) { return true; } else { return false; } }
它是如何工作的...
在这里发生的事情比前面的view_users代码要稍微复杂一些,但仍然简单直接。public function new_user()执行多个功能,例如加载视图文件、验证提交后输入的任何数据,以及显示视图。
如果public function new_user()是第一次被调用(即,它不是由表单提交调用的),那么验证检查($this->form_validation->run())将等于FALSE,并且括号内的代码将被执行。在这种情况下,代码将加载cust/new_user视图。
然而,如果函数是作为表单提交的结果加载的,那么 CodeIgniter 将开始检查用户输入。函数的第一行加载必要的库以启用检查用户的输入:$this->library('form_validation'),并且我们的错误分隔符通过函数set_error_deimiters()设置。然后,表单中的每个项目都会与我们指定的标准进行核对。完整的验证标准选项列表可在:ellislab.com/codeigniter/user-guide/libraries/form_validation.html找到
我们还将在第五章管理输入和输出中更详细地讨论表单验证。
如果验证未通过(用户的输入未满足我们设定的要求),则 $this->form_validation->run() 将返回 FALSE,表单将再次显示。视图中的表单元素能够显示用户的输入(因此他们不需要从头开始重新输入一切)。
一旦验证通过($this->form_validation->run() 返回 TRUE),然后我们将输入打包成一个数组:$data。由于我们使用 Active Record 与数据库交互,$data 数组的键必须与我们的数据库表列名匹配。然后,$data 数组被发送到 Users_model 以使用以下语法写入数据库:$this->Users_model->get_all_users()。
编辑用户
您始终需要一些方法来从应用程序内部自行编辑用户。在本节中,我们将查看创建执行此操作的功能:更新和编辑用户详细信息。
如何做到这一点...
我们需要创建一个文件:
path/to/codeigniter/application/views/users/edit_user.php
修改以下两个文件:
-
path/to/codeigniter/application/controllers/users.php -
path/to/codeigniter/application/models/users_model.php
-
将以下代码复制到
path/to/codeigniter/application/views/users/edit_user.php文件:<?php echo form_open('users/edit_user') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <table border="0" > <tr> <td>User First Name</td> <td><?php echo form_input($first_name); ?></td> </tr> <tr> <td>User Last Name</td> <td><?php echo form_input($last_name); ?></td> </tr> <tr> <td>User Email</td> <td><?php echo form_input($email); ?></td> </tr> <tr> <td>User Is Active?</td> <td><?php echo form_checkbox($is_active); ?></td> </tr> <?php echo form_hidden($id); ?> </table> <?php echo form_submit('submit', 'Update'); ?> or <?php echo anchor('users/index', 'cancel'); ?> <?php echo form_close(); ?> -
修改
path/to/codeigniter/application/controllers/users.php文件,使用以下代码:public function edit_user() { // Load support assets $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); // Set validation rules $this->form_validation->set_rules('first_name', 'First Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('last_name', 'Last Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('email', 'Email', 'required|min_length[1]|max_length[255]|valid_email'); $this->form_validation->set_rules('is_active', 'Is Active', 'min_length[1]|max_length[1]|integer|is_natural'); if ($this->input->post()) { $id = $this->input->post('id'); } else { $id = $this->uri->segment(3); } // Begin validation if ($this->form_validation->run() == FALSE) { // First load, or problem with form $query = $this->Users_model->get_user_details($id); foreach ($query->result() as $row) { $first_name = $row->first_name; $last_name = $row->last_name; $email = $row->email; $is_active= $row->is_active; } $data['first_name'] = array('name' => 'first_name', 'id' => 'first_name', 'value' => set_value('first_name', $first_name), 'maxlength' => '100', 'size' => '35'); $data['last_name'] = array('name' => 'last_name', 'id' => 'last_name', 'value' => set_value('last_name', $last_name), 'maxlength' => '100', 'size' => '35'); $data['email'] = array('name' => 'email', 'id' => 'email', 'value' => set_value('email', $email), 'maxlength' => '100', 'size' => '35'); $data['is_active'] = array('name' => 'is_active', 'id' => 'is_active', 'value' => set_value('is_active', $is_active), 'maxlength' => '100', 'size' => '35'); $data['id'] = array('id' => set_value('id', $id)); $this->load->view('users/edit_user', $data); } else { // Validation passed, now escape the data $data = array( 'first_name' => $this->input->post('first_name'), 'last_name' => $this->input->post('last_name'), 'email' => $this->input->post('email'), 'is_active' => $this->input->post('is_active'), ); if ($this->Users_model->process_update_user($id, $data)) { redirect('users/view_users'); } } } -
修改
path/to/codeigniter/application/models/users_model.php文件,使用以下代码:public function process_update_user($id, $data) { $this->db->where('id', $id); if ($this->db->update('users', $data)) { return true; } else { return false; } } public function get_user_details($id) { $this->db->where('id', $id); return $this->db->get('users'); }
它是如何工作的...
这与创建新用户(前面提到过)的功能类似,但不同之处在于,我们不是向用户表写入一行,而是基于用户的唯一键删除一行。
首先,我们需要获取用户的 ID。此时,用户的 ID 可能来自 URL,但也可能来自 post 数组(例如,如果返回 FALSE)。以下代码确定 $id 变量是如何进入的(无论是 post 还是 URL),并将其存储在 $id 变量中,以便稍后处理:
if ($this->input->post()) {
$id = $this->input->post('id');
} else {
$id = $this->uri->segment(3);
}
我们随后验证编辑后的用户数据——如果数据通过验证,我们将将传入的表单数据打包成一个关联数组(称为 $data)。我们使用数组键作为映射到我们数据库列名的映射器——也就是说,数组中的键与数据库列匹配——请查看以下代码:
$data = array(
'first_name' => $this->input->post('first_name'),
'last_name' => $this->input->post('last_name'),
'email' => $this->input->post('email'),
'is_active' => $this->input->post('is_active'),
);
您可以看到关联数组的键与数据库表中的列名匹配;因此,数组中的 first_name 键将映射到表中的 first_name 列。数组中的 last_name 键将映射到表中的 last_name 列。
接下来,我们将用户编辑后的信息写入数据库。我们通过将刚刚创建的 $data 数组(以及 $id 变量)发送到我们的 Users_model 函数 process_update_user() 来完成此操作,该函数将执行更新任务。
if ($this->Users_model->process_update_user($id, $data)) {
redirect('users/view_users');
}
删除用户
总是有一个好主意能够从界面上删除用户,而不是直接从数据库中删除他们,或者根本不删除他们。我们将创建一个 CRUD 界面,以便我们可以从数据库中删除用户。下面是如何做到这一点。
如何做到这一点...
我们需要创建一个文件:
path/to/codeigniter/application/views/users/delete_user.php
-
将以下代码添加到
views/users/delete_user.php文件中:<?php echo form_open('users/delete_user'); ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <?php foreach ($query->result() as $row) : ?> <?php echo $row->first_name . ' ' . $row->last_name; ?> <?php echo form_submit('submit', 'Delete'); ?> or <?php echo anchor('users/index', 'cancel'); ?> <?php echo form_hidden('id', $row->id); ?> <?php endforeach; ?> </form> -
修改以下两个文件:
-
path/to/codeigniter/application/controllers/users.php -
path/to/codeigniter/application/models/users_model.php
-
-
修改
controllers/users.php文件,添加以下代码:public function delete_user() { // Load support assets $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); // Set validation rules $this->form_validation->set_rules('id', 'User ID', 'required|min_length[1]|max_length[11]|integer|is_natural'); if ($this->input->post()) { $id = $this->input->post('id'); } else { $id = $this->uri->segment(3); } if ($this->form_validation->run() == FALSE) { // First load, or problem with form $data['query'] = $this->Users_model->get_user_details($id); $this->load->view('users/delete_user', $data); } else { if ($this->Users_model->delete_user($id)) { redirect('users/view_users'); } } } -
修改
controllers/users_model.php文件,添加以下代码:public function delete_user($id) { $this->db->where('id', $id); if ($this->db->delete('users')) { return true; } else { return false; } }
它是如何工作的...
这与创建新用户(前面已解释)的功能类似,但不同之处在于,我们不是向用户表写入一行,而是根据用户的唯一键删除一行。
首先,我们需要获取用户的 ID。在这个时候,用户的 ID 可能来自 URL,但也可能来自 POST 数组。
以下代码确定了$id变量是如何进入的(无论是通过 POST 还是通过 URL),并将其存储在$id变量中,以便稍后处理:
if ($this->input->post()) {
$id = $this->input->post('id');
} else {
$id = $this->uri->segment(3);
}
如果public function delete_user()是第一次被调用(即,它不是由表单提交调用的),那么用户的唯一键将从 URL 传递给public function delete_user()。它通过$this->uri->segment(3)被拾取,并通过将$data['id']分配给$this->load->view('user/delete_user', $data['id'])发送到users/delete_user.php视图。在视图中,$id值被写入一个隐藏的 HTML 表单元素。
必须将用户的 ID 作为隐藏元素分配到表单中,因为当表单提交时,public function delete_user()将需要用户的 ID。如果表单是提交而不是首次加载,则 ID 将无法从$this->uri->segment(3)中获取。
public function delete_user()执行了与public function new_user()类似的多项功能。这些功能包括加载视图文件、验证提交后的任何数据输入,并显示视图。
如果public function delete_user()是作为表单提交的结果被调用的,CodeIgniter 将开始检查和验证用户输入;在这种情况下,提交的输入仅包括用户 ID,它作为隐藏表单元素在视图中写入。函数的第一行加载必要的库以启用检查用户输入:$this->library('form_validation'),并且我们的错误定界符通过函数set_error_deimiters()设置。然后,用户 ID 将与我们指定的标准进行核对。完整的验证标准选项列表可在以下位置找到:
ellislab.com/codeigniter/user-guide/libraries/form_validation.html。我们还将更详细地讨论表单验证,见第五章,管理输入和输出。
如果验证未通过(用户的输入没有满足我们设定的要求),那么$this->form_validation->run()将返回FALSE,表单将再次显示。
一旦验证通过($this->form_validation->run()返回TRUE),然后我们将输入打包到一个数组中:$data。由于我们使用 Active Record 与数据库交互,$data数组的键必须匹配我们数据库表的列名。
然后,使用以下语法将$data数组发送到Users_model以从数据库中删除用户:$this->Users_model->delete_user($id)。
使用 CodeIgniter 生成密码
有两种方法可以解释这一点。由于这是一本食谱书,我将给出用户注册的结构(这个过程的一部分是从用户提供的密码创建哈希),以及登录表单(这个过程的一部分是验证密码与哈希)。但我意识到您可能不需要所有以下文件,以下示例中专注于密码哈希的行。这样,您可以快速了解这个过程是如何工作的,并将其应用于您的情况。
准备工作
首先,让我们创建数据库模式以支持食谱。如果您已经有了自己的表并且只是寻找哈希代码,您可能可以跳过这部分。否则,将以下代码复制到您的数据库中:
CREATE TABLE IF NOT EXISTS `register` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_first_name` varchar(125) NOT NULL,
`user_last_name` varchar(125) NOT NULL,
`user_email` varchar(255) NOT NULL,
`user_hash` text NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
提示
下载示例代码
您可以从您在www.packtpub.com的账户下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问
注册表描述如下:
| 项目名称 | 属性 | 描述 |
|---|---|---|
user_id |
INTEGER(11) |
表的主键 |
user_first_name |
VARCHAR(125) |
用户的姓名 |
user_last_name |
VARCHAR(125) |
用户姓氏 |
user_email |
VARCHAR(255) |
用户的电子邮件地址,例如,<name@example.org> |
user_hash |
TEXT |
由$this->encrypt->sha1($string_to_hash [, $key])生成的密码哈希 |
您还必须创建一个会话表,并确保配置文件已设置以处理数据库存储的会话。有关如何操作的说明,请参阅
数据库设置完成!我们将使用 CodeIgniter 的 encrypt 库来为我们处理密码哈希的重负载,具体来说,$this->encrypt->sha1($string_to_hash [, $key]),其中 $key 是可选的。我们首先需要设置一些事情。您需要决定要使用的加密密钥:这可以是您在 config.php 中的 $config['encryption_key'] 中设置的加密密钥,或者您可以将新密钥作为第二个参数传递给 CodeIgniter。第二个参数的存在会覆盖 $config['encryption_key'] 中设置的值。
在下面的菜谱中,我们使用 $config['encryption_key'] 中的值作为我们的加密密钥;因此,我们不会传递第二个参数。
小贴士
在创建密钥时,尽量不要只使用单个单词,因为这可能会被彩虹表破解;相反,使用一个相当长的随机字母数字字符串。
如何操作...
在这个菜谱中,我们将创建以下七个文件:
-
/path/to/codeigniter/application/controllers/register.php: 此文件包含一个允许用户注册的表单,然后记录被添加到数据库表(在 准备阶段 部分的 SQL) -
/path/to/codeigniter/application/models/register_model.php: 此文件与控制器交互以与数据库进行交互 -
/path/to/codeigniter/application/views/register/register.php: 此文件用于注册表单 -
/path/to/codeigniter/application/controllers/signin.php: 此文件处理登录过程,包括将密码与哈希值进行比较 -
/path/to/codeigniter/application/models/signin_model.php: 此文件与控制器交互以与数据库进行交互 -
/path/to/codeigniter/application/views/signin/signin.php: 此文件用于登录表单 -
/path/to/codeigniter/application/views/signin/loggedin.php: 此文件显示一个表示成功登录的页面
-
将以下代码复制到
/path/to/codeigniter/application/controllers/register.php文件中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Register extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('security'); $this->load->model('Register_model'); $this->load->library('encrypt'); $this->load->database(); } public function index() { redirect('register/register_user'); } public function register_user() { // Load support assets $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); // Set validation rules $this->form_validation->set_rules('first_name', 'First Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('last_name', 'Last Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('email', 'Email', 'required|min_length[1]|max_length[255]|valid_email'); $this->form_validation->set_rules('password1', 'Password', 'required|min_length[5]|max_length[15]'); $this->form_validation->set_rules('password2', 'Confirmation Password', 'required|min_length[5]|max_length[15]|matches[password1]'); // Begin validation if ($this->form_validation->run() == FALSE) { // First load, or problem with form $data['page_title'] = "Register"; $this->load->view('register/register',$data); } else { // Create hash from user password $hash = $this->encrypt->sha1($this->input->post('password1')); $data = array( 'user_first_name' => $this->input->post('first_name'), 'user_last_name' => $this->input->post('last_name'), 'user_email' => $this->input->post('email'), 'user_hash' => $hash ); if ($this->Register_model->register_user($data)) { redirect('signin'); } else { redirect('register'); } } } } -
将以下代码复制到
/path/to/codeigniter/application/models/register_model.php文件中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Register_model extends CI_Model { function __construct() { parent::__construct(); } public function register_user($data) { if ($this->db->insert('register', $data)) { return true; } else { return false; } } public function update_user($data, $email) { $this->db->where('user_email', $email); $this->db->update('register', $data); } } -
将以下代码复制到
/path/to/codeigniter/application/views/register/register.php文件中:<?php echo form_open('register/register_user') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <table border="0" > <tr> <td>First Name</td> <td><?php echo form_input(array('name' => 'first_name', 'id' => 'first_name','value' => set_value('first_name', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Last Name</td> <td><?php echo form_input(array('name' => 'last_name', 'id' => 'last_name', 'value' => set_value('last_name', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>User Email</td> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Password</td> <td><?php echo form_password(array('name' => 'password1', 'id' => 'password1', 'value' => set_value('password1', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Confirm Password</td> <td><?php echo form_password(array('name' => 'password2', 'id' => 'password2', 'value' => set_value('password2', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('form', 'cancel'); ?> <?php echo form_close(); ?> -
将以下代码复制到
/path/to/codeigniter/application/controllers/signin.php文件中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Signin extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('security'); } public function index() { redirect('signin/login'); } public function login() { if ($this->session->userdata('logged_in') == TRUE) { redirect('signin/loggedin'); } else { $this->load->library('form_validation'); // Set validation rules for view filters $this->form_validation->set_rules('email', 'Email', 'required|valid_email|min_length[5]|max_length[125]'); $this->form_validation->set_rules('password', 'Password ', 'required|min_length[5]|max_length[30]'); if ($this->form_validation->run() == FALSE) { $this->load->view('signin/signin'); } else { $email = $this->input->post('email'); $password = $this->input->post('password'); $this->load->model('Signin_model'); $query = $this->Signin_model->does_user_exist($email); if ($query->num_rows() == 1) { // One matching row found foreach ($query->result() as $row) { // Call Encrypt library $this->load->library('encrypt'); // Generate hash from a their password $hash = $this->encrypt->sha1($password); // Compare the generated hash with that in the // database if ($hash != $row->user_hash) { // Didn't match so send back to login $data['login_fail'] = true; $this->load->view('signin/signin', $data); } else { $data = array( 'user_id' => $row->user_id, 'user_email' => $row->user_email, 'logged_in' => TRUE ); // Save data to session $this->session->set_userdata($data); redirect('signin/loggedin'); } } } } } } function loggedin() { if ($this->session->userdata('logged_in') == TRUE) { $this->load->view('signin/loggedin'); } else { redirect('signin'); } } } -
将以下代码复制到
/path/to/codeigniter/application/models/signin_model.php文件中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Signin_model extends CI_Model { function __construct() { parent::__construct(); } public function does_user_exist($email) { $this->db->where('user_email', $email); $query = $this->db->get('register'); return $query; } } -
然后将以下代码复制到
/path/to/codeigniter/application/views/signin/signin.php文件中:<?php echo form_open('signin/login') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <?php if (isset($login_fail)) : ?> <h3>Login Error:</h3> <p>Username or Password is incorrect, please try again.</p> <?php endif; ?> <table border="0" > <tr> <td>User Email</td> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Password</td> <td><?php echo form_password(array('name' => 'password', 'id' => 'password', 'value' => set_value('password', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('signin', 'cancel'); ?> <?php echo form_close(); ?> -
然后将以下代码复制到
/path/to/codeigniter/application/views/signin/loggedin.php文件中:Success! Logged in as <?php echo $this->session->userdata('user_email'); ?>
它是如何工作的...
好的,这个例子中有很多内容,但实际上相当简单。再次看看前面的代码——特别是那些被突出显示的行,因为这些行是密码特定的。前面章节中创建的文件显示了创建用户和登录该用户的过程。当然,你的代码会有所不同;但让我们专注于那些突出显示的行。
他们展示了执行密码哈希和比较的代码(一个简化的版本可以在下面的菜谱中找到)。
首先,让我们看看注册用户的过程。注册控制器从 /path/to/codeigniter/application/views/register/register.php 视图中接受用户提交的信息。在成功通过以下验证行后:
$hash = $this->encrypt->sha1($password);
将使用用户提供的密码生成一个哈希值,这个哈希值存储在 $hash 变量中(很明显吧?)。
然后,将 $hash 添加到 $data 数组中,以便将其插入数据库,如下所示:
// Create hash from user password
$hash = $this->encrypt->sha1($this->input->post('password1'));
$data = array(
'user_first_name' => $this->input->post('first_name'),
'user_last_name' => $this->input->post('last_name'),
'user_email' => $this->input->post('email'),
'user_hash' => $hash
);
现在我们来了解一下登录过程。public function login() 函数接收用户提供的电子邮件地址和密码(来自 /path/to/codeigniter/application/views/signin/signin.php 视图),在成功通过验证后,我们按照以下方式在注册表中查找用户提供的电子邮件地址:
$this->load->model('Signin_model');
$query = $this->Signin_model->does_user_exist($email);
if ($query->num_rows() == 1) {
// One matching row found
foreach ($query->result() as $row) {
..
}
如果电子邮件存在,我们将从用户提供的密码生成一个哈希值。这个过程与注册过程中的功能相同,如下所示:
// Call Encrypt library
$this->load->library('encrypt');
// Generate hash from a their password
$hash = $this->encrypt->sha1($password);
// Compare the generated hash with that in the // database
if ($hash != $row->user_hash) {
// Didn't match so send back to login
$data['login_fail'] = true;
$this->load->view('signin/signin', $data);
} else {
$data = array(
'user_id' => $row->user_id,
'user_email' => $row->user_email,
'logged_in' => TRUE
);
// Save data to session
$this->session->set_userdata($data);
redirect('signin/loggedin');
}
现在,看看前面代码中突出显示的行。我们正在将用户提供的密码生成的哈希值与从注册表中提取的记录中的 user_hash 进行比较。如果两个哈希值不匹配,那么用户可能没有提供正确的密码,因此我们将他们送回登录表单并等待另一次尝试。然而,如果两个哈希值匹配,那么用户必须已经提供了正确的密码,因此我们将为他们启动一个会话并将他们重定向到 public function loggedin()。在这种情况下,这是一个简短的消息,表明他们已成功登录。然而,在你的应用程序中,这可能是某种受密码保护的会员区域,可能是仪表板。
使用 CodeIgniter 生成密码 – 简洁版
好的,这只是一个简洁的过程。如果你想看一个完整的例子,那么前面的菜谱就是为你准备的。这个菜谱是为那些已经有创建用户过程,但希望将一些密码保护集成到现有过程中的用户准备的。
如何操作...
如果你不需要前面的菜谱,只需要哈希/比较的简洁版;请参考以下步骤:
生成哈希值
要生成一个哈希值,请执行以下步骤:
-
使用
$config['encryption_key']中的关键字生成哈希值,如下所示:// Call Encrypt library $this->load->library('encrypt'); $hash = $this->encrypt->sha1($text_to_be_hashed); -
使用除
$config['encryption_key']之外的关键字生成哈希值,如下所示:// Call Encrypt library $this->load->library('encrypt'); $key = "This-is-the-key"; $hash = $this->encrypt->sha1($text_to_be_hashed, $key);小贴士
在生产环境中,将
$key值(This-is-the-key)替换为真实的值。使其成为一个由字母数字字符组成的较长字符串;越随机越好!
比较哈希值
哈希值比较如下:
// Call Encrypt library
$this->load->library('encrypt');
// Generate hash from a their password
$hash = $this->encrypt->sha1($password);
// Compare the generated hash with that in the database
if ($hash != $row->user_hash) {
// Didn't match so send back to login
redirect('signin/login');
} else {
// Did match so log them in if you wish
}
它是如何工作的...
**使用 \(config['encryption_key'] 值生成哈希**:首先,我们使用 `\)this->load->library('encrypt')加载加密库,然后调用加密库中的sha1函数,并将\(text_to_be_hashed` 变量传递给它。用于加密 `\)text_to_be_hashed字符串的密钥来自配置数组项\(config['encryption_key']`,在 `config.php` 文件中设置。`\)this->encrypt->sha1(\(text_to_be_hashed)` 将返回一个字符串,我们将将其存储在 `\)hash` 变量中。
**不使用 \(config['encryption_key'] 值生成哈希(即添加第二个参数)**:首先,我们使用 `\)this->load->library('encrypt')加载加密库,然后调用加密库中的sha1函数,并将$text_to_be_hashed` 和一个加密密钥作为第二个参数传递给它:
$this->encrypt->sha1($text_to_be_hashed, $key)
将此密钥作为第二个参数($key)添加将导致 CodeIgniter 使用该密钥而不是 $config['encryption_key'] 中设置的任何值。$this->encrypt->sha1($text_to_be_hashed, $key) 将返回一个字符串,我们将将其存储在变量 $hash 中。
在使用 $this->load->library('encrypt') 加载加密支持库后,一个文本字符串(在这种情况下,在 $password 变量中)被传递到加密库中的 sha1 函数,并将其结果存储在 $hash 变量中。我们现在可以使用这个变量来比较存储的值,例如来自数据库选择结果。在这个例子中,我们比较 $hash 与 $row->user_hash 中的值。如果它们不匹配,我们将发送 redirect() 到登录屏幕,但你可以轻松地编写任何操作,例如记录事件或显示消息而不是重定向。如果 $hash 和 $row->user_hash 的值匹配,那么你可以根据这个确认执行操作;一个例子就是登录用户。
忘记密码? – 使用 CodeIgniter 重置密码
每个人有时都会忘记他们的密码,用户可能希望被提醒他们的密码。然而,我们不能发送他们的密码,因为我们没有它;我们只存储它的哈希值——密码实际上并没有存储在数据库中。用户将不得不重置他们的密码;在这样做时生成一个新的哈希值。
准备工作
我们想要确保用户确实请求了新的密码,因此,我们将在注册表中添加一个列来支持这一点。新列名为 forgot_password,它将包含一个代码,当请求新密码时我们将生成这个代码;当用户从电子邮件中的 URL 被重定向回网站时,我们将检查这个代码,我们也会将这个电子邮件发送给他们。将以下代码复制到您的数据库中:
ALTER TABLE register ADD forgot_password INT(11) AFTER user_hash;
如何操作...
我们将创建以下两个文件:
-
/path/to/codeigniter/application/views/signin/forgot_password.php -
/path/to/codeigniter/application/views/signin/new_password.php
并且修改以下三个文件:
-
/path/to/codeigniter/application/controllers/signin.php -
/path/to/codeigniter/application/models/signin_model.php -
/path/to/codeigniter/application/views/signin/signin.php
-
将以下代码复制到
/path/to/codeigniter/application/views/signin/forgot_password.php文件中:<?php echo form_open('signin/forgot_password') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <?php if (isset($submit_success)) : ?> <h3>Email Sent:</h3> <p>An email has been sent to the address provided.</p> <?php endif; ?> <table border="0" > <tr> <td>User Email</td> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('form', 'cancel'); ?> <?php echo form_close(); ?> -
将以下代码复制到
/path/to/codeigniter/application/views/signin/new_password.php文件中:<?php echo form_open('signin/new_password') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <h2>Reset your password</h2> <table border="0"> <tr> <td>User Email</td> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Password</td> <td><?php echo form_password(array('name' => 'password1', 'id' => 'password1', 'value' => set_value('password1', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Confirm Password</td> <td><?php echo form_password(array('name' => 'password2', 'id' => 'password2', 'value' => set_value('password2', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <?php echo form_hidden('code', $code) ; ?> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('form', 'cancel'); ?> <?php echo form_close(); ?> -
修改
/path/to/codeigniter/application/controllers/signin.php文件,添加以下代码:public function forgot_password() { $this->load->library('form_validation'); $this->form_validation->set_rules('email', 'Email', 'required|valid_email|min_length[5]|max_length[125]'); if ($this->form_validation->run() == FALSE) { $this->load->view('signin/forgot_password'); } else { $email = $this->input->post('email'); $this->db->where('user_email', $email); $this->db->from('register'); $num_res = $this->db->count_all_results(); if ($num_res == 1) { // Make a small string (code) to assign to the user // to indicate they've requested a change of // password $code = mt_rand('5000', '200000'); $data = array( 'forgot_password' => $code, ); $this->db->where('user_email', $email); if ($this->db->update('register', $data)) { // Update okay, send email $url = "http://www.domain.com/signin/new_password/".$code; $body = "\nPlease click the following link to reset your password:\n\n".$url."\n\n"; if (mail($email, 'Password reset', $body, 'From: no-reply@domain.com')) { $data['submit_success'] = true; $this->load->view('signin/signin', $data); } } else { // Some sort of error happened, redirect user // back to form redirect('singin/forgot_password'); } } else { // Some sort of error happened, redirect user back // to form redirect('singin/forgot_password'); } } } public function new_password() { $this->load->library('form_validation'); $this->form_validation->set_rules('code', 'Code', 'required|min_length[4]|max_length[7]'); $this->form_validation->set_rules('email', 'Email', 'required|valid_email|min_length[5]|max_length[125]'); $this->form_validation->set_rules('password1', 'Password', 'required|min_length[5]|max_length[15]'); $this->form_validation->set_rules('password2', 'Confirmation Password', 'required|min_length[5]|max_length[15]|matches[password1]'); // Get Code from URL or POST and clean up if ($this->input->post()) { $data['code'] = xss_clean($this->input->post('code')); } else { $data['code'] = xss_clean($this->uri->segment(3)); } if ($this->form_validation->run() == FALSE) { $this->load->view('signin/new_password', $data); } else { // Does code from input match the code against the // email $this->load->model('Signin_model'); $email = xss_clean($this->input->post('email')); if (!$this->Signin_model->does_code_match($data['code'], $email)) { // Code doesn't match redirect ('signin/forgot_password'); } else {// Code does match $this->load->model('Register_model'); $hash = $this->encrypt->sha1($this->input->post('password1')); $data = array( 'user_hash' => $hash ); if ($this->Register_model->update_user($data, $email)) { redirect ('signin'); } } } } -
然后,修改
/path/to/codeigniter/application/models/signin_model.php文件,添加以下代码:public function update_user($data, $email) { $this->db->where('user_email', $email); $this->db->update('register', $data); } public function does_email_exist($email) { $this->db->where('user_email', $email); $this->db->from('register'); $num_res = $this->db->count_all_results(); if ($num_res == 1) { return TRUE; } else { return FALSE; } } public function does_code_match($code, $email) { $this->db->where('user_email', $email); $this->db->where('forgot_password', $code); $this->db->from('register'); $num_res = $this->db->count_all_results(); if ($num_res == 1) { return TRUE; } else { return FALSE; } } -
然后,修改
/path/to/codeigniter/application/views/signin.php文件,添加以下代码:<?php echo form_open('signin/login') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <?php if (isset($login_fail)) : ?> <h3>Login Error:</h3> <p>Username or Password is incorrect, please try again.</p> <?php endif; ?> <table border="0" > <tr> <td>User Email</td> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Password</td> <td><?php echo form_password(array('name' => 'password', 'id' => 'password', 'value' => set_value('password', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('signin', 'cancel'); ?> <?php echo anchor('signin/forgot_password', 'Forgot Password'); ?> <?php echo form_close(); ?>小贴士
我们只更改了此文件中的一行:高亮显示的行是一个
anchor()语句,它显示了一个指向忘记密码表单的链接。
它是如何工作的...
首先,看一下下面的流程图:

现在,让我们假设一个用户忘记了他们的密码并希望被提醒。用户将点击修改后的登录表单中的忘记密码链接(/path/to/codeigniter/application/views/signin/signin.php),这将他们重定向到签到控制器中的public function forgot_password()。forgot_password()函数立即显示/path/to/codeigniter/application/views/signin/forgot_password.php视图。用户输入电子邮件地址并使用提交按钮提交表单。
接下来,forgot_password()函数将验证用户提供的输入,如果该输入通过了验证规则,则forgot_password()函数将在数据库中查找是否存在一个在注册表中的行,其电子邮件与表单提交中提供的电子邮件匹配。如果找到匹配项,则生成一个跟踪代码(这用于表单视图文件中的隐藏表单元素),并将其分配给$data数组。然后,将此代码写入我们刚刚查找的数据库行中,并向与该账户(或行)关联的电子邮件地址发送电子邮件。在这种情况下,我们使用 PHP 的mail()函数而不是 CodeIgniter 的邮件功能;当然,您当然可以使用 CodeIgniter 发送电子邮件而不是 PHP 的mail()函数——无论如何,我们将在第四章中讨论如何在 CodeIgniter 中发送电子邮件,电子邮件、HTML 表格和文本库——回到故事中。
接下来,轮到我们的用户了。他们应该在他们的电子邮件收件箱中查找我们刚刚发送给他们的电子邮件,如果他们找到了,他们会看到一封电子邮件中的链接,该链接将他们引导回我们的系统,并到public function new_password()。点击该链接将打开/path/to/codeigniter/application/views/signin/new_password.php视图,该视图将显示重置密码表单。
记得我们生成的$code吗?$code是第三个 URL 参数,现在被设置为隐藏表单元素。用户输入他们的电子邮件和密码(两次以确认)然后点击提交。表单随后提交到public function new_password(),该函数用于验证表单。
通过验证后,电子邮件地址和代码在注册表中查找。如果找到(并且它们匹配),就会创建一个新的$hash数组并将其保存到数据库中的记录中。最后,他们被重定向到登录表单,在那里他们可以使用新密码进行 log()in。
第三章:创建电子商务功能
在本章中,我们将涵盖:
-
修改配置设置以在数据库中运行会话
-
创建基本购物车
-
通过产品分类添加和搜索
-
将购物车保存到数据库中
简介
CodeIgniter 中的Cart类提供了基本的购物车功能,例如添加项目、修改购物车、显示购物车详情以及从购物车中删除项目。在本章中,我们将探讨如何使用 CodeIgniter 的Cart类创建一个简单的商店。
修改配置设置以在数据库中运行会话
“为什么我需要这样做;我现在没有使用会话,而且,我已经在其他菜谱中涵盖了会话?”我知道;我听到了你的话;但是 CodeIgniter 的Cart类使用会话来为顾客或用户构建购物车。我们将在这里考虑Cart类来讲解会话。如果你已经实现了会话,你可能可以跳过这个菜谱。
准备工作
在我们开始购物车示例之前,我们需要做一些准备工作。首先,我们将修改一些配置设置,这将允许 CodeIgniter 将购物车信息保存到数据库中,然后我们将创建一个简单的数据库模式来处理产品等。
如何操作...
在我们开始之前,我们需要应用一些配置更改,如下所示:
-
打开文本编辑器中的
application/config/config.php文件,并进行以下修改:配置变量 值 说明 $config['encryption_key'] = ''Alphanumeric指定 CodeIgniter 在加密会话时应使用的加密密钥。该值可以是字母数字的,你必须决定一个字符串作为其值。 $config['sess_encrypt_cookie']TRUE/FALSE指定是否在数据库中加密会话;对于本例,应设置为 TRUE。$config['sess_use_database]TRUE/FALSE指定是否将会话存储在数据库中;对于本例,应设置为 TRUE。$config['sess_table_name']Alphanumeric数据库中用于存储会话的表名。对于本例,应设置为 sess_cart。 -
为了支持这个菜谱,需要一些表和一些虚拟数据。将以下 SQL 代码复制到你的数据库中:
CREATE TABLE IF NOT EXISTS `sessions` ( `session_id` varchar(40) COLLATE utf8_bin NOT NULL DEFAULT '0', `ip_address` varchar(16) COLLATE utf8_bin NOT NULL DEFAULT '0', `user_agent` varchar(120) COLLATE utf8_bin DEFAULT NULL, `last_activity` int(10) unsigned NOT NULL DEFAULT '0', `user_data` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (`session_id`), KEY `last_activity_idx` (`last_activity`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; CREATE TABLE IF NOT EXISTS `products` ( `product_id` int(11) NOT NULL AUTO_INCREMENT, `product_name` varchar(255) NOT NULL, `product_code` int(11) NOT NULL, `product_description` varchar(255) NOT NULL, `category_id` int(11) NOT NULL, `product_price` int(11) NOT NULL, PRIMARY KEY (`product_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3; INSERT INTO `products` (`product_id`, `product_name`, `product_code`, `product_description`, `category_id`, `product_price`) VALUES (1, 'Running Shoes', 423423, 'These are some shoes', 2, 50), (2, 'Hawaiian Shirt', 34234, 'This is a shirt', 1, 25); CREATE TABLE IF NOT EXISTS `categories` ( `cat_id` int(11) NOT NULL AUTO_INCREMENT, `cat_name` varchar(50) NOT NULL, PRIMARY KEY (`cat_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3; INSERT INTO `categories` (`cat_id`, `cat_name`) VALUES (1, 'Shirts'), (2, 'Footware');
工作原理...
好吧,我们刚才做了什么?以下是对两个表的描述:
分类表
分类表存储有关每个产品(在产品表中)关联的分类组的信息。它们如下所示:
| 项目名称 | 属性 | 描述 |
|---|---|---|
cat_id |
INTEGER(11) |
表的主键。 |
cat_name |
VARCHAR(50) |
分类名称。 |
产品表
产品表存储有关购物车中每个销售产品的信息。它通过外键(category_id)与分类表相关联。以下是产品表:
| 项目名称 | 属性 | 描述 |
|---|---|---|
product_id |
INTEGER(11) |
表的主键 |
product_name |
VARCHAR(125) |
产品名称 |
product_code |
INTEGER(11) |
产品代码,可以用作内部库存代码 |
product_description |
VARCHAR(255) |
产品详细描述 |
category_id |
INTEGER(11) |
产品分类 ID(作为从分类表的外键) |
product_price |
INTEGER(11) |
产品价值。 |
创建基本购物车
在本节中,我们将创建运行购物车所需的基本文件。在本章的后面部分,我们将添加更多的功能,但首先让我们做好准备。我们将创建以下四个文件:
-
path/to/codeigniter/application/controllers/shop.php: 此控制器将处理视图和模型之间的任何客户交互,例如处理任何表单和控制客户通过购物车的旅程。 -
path/to/codeigniter/application/models/shop_model.php: 此模型将处理控制器与数据库之间的任何数据库交互。它将包含获取产品和产品分类的函数,并在本章的后面部分,将购物车保存到数据库中。 -
path/to/codeigniter/application/views/shop/display_cart.php: 此视图将显示任何时刻客户的购物车摘要,并允许该客户修改购物车中项目的数量。 -
path/to/codeigniter/application/views/shop/view_products.php: 此视图将显示数据库中cart.products表的产品,并提供选项允许客户将项目添加到他们的购物车中。
如何操作...
我们将创建以下四个文件:
-
/path/to/codeigniter/application/controllers/shop.php -
/path/to/codeigniter/application/models/shop_model.php -
/path/to/codeigniter/application/views/shop/display_cart.php -
/path/to/codeigniter/application/views/shop/display_products.php
-
将以下代码复制到
/path/to/codeigniter/application/controllers/shop.php文件中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Shop extends CI_Controller { function __construct() { parent::__construct(); $this->load->library('cart'); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('security'); $this->load->model('Shop_model'); } public function index() { $data['query'] = $this->Shop_model->get_all_products(); $this->load->view('shop/display_products', $data); } public function add() { $product_id = $this->uri->segment(3); $query = $this->Shop_model->get_product_details($product_id); foreach($query->result() as $row) { $data = array( 'id' => $row->product_id, 'qty' => 1, 'price' => $row->product_price, 'name' => $row->product_name, ); } $this->cart->insert($data); $this->load->view('shop/display_cart', $data); } public function update_cart() { $data = array(); $i = 0; foreach($this->input->post() as $item) { $data[$i]['rowid'] = $item['rowid']; $data[$i]['qty'] = $item['qty']; $i++; } $this->cart->update($data); redirect('shop/display_cart'); } public function display_cart() { $this->load->view('shop/display_cart'); } public function clear_cart() { $this->cart->destroy(); redirect('index'); } } -
然后将以下代码复制到
/path/to/codeigniter/application/models/shop_model.php文件中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Shop_model extends CI_Model { function __construct() { parent::__construct(); $this->load->helper('url'); } public function get_product_details($product_id) { $this->db->where('product_id', $product_id); $query = $this->db->get('products'); return $query; } public function get_all_products() { $query = $this->db->get('products'); return $query; } } -
然后将以下代码复制到
path/to/codeigniter/application/views/shop/display_cart.php文件中:<?php echo form_open('shop/update_cart'); ?> <table cellpadding="6" cellspacing="1" style="width:50%" border="1"> <tr> <th>Quantity</th> <th>Description</th> <th>Item Price</th> <th>Sub-Total</th> </tr> <?php $i = 1; ?> <?php foreach ($this->cart->contents() as $items): ?> <?php echo form_hidden($i . '[rowid]', $items['rowid']); ?> <tr> <td><?php echo form_input(array('name' => $i . '[qty]', 'value' => $items['qty'], 'maxlength' => '3', 'size' => '5')); ?></td> <td> <?php echo $items['name']; ?> <?php if ($this->cart->has_options($items['rowid']) == TRUE): ?> <p> <?php foreach ($this->cart->product_options($items['rowid']) as $option_name => $option_value): ?> <strong><?php echo $option_name; ?>:</strong> <?php echo $option_value; ?><br/> <?php endforeach; ?> </p> <?php endif; ?> </td> <td><?php echo $this->cart->format_number($items['price']); ?></td> <td>$<?php echo $this->cart->format_number($items['subtotal']); ?></td> </tr> <?php $i++; ?> <?php endforeach; ?> <tr> <td colspan="2"> </td> <td><strong>Total</strong></td> <td>$<?php echo $this->cart->format_number($this->cart->total()); ?></td> </tr> </table> <p><?php echo form_submit('', 'Update Cart'); ?></p> <?php echo form_close() ; ?> -
最后,将以下代码复制到
path/to/codeigniter/application/views/shop/display_products.php文件中:<body> <table> <?php foreach ($query->result() as $row) : ?> <tr> <td><?php echo $row->product_id ; ?></td> <td><?php echo $row->product_name ; ?></td> <td><?php echo $row->product_description ; ?></td> <td><?php echo anchor('shop/add/'.$row->product_id, 'Add to cart') ; ?></td> </tr> <?php endforeach ; ?> </table> </body>
工作原理...
好吧,这里有很多内容;所以我们不会讨论每个文件中的内容,而是将其分解为用户操作。当用户与购物车交互时,可以执行几种操作。最常见的是以下操作:
-
用户浏览目录
-
用户将项目添加到购物车
-
用户更新或删除购物车中的项目
用户浏览目录
controllers/shop.php中的public function index()通过调用Shop_model中的$this->Cart_model->get_all_products()函数从数据库中加载所有产品,并通过$this->load->view('shop/display_products', $data)将其传递给views/shop/display_products.php视图。
用户将项目添加到购物车
用户点击views/shop/display_products.php文件中他们想要购买的项目旁边的“添加到购物车”链接,这将在controllers/shop.php中调用public function add()。public function add()通过$this->uri->segment(3)从 URL 获取传递给它的产品 ID。使用这个,$product_id通过$this->Shop_model->get_product_details($product_id)从数据库中查找产品详情。然后,通过$this->cart->insert($data)将这些数据写入购物车。最后,用户通过$this->load->view('shop/display_cart', $data)被重定向到购物车,以便他们可以查看购物车中的项目,并根据需要做出任何修改。
用户更新或删除购物车中的项目
这涵盖了两个动作:向购物车添加更多项目或完全删除购物车中的项目或所有项目。它们如下所述:
-
向购物车添加或减去项目:当用户查看他们的购物车时,他们会看到左侧一个表格,显示购物车中每个项目的数量。用户可以通过更改此文本框中的值来更改项目的数量。如果用户增加或减少特定项目的当前数量并按下“更新购物车”按钮,那么我们将运行商店控制器函数
update_cart()。update_cart()遍历 POST 数组,查看每个项目和它的新或期望的数量。它将使用$data数组中的行值跟踪每个项目,确保正确地更新了正确的数量。 -
从购物车中删除项目:此功能与之前描述的添加或删除项目的方式相同。然而,区别在于如果用户选择的数量是
0(零),那么 CodeIgniter 将完全从购物车中删除该项目。
添加和按产品类别搜索
从客户的角度来看,能够通过查看类别(如鞋子、衬衫、外套等)来缩小您的目录是有用的。如果您想添加此功能,您需要修改数据库。如果您需要此功能,请复制以下“准备就绪”部分中的代码。
准备就绪
为了支持按类别搜索和筛选,我们需要添加一个类别表。如果您在章节前面的“准备就绪”部分还没有这样做,请在您的数据库中创建以下表格:
CREATE TABLE IF NOT EXISTS `categories` (
`cat_id` int(11) NOT NULL AUTO_INCREMENT,
`cat_name` varchar(50) NOT NULL,
PRIMARY KEY (`cat_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3;
INSERT INTO `categories` (`cat_id`, `cat_name`) VALUES
(1, 'Shirts'),
(2, 'Footware');
如何操作...
我们现在需要对以下文件进行修改:
-
/path/to/codeigniter/views/shop/display_products.php:添加了一个小菜单,允许用户点击不同的类别;结果将相应地过滤。 -
/path/to/codeigniter/views/shop/display_cart.php:添加了一个小菜单,允许用户点击不同的类别;结果将相应地过滤。 -
/path/to/codeigniter/application/controllers/shop.php:这段添加的代码将显示类别,并在表单提交中查找用户的类别选择。 -
/path/to/codeigniter/application/models/shop_model.php:添加了从数据库根据类别 ID 获取类别的代码。
-
在
path/to/codeigniter/application/views/shop/display_cart.php文件的顶部复制以下代码:<?php echo form_open('shop/index') ; ?> <select name="cat"> <?php foreach ($cat_query->result() as $cat_row) : ?> <option value="<?php echo $cat_row->cat_id;?>"><?php echo $cat_row->cat_name;?></option> <?php endforeach ; ?> </select> <?php echo form_submit('', 'Search') ; ?> <?php echo form_close() ; ?> -
在
path/to/codeigniter/application/views/shop/display_products.php文件的顶部复制以下代码:<?php echo form_open('shop/index') ; ?> <select name="cat"> <?php foreach ($cat_query->result() as $cat_row) : ?> <option value="<?php echo $cat_row->cat_id;?>"><?php echo $cat_row->cat_name;?></option> <?php endforeach ; ?> </select> <?php echo form_submit('', 'Search') ; ?> <?php echo form_close() ; ?> -
将商店控制器中的
public function index()替换为以下内容:public function index() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); if ($this->input->post()) { $category_id = $this->input->post('cat'); } else { $category_id = null; } $this->form_validation->set_rules('cat', 'Category', 'required|min_length[1]|max_length[125]|integer'); if ($this->form_validation->run() == FALSE) { $data['query'] = $this->Shop_model->get_all_products($category_id); $data['cat_query'] = $this->Shop_model->get_all_categories(); $this->load->view('shop/display_products', $data); } else { $data['query'] = $this->Shop_model->get_all_products($category_id); $data['cat_query'] = $this->Shop_model->get_all_categories(); $this->load->view('shop/display_products', $data); } } -
将商店控制器函数
add()替换为以下代码(更改已突出显示):public function add() { $product_id = $this->uri->segment(3); $query = $this->Shop_model->get_product_details($product_id); foreach($query->result() as $row) { $data = array( 'id' => $row->product_id, 'qty' => 1, 'price' => $row->product_price, 'name' => $row->product_name, ); } $this->cart->insert($data); $data['cat_query'] = $this->Shop_model->get_all_categories(); $this->load->view('shop/display_cart', $data); } -
将控制器函数
displat_cart()替换为以下代码(更改已突出显示):public function display_cart() { $data['cat_query'] = $this->Shop_model->get_all_categories(); $this->load->view('shop/display_cart', $data); } -
将
shop_model中的public function get_all_products()替换为以下代码:public function get_all_products($category_id = null) { if ($category_id) { $this->db->where('category_id', $category_id); } $query = $this->db->get('products'); return $query; } -
将以下函数
get_all_categories()添加到shop_model中,如下所示:public function get_all_categories() { $query = $this->db->get('categories'); return $query; }
它是如何工作的...
我们已经修改了public function index(),以便我们可以通过$category_id(这是数据库中每个类别的主键)过滤用户的浏览结果。如果页面是首次加载(不是作为提交),则代码:
if ($this->input->post()) {
$category_id = $this->input->post('cat');
} else {
$category_id = null;
}
将自动将$category_id设置为 null,因此$this->Shop_model->get_all_products($category_id)将返回所有产品,无论category_id如何。然而,如果通过表单提交传递了$category_id,则$this->Shop_model->get_all_products($category_id)将只返回分配给该类别的产品。
将购物车保存到数据库
在您的客户准备好进行支付之前,您需要收集他们的支付、配送和您的记录详情,然后将会话表中的购物车移动到将存储订单的特定表中。
一旦订单已保存并且提供了客户详细信息,就会生成一个唯一的订单代码并存储在orders.order_fulfilment_code中。这可以被支付提供商(例如,PayPal、GoCardless、Stripe 等)用来跟踪通过他们的系统到您系统的支付处理。
如何操作...
-
首先,在您的数据库中创建以下表:
CREATE TABLE IF NOT EXISTS `customer` ( `cust_id` int(11) NOT NULL AUTO_INCREMENT, `cust_first_name` varchar(125) NOT NULL, `cust_last_name` varchar(125) NOT NULL, `cust_email` varchar(255) NOT NULL, `cust_created_at` int(11) NOT NULL, `cust_address` text NOT NULL COMMENT 'card holder address', PRIMARY KEY (`cust_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; CREATE TABLE `orders` ( `order_id` int(11) NOT NULL AUTO_INCREMENT, `cust_id` int(11) NOT NULL, `order_details` text NOT NULL, `order_created_at` int(11) NOT NULL, `order_closed` int(1) NOT NULL COMMENT '0 = open, 1 = closed', `order_fulfilment_code` varchar(255) NOT NULL COMMENT 'the unique code sent to a payment provider', `order_delivery_address` text NOT NULL, PRIMARY KEY (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1; -
现在我们已经创建了存储您的客户和订单的数据库表,让我们创建支持这个新功能的文件。我们将创建以下两个文件:
-
/path/to/codeigniter/application/controllers/cust.php -
/path/to/codeigniter/application/models/cart_model.php
-
-
在
/path/to/codeigniter/application/controllers/cust.php控制器中创建控制器并添加以下代码:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Cust extends CI_Controller { function __construct() { parent::__construct(); $this->load->library('cart'); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('security'); $this->load->model('Shop_model'); } public function index() { redirect('cust/user_details'); } public function user_details() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters(); // Set validation rules $this->form_validation->set_rules('first_name', 'First Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('last_name', 'Last Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('email', 'Email Address', 'required|min_length[1]|max_length[255]|valid_email'); $this->form_validation->set_rules('email_confirm', 'Comfirmation Email Address', 'required|min_length[1]|max_length[255]|valid_email|matches[email]'); $this->form_validation->set_rules('payment_address', 'Payment Address', 'required|min_length[1]|max_length[1000]'); $this->form_validation->set_rules('delivery_address', 'Delivery Address', 'min_length[1]|max_length[1000]'); // Begin validation if ($this->form_validation->run() == FALSE) { $this->load->view('shop/user_details'); } else { $cust_data = array( 'cust_first_name' => $this->input->post('cust_first_name'), 'cust_last_name' => $this->input->post('cust_last_name'), 'cust_email'=> $this->input->post('cust_email'), 'cust_address' => $this->input->post('payment_address'), 'cust_created_at' => time()); $payment_code = mt_rand(); $order_data = array( 'order_details' => serialize($this->cart->contents()), 'order_delivery_address' => $this->input->post('delivery_address'), 'order_created_at' => time(), 'order_closed' => '0', 'order_fulfilment_code' => $payment_code, 'order_delivery_address' => $this->input->post('payment_address')); if ($this->Shop_model->save_cart_to_database($cust_data, $order_data)) { echo 'Order and Customer saved to DB'; } else { echo 'Could not save to DB'; } } } } -
在
models/shop_model.php模型中添加public function save_cart_to_db(),如下所示:public function save_cart_to_database($cust_data, $order_data) { $this->db->insert('customer', $cust_data); $order_data['cust_id'] = $this->db->insert_id(); if ($this->db->insert('orders', $order_data)) { return true; } else { return false; } } -
修改
views/shop/display_cart.php文件。在页面顶部添加以下行:<?php echo anchor('cust/user_details', 'Proceed to checkout') ; ?> -
创建
filepath/to/codeigniter/application/views/shop/user_details.php文件,并将以下代码添加到其中:<body> <?php echo validation_errors(); ?> <?php echo form_open('/cust/user_details') ; ?> <?php echo form_input(array('name' => 'first_name', 'value' => 'First Name', 'maxlength' => '125', 'size' => '50')); ?><br /> <?php echo form_input(array('name' => 'last_name', 'value' => 'Last Name', 'maxlength' => '125', 'size' => '50')); ?><br /> <?php echo form_input(array('name' => 'email', 'value' => 'Email Address', 'maxlength' => '255', 'size' => '50')); ?><br /> <?php echo form_input(array('name' => 'email_confirm', 'value' => 'Confirm Email', 'maxlength' => '255', 'size' => '50')); ?><br /> <?php echo form_textarea(array('name' => 'payment_address', 'value' => 'Payment Address', 'rows' => '6', 'cols' => '40', 'size' => '50')); ?><br /> <?php echo form_submit('', 'Enter') ; ?><br /> <?php echo form_close() ; ?> </form> </body>
它是如何工作的...
为了解释这里发生的事情,让我们从顾客的角度来看待这个问题。顾客在商店里四处看了看,挑选了几件商品并将它们放入购物车。下一步是将购物车转换为订单。因此,用户点击查看购物车来查看他们想要订购的产品(我们已经讨论过这一点,所以不会再深入讨论。)当用户点击前往结账时,会调用公共函数user_details(),它将views/shop/user_details.php显示给顾客。这要求他们输入一些信息;在这种情况下,他们的名字、电子邮件地址、支付方式、送货地址等等。在成功提交表单(即没有验证错误)后,他们的订单将从购物车移动到数据库,并与他们提交的用户详细信息匹配。
还通过行$payment_code = mt_rand()创建了一个跟踪码,该跟踪码可用于通过支付提供商系统跟踪支付。
第四章:电子邮件、HTML 表格和文本库
在本章中,你将学习:
-
使用 CodeIgniter 电子邮件发送纯文本电子邮件
-
使用 CodeIgniter 电子邮件发送 HTML 电子邮件
-
使用 CodeIgniter 电子邮件发送附件
-
使用 CodeIgniter 电子邮件发送批量电子邮件
-
使用 DataTable 中的 HTML 表格
-
使用 DataTable 和数据库中的 HTML 表格
-
使用
word_limiter()进行表格输出 -
使用
word_censor()进行输入清理
简介
CodeIgniter 附带了一些有用的库和函数,用于处理应用程序开发的许多方面。在本章中,我们将探讨电子邮件和 HTML 表格。CodeIgniter 电子邮件库能够发送纯文本和 HTML 电子邮件,包括和不包括附件,这些附件可以用作(经过一些配置)替代标准的 PHP mail()函数。CodeIgniter 的 HTML 表格库在生成所需的 HTML 方面非常出色,几乎可以满足你对表格的所有需求——并且与 DataTable 一起,可以为用户提供出色的交互式表格。
使用 CodeIgniter 电子邮件发送纯文本电子邮件
能够发送电子邮件总是很有用的,CodeIgniter 附带了一个用于发送电子邮件的优秀库。本章中有几个关于发送电子邮件的食谱。然而,这是一个非常简单的 Hello World 类型示例。
如何操作...
使用 CodeIgniter 电子邮件发送纯文本电子邮件的简单方法如下:
-
在
path/to/codeigniter/application/controllers/路径下创建一个名为email.php的文件。 -
将以下代码添加到控制器文件
email.php中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Email extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->library('email'); } public function index() { redirect('email/send_email'); } public function send_email() { $config['protocol'] = 'sendmail'; $config['mailpath'] = '/usr/sbin/sendmail'; $config['charset'] = 'iso-8859-1'; $config['wordwrap'] = TRUE; $config['mailtype'] = 'text'; $this->email->initialize($config); $this->email->from('from@domain.com', 'Your Name'); $this->email->to('to@domain.com'); $this->email->subject('This is a text email'); $this->email->message('And this is some content for the text email.'); $this->email->send(); echo $this->email->print_debugger(); } }
工作原理...
在构造函数控制器中,我们加载了电子邮件库(以下代码中突出显示),它提供了发送电子邮件的支持:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->library('email');
}
接下来,public function index()将我们重定向到public function send_mail()函数,该函数为 CodeIgniter 电子邮件库设置了一些初始配置变量,以便与它一起工作,例如用于发送电子邮件的系统(在本例中为sendmail)、在系统上发送电子邮件的路径、mailtype变量(文本或 HTML)等。看看以下代码行:
$config['mailtype'] = 'text';
在这里,我们告诉 CodeIgniter 以纯文本而不是 HTML 的形式发送电子邮件。
这些配置设置被初始化(即传递给电子邮件库),然后我们通过设置to、from、subject和message属性来开始构建电子邮件:
$this->email->from('from@domain.com', 'Your Name');
$this->email->to('to@domain.com');
$this->email->subject('This is a text email');
$this->email->message('And this is some content for the text email.');
然后,发送电子邮件:
$this->email->send();
如果一切按计划进行,你应该会看到一个类似以下输出的结果:
User-Agent: CodeIgniter
Date: Fri, 4 Oct 2013 08:51:03 +0200
From: "Your Name" <from@domain.com>
Return-Path: <from@domain.com>
To: to@domain.com
Subject: =?iso-8859-1?Q?This_is_a_text_email?=
Reply-To: "from@domain.com" <from@domain.com>
X-Sender: from@domain.com
X-Mailer: CodeIgniter
X-Priority: 3 (Normal)
Message-ID: <524e6557968c5@domain.com>
Mime-Version: 1.0
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
And this is some content for the text email.
使用 CodeIgniter 电子邮件发送 HTML 电子邮件
有时候你可能希望显示格式化的电子邮件而不是纯文本,因此你可能希望在电子邮件正文中包含图片、文本格式和 URL。HTML 电子邮件将允许你这样做,并且 CodeIgniter 电子邮件库可以轻松地设置为执行此操作。
如何操作...
通过执行以下步骤可以发送 HTML 电子邮件:
-
在
/path/to/codeigniter/application/controllers/路径下创建一个名为email.php的文件。 -
将以下代码添加到控制器文件
email.php中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Email extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->library('email'); } public function index() { redirect('email/send_email'); } public function send_email() { $config['protocol'] = 'sendmail'; $config['mailpath'] = '/usr/sbin/sendmail'; $config['charset'] = 'iso-8859-1'; $config['wordwrap'] = TRUE; $config['mailtype'] = 'html'; $this->email->initialize($config); $this->email->from('from@domain.com', 'Your Name'); $this->email->to('to@domain.com'); $this->email->subject('This is a html email'); $html = 'This is an <b>HTML</b> email'; $this->email->message($html); $this->email->send(); echo $this->email->print_debugger(); } }
工作原理...
在构造函数控制器中,我们加载了 Email 库(以下代码中突出显示),它为我们提供了发送电子邮件的支持:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->library('email');
}
接下来,public function index() 将我们重定向到函数 public function send_mail(),该函数为 CodeIgniter Email 库设置了一些初始配置变量,以便与电子邮件发送系统(在这种情况下,sendmail)、系统上发送电子邮件的路径、mailtype 变量(文本或 HTML)等一起工作。请看以下代码行:
$config['mailtype'] = 'html';
在这里,我们告诉 CodeIgniter 以 HTML 格式而不是文本格式发送电子邮件。
这些配置设置被初始化(即传递给 Email 库),我们通过设置 to、from、subject 和 message 属性来开始构建电子邮件:
$this->email->from('from@domain.com', 'Your Name');
$this->email->to('to@domain.com');
$this->email->subject('This is a text email');
$this->email->message('And this is some content for the text email.');
然后,使用以下代码发送电子邮件:
$this->email->send();
如果一切按计划进行,你应该会看到以下类似的输出代码:
Your message has been successfully sent using the following protocol: sendmail
User-Agent: CodeIgniter
Date: Fri, 4 Oct 2013 08:56:59 +0200
From: "Your Name" <from@domain.com>
Return-Path: <from@domain.com>
To: to@domain.com
Subject: =?iso-8859-1?Q?This_is_a_html_email?=
Reply-To: "from@domain.com" <from@domain.com>
X-Sender: from@domain.com
X-Mailer: CodeIgniter
X-Priority: 3 (Normal)
Message-ID: <524e66bbf282f@domain.com>
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary="B_ALT_524e66bbf2868"
This is a multi-part message in MIME format.
Your email application may not support this format.
--B_ALT_524e66bbf2868
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
This is an HTML email
--B_ALT_524e66bbf2868
Content-Type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable
This is an <b>HTML</b> email
--B_ALT_524e66bbf2868--
使用 CodeIgniter Email 发送附件
有时候你可能希望在电子邮件中发送附件,例如向客户发送最近购买的发票或图片。CodeIgniter Email 库可以轻松设置以实现这一点。
如何操作...
你可以通过以下步骤使用 CodeIgniter Email 发送附件:
-
在
/path/to/codeigniter/application/controllers/创建一个名为email.php的文件。 -
将以下代码添加到控制器文件
email.php中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Email extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->library('email'); } public function index() { redirect('email/send_email'); } public function send_email() { $config['protocol'] = 'sendmail'; $config['mailpath'] = '/usr/sbin/sendmail'; $config['charset'] = 'iso-8859-1'; $config['wordwrap'] = TRUE; $config['mailtype'] = 'html'; $this->email->initialize($config); $this->email->from('from@domain.com', 'Your Name'); $this->email->to('to@domain.com'); $this->email->subject('This is a html email'); $html = 'This is an <b>HTML</b> email with an attachment, <i>lovely!</i>'; $this->email->message($html); $this->email->attach('/path/to/attachment'); $this->email->send(); echo $this->email->print_debugger(); } }
工作原理...
在构造函数控制器中,我们加载了 Email 库(以下代码中突出显示),它为我们提供了发送电子邮件的支持:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->library('email');
}
接下来,public function index() 将我们重定向到函数,public function send_mail(),该函数为 CodeIgniter Email 库设置了一些初始配置变量,以便与电子邮件发送系统(在这种情况下,sendmail)、系统上发送电子邮件的路径、mailtype 变量(文本或 HTML)等一起工作。这些配置设置被初始化(即传递给 Email 库),我们开始构建电子邮件;设置 to、from、subject 和 message 属性,以及我们发送电子邮件中附件的路径(以下代码中突出显示):
$this->email->from('from@domain.com', 'Your Name');
$this->email->to('to@domain.com');
$this->email->subject('This is a html email');
$html = 'This is an <b>HTML</b> email with an attachment, <i>lovely!</i>';
$this->email->message($html);
$this->email->attach('/path/to/attachment');
然后,使用以下代码发送电子邮件:
$this->email->send();
使用 CodeIgniter Email 发送大量电子邮件
有时候你可能希望发送大量电子邮件;也许是要发送给所有支付了旅行费用的人。你可能希望给他们每个人发送个性化的电子邮件,并附加文件。你可能还希望从数据库中的账户中提取他们的电子邮件偏好(纯文本或 HTML)并发送正确的电子邮件格式。这正是我们要做的。
准备工作
我们需要了解每个人的偏好,例如他们是否想要 HTML 电子邮件或文本,以及他们旅行的个人参考号(或预订 ID)。根据这一要求,我们将有一个数据库来存储所有信息;因此,将以下代码复制到您的数据库中:
CREATE TABLE `bookers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(50) NOT NULL,
`lastname` varchar(50) NOT NULL,
`email` varchar(255) NOT NULL,
`email_pref` varchar(4) NOT NULL,
`booking_ref` varchar(10) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `bookers` (`id`, `firstname`, `lastname`, `email`, `email_pref`, `booking_ref`) VALUES
(1, 'Robert', 'Foster', 'example1@domain1.com', 'html', 'ABC123'),
(2, 'Lucy', 'Welsh', 'example2@domain2.com', 'html', 'DEF456');
如何做...
-
在
/path/to/codeigniter/application/controllers/创建一个名为email.php的文件。 -
将以下代码添加到控制器文件中,
email.php:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Email extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->library('email'); } public function index() { redirect('email/send_email'); } public function send_email() { $config['protocol'] = 'sendmail'; $config['mailpath'] = '/usr/sbin/sendmail'; $config['charset'] = 'iso-8859-1'; $config['wordwrap'] = TRUE; $query = "SELECT * FROM bookers "; $result = $this->db->query($query); foreach ($result->result() as $row) { $this->email->clear(); if ($row->email_pref == 'text') { $config['mailtype'] = 'text'; $body = 'Hi ' . $row->firstname . ', Thanks you for booking with us, please find attached the itinerary for your trip. This is your booking reference number: ' . $row->booking_ref . ' Thanks for booking with us, have a lovely trip.'; } else { $config['mailtype'] = 'html'; $body = 'Hi ' . $row->firstname . ',<br /><br />Thanks you for booking with us, please find attached the itinerary for your trip. </p>This is your booking reference number: <b>' . $row->booking_ref . '</b><br /><br />Thanks for booking with us, have a lovely trip.'; } $this->email->initialize($config); $this->email->to($row->email); $this->email->from('bookings@thecodeigniterholidaycompany.com'); $this->email->subject('Holiday booking details'); $this->email->message($body); $this->email->send(); } echo $this->email->print_debugger(); } }
它是如何工作的...
在构造函数控制器中,我们加载 Email 库(以下代码中突出显示),它为我们提供发送电子邮件的支持:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->library('email');
}
接下来,public function index() 将我们重定向到函数 public function send_mail(),该函数为 CodeIgniter 邮件库设置一些初始配置变量,以便与系统一起工作,例如用于发送电子邮件的系统(在本例中为 sendmail),从您的系统发送邮件的路径。
然后,我们查询数据库以获取每位客户的预订详情:
$query = "SELECT * FROM bookers ";
$result = $this->db->query($query);
foreach ($result->result() as $row) {
}
查询将遍历每个结果,并根据每个循环从数据库中检索的值发送特定的电子邮件:
首先,我们通过使用 CodeIgniter 的 email 函数清除前一个循环迭代中的所有设置和变量,给自己一个干净的起点:
$this->email->clear();
然后,我们查看他们的电子邮件偏好,并根据需要设置电子邮件发送(mailtype)变量,以及电子邮件正文的文本。所以,如果有人更喜欢 HTML,我们会查找该偏好并定义 HTML 电子邮件的正文,否则对于文本电子邮件,我们会查找文本电子邮件偏好并定义文本电子邮件的正文:
if ($row->email_pref == 'text') {
$config['mailtype'] = 'text';
$body = 'Hi ' . $row->firstname . ', Thank you for booking with us, please find attached the itinerary for your trip. This is your booking reference number: ' . $row->booking_ref . ' Thanks for booking with us, have a lovely trip.';
} else {
$config['mailtype'] = 'html';
$body = 'Hi ' . $row->firstname . ',<br /><br />Thank you for booking with us, please find attached the itinerary for your trip. </p>This is your booking reference number: <b>' . $row->booking_ref . '</b><br /><br />Thanks for booking with us, have a lovely trip.';
}
在此之后,我们初始化配置变量。那些查看过前几个菜谱的人会注意到,初始化在这个菜谱的代码中比在其他菜谱中晚。这是因为我们不能提前初始化 config 变量,因为一些变量依赖于个别客户的偏好,这些偏好是从数据库中获取的。因此,我们必须等待每个用户的详细信息从数据库中获取,然后初始化配置设置的每个迭代。最后,我们发送电子邮件:
$this->email->send();
如果一切顺利,您应该会看到一个类似于以下输出的结果:
Your message has been successfully sent using the following protocol: sendmail
User-Agent: CodeIgniter
Date: Fri, 4 Oct 2013 20:06:13 +0200
To: to@domain.com
From: <bookings@thecodeigniterholidaycompany.com>
Return-Path: <bookings@thecodeigniterholidaycompany.com>
Subject: =?iso-8859-1?Q?Holiday_booking_details?=
Reply-To: "bookings@thecodeigniterholidaycompany.com" <bookings@thecodeigniterholidaycompany.com>
X-Sender: bookings@thecodeigniterholidaycompany.com
X-Mailer: CodeIgniter
X-Priority: 3 (Normal)
Message-ID: <524f0395942a2@thecodeigniterholidaycompany.com>
Mime-Version: 1.0
Content-Type: multipart/alternative; boundary="B_ALT_524f0395942bb"
This is a multi-part message in MIME format.
Your email application may not support this format.
--B_ALT_524f0395942bb
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit
Hi RobertThanks you booking with us,
please find attached the itinerary for your trip.
This is your booking reference number:
ABC123
Thanks for booking with us, have a lovely trip.
--B_ALT_524f0395942bb
Content-Type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable
Hi Robert<br /><br />Thanks you booking with us,=20
please find attached the itinerary for your trip.
</p>This is your booking reference number:=20
<b>ABC123</b><br /><br />
Thanks for booking with us, have a lovely trip.
--B_ALT_524f0395942bb--
使用带有 DataTable 的 HTML 表格
DataTable 是一个免费使用的库,可以将您的普通外观 HTML 表格转换为交互式的奇迹,具有可排序和可搜索的列以及更多功能;我们将与 CodeIgniter 一起使用它,合并 DataTable 和 CodeIgniter 表功能。它简单易用,能够处理您需要的几乎所有事情。在这里,在这个菜谱中,我们将使用它与 DataTable 创建一个可排序和可搜索的交互式 HTML 表格。它还具有分页功能!如果您想要数据库结果,请继续查看下一个菜谱,使用带有 DataTable 和数据库的 HTML 表格,我们将查看如何从数据库查询中填充表格。
准备工作
对于这个示例,你需要遵循给定的程序:
-
确保你已经从以下链接下载了 DataTable:
-
解压下载的
.zip文件,并将文件移动到你的 Web 服务器或本地主机上的一个位置,这样 CodeIgniter 就可以访问了。对于这个示例,我已经将文件夹放在了application/views;但如果你愿意,你可以自己选择位置。
如何做到这一点...
-
按照要求创建四个文件:
-
/path/to/codeigniter/application/controllers/table.php -
/path/to/codeigniter/application/views/table_header.php -
/path/to/codeigniter/application/views/table_body.php -
/path/to/codeigniter/application/views/table_footer.php
-
-
创建控制器文件,
table.php,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Table extends CI_Controller { function __construct() { parent::__construct(); $this->load->library('table'); } public function index() { $tmpl = array ( 'table_open' => '<table border="0" cellpadding="4" cellspacing="0" id="example">', 'heading_row_start' => '<tr>', 'heading_row_end' => '</tr>', 'heading_cell_start' => '<th>', 'heading_cell_end' => '</th>', 'row_start' => '<tr>', 'row_end' => '</tr>', 'cell_start' => '<td>', 'cell_end' => '</td>', 'row_alt_start' => '<tr>', 'row_alt_end' => '</tr>', 'cell_alt_start' => '<td>', 'cell_alt_end' => '</td>', 'table_close' => '</table>' ); $this->table->set_template($tmpl); $this->table->set_heading(array('ID', 'First Name', 'Last Name')); $this->table->add_row(array('1', 'Rob', 'Foster')); $this->table->add_row(array('2', 'Lucy', 'Welsh')); $this->table->add_row(array('3', 'George', 'Foster')); $this->table->add_row(array('4', 'Jackie', 'Foster')); $this->table->add_row(array('5', 'Antony', 'Welsh')); $this->table->add_row(array('6', 'Rowena', 'Welsh')); $this->table->add_row(array('7', 'Peter', 'Foster')); $this->table->add_row(array('8', 'Jenny', 'Foster')); $this->table->add_row(array('9', 'Oliver', 'Welsh')); $this->table->add_row(array('10', 'Felicity', 'Foster')); $this->table->add_row(array('11', 'Harrison', 'Foster')); $this->table->add_row(array('12', 'Mia', 'The Cat')); $data['table'] = $this->table->generate(); $this->load->view('tables/table_header'); $this->load->view('tables/table_body',$data); $this->load->view('tables/table_footer'); } } -
创建视图文件,
table_header.php,并将以下代码添加到其中:<html> <head> <style type="text/css" title="currentStyle"> @import "<?php echo $this->config->item('base_url') ; ?>application/views/DataTables-1.9.4/media/css/demo_page.css"; @import "<?php echo $this->config->item('base_url') ; ?>application/views/DataTables-1.9.4/media/css/jquery.dataTables.css"; </style> <script type="text/javascript" language="javascript" src="img/jquery.js"></script> <script type="text/javascript" language="javascript" src="img/jquery.dataTables.js"></script> <script type="text/javascript" charset="utf-8"> $(document).ready(function() { $('#example').dataTable(); } ); </script> </head> <body>看一下
<script>标签:<script type="text/javascript" charset="utf-8"> $(document).ready(function() { $('#example').dataTable(); } ); </script>#example参数是表格的 ID(在以下工作原理...部分中详细说明)。确保<script>和表格标记中的example值相同。 -
创建视图文件,
table_body.php,并将以下代码添加到其中:<?php echo $table ; ?> -
创建控制器文件,
table_footer.php,并将以下代码添加到其中:</body> </html>
工作原理...
表格控制器中的构造函数加载了 CodeIgniter 的表格库:
function __construct() {
parent::__construct();
$this->load->library('table');
}
public function index()函数被调用。然后我们定义我们想要的 HTML 表格标记的样式。这是你可以放置任何特定 CSS 标记的地方,使用它可以样式化表格的元素:
$tmpl = array (
'table_open' => '<table border="0" cellpadding="4" cellspacing="0" id="example">',
'heading_row_start' => '<tr>',
'heading_row_end' => '</tr>',
'heading_cell_start' => '<th>',
'heading_cell_end' => '</th>',
'row_start' => '<tr>',
'row_end' => '</tr>',
'cell_start' => '<td>',
'cell_end' => '</td>',
'row_alt_start' => '<tr>',
'row_alt_end' => '</tr>',
'cell_alt_start' => '<td>',
'cell_alt_end' => '</td>',
'table_close' => '</table>'
);
更仔细地看看$tmpl数组中的table_open元素。查找前面代码中突出显示的项目。id="example"项目被 DataTable(在table_header.php文件的<script>标签中)用来应用其 CSS 和功能。当然,你可以将其命名为任何你喜欢的名字,但请确保在 JavaScript 中反映这一变化。
然后我们调用$this->table->set_template()来应用 HTML 表格标记:
$this->table->set_template($tmpl);
然后我们设置表格标题并应用行数据。确保表格标题中的项目数与表格数据中的项目数相同:
$this->table->set_heading(array('ID', 'First Name', 'Last Name'));
$this->table->add_row(array('1', 'Rob', 'Foster'));
$this->table->add_row(array('2', 'Lucy', 'Welsh'));
$this->table->add_row(array('3', 'George', 'Foster'));
$this->table->add_row(array('4', 'Jackie', 'Foster'));
$this->table->add_row(array('5', 'Antony', 'Welsh'));
$this->table->add_row(array('6', 'Rowena', 'Welsh'));
$this->table->add_row(array('7', 'Peter', 'Foster'));
$this->table->add_row(array('8', 'Jenny', 'Foster'));
$this->table->add_row(array('9', 'Oliver', 'Welsh'));
$this->table->add_row(array('10', 'Felicity', 'Foster'));
$this->table->add_row(array('11', 'Harrison', 'Foster'));
$this->table->add_row(array('12', 'Mia', 'The Cat'));
然后我们生成表格。$this->table->generate()函数将返回一个 HTML 字符串,我们将其保存到$data['table']。
$data['table'] = $this->table->generate();
然后将$data数组传递给我们的视图文件以在浏览器中渲染:
$this->load->view('tables/table_header');
$this->load->view('tables/table_body',$data);
$this->load->view('tables/table_footer');
使用带有 DataTable 的 HTML 表格和数据库
CodeIgniter 附带了一个有用的库来处理 HTML 表格。它易于使用,并且能够处理你需要的几乎所有事情。在这个示例中,我们将使用它与 DataTable 一起创建一个可排序和可搜索的交互式 HTML 表格。它甚至有分页功能!这个示例使用数据库查询来填充表格。如果你不想要这个功能,只想创建一个简单的表格,请尝试前面的示例——使用带有 DataTable 的 HTML 表格。
准备工作
对于这个示例,你必须:
-
确保你已经从
datatables.net/download/下载了 DataTable。 -
解压下载的
.zip文件,并将文件移动到你的 Web 服务器或本地主机上的一个位置,这将使 CodeIgniter 能够访问。对于这个食谱,我已经将文件夹放在了application/views文件夹中;但如果你愿意,你可以自己选择。 -
由于我们正在使用 CodeIgniter 的 HTML 表格库来创建一个包含数据库表格数据的表格,我们首先需要创建这个表格;因此,将以下代码复制到你的数据库中:
CREATE TABLE IF NOT EXISTS `person` ( `id` int(11) NOT NULL AUTO_INCREMENT, `first_name` varchar(50) NOT NULL, `last_name` varchar(50) NOT NULL, `email` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ; INSERT INTO `person` (`id`, `first_name`, `last_name`, `email`) VALUES (1, 'Rob', 'Foster', 'rfoster@dudlydog.com'), (2, 'Lucy', 'Welsh', 'lwelsh@cocopopet.com'), (3, 'Chloe', 'Graves', 'cgraves@mia-cat.com'), (4, 'Claire', 'Strickland', 'cstrickland@an-other-domain.com');
如何做到这一点...
-
按照要求创建四个文件:
-
/path/to/codeigniter/application/controllers/table.php -
/path/to/codeigniter/application/views/table_header.php -
/path/to/codeigniter/application/views/table_body.php -
/path/to/codeigniter/application/views/table_footer.php
-
-
创建控制器文件
table.php,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Table extends CI_Controller { function __construct() { parent::__construct(); $this->load->library('table'); $this->load->database(); } public function index() { $tmpl = array ( 'table_open' => '<table border="0" cellpadding="4" cellspacing="0" id="example">', 'heading_row_start' => '<tr>', 'heading_row_end' => '</tr>', 'heading_cell_start' => '<th>', 'heading_cell_end' => '</th>', 'row_start' => '<tr>', 'row_end' => '</tr>', 'cell_start' => '<td>', 'cell_end' => '</td>', 'row_alt_start' => '<tr>', 'row_alt_end' => '</tr>', 'cell_alt_start' => '<td>', 'cell_alt_end' => '</td>', 'table_close' => '</table>' ); $this->table->set_template($tmpl); $this->table->set_heading(array('ID', 'First Name', 'Last Name', 'Email')); $query = $this->db->query("SELECT * FROM person"); $data['table'] = $this->table->generate($query); $this->load->view('tables/table_header'); $this->load->view('tables/table_body',$data); $this->load->view('tables/table_footer'); } } -
创建视图文件
table_header.php,并将以下代码添加到其中:<html> <head> <style type="text/css" title="currentStyle"> @import "<?php echo $this->config->item('base_url') ; ?>application/views/DataTables-1.9.4/media/css/demo_page.css"; @import "<?php echo $this->config->item('base_url') ; ?>application/views/DataTables-1.9.4/media/css/jquery.dataTables.css"; </style> <script type="text/javascript" language="javascript" src="img/jquery.js"></script> <script type="text/javascript" language="javascript" src="img/jquery.dataTables.js"></script> <script type="text/javascript" charset="utf-8"> $(document).ready(function() { $('#example').dataTable(); } ); </script> </head> <body>#example参数是表格的 ID(在以下How it works部分中详细说明)。确保<script>和表格标记中的example值相同。 -
创建视图文件
table_body.php,并将以下代码添加到其中:<?php echo $table ; ?> -
创建控制器文件
table_footer.php,并将以下代码添加到其中:</body> </html>
它是如何工作的...
table控制器中的构造函数加载了 CodeIgniter 的表格库:
function __construct() {
parent::__construct();
$this->load->library('table');
}
调用了public function index()函数。然后我们定义了我们的 HTML 表格标记应该如何看起来。这就是你可以放置任何用于特定 CSS 的标记的地方,使用它可以对表格的元素进行样式化:
$tmpl = array (
'table_open' => '<table border="0" cellpadding="4" cellspacing="0" id="example">',
'heading_row_start' => '<tr>',
'heading_row_end' => '</tr>',
'heading_cell_start' => '<th>',
'heading_cell_end' => '</th>',
'row_start' => '<tr>',
'row_end' => '</tr>',
'cell_start' => '<td>',
'cell_end' => '</td>',
'row_alt_start' => '<tr>',
'row_alt_end' => '</tr>',
'cell_alt_start' => '<td>',
'cell_alt_end' => '</td>',
'table_close' => '</table>'
);
仔细查看$tmpl数组中的table_open元素。在前面代码中查找我突出显示的项目。id="example"项被 DataTable(在table_header.php文件的<script>标签中)用于应用其 CSS 和功能。当然,你可以将其命名为任何你喜欢的名字,但请确保在 JavaScript 代码中反映这一变化。
然后我们调用$this->table->set_template()来应用 HTML 表格标记:
$this->table->set_template($tmpl);
然后我们设置表格标题,并从数据库查询中应用我们的行数据。确保表格标题中的项目数与表格数据中的项目数相同:
$this->table->set_heading(array('ID', 'First Name', 'Last Name', 'Email'));
$query = $this->db->query("SELECT * FROM person");
$data['table'] = $this->table->generate($query);
然后我们生成表格。$this->table->generate()函数将返回一个 HTML 字符串,我们将它保存到$data['table']中。
$data['table'] = $this->table->generate();
然后将$data数组传递到我们的视图文件中,以便在浏览器中渲染:
$this->load->view('tables/table_header');
$this->load->view('tables/table_body',$data);
$this->load->view('tables/table_footer');
使用 word_limiter()进行表格输出
假设你正在制作一个 CMS 或某种类型的管理员界面,并且你目前正在制作一个列出——哦,我不知道——文章的视图。假设你的数据库中有些文章,你必须列出它们。对于 CMS 的用户来说,提供文章的简要预览可能是有用的,这样他们就可以确定他们正在删除、编辑或只是查看正确的文章。预览——类似于电子邮件客户端中的预览——显示前几行,这样用户就可以确定他们正在查看的内容。CodeIgniter 提供了一个方便的函数,用于限制从文本字符串中显示的单词数量;这个函数非常适合这样的用途。在这里,我们将为我们的视图配方构建一个非常小的示例,列出一些文章并预览每篇文章的前几行。
准备工作
由于文章将存储在数据库中,我们需要构建一个表来存储它们;这将是一个包含文章的简单表。在任何应用程序中,你很可能会自己构建文章和文章管理(或你托管的内容)的数据库表,并且会有比这里示例中更多的不同设计和模式。然而,这只是一个简短的示例——请随意根据需要调整和修改配方:
在你的数据库中创建以下表:
CREATE TABLE `articles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`body` text NOT NULL,
`created_at` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
将以下条目插入到表中:
INSERT INTO `articles` (`id`, `title`, `body`, `created_at`) VALUES
(1, 'Article One', '<p>It suddenly struck me that that tiny pea, pretty and blue, was the Earth. I put up my thumb and shut one eye, and my thumb blotted out the planet Earth. I didn't feel like a giant. I felt very, very small.</p>\r\n<p>Houston, Tranquillity Base here. The Eagle has landed.</p>\r\n<p>That's one small step for [a] man, one giant leap for mankind.</p>', 1381009509),
(2, 'Article Two', '<p>Space, the final frontier. These are the voyages of the Starship Enterprise. Its five-year mission: to explore strange new worlds, to seek out new life and new civilizations, to boldly go where no man has gone before.</p>\r\n<p>We choose to go to the moon in this decade and do the other things, not because they are easy, but because they are hard, because that goal will serve to organize and measure the best of our energies and skills, because that challenge is one that we are willing to accept, one we are unwilling to postpone, and one which we intend to win.</p>\r\n<p>The sky is the limit only for those who aren't afraid to fly!</p>', 1381003509);
我们在这里显示文章(通常相当长)。因此,为了节省书籍的空间并限制你需要输入的代码量,我只包括了 2 篇简短的文章。我从 Space Ipsum 网站(spaceipsum.com/)获取了文章的文本。如果你不想输入前面代码中的所有文本(你为什么要这样做呢),你可以去 Space Ipsum 获取你自己的文章内容用于这个配方。无论如何,这都是好的。
如何操作...
我们将创建以下三个文件:
-
/path/to/codeigniter/application/controllers/下的limit.php:这将为我们运行程序,调用模型并将数据传递到limit/下的视图文件view_all.php。 -
/path/to/codeigniter/application/views/limit/view_all.php:这将显示每个文章的表格摘要,并通过word_limiter()限制正文字段的内容 -
/path/to/codeigniter/application/models/limit_model.php:这将使用 Active Record 从数据库检索文章
-
我们将创建以下三个文件:创建控制器文件,
limit.php,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Limit extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('text'); $this->load->model('Limit_model'); } public function index() { redirect('limit/view_all'); } public function view_all() { $data['query'] = $this->Limit_model->get_all(); $this->load->view('limit/view_all', $data); } } -
创建视图文件,
views/limit/view_all.php,并将以下代码添加到其中:<table> <tr> <td><b>Title</b></td> <td><b>Preview</b></td> <td><b>Created At</b></td> </tr> <?php foreach ($query->result() as $row) : ?> <tr> <td><?php echo $row->title ; ?></td> <td><?php echo word_limiter($row->body, 15) ; ?></td> <td><?php echo $row->created_at ; ?></td> </tr> <?php endforeach ; ?> </table> -
创建模型文件,
limit_model.php,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Limit_model extends CI_Model { function __construct() { parent::__construct(); } function get_all() { $query = $this->db->get('articles'); return $query; } }
工作原理...
我们首先在构造函数中加载支持资源:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->helper('text');
$this->load->model('Limit_model');
}
url和text变量将分别为redirect()和word_limit()函数提供支持,而Limit_model类将为我们提供数据库访问支持。
public function index()函数将我们重定向到公共函数view_all,该函数调用Limit_model的get_all()函数:
$data['query'] = $this->Limit_model->get_all();
$this->load->view('limit/view_all', $data);
get_all()函数将使用 Active Record 查询数据库并提取内容,然后将这些内容传递回我们的控制器,并保存在$data数组中(以下代码中突出显示),然后传递给limit/view_all视图文件:
$data['query'] = $this->Limit_model->get_all();
$this->load->view('limit/view_all', $data);
如果一切按计划进行,您应该会看到以下截图类似的内容:

您可以看到预览列的单词计数限制为 15 个单词,其中被截断并附加了三个句点(...)。
使用word_censor()函数进行输入清理
在构建应用程序的过程中,可能会有需要不仅验证不受欢迎的数据,还要检查该数据内容中是否存在任何不受欢迎的单词或短语的情况。例如,假设您正在构建一个简单的博客引擎,并且不希望人们用粗鲁的词语和短语回复您的博客文章——这是合理的。因此,您需要能够查看用户输入并过滤掉可能存在的任何不受欢迎的内容。CodeIgniter 提供了这样的功能,即word_censor()函数。让我们看看如何使用它。
准备工作
我们将把审查后的词语存储在数据库中;因此,我们将寻找可以替代实际粗鲁词语的词语,例如rude_word_number_1等。
在您的数据库中运行以下 MySQL 代码:
CREATE TABLE `censored_words` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`word` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `censored_words` (`id`, `word`) VALUES
(1, 'rude_word_number_1'),
(2, 'rude_word_number_2'),
(3, 'rude_word_number_3'),
(4, 'rude_word_number_4');
我们将把审查后的文本存储在另一个表中。由于没有更好的标题,我将其表命名为censor。以下是该表的架构代码,您可以在您的数据库中运行:
CREATE TABLE `censor` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`body` text NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
如何操作...
-
按照以下所示创建三个文件:
-
/path/to/codeigniter/application/controllers/censor.php -
/path/to/codeigniter/application/views/censor/create.php -
/path/to/codeigniter/application/models/censor_model.php
-
-
创建控制器文件
censor.php,并向其中添加以下代码:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Censor extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('text'); $this->load->model('Censor_model'); } public function index() { redirect('censor/create'); } public function create() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); $this->form_validation->set_rules('name', 'Name', 'required|min_length[1]|max_length[225]|trim'); $this->form_validation->set_rules('body', 'Body', 'required|min_length[1]|max_length[2000]|trim'); if ($this->form_validation->run() == FALSE) { $this->load->view('censor/create'); } else { $query = $this->Censor_model->get_censored_words(); $censored_words = array(); foreach ($query->result() as $row) { $censored_words[] = $row->word; } $data = array( 'name' => $this->input->post('name'), 'body' => word_censor($this->input->post('body'), $censored_words, 'BOOM') ); if ($this->Censor_model->create($data)) { echo 'Entered into DB:<br /><pre>'; var_dump($data); echo '</pre>'; } } } } -
在
/path/to/codeigniter/application/views/censor/目录下创建视图文件create.php,并向其中添加以下代码:<?php echo validation_errors() ; ?> <?php echo form_open('censor/create');?> <input type="text" name="name" size="20" value="<?php echo set_value('body') ; ?>" /> <br /> <textarea name="body"><?php echo set_value('body') ; ?></textarea> <br /> <?php echo form_submit('submit','Submit!') ; ?> <?php echo form_close() ; ?> -
在
/path/to/codeigniter/application/models/目录下创建模型文件censor_model.php,并向其中添加以下代码:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Censor_model extends CI_Model { function __construct() { parent::__construct(); } function get_censored_words() { $query = $this->db->get('censored_words'); return $query; } function create($data) { if ($this->db->insert('censor', $data)) { return true; } else { return false; } } }
如何工作...
我们Censor类中的构造函数加载我们的支持文件:助手url和text,以及我们的模型Censor_model:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->helper('text');
$this->load->model('Censor_model');
}
public function index()函数将我们重定向到public function create(),在那里我们开始表单验证。我们加载表单验证库并设置错误定界符,之后我们在视图文件create.php中设置表单元素的规则,该文件位于/path/to/codeigniter/application//views/censor/。
如果有错误,或者控制器第一次被访问,$this->form_validation->run()将等于FALSE,我们将只显示视图文件供用户填写。
然而,一旦表单已经提交(并且假设 CodeIgniter 的验证功能没有发现任何错误),我们现在想要检查输入中是否有任何不想要的词。为了做到这一点,我们首先需要获取一个不想要的词列表。因为我们把这些词存储在数据库表censored_words中,所以我们也应该获取它们。因此,我们使用censor_model函数的get_censored_words()从数据库中获取它们,并将它们添加到一个名为$censored_words的数组中:
$query = $this->Censor_model->get_censored_words();
$censored_words = array();
foreach ($query->result() as $row) {
$censored_words[] = $row->word;
}
然后,我们构建一个$data数组,准备传递给Censor_model函数的create()。看看数组的body元素,我在以下代码中已经突出显示了:
$data = array(
'name' => $this->input->post('name'),
'body' => word_censor($this->input->post('body'), $censored_words, 'BOOM')
);
在这里,我们调用文本辅助函数word_censor(),传递我们希望检查的字符串。在这种情况下,body包含提交表单的项目,以及一个包含审查词的数组作为第二个参数;第三个参数是可选的。留空第三个参数将导致 CodeIgniter 用默认字符串####替换审查词。然而,对于这个食谱,我们用字符串BOOM替换每个审查词。
然后,$data数组被写入数据库表censor(我真的必须为这个表想一个更好的名字)。
让我们试一试。在你的浏览器中运行审查控制器,你应该会看到一个类似于以下截图的输出:

在这里,我们输入了值Rob和rude_word_number_1 TOWN。当你点击提交按钮时,你应该会看到$data的var_dump数组显示:
Entered into DB:
array(2) {
["name"]=>
string(3) "Rob"
["body"]=>
string(9) "BOOM TOWN"
}
如果你看到BOOM TOWN,你就知道它已经工作了!
第五章. 管理数据输入和输出
在本章中,我们将涵盖:
-
向多个视图发送不同的数据
-
验证用户输入
-
准备用户输入
-
CodeIgniter 中的粘性表单元素
-
在表单项旁边显示错误
-
从文件系统中读取文件
-
将文件写入文件系统
-
创建和下载 ZIP 文件
-
使用 CodeIgniter 上传文件
-
创建和使用验证回调
-
使用语言类
-
使用语言类 – 动态切换语言
-
从用户处确认 cookie 接受
简介
管理数据是一个重要的主题,不仅涵盖输出数据的格式、数据库结构和访问方法,还包括安全性;关于数据和管理的任何讨论显然都会涉及安全性和保护您的系统和数据。因此,这将与安全章节有一些交叉,我建议您在阅读本章的同时也阅读那个章节。
向多个视图发送不同的数据
最近有人问我是否可以在同一浏览器页面上向不同的视图发送不同的数据,并在其自己的部分显示这些数据。幸运的是,这可以轻松完成;你可以同时向多个视图传递多个数据数组。
如果您的网页被分成几个部分,每个部分显示其自己的数据,这可能会非常有用。例如,您可能希望一个部分显示最受欢迎的文章,另一个部分显示最受欢迎的分享文章。
准备工作
由于我们将从数据库中提取数据,我们需要确保一些 config 变量被设置为允许我们这样做。打开 /path/to/codeigniter/application/config/database.php 文件并找到以下设置。然后,根据您的需求修改它们:
| 选项名称 | 有效选项 | 描述 |
|---|---|---|
$db['default']['hostname'] |
通常为 localhost | 这是数据库所在的服务器 |
$db['default']['username'] |
? | 数据库访问用户名 |
$db['default']['password'] |
? | 数据库密码 |
$db['default']['database'] |
? | 数据库名称 |
现在我们已经配置了 CodeIgniter 以连接到数据库,将以下代码复制到您的数据库中:
CREATE TABLE IF NOT EXISTS `articles` (
`article_id` int(11) NOT NULL AUTO_INCREMENT,
`article_title` varchar(255) NOT NULL,
`article_body` text NOT NULL,
`is_main` varchar(3) NOT NULL,
PRIMARY KEY (`article_id`)
);
INSERT INTO `articles` (`article_id`, `article_title`, `article_body`, `is_main`) VALUES
(1, 'Article One', 'Article One Body', 'yes'),
(2, 'Article Two', 'Article Two Body', 'no'),
(3, 'Article Three', 'Article Three Body', 'no'),
(4, 'Article Four', 'Article Four Body', 'no'),
(5, 'Article Five', 'Article Five Body', 'no'),
(6, 'Article Six', 'Article Six Body', 'no');
如何做到...
我们将创建四个文件:
-
/path/to/codeigniter/application/controllers/articles.php -
/path/to/codeigniter/application/models/content_model.php -
/path/to/codeigniter/application/views/articles/left.php -
/path/to/codeigniter/application/views/articles/right.php
-
创建
/path/to/codeigniter/application/controllers/articles.php文件并将以下代码复制到其中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Articles extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); } public function index() { // Load content model $this->load->model('content_model'); // Fetch stuff from database and store in specific arrays $left_data['main_article'] = $this->content_model->get_main_article(); $right_data['article_list'] = $this->content_model->get_article_list(); // Load views and pass data to them $this->load->view('articles/left', $left_data); $this->load->view('articles/right', $right_data); } } -
创建
/path/to/codeigniter/application/models/content_model.php文件并将以下代码复制到其中:class Content_model extends CI_Model { function get_main_article() { $this->db->where('is_main', 'yes'); return $this->db->get('articles'); } function get_article_list() { return $this->db->get('articles'); } } -
创建
/path/to/codeigniter/application/views/articles/left.php文件并将以下代码复制到其中:<?php foreach ($main_article->result() as $main_row) : ?> <h2><?php echo $main_row->article_title ; ?></h2> <p><?php echo $main_row->article_body ; ?></p> <?php endforeach ; ?> -
创建
/path/to/codeigniter/application/views/articles/right.php文件并将以下代码复制到其中:<?php foreach ($article_list->result() as $list_row) : ?> <?php echo anchor('#', $list_row->article_title) ; ?><br /> <?php endforeach ; ?>
它是如何工作的...
public function index() 通过 $this->load->model('content_model'); 加载 content_model 模型,并调用两个模型函数 get_main_article() 和 get_article_list()。get_main_article() 函数获取左侧视图的行并将其存储在 $left_data 数组中,而 get_article_list() 函数获取右侧视图的结果并将其存储在 $right_data 数组中:
// Fetch stuff from database and store in $data
$left_data['main_article'] = $this->content_model->get_main_article();
$right_data['article_list'] = $this->content_model->get_article_list();
左视图和右视图都被调用,并且将数组($left_data 和 $right_data)传递给它们:
// Load views and pass $data variable to them
$this->load->view('articles/left', $left_data);
$this->load->view('articles/right', $right_data);
然后,每个视图将遍历特定的数组,输出我们想要的字段。
验证用户输入
验证用户输入允许您设置规则,以便可以对来自用户的输入进行判断。例如,您可能希望对电子邮件字段强制执行某些条件,最明显的是检查有效的电子邮件语法,但也可以检查最小和最大长度,以及是否为必填项。CodeIgniter 甚至可以检查数据库中的重复值。在这个菜谱中,我们将构建一个控制器和视图,它们一起将允许用户输入数据,并对其与设置的规则进行验证;如果有任何错误,将反馈给用户。
准备工作
在开始之前,您需要了解一些事情。以下是一个所有可用 CodeIgniter 验证规则的表格:
| 规则 | 参数 | 描述 |
|---|---|---|
| 必填 | 否 | 它指定特定的表单元素在用户提交时是否必须具有数据。如果为空,则返回 FALSE。 |
| 匹配 | 是 | 它比较两个表单元素之间的数据,以查看它们是否匹配。如果不匹配,则返回 FALSE;如果匹配,则返回 TRUE。示例用法:
$this->form_validation->set_rules('item1', 'Item1', '');
$this->form_validation->set_rules('item2', 'Item2', 'matches[Item1]');
|
| 唯一 | 是 | 它查询数据库以查看表记录项的值是否与提交的表单元素的值匹配。如果表单元素不是唯一的,则返回 FALSE。示例用法:
$this->form_validation->set_rules('item1','Item1', 'matches[users.username]');
|
| 最小长度 | 是 | 它检查表单元素中的值的长度是否小于指定的参数。如果表单元素小于指定的值,则返回 FALSE。示例用法:
$this->form_validation->set_rules('item1', 'Item1', 'min_length[12]');
|
| 最大长度 | 是 | 它是 min_length 的反义词;它检查表单元素中的值的长度是否大于指定的参数。如果表单元素大于指定的值,则返回 FALSE。示例用法:
$this->form_validation->set_rules('item1', 'Item1', 'max_length[12]');
|
| 精确长度 | 是 | 它检查表单元素中的值的长度是否与指定的参数完全相同。如果表单元素不是指定的内容,则返回 FALSE。示例用法:
$this->form_validation->set_rules('item1', 'Item1', 'exact_length [12]');
|
| 大于 | 是 | 它检查表单元素中的值是否大于提供的参数。如果表单元素小于参数值或该值不是数字,则返回 FALSE。示例用法:
$this->form_validation->set_rules('item1', 'Item1', 'greater_than[12]');
|
| less_than | 是 | 它是 greater_than 的反义词。它检查表单元素中的值是否小于提供的参数。如果表单元素大于参数值或该值不是数字,它将返回 FALSE。示例用法:
$this->form_validation->set_rules('item1', 'Item1', 'less_than[12]');
|
| alpha | 否 | 它检查表单元素中的值是否只包含字母字符。如果表单元素值不是这样,它将返回 FALSE。 |
|---|---|---|
| alpha_numeric | 否 | 它检查表单元素中的值是否只包含字母和整数。如果表单元素值不是这样,它将返回 FALSE。 |
| alpha_dash | 否 | 它检查表单元素的值是否只包含字母数字字符、下划线或破折号。如果包含其他任何值,它将返回 FALSE。 |
| numeric | 否 | 它检查表单元素的值是否只包含数字字符。如果表单元素包含除数字以外的任何内容,它将返回 FALSE。 |
| integer | 否 | 它检查表单元素的值是否只包含整数。如果表单元素包含除整数以外的任何内容,它将返回 FALSE。 |
| decimal | 是 | 它检查表单元素的值是否包含十进制值,即用小数点(.)分隔的数字,否则它将返回 FALSE。 |
| is_natural | 否 | 它检查表单元素的值是否只包含自然数——也就是说,除了 1、2、3、4、5 等等之外的内容。如果表单元素包含除这些之外的内容,它将返回 FALSE。 |
| is_natural_no_zero | 否 | 它检查表单元素的值是否只包含大于零的自然数。如果值不是自然数或零,它将返回 FALSE。 |
| valid_email | 否 | 它检查表单元素的值是否包含由 CodeIgniter 内部的正则表达式计算出的有效电子邮件。如果表单元素不包含有效电子邮件地址,它将返回 FALSE。 |
| valid_emails | 否 | 它检查表单元素的值是否包含由 CodeIgniter 内部的正则表达式计算出的有效电子邮件地址。如果表单元素不包含有效电子邮件地址,它将返回 FALSE。 |
| valid_ip | 否 | 它检查提供的 IP 地址是否有效。 |
| valid_base64 | 否 | 如果提供的字符串包含除有效的 Base64 字符以外的任何内容,它将返回 FALSE。 |
在我们开始处理我们的食谱之前,我们需要做一些基本的配置更改。我们将修改 path/to/codeigniter/application/config/config.php 文件。
| 配置项 | 更改为值 | 描述 |
|---|---|---|
$config['global_xrsf_filtering'] |
是 | 它指定 CodeIgniter 是否始终过滤跨站脚本。出于安全考虑,建议将其设置为 TRUE。 |
$config['csrf_protection'] |
TRUE | 它指定是否使用跨站请求伪造保护。出于安全考虑,建议将其设置为 TRUE。 |
$config['csrf_token_name'] |
您自己的字符串 | 它指定如果用户关闭他的/她的浏览器,会话将变为无效。 |
$config['csrf_cookie_name'] |
您选择的另一个字符串 | 它指定了是否应在用户的计算机上对 cookie 进行加密。出于安全考虑,应将其设置为 TRUE。 |
$config['csrf_expire'] |
7200 | 它指定了以秒为单位的时间长度。 |
如何操作...
在您的 CodeIgniter 安装中创建以下文件:
-
/path/to/codeigniter/application/controllers/form.php -
/path/to/codeigniter/application/views/new_record.php
-
将以下代码添加到
path/to/codeigniter/application/controllers/form.php文件中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Form extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('security'); $this->load->library('form_validation'); } public function index() { redirect('form/submit_form'); } public function submit_form() { $this->form_validation->set_error_delimiters('', '<br />'); $this->form_validation->set_rules('first_name', 'First Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('last_name', 'Last Name', 'required|min_length[1]|max_length[125]'); $this->form_validation->set_rules('email', 'Email', 'required|min_length[1]|max_length[255]|valid_email'); $this->form_validation->set_rules('contact', 'Contact','required|min_length[1]|max_length[1]|integer|is_natural'); $this->form_validation->set_rules('answer', 'Question','required|min_length[1]|max_length[2]|integer|is_natural'); // Begin validation if ($this->form_validation->run() == FALSE) { // First load, or problem with form $this->load->view('new_record'); } else { // Validation passed, now escape the data $data = array( 'first_name' => $this->input->post('first_name'), 'last_name' => $this->input->post('last_name'), 'email' => $this->input->post('email'), 'contact' => $this->input->post('contact'), 'answer' => $this->input->post('answer') ); echo '<pre>'; var_dump($data); echo '</pre>'; } } } -
将以下代码添加到
path /to/codeigniter/application/views/new_record.php文件中:<?php echo form_open('form/submit_form') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <table border="0" > <tr> <td>First Name</td> <td><?php echo form_input(array('name' => 'first_name', 'id' => 'first_name', 'value' => '', 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Last Name</td> <td><?php echo form_input(array('name' => 'last_name', 'id' => 'last_name', 'value' => '', 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>User Email</td> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => '', 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Do you want to be contacted in the future?</td> <td><?php echo 'Yes'.form_checkbox('contact', '1', TRUE).'No'.form_checkbox('contact', '0', FALSE); ?></td> </tr> <tr> <td>What is 10 + 5?</td> <td><?php echo form_input(array('name' => 'answer', 'id' => 'answer', 'value' => '', 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('form', 'cancel'); ?> <?php echo form_close(); ?>
它是如何工作的...
CodeIgniter 首先运行 public function index(),这将立即重定向到 public function submit_form()。submit_form() 函数将使用以下行设置我们的错误定界符 $this->form_validation->set_error_delimiters('', '<br />'); 并列出每个表单元素的验证规则:
$this->form_validation->set_rules('first_name', 'First Name', 'required|min_length[1]|max_length[125]');
$this->form_validation->set_rules('last_name', 'Last Name', 'required|min_length[1]|max_length[125]');
$this->form_validation->set_rules('email', 'Email', 'required|min_length[1]|max_length[255]|valid_email');
$this->form_validation->set_rules('contact', 'Contact', 'required|min_length[1]|max_length[1]|integer|is_natural');
$this->form_validation->set_rules('answer', 'Question', 'required|min_length[1]|max_length[2]|integer|is_natural');
当表单首次运行时,$this->form_validation->run() 将返回 FALSE,因此加载视图文件 $this->load->view('new_record');,这将向用户渲染表单。用户可以随后将他的/她的详细信息输入到表单中。一旦用户点击提交按钮,CodeIgniter 再次加载公共函数 submit_form(),但这次,由于表单正在提交,验证规则将应用于提交的数据。CodeIgniter 将比较提交的数据与规则,如果数据未通过验证规则,则返回 FALSE。如果这些规则未满足,用户将在视图中看到错误消息。以下代码检查是否存在任何验证错误,如果有,则逐个显示:
<?php if (validation_errors()) : ?>
<h3>Whoops! There was an error:</h3>
<p><?php echo validation_errors(); ?></p>
<?php endif; ?>
准备用户输入
验证规则也可以用来为您准备输入。例如,您可以从输入中删除空白字符或应用 htmlspecialchars()。只要该函数默认接受一个参数作为参数,就可以使用任何 PHP 函数。
如何操作...
假设我们想要从输入的开始和结束处删除空白字符并生成输入的 md5 哈希:
$this->form_validation->set_rules('input_name', 'Input Name', 'trim|md5');
CodeIgniter 中的粘性表单元素
提供反馈对用户体验很有好处;我们在前面的部分中使用 validation_errors() 来做这件事,但保留用户数据在表单元素中也很有用,以避免在出现错误时需要重新输入所有内容。为此,我们需要使用 CodeIgniter 的 set_value() 函数。
准备工作
确保你在控制器中的 __constructor() 内加载 $this->load->helper('form');;然而,你始终可以从 /path.to/codeigniter/application/config/autoload.php 中自动加载辅助函数。
如何做到这一点...
我们将编辑 /path/to/codeigniter/application/views/new_record.php 文件。
-
修改文件以显示以下内容(粗体表示更改):
<?php echo form_open('form/submit_form') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <p><?php echo validation_errors(); ?></p> <?php endif; ?> <table border="0" > <tr> <td>First Name</td> <td><?php echo form_input(array('name' => 'first_name', 'id' => 'first_name', 'value' => set_value('first_name', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Last Name</td> <td><?php echo form_input(array('name' => 'last_name', 'id' => 'last_name', 'value' => set_value('last_name', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>User Email</td> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Do you want to be contacted in the future?</td> <td><?php echo 'Yes'.form_checkbox('contact', '1', TRUE).'No'.form_checkbox('contact', '0', FALSE); ?></td> </tr> <tr> <td>What is 10 + 5?</td> <td><?php echo form_input(array('name' => 'answer', 'id' => 'answer', 'value' => set_value('answer', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('form', 'cancel'); ?> <?php echo form_close(); ?>
它是如何工作的...
实质上,这与 验证用户输入 食谱中的功能完全相同,但现在 CodeIgniter 函数 set_value() 将用户之前提交的数据填充到表单元素值中。
在表单项旁边显示错误
在前面的例子中,我们在 HTML 页面的顶部逐个显示错误;然而,你可能希望将每个单独的错误显示在它所引用的表单元素附近。
如何做到这一点...
我们将修改 /path/to/codeigniter/application/views/new_record.php 文件。
-
修改代码以反映以下内容(粗体表示更改):
<?php echo form_open('form/submit_form') ; ?> <?php if (validation_errors()) : ?> <h3>Whoops! There was an error:</h3> <?php endif; ?> <table border="0" > <tr> <td>First Name</td> <?php if (form_error('first_name')) : ?> <?php echo form_error('first_name') ; ?> <?php endif ; ?> <td><?php echo form_input(array('name' => 'first_name', 'id' => 'first_name', 'value' => set_value('first_name', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>Last Name</td> <?php if (form_error('last_name')) : ?> <?php echo form_error('last_name') ; ?> <?php endif ; ?> <td><?php echo form_input(array('name' => 'last_name', 'id' => 'last_name', 'value' => set_value('last_name', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <td>User Email</td> <?php if (form_error('email')) : ?> <?php echo form_error('email') ; ?> <?php endif ; ?> <td><?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => set_value('email', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> <tr> <?php if (form_error('contact')) : ?> <?php echo form_error('contact') ; ?> <?php endif ; ?> <td>Do you want to be contacted in the future?</td> <td><?php echo 'Yes'.form_checkbox('contact', '1', TRUE).'No'.form_checkbox('contact', '0', FALSE); ?></td> </tr> <tr> <td>What is 10 + 5?</td> <td><?php echo form_input(array('name' => 'answer', 'id' => 'answer', 'value' => set_value('answer', ''), 'maxlength' => '100', 'size' => '50', 'style' => 'width:100%')); ?></td> </tr> </table> <?php echo form_submit('submit', 'Submit'); ?> or <?php echo anchor('form', 'cancel'); ?> <?php echo form_close(); ?>
它是如何工作的...
实质上,这与前面的食谱中的验证功能完全相同;唯一的区别是我们显示错误的方式。我们已经移除了 <p><?php echo validation_errors(); ?></p> 这一行,因为我们不是逐个列出错误。我们添加了 CodeIgniter 的 form_error() 语句,传递给它 HTML 表单元素的名称,这样如果 CodeIgniter 的验证类发现提交的表单数据不符合分配给它的验证规则,错误将显示在表单元素上方。
从文件系统中读取文件
尽管你可能会在数据库中编写和读取数据,但你肯定会接触到将内容写入磁盘和从存储在其上的文件中读取的需求。CodeIgniter 可以支持多种与文件交互的方法。
准备工作
这里没有配置选项可以更改,但请确保你在控制器构造函数中加载文件辅助函数(以及 url 辅助函数):
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->helper('file');
}
如何做到这一点...
我们将读取磁盘上的文件并将它们的详细信息显示给视图。首先,我们将创建两个文件:
-
/path/to/codeigniter/application/controllers/file.php -
/path/to/codeigniter/application/views/file/view_file.php
-
将以下代码添加到
/path/to/codeigniter/application/controllers/file.php:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class File extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('file'); } public function index() { redirect('file/view_all_files'); } public function view_all_files() { $data['dir'] = '/full/path/to/read'; $data['files'] = get_dir_file_info($data['dir']); $this->load->view('files/view_file', $data); } } -
将以下代码添加到
/path/to/codeigniter/application/views/files/view_file.php:<html> <head> <title>Viewing Files</title> </head> <body> <?php echo anchor('file/create_file', 'Create File'); ?> <?php echo anchor('file/read_file', 'Read File'); ?> <?php echo anchor('file/view_all_files', 'View Files'); ?> <table border="1"> <tr> <td><b>Filename</b></td> <td><b>Size</b></td> <td><b>Created</b></td> <td colspan="3">Actions</td> </tr> <?php foreach ($files as $file) : ?> <tr> <td> <?php if (is_dir($file['server_path'])) : ?> <b><?php echo $file['name']; ?></b> <?php else : ?> <?php echo $file['name']; ?> <?php endif; ?> </td> <td> <?php echo $file['size']; ?> </td> <td> <?php echo date("d/m/Y H:i:s", $file['date']); ?> </td> <td> <?php echo anchor('file/edit_file/' . $file['name'], 'Edit'); ?> <?php echo anchor('file/delete_file/' . $file['name'], 'Delete'); ?> <?php echo anchor('file/view_file/' . $file['name'], 'View'); ?> </td> </tr> <?php endforeach; ?> </table> </body> </html>
它是如何工作的...
这一部分的业务逻辑是控制器中的 function view_all_files() 函数。我们做了三件事。首先,是设置我们希望读取的目标目录,使用 $data['dir'] = '/full/path/to/read'; 这行代码,显然将 '/full/path/to/read' 替换为实际路径。
然后,我们将 $data['dir'] 传递给 CodeIgniter 函数,它为我们做繁重的工作,get_dir_file_info() 为目标目录中的每个项目返回一个数组。我们将其存储在 $data['files'] 中。
$this->load->view('files/view_file', $data); 调用 HTML 模板,将其文件数组传递给它,然后它反过来遍历 $files 数组,输出到 HTML 表格中。
我们还使用 PHP 函数 is_dir() 来测试一个项目是否是目录;如果是,我们在 HTML 代码中将其加粗——没有其他原因,只是知道你在看什么是个好习惯。
将这部分功能移动到库或辅助函数中是个不错的想法,这样在必要时可以更容易地被应用程序的其他部分共享。
将文件写入文件系统
如果你正在从磁盘读取(如我们之前所见),你可能希望将数据写入磁盘。现在,我们将查看创建几种类型的文件并将它们写入磁盘上的位置。
如何操作...
我们将修改以下文件:
/path/to/codeigniter/application/controllers/write_file.php
修改 /path/to/codeigniter/application/controllers/file/file.php 以反映以下内容:
public function write() {
// Set data
$data_to_write = 'This is text which will be written to the file';
// Define path for file
$path = "/path/to/write/to/with/filename.extension";
if (!write_file($path, $data_to_write)) { // Error
echo 'Error writing to file: ' . $path;
} else { // Everything worked
echo 'Data written to '. $path;
}
}
它是如何工作的...
public function write() 函数将 $data_to_write 变量设置为一个字符串;然而,这可以被修改为接受用户输入、数据库结果等。$path 数组也被定义,这应该是带有文件扩展名的完整路径。目标目录应该有足够的权限允许 CodeIgniter 写入。然后,我们测试 CodeIgniter 函数 write_file() 的返回结果。如果有错误,我们显示一条简短的消息;然而,你可以修改它,也许报告到错误日志。如果成功,它将显示一条成功消息;同样,这也可以被修改为其他行为。
创建和下载 ZIP 文件
你可能希望从你的应用程序中生成 ZIP 文件夹并强制用户下载;例如,如果你有一组文件,如新闻包,你希望它们保持在一起,或者一组 CSV 文件。将它们保存到 ZIP 文件中并允许下载是做这件事的一个很好的方法。
如何操作...
我们将创建一个新文件:
/path/to/codeigniter/application/controllers/zip.php
将以下代码复制到其中:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Zip extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->helper('form');
$this->load->helper('url');
$this->load->library('zip');
}
public function index() {
redirect('zip/zipme');
}
public function zipme() {
$file_name = '/path/to/zip/tozip.txt';
// Create some data for the file
$data = 'This is a string of text which we will use to write to the file in the variable $file_name';
// Set the time (to be used as ZIP filename)
$date = date("d-m-Y", time());
// Save some data to the ZIP archive
$this->zip->add_data($file_name, $data);
// Create the ZIP archive on your server - make sure this path is
// outside of your web root
$this->zip->archive('/path/in/zip/'.$date.'.zip');
// Download the ZIP archive
$this->zip->download($date.'.zip');
// Clear the cached ZIP archive
$this->zip->clear_data();
}
}
它是如何工作的...
这实际上非常简单,我们首先声明一个 $file_name。这是一个字符串,你会注意到文件名还包含一个文件夹名 my_zipped_files_folder——你不必包含文件夹,但如果你这样做,CodeIgniter 将会自动在 ZIP 存档中创建一个文件夹。
然后,我们创建一些数据——在这种情况下,它被写成一个文本字符串;然而,它可以从数据库中轻松输出。例如,我们可以将 $data 行更改为:
$data = '';
foreach ($database_result->result() as $row) {
$data .= $row->item_1;
$data .= $row->item_2;
$data .= $row->item_3;
}
在我们创建 $data 之后,我们接着创建日期,我们将使用该日期作为 ZIP 文件名。行 $this->zip->add_data($file_name, $data); 将我们之前创建的文件名和数据作为参数,并在 ZIP 文件内创建一个文件,并用 $data 中的字符串填充它。$this->zip->archive('/path/to/your/zip/folder/'.$date.'.zip'); 将使用 $date 作为 ZIP 文件名将 ZIP 文件写入磁盘。$this->zip->download($date.'.zip'); 将强制 ZIP 文件在客户端浏览器中打开,而 $this->zip->clear_data(); 将清除 ZIP 文件缓存。
使用 CodeIgniter 上传文件
CodeIgniter 提供了非常好的文件上传支持,这可以减少编写上传函数的许多麻烦。
准备工作
您应该注意一些设置,您可能需要根据您的环境进行更改。首先,确保您使用以下方式加载上传库:
$this->load->library('upload');
以下是在您使用的控制器(如以下 Fileupload 控制器)中的 $config 数组中应放置的设置表:
| Setting | Default | Change to | Description |
|---|---|---|---|
upload_path |
None | None | 它指定了上传文件应放入的文件夹路径。请确保您已设置正确的权限以启用 CodeIgniter 向其写入。 |
| allowed_types | None | None | 它指定了允许上传的 MIME 类型。这很有用,因为它允许您将上传的文件类型列入白名单;也就是说,它允许您仅定义允许的类型。您应该使用管道(|)分隔每个类型。例如:
$config['allowed_types'] ="jpg|gif|bmp|png";
|
file_name |
None | Desired file name | 如果设置了此值,CodeIgniter 将在上传时尝试将文件重命名为此值。 |
|---|---|---|---|
overwrite |
FALSE | TRUE/FALSE | CodeIgniter 会检查上传目标文件夹中是否存在具有匹配文件名的文件。如果设置为 TRUE,则该文件将被覆盖;如果设置为 FALSE,则会在文件名后附加一个数字。 |
max_size |
0 | None | 它指定了文件允许的最大大小(以千字节为单位)。将其设置为零将告诉 CodeIgniter 没有限制。 |
max_width |
0 | None | 它指定了以像素为单位的最大宽度。将其设置为零将告诉 CodeIgniter 没有限制。 |
max_height |
0 | None | 它指定了以像素为单位的最大高度。将其设置为零将告诉 CodeIgniter 没有限制。 |
max_filename |
0 | None | 它指定了上传文件的文件名最大字符长度。将其设置为零将告诉 CodeIgniter 没有限制。 |
encrypt_name |
FALSE | TRUE/FALSE | 它告诉 CodeIgniter 您希望在上传时加密图像的文件名。 |
remove_spaces |
TRUE | TRUE/FALSE | 它指定了您是否希望在上传时从文件名中删除空格。 |
如何操作...
我们将创建两个文件:
-
/path/to/codeigniter/application/controllers/fileupload.php -
/path/to/codeigniter/application/views/upload/upload.php
-
将以下代码添加到文件
/path/to/codeigniter/application/controllers/fileupload.php中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Fileupload extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); } function index() { $this->load->view('upload/upload_form'); } function upload() { $config['upload_path'] = '/path/to/upload/dir/'; $config['allowed_types'] = 'gif|jpg|png'; $config['max_size'] = '100'; $this->load->library('upload', $config); if (!$this->upload->do_upload()) { // Upload error, display form & errors $data['error'] = $this->upload->display_errors(); $this->load->view('upload/upload_form', $data); } else { // Success, display success message $data['upload_data'] = $this->upload->data(); $data['success'] = TRUE; $this->load->view('upload/upload_success', $data); } } } -
将以下代码添加到文件
/path/to/codeigniter/application/views/upload.php中:<html> <body> <?php if (isset($error)) : ?> <?php echo $error;?> <?php endif ; ?> <?php echo form_open_multipart('fileupload/upload');?> <input type="file" name="userfile" size="20" /><br /> <input type="submit" value="Upload File!" /> </form> <?php if (isset($success)) : ?> <h2>Success</h2> <p>The file was successfully uploaded, here's some information about the file:</p> <ul> <?php foreach ($upload_data as $key => $value):?> <li><?php echo $key . " : " . $value ;?></li> <?php endforeach; ?> </ul> <?php endif ; ?> </body> </html>
它是如何工作的...
这很幸运地非常简单;当Fileupload运行时,function index()将redirect()到function upload(),然后function upload()将加载 HTML 上传表单。一旦用户提交了该表单,function upload()将再次运行并尝试使用 CodeIgniter 函数do_upload()上传文件。do_upload()函数将执行上传文件的任务,并为写入文件系统做准备,例如检查文件大小、文件类型等是否与你的设置匹配,确保上传目标目录存在且可写,最后使用 PHP 函数move_uploaded_file()来完成上传文件的任务。
if语句捕获了这个结果;如果返回TRUE,文件上传成功,并显示成功表单,$data数组被填充了 CodeIgniter 函数$this->upload->data()的输出。
然而,如果do_upload()返回FALSE,HTML 上传表单将再次显示,这次会向用户显示错误消息。
更多...
你第一次尝试时可能会遇到一些错误,通常是因为上传路径不正确,或者目标文件夹可能被分配了错误的权限。可能需要一点调整才能正确设置。以下是一些更常见的错误:
-
上传路径似乎不正确:这意味着你在
$config['upload_path']中输入了错误的值,请检查你是否有了正确的上传文件夹路径。确保你的路径末尾有一个尾随斜杠 /。 -
上传目标文件夹似乎不可写:这意味着你想要上传的目录没有写权限;通常可以在命令行上通过
chmod 777 -R [dir_name]来修复,其中[dir_name]是你想要上传到的目录的路径。当然,拥有权限为777的文件夹可能会给你带来麻烦,所以请确保上传文件夹位于 Web 根目录之外。
创建和使用验证回调
当你想以可能不被 CodeIgniter 验证类支持的方式验证数据时,会使用回调函数。使用回调的好处是可以通过你定义的自定义函数轻松验证提交的数据,如果有错误,错误会被传递到错误报告函数中。
如何做到这一点...
我们将修改文件:
/path/to/codeigniter/application/controllers/form.php
修改该文件以显示以下内容:
$this->form_validation->set_rules('first_name', 'First Name', 'required|min_length[1]|max_length[125]');
$this->form_validation->set_rules('last_name', 'Last Name', 'required|min_length[1]|max_length[125]');
$this->form_validation->set_rules('email', 'Email', 'required|min_length[1]|max_length[255]|valid_email|callback_email_check');
$this->form_validation->set_rules('contact', 'Contact', 'required|min_length[1]|max_length[1]|integer|is_natural');
$this->form_validation->set_rules('answer', 'Question', 'required|min_length[1]|max_length[2]|integer|
is_natural');
public function email_check($email) {
if ($email_is_unique == false) {
$this->form_validation->set_message('email_check', 'The value entered in %s already exists in the database.');
return false;
} else {
return true;
}
}
它是如何工作的...
这实际上非常简单,尤其是如果你在其他应用程序中使用过回调。当用户提交表单时,CodeIgniter 会像通常那样验证表单,除了当 CodeIgniter 到达验证用户电子邮件时,会运行函数 callback_email_check($email)。这个函数可以执行任何测试并返回 TRUE 或 FALSE,如果 FALSE 则返回一条消息。
使用语言类
CodeIgniter 最有用的功能之一是其语言类和对其的支持。它允许你存储内容并将内容设置为属于各种语言;然后可以在视图文件中的相同占位符之间切换语言以显示不同的文本。设置起来非常简单,这就是你如何操作的方法。
准备工作
关于语言文件的简要信息。你需要了解它们的命名规则。语言文件存储在 /path/to/codeigniter/application/system/language/[language_name]/。
其中 [language_name] 是你希望支持的语言的名称。例如,如果你想支持英语、法语和德语,你将创建三个文件名:
-
/path/to/codeigniter/application/language/english/en_lang.php -
/path/to/codeigniter/application/language/french/fr_lang.php -
/path/to/codeigniter/application/language/german/de_lang.php
你可以看到上面的三个文件分别命名为 'en'、'fr' 和 'de'。名称后附加的是 _lang.php;你必须将每个文件附加 _lang.php,这样 CodeIgniter 才知道它是一个语言文件。
如何操作...
因此,为了创建一个英语语言文件,你需要创建以下文件:
-
/path/to/codeigniter/application/system/language/english/en_lang.php -
/path/to/codeigniter/application/controllers/lang.php -
/path/to/codeigniter/application/views/lang/english.php
-
将以下代码添加到
/path/to/codeigniter/application/controllers/lang.php<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Lang extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('language'); $this->lang->load('en', 'english'); } public function index() { redirect('lang/submit'); } public function submit() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); // Set validation rules $this->form_validation->set_rules('email', $this->lang->line('form_email'), 'required|min_length[1]|max_length[50]|valid_email'); // Begin validation if ($this->form_validation->run() == FALSE) { $this->load->view('lang/form'); } else { echo $this->lang->line('form_confirm_email') . $this->input->post('email'); } } } -
将以下代码添加到
/path/to/codeigniter/application/views/lang/form.php<html> <body> <h2><?php echo $this->lang->line('form_title') ; ?></h2> <?php echo validation_errors() ; ?> <?php echo form_open('lang/submit') ; ?> <?php echo $this->lang->line('form_email') ; ?> <?php echo form_input(array('name' => 'email','id' => 'email','value' => '','maxlength' => '100','size' => '50','style' => 'width:10%')) ; ?> <?php echo form_submit('', $this->lang->line('form_submit_button')) ; ?> <?php echo form_close() ; ?> </body> </html> -
将以下代码添加到
/path/to/codeigniter/application/language/english/en_lang.php<?php $lang['form_title'] = "Form title in English"; $lang['form_email'] = "Email"; $lang['form_submit_button'] = "Submit"; $lang['form_confirm_email'] = "Your email is: "; ?>
它是如何工作的...
在控制器 /path/to/codeigniter/application/controllers/lang.php 的构造函数中,我们正在加载辅助函数,如表单和 URL,但我们也在做两个与语言相关的事情:加载语言辅助函数并设置要使用的语言:
$this->load->helper('language');
$this->lang->load('en', 'english');
其中 'en' 是语言,'English' 是我们存储所有与英语相关内容的文件夹。
我们正在加载语言辅助函数并声明语言文件名和要使用的语言,具体如下:
$this->lang->load('filename','language');
在这里,第一个参数是语言文件名(不带 _lang.php),所以 en_lang.php 将是 'en',fr_lang.php 将是 'fr' 等等。第二个参数是语言(在这种情况下,它是 /path/to/codeigniter/application/language/ 文件夹中的文件夹)。
一旦我们加载了语言类并定义了正确的语言和文件名,我们就可以开始从 $lang 数组中提取项目。我们从 $lang 数组中提取项目的方式是通过输出 $this->lang->line(array_element_name);,因此,要提取表单标题,我们将编写 echo $this->lang->line('form_title');
确认用户接受 cookie
现在许多州和地区要求网站询问用户是否同意该网站向他们的计算机写入 cookie。关于网站如何提供这种服务以及用户的同意构成什么存在一些争议。你可能已经注意到,最近网站向用户显示了一个请求批准的通知。所谓的默示同意是目前的观点;显示一个通知告知用户,如果他们继续使用该网站,他们将满意 cookie 的写入。
以下配方正是如此;并且会向用户显示一个通知,如果他们点击链接表示他们同意写入 cookie,则该通知会消失。
准备工作
我们需要确保一些配置变量被设置,以便我们能够读取和写入用户的计算机上的 cookie。
打开 /path/to/codeigniter/application/config/config.php 文件并做出以下更改:
| $config 数组项 | 描述 |
|---|---|
$config[‘cookie_prefix’] = “”; | 它指定了是否希望在 cookie 名称之前有一个字符;例如,$config[‘cookie_prefix’] = “thisprefix_”; 将生成一个名为 thisprefix_cookie_con 的 cookie(cookie_conf 是示例 cookie,在你的应用程序中,你将用你正在处理的 cookie 名称替换它)。 |
|
$config[‘cookie_domain’] = “”; |
它指定了服务器的域名;如果你在本地主机上进行开发,最好将此值留空,一旦你离开本地主机环境,再替换为域名和路径。 |
$config[‘cookie_path’] = /; |
它指定了 cookie 的路径——你可能会想要保持它为 /。 |
$config[‘cookie_secure’] = FALSE; |
它指定了是否希望加密 cookie 值,如果想要加密则设置为 TRUE,如果不加密则设置为 FALSE。 |
如何操作...
我们将创建两个文件:
-
/path/to/codeigniter/application/controllers/cookie_conf.php -
/path/to/codeigniter/application/views/cookie_conf/cookie_conf.php
-
创建文件
/path/to/codeigniter/application/controllers/cookie_conf.php并将以下代码复制到其中:<?php if (!defined(‘BASEPATH’)) exit(‘No direct script access allowed’); class Cookie_conf extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper(‘url’); $this->load->helper(‘cookie’); } public function index() { // If the cookie doesn’t exist, make it if ( ! $this->input->cookie(‘cookie_conf’)) { $cookie = array( ‘name’ => ‘cookie_conf’, ‘value’ => ‘cookie-conf-unconfirmed’, ‘expire’ => 7200, ‘domain’ => ‘’, ‘path’ => ‘/’, ‘prefix’ => ‘’, ‘secure’ => FALSE ); $this->input->set_cookie($cookie); } if ( $this->input->cookie(‘cookie_conf’)) { // If cookie exists // Is the cookie unconfirmed? if ($this->input->cookie(‘cookie_conf’, FALSE) == ‘cookie-conf-unconfirmed’) { $data[‘display_cookie_conf’] = TRUE; } else { $data[‘display_cookie_conf’] = FALSE; } } else { // If cookie doesn’t exist yet $data[‘display_cookie_conf’] = TRUE; } $this->load->view(‘cookie_conf/cookie_conf’, $data); } public function agree() { $cookie = array( ‘name’ => ‘cookie_conf’, ‘value’ => ‘confirmed’, ‘expire’ => 7200, ‘domain’ => ‘’, ‘path’ => ‘/’, ‘prefix’ => ‘’, ‘secure’ => FALSE ); // Set the cookie to confirmed $this->input->set_cookie($cookie); echo ‘You agree to the cookie’; } public function disagree() { echo ‘You don\’t agree to the cookie’; } } -
创建文件
/path/to/codeigniter/application/views/cookie_conf/cookie_conf.php并将以下代码复制到其中:<html> <head> <script type=”text/javascript” src=”http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js”></script> <?php if (isset($display_cookie_conf) && ($display_cookie_conf == TRUE)) : ?> <script type=”text/javascript”> $(document).ready(function() { // User has agreed $(‘#agree’).click(function(answer){ $.ajax({ type: “POST”, url: “cookie_conf/agree”, success: function(data) { // If they have agreed then remove the cookie-conf-container from their browser $(‘#cookie-conf-container’).slideUp(500); }, error: function(){alert(‘error in agree response’);} }); }); // User has disagreed $(‘#disagree’).click(function(answer) { $.ajax({ type: “POST”, url: “cookie_conf/disagree”, success: function(data){ // They’ve not approved - we can display an error if we want $(‘#response’).html(data); }, error: function(){alert(‘error in disagree response’);} }); }); }); </script> <?php endif; ?> </head> <body> <?php if (isset($display_cookie_conf) && ($display_cookie_conf == TRUE)) : ?> <span id=”cookie-conf-container”> <p>This is a message to the user regarding cookies - obviously replace it with the text appropriate to your site. </p> <span id=’agree’>Agree</span> <span id=’disagree’>Disagree</span> <div id=’response’></div> </span> <?php endif; ?> </body> </html>
如何工作...
这里发生了一些事情,但本质上非常简单。看看下面的流程图,它给出了一个相当好的概述:

我们从页面加载开始。cookie 存在吗?以下代码来自cookie_conf控制器。它检查是否存在名为cookie_conf的 cookie,如果不存在,则创建该 cookie,其值为cookie-conf-unconfirmed。
if ( ! $this->input->cookie(‘cookie_conf’)) {
$cookie = array(
‘name’ => ‘cookie_conf’,
‘value’ => ‘cookie-conf-unconfirmed’,
‘expire’ => 7200,
‘domain’ => ‘’,
‘path’ => ‘/’,
‘prefix’ => ‘’,
‘secure’ => FALSE
);
$this->input->set_cookie($cookie);
}
以下代码也来自cookie_conf。在检查 cookie 是否存在(如果不存在则创建一个)之后,控制器会查看该 cookie 的值。如果 cookie 不存在或包含值cookie-conf-unconfirmed,则$data[‘display_cookie_conf’]被设置为TRUE,否则设置为FALSE。
if ( $this->input->cookie(‘cookie_conf’)) { // If cookie exists
// Is the cookie unconfirmed?
if ($this->input->cookie(‘cookie_conf’, FALSE) == ‘cookie-conf-unconfirmed’) {
$data[‘display_cookie_conf’] = TRUE;
} else {
$data[‘display_cookie_conf’] = FALSE;
}
} else { // If cookie doesn’t exist yet
$data[‘display_cookie_conf’] = TRUE;
}
cookie_conf随后加载视图。在视图中有一些 PHP 代码,它检查$display_cookie_conf是否已设置,如果是,则查看其值。如果它是FALSE,则代码会被跳过;然而,如果它是TRUE,则显示 HTML 代码。用户有两个选择,一个是同意 cookie 策略,另一个是不同意。
如果用户不同意,你将不得不实现自己的动作来处理这个事件。前面的代码将通过输出文本‘您不同意 cookie’来响应;但在实际情况中,你必须决定你想要如何进行。
如果用户同意,cookie-conf-container将会滑动上升,并且通过 AJAX 调用Cookie_conf控制器中的public function agree(),将 cookie 的值从cookie-cong’unconfirmed’更改为‘confirmed’。
public function agree() {
$cookie = array(
‘name’ => ‘cookie_conf’,
‘value’ => ‘confirmed’,
‘expire’ => 7200,
‘domain’ => ‘’,
‘path’ => ‘/’,
‘prefix’ => ‘’,
‘secure’ => FALSE
);
// Set the cookie to confirmed
$this->input->set_cookie($cookie);
echo ‘You agree to the cookie’;
}
任何随后的访问都会让cookie_conf寻找那个 cookie,只要它存在并且包含值‘confirmed’,则‘cookie-cong-container’将不会显示。
还有更多...
我想提及一些你可能在使用 cookie 授权配方实现过程中遇到的问题。
-
本地主机和 Cookies:首先,是域属性。如果你在本地主机上进行开发,你应该留空这个值。原因是网络浏览器在域设置为本地主机时经常难以实现 cookie。为什么?因为浏览器被编程为期望域属性中至少有两个项目,即域名和顶级域名(tld),因此浏览器期望类似 domain.com 的东西——显然 localhost 看起来不像那样。所以,在本地主机上进行开发时,请留空域属性,一旦你离开本地主机环境,就替换为正确的域名和路径。
-
过期值:确保将过期值定义为整数而不是字符串;因此不要将过期值放在单引号或双引号中,你应该这样写:12345,而不是这样:‘12345’。
第六章. 与数据库一起工作
在本章中,我们将涵盖:
-
配置 CodeIgniter 数据库
-
连接到多个数据库
-
Active Record – 创建(插入)
-
Active Record – 读取(选择)
-
Active Record – 更新
-
Active Record – 删除
-
遍历数据库结果
-
使用 num_rows() 计算返回结果的数量
-
使用 count_all_results() 计算返回结果的数量
-
计算返回结果的数量
-
查询绑定
-
查找最后插入的 ID
-
查找受影响行数
-
查找最后的数据库查询
-
使用 CodeIgniter 数据库迁移
-
使用 current() 移动到当前版本
-
使用 version() 回滚/前进
-
从数据库结果生成 XML
-
从数据库结果生成 CSV
简介
几乎您构建的任何应用程序都需要数据库访问和功能,从基本的 创建、读取、更新 和 删除 (CRUD)操作到更复杂的方法。在本章中,我们将查看一些相当简单的食谱(例如,简单的 CRUD 操作),然后是一些更强大的食谱,例如连接到多个数据库、数据库缓存和生成输出文件。
一些食谱相当简单,所以我不提供所有文件供您复制(在某些情况下,这可能是不必要的);相反,许多食谱都是小块代码,您可以在需要时将其放入实际场景中。
配置 CodeIgniter 数据库
如果您已经配置了 CodeIgniter 以与数据库连接,您可以跳过这部分,因为我们将要做的只是确保我们可以连接到数据库;为此,我们将修改以下两个文件:
-
/path/to/codeigniter/application/config/database.php -
/path/to/codeigniter/application/config/autoload.php
如何做到这一点...
-
在
database.php配置文件中,查找以下行并相应修改:$db['default']['hostname'] = 'localhost'; $db['default']['username'] = 'Replace with database username'; $db['default']['password'] = 'Replace with database password'; $db['default']['database'] = 'Replace with database name';很可能您不需要更改
$db['default']['hostname']从'localhost',并将其他值(username、password和database)替换为您的环境中的特定值。 -
在
autoload.php配置文件中,查找以下行(大约在第 55 行):$autoload['libraries'] = array(); -
确保数据库正在通过将其添加到
$autoload数组中自动加载,如下所示:$autoload['libraries'] = array('database');小贴士
确保用逗号分隔您正在自动加载的每个库,例如,
$autoload['libraries'] = array('database', 'session', 'javascript')等等。
它是如何工作的...
实际上并没有太多;这只是设置配置设置,但一个有趣的观点是自动加载库。通过在自动加载配置文件中将库名放入此数组中,您就不需要在应用程序中的控制器中显式加载库了。
连接到多个数据库
有时候你可能需要你的应用程序连接到多个数据库或数据库服务器。例如,想象你管理一个在线商店,你可能希望有一个数据库来处理客户订单、账单、发票等,而另一个数据库用于存储和维护产品库存信息。CodeIgniter 可以配置为使用多个数据库实例,以下部分展示了如何操作。
准备工作
为了让 CodeIgniter 与两个或多个数据库交互,我们需要在以下 config 文件中修改一些设置:
/path/to/codeigniter/application/config/database.php
滚动到文件底部并将以下内容复制进去。请记住,用你的设置中的正确细节替换 hostname、username、password 和 database。
$db['database1']['hostname'] = '';
$db['database1']['username'] = '';
$db['database1']['password'] = '';
$db['database1']['database'] = 'database1';
$db['database1']['dbdriver'] = 'mysql';
$db['database1']['dbprefix'] = '';
$db['database1']['pconnect'] = FALSE;
$db['database1']['db_debug'] = FALSE;
$db['database1']['cache_on'] = FALSE;
$db['database1']['cachedir'] = '';
$db['database1']['char_set'] = 'utf8';
$db['database1']['dbcollat'] = 'utf8_general_ci';
$db['database1']['swap_pre'] = '';
$db['database1']['autoinit'] = TRUE;
$db['database1']['stricton'] = FALSE;
$db['database2']['hostname'] = '';
$db['database2']['username'] = '';
$db['database2']['password'] = '';
$db['database2']['database'] = 'database2';
$db['database2']['dbdriver'] = 'mysql';
$db['database2']['dbprefix'] = '';
$db['database2']['pconnect'] = FALSE;
$db['database2']['db_debug'] = FALSE;
$db['database2']['cache_on'] = FALSE;
$db['database2']['cachedir'] = '';
$db['database2']['char_set'] = 'utf8';
$db['database2']['dbcollat'] = 'utf8_general_ci';
$db['database2']['swap_pre'] = '';
$db['database2']['autoinit'] = TRUE;
$db['database2']['stricton'] = FALSE;
仔细查看粗体行。每个数据库组的前四行详细说明了你希望使用的每个数据库的标准主机、用户名、密码和数据库名。但,也要看看以下行:
$db['database1']['pconnect'] = FALSE;
$db['database2']['pconnect'] = FALSE;
数据库配置设置 'pconnect' 告诉 CodeIgniter 你是否希望有持久连接。将此值设置为 False 在每个数据库组中允许 CodeIgniter 与多个数据库通信。我们将创建两个数据库,每个数据库中有一个表。显然,你的需求可能不同,但你可以根据需要调整配方。将以下代码复制到你的数据库中:
CREATE DATABASE `database1` ;
USE `database1`;
CREATE TABLE `table1` (
`t1_id` int(11) NOT NULL AUTO_INCREMENT,
`t1_first_name` varchar(255) NOT NULL,
`t1_last_name` varchar(255) NOT NULL,
PRIMARY KEY (`t1_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `table1` (`t1_id`, `t1_first_name`, `t1_last_name`) VALUES
(1, 'Lucy', 'Welsh'),
(2, 'Rob', 'Foster');
CREATE DATABASE `database2` ;
USE `database2`;
CREATE TABLE `table2` (
`t2_id` int(11) NOT NULL AUTO_INCREMENT,
`t2_first_name` varchar(255) NOT NULL,
`t2_last_name` varchar(255) NOT NULL,
PRIMARY KEY (`t2_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `table2` (`t2_id`, `t2_first_name`, `t2_last_name`) VALUES
(1, 'Oliver', 'Welsh'),
(2, 'Chloe', 'Graves');
如何操作...
现在,我们有两个数据库可以工作,并且已经配置了 database.php 配置文件以与它们通信,因此现在我们可以依次访问每个数据库。
我们将创建控制器文件 'database1' 和 'database2',它们将位于:
-
/path/to/codeigniter/application/controllers/multi_database.php: 这是一个控制器文件;它将调用两个数据库'database1'和'database2'的模型。 -
/path/to/codeigniter/application/models/multi_database_model_db_1.php: 此模型将与第一个数据库'database1'通信。 -
/path/to/codeigniter/application/models/multi_database_model_db_2.php: 此模型将与第二个数据库'database2'通信。
以下步骤将帮助我们访问每个数据库:
-
确保
/config/database.php中的$db['users']['pconnect']设置为FALSE,并且你已经为每个数据库输入了正确的访问信息。 -
创建文件
/path/to/codeigniter/application/controllers/multi_database.php。此控制器将调用两个数据库模型并输出每个模型的结果。将以下代码添加到控制器文件multi_database.php中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Multi_database extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); } public function index() { redirect('multi_database/select'); } public function select() { $this->load->model('Multi_database_model_db_1'); $query1 = $this->Multi_database_model_db_1->select_1(); $this->load->model('Multi_database_model_db_2'); $query2 = $this->Multi_database_model_db_2->select_2(); foreach ($query1->result() as $row1) { echo $row1->t1_first_name . ' ' . $row1->t1_last_name; echo '<br />'; } foreach ($query2->result() as $row2) { echo $row2->t2_first_name . ' ' . $row2->t2_last_name; echo '<br />'; } } } -
创建模型
/path/to/codeigniter/application/models/multi_database_model_db_1php。此模型将与'database1'通信。将以下代码添加到模型中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Multi_database_model_db_1 extends CI_Model { function __construct() { parent::__construct(); } function select_1() { $DBconn1 = $this->load->database('database1', TRUE); $query1 = $DBconn1->query("SELECT * FROM `table1`"); return $query1; } } -
创建模型
/path/to/codeigniter/application/models/multi_database_model_db_2.php。此模型将与'database2'进行通信。将以下代码添加到模型中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Multi_database_model_db_2 extends CI_Model { function __construct() { parent::__construct(); } function select_2() { $DBconn2 = $this->load->database('database2', TRUE); $query2 = $DBconn2->query("SELECT * FROM `table2`"); return $query2; } }
它是如何工作的...
首先,让我们关注在文件 /path/to/codeigniter/application/config/database.php 中为我们的每个数据库定义的设置。这些数据库设置是针对我们想要连接的每个数据库特定的。我们还为我们的每个数据库设置了配置变量 'pconnect' 为 FALSE(见前面的粗体文本)。当我们通过浏览器运行 Multi_database 控制器时,控制器将加载我们的两个数据库模型,为了便于解释,命名为 'Multi_database_model_db_1' 和 'Multi_database_model_db_2'。Multi_database 控制器将随后调用每个模型中的一个函数,再次为了便于解释,命名为 select_1 和 select_2。以下代码显示了相同的内容:
$this->load->model('Multi_database_model_db_1');
$query1 = $this->Multi_database_model_db_1->select_1();
$this->load->model('Multi_database_model_db_2');
$query2 = $this->Multi_database_model_db_2->select_2();
好的!到目前为止一切顺利。这里没有什么新内容——只是调用一些数据库模型;然而,有趣的事情发生在这些模型内部。让我们看看模型 Multi_database_model_db_1 的代码:
function select_1() {
$DBconn1 = $this->load->database('database1', TRUE);
$query1 = $DBconn1->query("SELECT * FROM `table1`");
return $query1;
}
我们正在加载数据库 'database1'——这意味着,我们正在使用在 database.php 配置文件中为 'database1' 定义的设置来连接一个名为 'database1' 的数据库,并将其存储在名为 $DBconn1 的对象中:
$DBconn1 = $this->load->database('database1', TRUE);
接下来,我们使用数据库对象 $DBconn1 来运行一个查询,并将数据库结果对象存储在变量 $query1 中:
$query1 = $DBconn1->query("SELECT * FROM `table1`");
然后,我们将 $query 返回给调用控制器。Multi_database 控制器随后遍历 $query1 结果对象,边走边输出:
foreach ($query1->result() as $row1) {
echo $row1->t1_first_name . ' ' . $row1->t1_last_name;
echo '<br />';
}
活动记录 – 创建(插入)
使用 CodeIgniter 活动记录将数据插入数据库有几种方法;例如,$this->db->insert() 和 $this->db->insert_batch()。第一个一次只插入一条记录,而第二个将数据数组作为单独的行插入数据库;如果你知道需要一次插入多条记录,这可以非常有用,从而避免多次调用 insert()。
准备工作
这是支持此菜谱所需的 SQL 代码;你需要根据你的情况对其进行调整。将以下 SQL 代码复制到你的数据库中:
CREATE TABLE IF NOT EXISTS `ch6_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(50) NOT NULL,
`lastname` varchar(50) NOT NULL,
`username` varchar(20) NOT NULL,
`password` varchar(20) NOT NULL,
`created_date` int(11) NOT NULL,
`is_active` varchar(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
如何操作...
我们将创建以下两个文件(或者如果你已经创建了这些文件,则修改它们):
-
/path/to/codeigniter/application/controllers/database.php -
/path/to/codeigniter/application/models/database_model.php
以下步骤将演示如何使用 CodeIgniter 活动记录将数据插入数据库:
-
将以下代码添加到控制器
database.php中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Database extends CI_Controller { function __construct() { parent::__construct(); } public function index() { redirect('database/create'); } public function create() { $data = array( 'firstname' => 'Lucy', 'lastname' => 'Welsh', 'username' => 'lucywelsh', 'password' => 'password', 'created_date' => time(), 'is_active' => 'yes' ); $this->load->model('Database_model'); if ($this->Database_model->insert_data($data) ) { echo 'Success'; } else { echo 'Cannot insert to database'; } } public function create_batch() { $data = array( array( 'firstname' => 'Lucy', 'lastname' => 'Welsh', 'username' => 'lwelsh', 'password' => 'password', 'created_date' => time(), 'is_active' => 'yes'), array( 'firstname' => 'claire', 'lastname' => 'Strickland', 'username' => 'cstrickland', 'password' => 'password', 'created_date' => time(), 'is_active' => 'yes'), array( 'firstname' => 'Douglas', 'lastname' => 'Morrisson', 'username' => 'dmorrisson', 'password' => 'password', 'created_date' => time(), 'is_active' => 'yes') ); $this->load->model('Database_model'); if ($this->Database_model->insert_batch_data($data)) { echo 'Success'; } else { echo 'Cannot insert to database'; } } } -
将以下代码添加到模型
database_model.php中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Database_model extends CI_Model { function __construct() { parent::__construct(); } function insert_data($data) { $this->db->insert('ch6_users', $data); } function insert_batch_data($data) { $this->db->insert_batch('ch6_users',$data); } }
它是如何工作的...
这里使用了两种方法:create() 和 create_batch()。让我们逐一查看每个函数的工作原理。
公共函数 create()
create() 函数应该相当熟悉;我们正在创建一个数组(命名为 $data)并用一个用户的数据或相当于一行插入的数据填充它。然后 create() 方法将 $data 数组传递给模型函数 insert_data(),如下所示:
$this->load->model('Database_model');
$this->Database_model->insert_data($data);
模型将随后向表 ch6_users 插入一行:
function insert_data($data) {
$this->db->insert('ch6_users', $data);
}
公共函数 create_batch()
与前面的 create() 功能类似的 create_batch() 公共函数,但不是传递包含一组项目的数组,而是创建一个包含多行数据的二维数组,如下所示:
$data = array(
array(
'firstname' => 'Lucy',
'lastname' => 'Welsh',
'username' => 'lwelsh',
'password' => 'password',
'created_date' => time(),
'is_active' => 'yes'),
array(
'firstname' => 'claire',
'lastname' => 'Strickland',
'username' => 'cstrickland',
'password' => 'password',
'created_date' => time(),
'is_active' => 'yes'),
array(
'firstname' => 'Douglas',
'lastname' => 'Morrisson',
'username' => 'dmorrisson',
'password' => 'password',
'created_date' => time(),
'is_active' => 'yes')
);
然后,我们将该数组发送到新的模型函数 create_batch():
function insert_batch_data($data) {
$this->db->insert_batch('ch6_users',$data);
}
function create_batch() 函数使用 CodeIgniter 的 function insert_batch() 将每一行插入到数据库中。
Active Record – 读取(选择)
CRUD 中的 R 代表从数据库中选择数据的过程。CodeIgniter 使用 $this->db->get() 数据库函数从数据库中检索行。其用法将在以下章节中解释。
准备工作
以下是需要支持此菜谱的 SQL 代码;您需要根据您的具体情况对其进行调整。
CREATE TABLE IF NOT EXISTS `ch6_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(50) NOT NULL,
`lastname` varchar(50) NOT NULL,
`username` varchar(20) NOT NULL,
`password` varchar(20) NOT NULL,
`created_date` int(11) NOT NULL,
`is_active` varchar(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `ch6_users` (`firstname`, `lastname`, `username`, `password`, `created_date`, `is_active`) VALUES
('claire', 'Strickland', 'cstrickland', 'password', 1366114115, 'yes'),
('Douglas', 'Morrisson', 'dmorrisson', 'password', 1366114115, 'yes'),
('Jessica', 'Welsh', 'jesswelsh', 'password', 1366114115, 'yes');
如何操作...
我们将创建以下两个文件(或者如果您已经创建了这些文件,则修改它们):
-
/path/to/codeigniter/application/controllers/database.php -
/path/to/codeigniter/application/models/database_model.php
以下步骤将演示如何使用 CodeIgniter Active Record 将数据读取到数据库中:
-
将以下代码添加到文件
/path/to/codeigniter/application/controllers/database.php中:public function select_row() { $id = 1; $this->load->model('Database_model'); $result = $this->Database_model->select_row($id); echo '<pre>'; var_dump($result->result()); } -
将以下代码添加到文件
/path/to/codeigniter/application/models/database_model.php中:function select_row($id) { $this->db->where('id', $id); $query = $this->db->get('ch6_users'); return $query; }
如果您看到以下输出,则表示操作成功:
array(1) {
[0]=>
object(stdClass)#20 (7) {
["id"]=>
string(1) "1"
["firstname"]=>
string(4) "Lucy"
["lastname"]=>
string(5) "Welsh"
["username"]=>
string(6) "lwelsh"
["password"]=>
string(8) "password"
["created_date"]=>
string(10) "1366114115"
["is_active"]=>
string(3) "yes"
}
}
它是如何工作的...
在前面的控制器中,public function select_row() 将 $id 赋值为 1——然而,这也可以从 post、get、session 或其他来源完成——并加载数据库模型,如下所示将变量 $id 传递给它:
$this->load->model('Database_model');
$this->Database_model->insert_batch_data($data);
模型函数 select_row() 从表 'ch6_users' 中提取匹配的记录并将其返回给调用控制器。
Active Record – 更新
CRUD 中的 U 代表在数据库中更新数据记录的过程。CodeIgniter 使用数据库函数 $this->db->update() 更新数据库记录;本菜谱将解释如何进行。
准备工作
以下是需要支持此菜谱的 SQL 代码;您需要根据您的具体情况对其进行调整。
CREATE TABLE IF NOT EXISTS `ch6_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(50) NOT NULL,
`lastname` varchar(50) NOT NULL,
`username` varchar(20) NOT NULL,
`password` varchar(20) NOT NULL,
`created_date` int(11) NOT NULL,
`is_active` varchar(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `ch6_users` (`firstname`, `lastname`, `username`, `password`, `created_date`, `is_active`) VALUES ('Jessica', 'Welsh', 'jesswelsh', 'password', 1366114115, 'yes');
如何操作...
我们将创建以下两个文件(或者如果您已经创建了这些文件,则修改它们):
-
/path/to/codeigniter/application/controllers/database.php -
/path/to/codeigniter/application/models/database_model.php
以下步骤将演示如何使用 CodeIgniter Active Record 更新数据库中的数据:
-
将以下代码添加到文件:
/path/to/codeigniter/application/controllers/database.phppublic function update_row() { $id = 1; $data = array( 'firstname' => 'Jessica', 'lastname' => 'Welsh', 'username' => 'jesswelsh', 'password' => 'password', 'created_date' => time(), 'is_active' => 'yes' ); $this->load->model('Database_model'); $result = $this->Database_model->update_row($id, $data); redirect('database/select_row'); } Add the following code into the file: /path/to/codeigniter/application/models/database_model.php::: function update_row($id, $data) { $this->db->where('id', $id); $this->db->update('ch6_users', $data); } array(1) { [0]=> object(stdClass)#20 (7) { ["id"]=> string(1) "1" ["firstname"]=> string(7) "Jessica" ["lastname"]=> string(5) "Welsh" ["username"]=> string(9) "jesswelsh" ["password"]=> string(8) "password" ["created_date"]=> string(10) "1366117753" ["is_active"]=> string(3) "yes" } }
它是如何工作的...
在我们刚才看到的控制器中,public function update_row()将$id赋值为1——然而,这可以来自 post、get、session 或另一个来源——并将数据库模型加载进来,如下将变量$id传递给它:
$this->load->model('Database_model');
$result = $this->Database_model->update_row($id, $data);
模型函数update_row()按照以下方式更新表中的匹配记录:
function update_row($id, $data) {
$this->db->where('id', $id);
$this->db->update('ch6_users', $data);
}
ActiveRecord – 删除
CRUD 中的 D 用于在数据库表中删除数据行。CodeIgniter 使用$this->db->delete()数据库函数从数据库中删除行;它将在以下部分中使用。
准备工作
以下是需要支持此食谱的 SQL 代码;你需要根据你的情况对其进行调整:
CREATE TABLE IF NOT EXISTS `ch6_users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(50) NOT NULL,
`lastname` varchar(50) NOT NULL,
`username` varchar(20) NOT NULL,
`password` varchar(20) NOT NULL,
`created_date` int(11) NOT NULL,
`is_active` varchar(3) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `ch6_users` (`firstname`, `lastname`, `username`, `password`, `created_date`, `is_active`) VALUES ('Jessica', 'Welsh', 'jesswelsh', 'password', 1366114115, 'yes');
如何操作...
我们将创建以下两个文件(或者如果你已经创建了这些文件,则修改它们):
-
/path/to/codeigniter/application/controllers/database.php -
/path/to/codeigniter/application/models/database_model.php
以下步骤将演示如何使用 CodeIgniter Active Record 从数据库中删除数据:
-
将以下代码添加到文件
/path/to/codeigniter/application/controllers/database.php中:public function delete_row() { $id = 1; $this->load->model('Database_model'); $result = $this->Database_model->delete_row($id); redirect('database/select_row'); } -
将以下代码添加到文件
/path/to/codeigniter/application/models/database_model.php中:function delete_row($id) { $this->db->where('id', $id); $this->db->delete('ch6_users'); }
工作原理...
在前面的控制器中,public function delete_row()将$id赋值为1——然而,这可以来自 post、get、session 或另一个来源——并将数据库模型加载进来,如下将变量$id传递给它:
$this->load->model('Database_model');
$result = $this->Database_model->delete_row($id);
模型函数delete_row()从表中删除匹配的记录:
function delete_row($id) {
$this->db->where('id', $id);
$this->db->delete('ch6_users');
}
遍历数据库结果
在任何具有数据库连接的应用程序中,你可能需要显示数据库中的记录;遍历查询返回的数据行是你在编程中将要执行的最常见任务之一。CodeIgniter 使用 PHP 中的每个语句来处理遍历数据库结果。在这个食谱中,我们将一次遍历每条记录,输出相关信息。
准备工作
为了支持这个食谱,我们将创建一个数据库表并向其中写入一些数据。如果你已经有了数据,你可以跳过这个食谱;如果没有,请将以下代码复制到你的数据库中:
CREATE TABLE IF NOT EXISTS `loop_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(255) NOT NULL,
`last_name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `loop_table` (`id`, `first_name`, `last_name`) VALUES
(1, 'Lucy', 'Welsh'),
(2, 'Rob', 'Foster');
如何操作...
-
将以下代码添加或修改到你的控制器中:
public function loop_through_data() { $this->load->model('Some_model'); $data['query'] = $this->Some_model->select_data(); $this->load->view('some_view', $data); } -
将以下代码添加或修改到你的模型中:
function select_data() { $query = $this->db->get('loop_table'); return $query; } -
将以下代码添加或修改到你的视图中:
foreach ($query->result() as $row) { echo $row->first_name . ' ' . $row->last_name; echo '<br />'; }
工作原理...
首先,让我们看看 SQL 代码;如果你使用了前面的 SQL 代码,我们所做的只是创建一个非常简单的表,并用两行数据填充它。
接下来,我们调用控制器函数loop_through_data(),它加载一个模型;在这种情况下,将Some_model重命名为与你的应用程序相关的模型。我们调用模型函数select_data(),将返回的结果存储在$data数组中,或者更具体地说,在$data数组的一个部分,我们称之为'query':
$data['query'] = $this->Some_model->select_data();
模型函数select_data()从数据库表loop_table中检索所有行,并将其返回给调用控制器函数。
返回到我们的控制器,现在我们已经将数据库结果存储在$data数组中,我们可以调用view文件some_view.php——显然,您需要将其重命名为您应用程序中的其他名称——并将$data数组传递给它:
$this->load->view('some_view', $data);
view文件随后使用简单的foreach()循环遍历$query中的每个结果。让我们更仔细地看看这个foreach()循环。看看以下代码行:
foreach ($query->result() as $row) {
记得我们是如何将数据库结果存储在$data['query']中的吗?那么,我们将使用$data数组中的'query'部分,它已存储数据库结果,并将使用 CodeIgniter 函数result()来处理它。我听到你问,“result()函数做什么?”result()函数将接受一个对象或数组,并允许您遍历每一行,允许您对那一行中的单个数据项进行操作。
因此,我们使用result()将$query拆分为每一行,将那一行传递给$row(因为这是显而易见的),并允许我们执行如下操作:
echo $row->first_name . ' ' . $row->last_name;
这是在$row中显示每个人的姓名和姓氏。
使用num_rows()计算返回的结果数量
计算返回的结果数量是有用的——如果一段代码期望至少有一行,但传递了零行,则可能会出现错误。如果不处理零结果的可能性,应用程序可能会变得不可预测地不稳定,并可能向恶意用户提供有关应用程序架构的线索。确保正确处理零结果是我们在这里要关注的问题。
如何做...
-
我们将创建一个模型和控制器代码块。您可能已经在控制器、模型或视图中编写了执行以下所有或部分操作的代码——显然,您可以跳过您不需要的任何步骤。将以下代码添加或修改到您的控制器中:
$this->load->model('Some_model'); $data['query'] = $this->Some_model->some_model_function(); $this->load->view('some_view', $data); -
将以下代码添加或修改到您的模型中:
function some_model_function() { $query = $this->db->get('database_table_name'); return $query; } -
将以下代码添加或修改到您的视图中:
if ($query->num_rows() > 0) { foreach ($query->result() as $row) { echo $row->item1; echo $row->item2; } } else { echo 'No results returned'; }
它是如何工作的...
这相当常见;控制器加载所需的模型并调用该模型中的函数;该模型的结果存储在数组中。然后将其传递到视图中。正是在这里,我们会计算行数。看看加粗的行。我们使用 CodeIgniter 函数num_rows()来查看$query结果,并计算模型返回的行数。我们询问行数是否大于零。如果是,则意味着模型至少有一个结果——然后我们像通常一样遍历$query数组。然而,如果结果的数量不大于零,这意味着模型没有返回任何结果。因此,我们使用 else 语句显示一条简短的消息,说明没有返回结果。
使用count_all_results()计算返回的结果数量
计算返回的结果数量是有用的——如果代码部分期望至少有一行,而传递了零行,则可能会出现错误。如果不处理零结果的可能性,应用程序可能会变得不可预测地不稳定,并可能向恶意用户提供有关应用程序架构的线索。确保正确处理零结果是我们在这里要关注的内容。
如何操作...
-
将以下代码添加或修改到您的控制器中:
$this->load->model('Some_model'); $data['num_results'] = $this->Some_model->some_model_function(); $this->load->view('some_view', $data); -
将以下代码添加或修改到您的模型中:
function some_model_function() { $this->db->from('table'); return $num_rows = $this->db->count_all_results(); } -
将以下代码添加或修改到您的视图中:
if (isset($num_results)) { echo 'There are ' . $num_results . ' returned'; }
这与上面 num_rows() 的配方相当类似,但有一些关键的区别。我们首先调用一个控制器,该控制器加载所需的模型并在其中调用一个函数。看看加粗的代码:$this->db->count_all_results();。这将返回给定查询返回的结果数量。此代码的结果存储在数组中,并传递到视图中,我们测试变量 $num_results 是否已设置;如果是,我们输出一个简短的消息,指示结果数量。
查询绑定
绑定查询是另一个有用的安全过程;如果您在查询中使用绑定,CodeIgniter 会自动转义值,您无需手动进行。
准备工作
将以下 SQL 代码复制到您的数据库中:
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_first_name` varchar(125) NOT NULL,
`user_last_name` varchar(125) NOT NULL,
`user_email` varchar(255) NOT NULL,
`user_created_date` int(11) NOT NULL COMMENT 'unix timestamp',
`user_is_active` varchar(3) NOT NULL COMMENT 'yes or no',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `users` (`user_first_name`, `user_last_name`, `user_email`, `user_created_date`, `user_is_active`) VALUES
('Chloe', 'Graves', 'cgraves@domain.com', 1366114115, 'yes'),
('Mark', 'Brookes', 'mbrookes@domain.com', 1366114115, 'yes');
如何操作...
在您的任何模型中,调整您的查询代码以反映以下内容:
$query = "SELECT * FROM users WHERE users.is_active = ? AND users.created_date > ?";
$this->db->query($query, 'yes', '1366114114');
它是如何工作的
以名为 users 的表为例,查询将尝试获取所有 users.is_active 等于 Y 且 users.created_date 大于 1359706809 (02/01/2013 – 03:20) 的记录。但,您会注意到查询中有两个问号,每个问号代表 $data array 中的一个项目。$data 数组中的值按顺序传递到查询中,通过这一行 $this->db->query($query, $data);。因此,查询中的第一个问号将被数组中的第一个项目替换,查询中的第二个问号将被数组中的第二个项目替换,依此类推。
查找最后插入的 ID
返回最后插入行的主键在您可能希望将数据写入多个表且数据可能通过键相关联的情况下非常有用。CodeIgniter 提供了返回最后插入键的支持。
如何操作...
-
将以下代码添加或修改到模型中:
function insert($data) { if ($this->db->insert($data, 'table_name')) { return $this->db->last_id(); } else { return false; } }
它是如何工作的...
看看加粗的行。我们测试 $this->db->insert($data); 返回的值,如果成功则返回 true,如果出错则返回 false。如果返回值是 true,我们获取此连接最后插入记录的主键;此值与 return $this->db->insert_id(); 一起从模型返回到调用函数的代码。如果数据库插入失败,它将返回 false。您可以轻松地调整上述配方;只需将加粗的行放入您的模型中。
查找受影响行数
查找受影响行数可以在几种方式下有用——也许你想要更新一些记录,并且只有当一定数量的记录被更新时才继续,或者也许你只是想显示被查询删除或更新的行数。
如何操作...
-
将以下代码添加或修改到你的模型中:
function update($id, $data) { $this->db->where('id', $id); if ($data->db->update($data, 'table_name')) { return $this->db->affected_rows(); } else { return false; } }
它是如何工作的...
模型function update()接受两个参数:一个$data数组和我们希望更新的数据库行的$id数组。
接下来,我们测试$this->db->update($data);返回的值,如果成功将返回 true,如果出错将返回 false。如果返回值是 true,我们将使用以下行获取更新的受影响行数:
return $this->db->affected_rows();
如果更新没有发生,返回值将是 false。
查找最后数据库查询
有时,了解对数据库执行的最后查询是有用的,无论是为了调试目的,还是出于你希望有数据库每次交互的审计记录的原因——你会惊讶于你需要这样做多少次。CodeIgniter 提供了一个非常实用的函数,你可以用它来记录 CodeIgniter 最近发送到数据库的查询。
如何操作...
-
将以下行代码添加或修改到你的控制器或模型中:
$this->db->last_query();
它是如何工作的...
简单来说,这个函数将返回发送到数据库的最后查询;你可以将它放在控制器或模型中(如果你愿意,甚至可以放在视图中,但最好将其保留在应用程序的逻辑部分而不是视图中)。它将以字符串的形式返回你可以用作审计的查询;例如,考虑以下代码行:
log_message('level', $this->db->last_query())
上一行代码将最后查询写入到你的日志文件中,其中'level'表示消息类型。我们将在第九章中介绍一些错误报告和日志记录配方,扩展核心。
使用 CodeIgniter 数据库迁移
假设你在一个由其他开发者组成的团队中工作,每个人都忙于工作,对代码和数据库结构进行更改。跟上所有这些数据库更改可能成为一个挑战,尤其是当许多人几乎在项目的同一区域工作时。
CodeIgniter 迁移为你提供了安装(或回滚)可能支持代码更改的数据库结构更改的选项。例如,如果你正在对用户注册脚本进行编码更改——这个更改需要在数据库表中添加一个列;你可以在你的版本控制提交中包含一个 CodeIgniter 数据库迁移脚本(假设你在使用版本控制)——其他开发者现在将知道,为了你的代码更改能够工作,他们必须运行迁移,这将修改他们的数据库。
迁移还允许你回滚更改。这不应与数据库事务回滚的概念混淆;将使用迁移回滚想象成卸载之前安装的更改。
准备工作
在进行此操作之前,我们需要更改一些配置设置,因此打开 /path/to/codeigniter/application/config/migration.php 并找到以下选项:
| Preference | Default Value | Options | Description |
|---|---|---|---|
migration_enabled |
FALSE | TRUE/FALSE | 指定你是否希望启用迁移;TRUE 是启用,FALSE 是禁用。 |
migration_version |
0 | None | 指定数据库当前使用的迁移版本,或者更确切地说,是你希望工作的最合适的迁移版本。我们将在本章后面详细讨论这个问题。通过使用 current(),我们将安装 'migration_version' 中设置的最新值。 |
migration_path |
APPPATH.'migrations/' | None | 指定存储迁移文件的文件夹路径。迁移文件是 PHP 脚本,其中包含定义必要数据库更改的查询。请确保已将 migrations 文件夹设置为可写。 |
确保使用以下行在你的控制器中加载迁移库:
$this->load->library('migration');
在这个菜谱中,我们将创建一个简单的用户表,并使用迁移库添加和删除该表中的一个列。将以下 SQL 语句输入到你的数据库中:
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_first_name` varchar(125) NOT NULL,
`user_last_name` varchar(125) NOT NULL,
`user_email` varchar(255) NOT NULL,
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
如何操作...
首先,所有你的数据库迁移文件都应该放在 /path/to/codeigniter/application/migrations/ 路径下的 migrations 文件夹中。
如果文件夹尚不存在,你需要在 /path/to/codeigniter/application/ 文件夹中创建它——确保给它写入权限。
-
我们将要创建以下两个文件:
-
/path/to/codeigniter/application/migrations/001_add_icon.php -
/path/to/codeigniter/application/controllers/migrate.php小贴士
注意文件名
001_add_icon.php。第一部分(001)是迁移号;每次添加新的迁移文件时,它都会递增。第二部分(add_icon)是对迁移文件目的的描述性指示。将以下代码添加到文件001_add_icon.php中。此迁移文件定义了要运行的查询以实现迁移更改或从该更改中回滚。<?php defined('BASEPATH') OR exit('No direct script access allowed'); class Migration_Add_icon extends CI_Migration { public function up() { $this->db->query("ALTER TABLE `users` ADD COLUMN `user_icon` TEXT NULL AFTER `user_email`;"); } public function down() { $this->db->query("ALTER TABLE `users` DROP COLUMN `user_icon`;"); } }
-
-
将以下代码添加到
/path/to/codeigniter/application/controllers/migrate.php;迁移控制器为我们提供了访问 CodeIgniter 迁移函数的权限。<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Migrate extends CI_Controller { function __construct() { parent::__construct(); if ( ! $this->input->is_cli_request() ) { echo 'Only access via command line.'; exit; } $this->load->library('migration'); } public function index() { echo 'Config: ' . $config['migration_version']; } public function current() { if ( ! $this->migration->current()) { show_error($this->migration->error_string()); } } public function latest() { if ( ! $this->migration->latest()) { show_error($this->migration->error_string()); } } public function version() { if ( $this->uri->segment(3) == '') { echo 'You must specify a migration version number'; } else { if ( ! $this->migration->version($this->uri->segment(3)) ) { show_error($this->migration->error_string()); } } } }
好的,到目前为止我们做了什么?我们已经配置了 CodeIgniter 中的迁移,我们创建了第一个迁移文件(注意正确命名),并且我们有两个文件:控制器 migrate.php 和迁移文件 001_add_icon.php。
在迁移文件001_add_icon.php中,有 222 个函数;在这些函数中,up()和down()是定义与你的代码更改一起的 SQL 语句的函数。down()函数是定义如果有人(可能是另一个开发者)希望撤销你可能做出的代码更改时,将定义用于删除更改的 SQL 语句的地方;因此,它支持 SQL。
在控制器migrate.php中,我们创建了一些供我们操作的迁移函数,例如current()和latest()。以下两个食谱将向你展示这些迁移的一些基本用法。
使用 current()移动到当前版本
要简单地更改你的数据库,使其与$config['migration_version']中的版本号相对应,你应该使用current()函数。
准备工作
确保你已经遵循了前面的食谱,使用 CodeIgniter 数据库迁移。
如何操作...
-
使用你的命令行(终端应用程序),导航到你的 CodeIgniter 安装根目录(
index.php文件所在的位置)并输入以下内容:php index.php migrate current
它是如何工作的...
考虑以下命令行:
php index.php migrate current
我们首先应该牢记的是迁移控制器中的构造函数。构造函数正在查看迁移控制器是如何被访问的;如果它不是通过命令行访问的,它将拒绝访问迁移控制器——这是一个有用的安全措施。
通过输入我们刚才看到的命令,你会运行public function current()。该函数不接受任何参数。CodeIgniter 会检查迁移文件夹中与配置文件/path/to/codeigniter/application/config/migration.php中设置的$config['migration_version']值对应的文件编号的文件。
使用 version()回滚/前进版本
你可能希望故意通过指向特定的迁移编号来更改数据库。这可以通过在 CodeIgniter 中使用version()函数来实现。
准备工作
确保你已经遵循了前面的食谱,使用 CodeIgniter 数据库迁移。
如何操作...
-
使用你的命令行(终端应用程序),导航到你的 CodeIgniter 安装根目录(
index.php文件所在的位置)并输入以下内容:php index.php migrate version 1
它是如何工作的...
考虑以下命令行:
php index.php migrate version number
number被突出显示,因为它指定了要移动到的迁移文件编号,即1、2、3等等。
我们首先应该牢记的是迁移控制器中的构造函数。构造函数正在查看迁移控制器是如何被访问的;如果它不是通过命令行访问的,它将拒绝访问迁移控制器——这是一个有用的安全措施。
通过输入前面的命令,您将运行 public function version(),传递给它第三个参数(其值为 1)。CodeIgniter 将检查迁移文件夹,寻找与第三个参数(1)对应的文件编号,令人惊讶的是,这个编号正是我们创建的迁移文件编号——谁知道呢?
CodeIgniter 将加载迁移文件 001_add_icon.php 并立即运行 public function up(),这将向数据库表 'users' 添加 user_icon 列。
通过在命令行中输入以下内容,我们可以撤销创建 user_icon 列的操作:
php index.php migrate version 0
然后,CodeIgniter 将在迁移文件中运行公共函数 down(),这将删除 user_icon 列。
从数据库结果生成 XML
从数据库生成 XML 可能以多种方式有用,也许您希望使用 SOAP 请求通过网络发送查询数据,或者也许您正在使用它来构建一些用于网络服务的数据。无论您的目的如何,这就是如何操作——我们还将探讨一些实际应用——例如,我们将从数据库查询生成 XML 输出。
准备工作
首先,我们需要创建一个表并输入一些示例数据,这样您将看到一些以 CSV 格式显示的数据,所以考虑到这一点,将以下代码复制到 SQL:
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_first_name` varchar(125) NOT NULL,
`user_last_name` varchar(125) NOT NULL,
`user_email` varchar(255) NOT NULL,
`user_created_date` int(11) NOT NULL COMMENT 'unix timestamp',
`user_is_active` varchar(3) NOT NULL COMMENT 'yes or no',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `users` (`user_first_name`, `user_last_name`, `user_email`, `user_created_date`, `user_is_active`) VALUES
('Chloe', 'Graves', 'cgraves@domain.com', 1366114115, 'yes'),
('Mark', 'Brookes', 'mbrookes@domain.com', 1366114115, 'yes');
如何操作...
我们将创建以下文件:
/path/to/codeigniter/application/controllers/export.php
-
创建控制器
export.php。此控制器将加载 CodeIgniter 的dbutil(数据库工具)类,该类将提供对各种数据库特定操作的支持并生成 XML。将以下代码添加到您的export.php控制器中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Export extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->dbutil(); } public function index() { redirect('export/xml'); } public function xml() { $config = array ('root' => 'root', 'element' => 'element', 'newline' => "\n", 'tab' => "\t" ); $query = $this->db->query("SELECT * FROM users"); echo $this->dbutil->xml_from_result($query, $config); } }
它是如何工作的...
好的,看看加粗的那一行——我们在控制器构造函数中加载数据库工具类。这个工具类包含一些用于处理数据库的出色函数。我们使用它来提供对 xml_from_result() 函数的访问。
export.php 控制器的 index() 函数将重定向我们到 public function xml(),该函数执行数据库查询。当然,您可以使用任何数据源,但我们在这里调用数据库并将结果存储在数组 $query 中。这被传递给 CodeIgniter 函数 xml_from_result()。xml_from_result() 函数接受以下两个参数:
-
$query:这是 XML 的数据;在这种情况下,我们数据库查询的输出。 -
$config:这是配置参数;在这种情况下,XML 格式化选项。
我们随后将 xml_from_result() 的结果输出到屏幕上——您可以通过在浏览器中查看页面源代码来查看结果。您不必输出它;如果您需要将 XML 用于其他目的,您可以将其存储在变量中。
请确保将数据库查询单独放入其自己的模型中——查询在先前的控制器中显示,用于说明目的。
从数据库结果生成 CSV
可能你被要求做的最常见的事情之一,尤其是如果你正在构建一个可能包含用户、产品、订单和各种其他指标复杂的应用程序,就是提供某种形式的数据报告。可能你会被要求生成一个 CSV 文件,以下部分展示了如何操作。
准备工作
首先,我们需要创建一个表并输入一些示例数据,这样你就能看到一些以 CSV 格式的数据,所以带着这个想法,将以下代码复制到 SQL 中:
CREATE TABLE IF NOT EXISTS `users` (
`user_id` int(11) NOT NULL AUTO_INCREMENT,
`user_first_name` varchar(125) NOT NULL,
`user_last_name` varchar(125) NOT NULL,
`user_email` varchar(255) NOT NULL,
`user_created_date` int(11) NOT NULL COMMENT 'unix timestamp',
`user_is_active` varchar(3) NOT NULL COMMENT 'yes or no',
PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
INSERT INTO `users` (`user_first_name`, `user_last_name`, `user_email`, `user_created_date`, `user_is_active`) VALUES
('Chloe', 'Graves', 'cgraves@domain.com', 1366114115, 'yes'),
('Mark', 'Brookes', 'mbrookes@domain.com', 1366114115, 'yes');
现在数据库已经准备好了,我们需要确保你调用了数据库实用工具类;确保你使用以下行调用它:
$this->load->dbutil();
你可以将此行放在你控制器的构造函数中,或者在你的控制器函数中调用它。
此外,由于我们将要创建一个文件,我们需要'file'辅助函数的支持,所以请确保你使用以下行调用辅助函数:
$this->load->helper('file');
如何操作...
我们将要创建以下文件:
/path/to/codeigniter/application/controllers/export.php
强制下载
将以下代码添加到你的export.php控制器中:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Export extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->helper('download');
$this->load->dbutil();
$this->load->helper('url');
}
public function index() {
redirect('export/csv');
}
public function csv() {
$query = $this->db->query("SELECT * FROM users");
$delimiter = ",";
$newline = "\r\n";
force_download('myfile.csv', $this->dbutil->csv_from_result($query, $delimiter, $newline));
}
它是如何工作的...
好的,看看加粗的行——我们在控制器构造函数中加载了 CodeIgniter 的'download'辅助函数和数据库实用工具类。这将帮助我们完成这个配方。
export.php控制器函数index()将我们重定向到public function csv(),它执行一个数据库查询。当然,这里可以是任何数据源,但我们调用数据库并将结果存储在数组$query中。这被传递给接受以下两个参数的 CodeIgniter 函数force_download():
-
要创建的文件名和扩展名(或在这种情况下,下载的文件)
-
将进入文件的数据;在这种情况下,我们使用 CodeIgniter 函数
csv_from_result(),它将从数据库查询中取出一行数据并将其转换为以分隔符分隔的文本字符串。csv_from_result()函数接受以下三个参数:-
$query: 这是 CSV 中的数据;在这种情况下,我们数据库查询的输出 -
$delimiter: 这是数据分隔符,即它指定了我们如何分隔每个单元格的数据;这通常是逗号(,)。 -
$newline: 这是新行字符;它通常是 '\n\n'
-
如果一切按计划进行,force_download()将像其名称所暗示的那样,强制下载 CSV 文件。
保存到文件
将以下代码添加到你的export.php控制器中:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
class Export extends CI_Controller {
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->helper('file');
$this->load->dbutil();
}
public function index() {
redirect('export/csv');
}
public function csv() {
$query = $this->db->query("SELECT * FROM users");
$delimiter = ",";
$newline = "\r\n";
$data = $this->dbutil->csv_from_result($query, $delimiter, $newline);
$path = '/path/to/write/to/myfile.csv';
if ( ! write_file($path, $data)) {
echo 'Cannot write file - permissions maybe?';
} else {
echo 'File write OK';
}
}
它是如何工作的...
好的,看看加粗的行;我们在控制器构造函数中加载了 CodeIgniter 的'download'、'file'辅助函数和数据库实用工具类。这将帮助我们完成这个配方。我们还在写入磁盘时添加了 CodeIgniter 特定的语法。
export.php 控制器的 index() 函数将我们重定向到 public function csv(),该函数执行一个数据库查询。当然,这里可以是任何数据源,但我们在这里调用数据库查询并将结果存储在 $query 数组中。然后我们调用 CodeIgniter 的 csv_from_result() 函数,其中 csv_from_result() 接受以下三个参数:
-
$query: CSV 的数据;在这种情况下,是我们数据库查询的输出 -
$delimiter: 数据分隔符,即它指定了我们如何分隔每个数据单元格 -
$newline: 新行字符;通常设置为'\n\n'
csv_from_result() 函数将它的输出存储在变量 $data 中。然后我们尝试运行 CodeIgniter 的 write_file() 函数,该函数接受以下两个参数:
-
要写入文件的路径,包括文件名和扩展名;请记住,此路径应该是可写的
-
要写入文件的数据
如果一切按计划进行,该食谱将返回消息 文件写入 OK——当然,你应该根据需要替换它。如果失败,它将返回错误消息,并在必要时用你自己的代码替换。
还有更多...
如果文件没有写入,那么你很可能没有足够的权限写入目标文件夹。你需要修改目标文件夹的权限,使其达到允许 CodeIgniter 写入的水平。例如,在 Linux/Mac 中,你会在终端中使用 chmod 命令。
第七章。创建一个安全用户环境
在本章中,我们将介绍:
-
避免用户输入
-
防止跨站请求伪造
-
避免数据库中的数据转义
-
在 CodeIgniter 中使用 HTTPS
简介
首先,一个免责声明:没有任何方法或系统可以永远完全无懈可击且始终保持安全,你应该了解在编程任务或你正在编码的上下文中应该应用的正确安全措施。我将在本章末尾提供一些其他信息资源的链接。话虽如此,CodeIgniter 提供了一些有用的技术来降低出错的可能性,例如,在本章中提供了一些可以帮助降低意外发生概率的食谱——然而,你始终应该保持警惕,并确保你正在安全地构建。
避免用户输入
CodeIgniter 安全类函数 xss_clean() 尝试清理 POST 或 COOKIE 数据,以减轻允许向网站注入代码的技术。例如,它会试图防止用户提交的博客文章中包含的 JavaScript 代码被执行,或者检查文本输入字段中提交的数据并转义不允许的字符。
准备工作
你可以将此应用于你创建的任何控制器,或者如果你已经通过 MY_Controller 扩展,你可以根据需要将其添加到其中。你还可以通过在 /path/to/codeigniter/application/config/autoload.php 文件中将它添加到 $autoload['helper'] = array() 来自动加载安全助手。为了明确起见,这里我们是在控制器的构造函数中加载安全助手(即任何你拥有的控制器):
function __construct() {
parent::__construct();
$this->load->helper('security');
}
如何操作...
有两种方法可以实现这一点,全局的(CodeIgniter 每次遇到 POST 或 COOKIE 数据时都会这样做),以及单独的(CodeIgniter 允许你定义何时调用清理后的 COOKIE 或 POST 数据)。
全局
-
CodeIgniter 可以在每次遇到 POST 或 COOKIE 数据时自动调用 xss_clean(),而无需你显式调用 xss_clean()。为此,你需要修改以下文件:
/path/to/codeigniter/application/config/config.php -
将
$config['global_xss_filtering']的值更改为TRUE,如下所示:$config['global_xss_filtering'] = TRUE;然而,请注意,这样做会有计算开销,并且不一定总是需要你一直运行它。
单独
确保将 $config['global_xss_filtering'] 设置为 FALSE,如下所示:
$config['global_xss_filtering'] = FALSE
这将关闭全局 XSS 过滤。当你希望使用 xss_clean() 时,请将以下代码输入到你的控制器或模型中:
$cleaned_data = $this->security->xss_clean($data_to_be_cleaned);
它是如何工作的...
在任何示例中,你都在调用相同的 CodeIgniter 方法;一个方法是自动调用的,另一个是逐个调用的。相关代码可以在 /path/to/codeigniter/system/core/Security.php(找到函数 xss_clean())中找到。
防止跨站请求伪造
跨站请求伪造是指攻击者假装成网站识别的用户(例如已登录用户),然后攻击者能够像真正的用户一样访问已登录用户的个人资料。关于这种情况的详细信息,如网站、书籍等,有很多技术信息,因此我们在这里不会深入研究。相反,我们将探讨 CodeIgniter 如何减轻跨站请求伪造的影响。
如何操作...
我们将通过以下步骤修改一个文件并创建两个文件:
-
首先,我们需要修改一些配置项。为此,我们需要打开以下文件:
/path/to/codeigniter/application/config/config.php找到以下配置选项,并根据表格中的说明进行修改:
配置项 默认值 修改为/描述 $config['csrf_protection']TRUE指定是否开启请求伪造保护 $config['csrf_token_name']csrf_test_name指定在表单中使用的隐藏表单元素的名称(见 工作原理... 部分) $config['csrf_cookie_name']csrf_cookie_name指定在用户机器上设置的 cookie 的名称 $config['csrf_expire']7200单个令牌允许存在的秒数;在此时间后,如果提交表单,CodeIgniter 将抛出错误 -
接下来,我们创建以下两个文件:
-
/path/to/codeigniter/application/controllers/csrf.php -
/path/to/codeigniter/application/views/csrf/csrf.php
-
-
将以下代码添加到
csrf.php控制器中。此控制器将加载所需的辅助函数并在views/csrf/csrf.php文件中显示简单的表单:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Csrf extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('security'); } public function index() { $this->load->view('csrf/csrf'); if ($this->input->post()) { var_dump($this->input->post()); } } } -
将以下代码添加到
csrf.php视图文件中。此视图将创建 HTML 表单。我们使用 CodeIgniter 的form_open()功能来为我们完成工作,这样我们就不必手动完成:<?php echo form_open('csrf') ; ?> What's your name? <input type="text" name="firstname" /> <input type="submit" value="Submit" /> <?php echo form_close() ; ?>
工作原理...
如果你将控制器加载到网页浏览器中并查看页面的 HTML 源代码,你应该会看到以下代码片段:
<form action="http://path/to/codeigniter/csrf" method="post" accept-charset="utf-8"><div style="display:none">
<input type="hidden" name="csrf_test_name" value="577052974b00424157586e40b4c09756" />
</div>What's your name? <input type="text" name="firstname" />
<input type="submit" value="Submit" />
</form>
仔细查看高亮行。CodeIgniter 添加了一个名为 csrf_test_name 的隐藏表单元素。我们在配置文件 config.php 中设置了名称(详情见前文)。此字段的实际值每次运行都会不同。
那么,当你点击 提交 按钮时会发生什么?嗯,CodeIgniter 会将用户机器上的 cookie 中设置的值(在 config.php 中设置为 csrf_cookie_name)与隐藏表单元素中设置的值(在 config.php 中设置为 csrf_test_name)进行比较。如果这两个值不匹配,CodeIgniter 假设存在问题并抛出错误,如下面的截图所示:

您可以通过调整 csrf_exipre 值从默认的 7200 秒到一个更容易等待的时间,例如 10 秒来自己查看。然后在浏览器中加载控制器,等待新的秒数设置,然后点击提交按钮。您将看到前面的错误。请记住,在完成操作后,将此值恢复到 7200(或您希望的任何值)。
此 CSRF 检查使 CodeIgniter 能够减轻 CSRF 攻击,因为设置在用户机器上的 cookie 不太可能被另一台机器上的攻击者猜测和模仿,然后攻击者可以在 csrf_cookie_name 和 csrf_test_name 中设置该值。
小贴士
CodeIgniter 中的 CSRF 保护不适合 AJAX 表单。
数据转义 – 用于数据库
信任来自用户的信息或数据从来都不是一个好主意;您应该始终认为来自用户的数据是不可信的,并且可能具有潜在的危险性。强烈建议您确保对来自用户的数据进行转义,并且除非您已经通过各种应该使数据足够安全以供使用的过程处理了这些数据,否则不要信任任何来自用户的数据。这些技术之一是数据转义。这个配方演示了在数据库查询中转义变量的过程。
准备工作
首先,我们需要创建一个数据库表。在这个例子中,让我们假设数据库表名为 escape。将以下 SQL 语句输入到您的数据库中:
CREATE TABLE `escape` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`firstname` VARCHAR( 25 ) NOT NULL ,
`lastname` VARCHAR( 25 ) NOT NULL
) ENGINE = INNODB;
如何操作...
现在我们已经创建了数据库表,我们将开始对用户输入进行转义。我们将创建以下三个文件:
-
/path/to/codeigniter/application/controllers/escape.php -
/path/to/codeigniter/application/models/escape_model.php -
/path/to/codeigniter/application/views/escape/escape.php
-
创建文件
/path/to/codeigniter/application/controllers/escape.php,并将以下代码添加到该文件中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Escape extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('security'); $this->load->helper('url'); $this->load->database(); } public function index() { redirect('escape/display_form'); } public function display_form() { $this->load->view('escape/escape'); } public function escape_post() { $data = array( 'firstname' => $this->input->post('firstname'), 'lastname' => $this->input->post('lastname') ); $this->load->model('Escape_model'); if ($this->Escape_model->insert_data($data)) { echo 'Success'; } else { echo 'Did not write to database'; } } } -
创建文件
/path/to/codeigniter/application/views/escape/escape.php,并将以下代码添加到该文件中。escape.php控制器将向用户显示一个简单的表单,要求他们输入他们的名字和姓氏,如下所示:<p>Please enter your first and last names.</p> <?php echo form_open('escape/escape_post') ; ?> <p>First Name</p> <?php echo form_input(array('name' => 'firstname', 'id' => 'firstname', 'value' => set_value('firstname', ''))); ?> <p>Last Name</p> <?php echo form_input(array('name' => 'lastname', 'id' => 'lastname', 'value' => set_value('lastname', ''))); ?> <br /> <?php echo form_submit('submit', 'Submit'); ?> <?php echo form_close(); ?> -
创建文件
/path/to/codeigniter/application/models/escape_model.php,并将以下代码添加到该文件中。由于我们正在显式地编写查询,我们将使用$this->db->escape()来为我们执行转义,如下所示:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Escape_model extends CI_Model { function __construct() { parent::__construct(); } function insert_data($data) { $query = "INSERT INTO `escape` (`firstname`, `lastname`) VALUES ( ".$this->db->escape($data['firstname']).", ".$this->db->escape($data['lastname']).") "; if ($this->db->query($query)) { return true; } else { return false; } } }
工作原理...
好的,如果你在浏览器中加载控制器,你会看到表单。这个表单要求用户输入他们的第一个文件名和最后一个()名字,所以让我们以Rob's作为第一个名字,以Foster作为最后一个名字。你会注意到单词Rob的末尾有一个撇号,接下来点击提交按钮。表单应该提交到escape.php控制器,该控制器将表单的输入封装成一个数组并发送到模型。这就是工作的开始;看看前面模型脚本中突出显示的文本,检查以下行,$this->db->escape($data['firstname'])和$this->db->escape($data['lastname']),CodeIgniter 函数正在将其传递给它的输入进行转义,并安全地插入到数据库中。你可以通过查看数据库来看到这一点;要这样做,运行以下命令:
select * from escape
你应该会看到以下截图类似的内容:

为了演示,你可以从模型查询中移除$this->db->escape(),看看会发生什么。修改模型中的代码以反映以下内容:
function insert_data($data) {
$query = "INSERT INTO `escape` (`firstname`, `lastname`) VALUES ('".$data['firstname']."', '".$data['lastname']."') ";
if ($this->db->query($query)) {
return true;
} else {
return false;
}
}
你会看到一个数据库错误,如下面的截图所示:

从前面的错误中可以看出,第一个名字和最后一个名字的值还没有被正确转义。实际上,Rob(Rob's)中的撇号被当作 SQL 查询语法处理,而不是一个实际的变量。你可以看到$this->db->escape()是如何为你工作,使查询更安全、更简单的。
还有更多...
你应该还知道另外两个转义函数,这些是escape_str()和escape_like_str()。它们的使用与escape()相同;然而,你需要分别使用$this->db->escape_str()和$this->db->escape_like_str()来调用每个函数。
它们是做什么用的?escape_str()仍然会像escape()函数一样转义传递给它的数据,但它还可以转义除了字符串之外的数据(escape()仅限于字符串)。escape_like_str()可以在你依赖通配符来缩小查询结果时使用。有关更多信息,请访问ellislab.com/codeigniter/user-guide/database/queries.html。
在 CodeIgniter 中使用 HTTPS
使用 SSL 是一个很大的话题,在线安全也是如此;因此,我强烈建议你广泛阅读有关网络安全的资料(因为这篇食谱并不是真正的安全入门指南)。然而,如果你特别希望使用 SSL 证书保护某些页面,有一个简单的方法可以做到。我们可以创建一个 CodeIgniter 辅助文件来切换 SSL 支持的开或关。让我们看看如何做到这一点。
准备工作
我相信您知道要求网站上的某些页面使用 SSL 证书进行保护的好处。看到那个绿色的地址栏和小的锁头可以大大减轻用户在网站上输入数据的担忧。CodeIgniter 并未内置 SSL 支持;然而,使用简单的辅助函数实现它非常容易。显然,SSL 支持并不是网站安全的全部,应始终与其他安全措施一起实施,以减轻不受欢迎的访问者。
如何做到这一点...
我们将创建一个菜谱,允许用户查看一个不受 HTTPS 连接保护的页面,并让他们点击一个链接,该链接将把他们重定向到一个受 HTTPS 连接保护的页面。实现 HTTPS 连接和检查页面是否通过 HTTPS 观看的代码部分被突出显示,这样您可以快速了解正在发生的事情的核心。
我们将创建以下五个文件:
-
/path/to/codeigniter/application/views/https/with_https.php -
/path/to/codeigniter/application/views/https/without_https.php -
/path/to/codeigniter/application/controllers/with_https.php -
/path/to/codeigniter/application/controllers/without_https.php -
/path/to/codeigniter/application/helpers/ssl_helper.php
-
创建文件
/path/to/codeigniter/application/views/https/with_https.php,并将以下代码添加到该文件中:<?php echo '<p>This page is being viewed with HTTPS.</p>'; echo anchor ('without_https','Click here to view a page without HTTPS') ; ?> -
创建文件
/path/to/codeigniter/application/views/https/without_https.php,并将以下代码添加到该文件中:<?php echo '<p>This page is being viewed without HTTPS.</p>'; echo anchor('with_https', 'Click here to view a page with HTTPS'); ?> -
创建文件
/path/to/codeigniter/application/controllers/with_https.php。此控制器将加载ssl_helper并将支持设置为on。它还将显示一个链接到without_https控制器的链接。将以下代码添加到With_https控制器中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class With_https extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('ssl_helper'); toggle_ssl("on"); } public function index() { $this->load->view('https/with_https'); } } -
创建文件
/path/to/codeigniter/application/controllers/without_https.php,并将以下代码添加到该文件中。此控制器将加载ssl_helper并将 SSL 支持设置为off。它还将显示一个链接到with_https控制器的链接。将以下代码添加到Without_https控制器中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Without_https extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('ssl_helper'); toggle_ssl("off"); } public function index() { $this->load->view('https/without_https'); } } -
创建文件
/path/to/codeigniter/application/helpers/ssl_helper.php。此辅助函数将接受由调用控制器传递给它的一个函数参数,并根据我们是否需要 SSL 支持来更改base_url的值。将以下代码添加到辅助函数ssl_helper中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); function toggle_ssl($action) { $CI =& get_instance(); if ($action == "on") { $CI->config->config['base_url'] = str_replace('http://', 'https://', $CI->config->config['base_url']); if ($_SERVER['SERVER_PORT'] != 443) { redirect($CI->uri->uri_string()); } } elseif ($action == "off") { $CI->config->config['base_url'] = str_replace('https://', 'http://', $CI->config->config['base_url']); if ($_SERVER['SERVER_PORT'] != 80) { redirect($CI->uri->uri_string()); } } else { // if neither turn https support off $CI->config->config['base_url'] = str_replace('https://', 'http://', $CI->config->config['base_url']); if ($_SERVER['SERVER_PORT'] != 80) { redirect($CI->uri->uri_string()); } } }小贴士
端口 443 是默认的 HTTPS 端口;然而,这并不总是如此,SSL 端口可能根据您的环境配置不同,或者您正在开发或为的系统上可能是另一个数字。请记住在您的环境中使用正确的端口。
它是如何工作的...
无论我们加载哪个控制器(with_https 或 without_https),构造函数中首先执行的操作之一是加载辅助函数 ssl_helper,如下所示:
$this->load->helper('ssl_helper')
你可以在每个控制器中看到这一行被突出显示。然后我们需要调用辅助函数toggle_ssl(string),向其传递一个on或off的字符串。显然,on将强制执行 SSL,而off将移除它。当调用ssl_helper时,它立即通过引用(使用&来通过引用复制)调用主 CodeIgniter 超级对象。我们可以通过在 PHP 函数get_instance()之前包含符号&来看到它是通过引用被调用的。然后对象被存储起来,供我们在辅助函数中使用,变量为$CI,如下所示:
$CI =& get_instance()
根据传递给它的值,辅助函数将执行以下三个操作之一:
-
如果值是
on,那么我们希望开启 SSL 支持。使用 PHP 函数str_replace,我们将base_url值中的http://部分替换为https://,并在飞行中保存为 CodeIgniter 的新base_url值,如下所示:$CI->config->config['base_url'] = str_replace('https://', 'http://', $CI->config->config['base_url']); -
如果值是
off,我们做完全相同的事情,但方向相反。我们将当前base_url值中的https://部分替换为http://,如下所示:$CI->config->config['base_url'] = str_replace('https://', 'http://', $CI->config->config['base_url']);- 在进行上述两次
str_replace之后,我们测试$_SERVER数组元素SERVER_PORT;的当前值,并相应地进行重定向。
- 在进行上述两次
-
如果传递给
toggle_ssl的值既不是on也不是off,则默认操作将 SSL 支持设置为off。
还有更多...
一些人对在他们的机器上设置 SSL 可能不太熟悉。设置 SSL 超出了这本书的范围;然而,它相当简单。在以下部分有一个 Apache 基金会提供的非常棒的链接,详细说明了如何设置 SSL 证书。
在本地主机上设置 HTTPS
在本地主机上设置 HTTPS 是可能的(假设你正在开发的是这个)。我找到了一个特别有用的 URL,它有助于在本地主机上获得自签名的 SSL 证书。
自签名的 SSL 证书只是你自己制作的 SSL 证书。它将和购买的一样好;然而,如果你将其推送到实际的生产环境并且有真实用户访问,他们的浏览器会告诉他们颁发机构是未知的(因为你自己制作的),用户可能会认为这意味着该网站是危险的并离开。因此,对于实际网站,你需要从认可的颁发机构获取证书,而在测试时,你可以自己制作一个。以下链接将帮助你制作一个:
httpd.apache.org/docs/2.2/ssl/ssl_faq.html
或者,如果你管理了很多托管服务,你从提供服务的公司那里获得的套餐或产品将包含一个 SSL 证书,或者很可能是托管提供商能够为你设置一个(检查合同的条款)。
第八章. 日历、正确的时间和地点
在本章中,我们将涵盖:
-
使用数据库结果构建 CodeIgniter 日历助手
-
使用日历库构建约会管理器
-
创建一个用于处理个人出生日期的助手
-
在 CodeIgniter 中处理模糊日期
简介
CodeIgniter 附带了许多函数和助手,以帮助支持您在处理时间、日期、日历等时的应用程序。在本章中,我们将使用其中的一些,但我们还将创建一些自己的助手,这些助手在日常任务中非常有用,例如计算一个人的年龄(对于年龄验证脚本很有用)和处理模糊日期(即,编写日期或时间的描述而不是只写出准确的日期)。
使用数据库结果构建 CodeIgniter 日历助手
CodeIgniter 附带了一个非常有用的日历助手,允许您以网格形式显示月份。您可以开发从数据库(例如存储日记约会的表)中提取事件的功能,并告知用户在特定一天是否有约会。
准备工作
由于我们在数据库中存储约会,我们需要一个数据库表。将以下代码复制到您的数据库中:
CREATE TABLE `appointments` (
`app_id` int(11) NOT NULL AUTO_INCREMENT,
`app_date` varchar(11) NOT NULL,
`app_url` varchar(255) NOT NULL,
`app_name` varchar(255) NOT NULL,
PRIMARY KEY (`app_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `appointments` (`app_id`, `app_date`, `app_url`, `app_name`) VALUES
(1, '1375465528', 'http://localhost/1', 'My Appointment'),
(2, '1375638327', 'http://localhost/2', 'My Second Appointment'),
(3, '1375897527', 'http://localhost/3', 'My Third Appointment'),
(4, '1381167927', 'http://localhost/4', 'My Forth Name');
如何做到这一点…
我们将创建两个文件:
-
/path/to/codeigniter/application/controllers/app_cal.php -
/path/to/codeigniter/application/models/app_cal_model.php -
/path/to/codeigniter/application/views/app_cal/view.php
为了创建这两个文件,我们执行以下步骤:
-
创建文件
/path/to/codeigniter/application/controllers/app_cal.php并添加以下代码:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class App_cal extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('date'); } public function index() { redirect('app_cal/show'); } public function show() { $prefs = array ( 'start_day' => 'monday', 'month_type' => 'long', 'day_type' => 'short', 'show_next_prev' => TRUE, 'next_prev_url' => 'http://www.your_domain.com/app_cal/show/' ); $this->load->library('calendar', $prefs); if ($this->uri->segment(4)) { $year= $this->uri->segment(3); $month = $this->uri->segment(4); } else { $year = date("Y", time()); $month = date("m", time()); } $this->load->model('App_cal_model'); $appointments = $this->App_cal_model->get_appointments($year, $month); $data = array(); foreach ($appointments->result() as $row) { $data[(int)date("d",$row->app_date)] = $row->app_url; } $data['cal_data'] = $this->calendar->generate($year, $month, $data); $this->load->view('app_cal/view', $data); } } -
创建文件
/path/to/codeigniter/application/models/app_cal_model.php并添加以下代码:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class App_cal_model extends CI_Model { function __construct() { parent::__construct(); } function get_appointments($year, $month) { $month_as_written = array( '01' => 'January', '02' => 'February', '03' => 'March', '04' => 'April', '05' => 'May', '06' => 'June', '07' => 'July', '08' => 'August', '09' => 'September', '10' => 'October', '11' => 'November', '12' => 'December' ); $start_date = '01' . ' ' . $month_as_written[$month] . ' ' . $year; $start_of_month = strtotime($start_date); $end_date = days_in_month($month, $year) . ' ' . $month_as_written[$month] . ' ' . $year; $end_of_month = strtotime($end_date); $this->db->where('app_date > ', $start_of_month); $this->db->where('app_date < ', $end_of_month); $query = $this->db->get('appointments'); return $query; } } -
创建文件
/path/to/codeigniter/application/views/app_cal/view.php并添加以下代码:<?php echo $cal_data ; ?>
它是如何工作的…
我们首先在我们的网页浏览器中加载控制器 app_cal(如果你想知道,它代表约会日历),在构造函数中加载助手 'url' 和 'date':
$this->load->helper('url');
$this->load->helper('date');
加载的第一个函数是 index(),它将我们重定向到 show() 函数,在那里我们立即开始定义日历功能的一些偏好:
$prefs = array (
'start_day' => 'monday',
'month_type' => 'long',
'day_type' => 'short',
'show_next_prev' => TRUE,
'next_prev_url' => 'http://www.your_domain.com/app_cal/show/'
);
每个项目相当直观,但我仍然会详细介绍它们:
| Preference | 描述 |
|---|---|
| start_day | 指定日历网格中最左侧是星期几,所以如果你输入 'sunday',日历网格中的日行(描述日子的行)将从星期日开始。如果你出于某种奇怪的原因想让你的日历从星期三开始,你将输入 'wednesday',日历周将从星期三开始。但请真的不要这样做;这看起来会很奇怪! |
| month_type | 指定月份的书写方式。'long' 是完整的月份名称,例如 August,而 'short' 是缩写版本,例如 Aug。 |
| day_type | 指定日历网格中日期行的星期几是如何书写的。'long' 是 Monday、Tuesday、Wednesday 等等,而 'short' 是 Mon、Tue、Wed 等等。 |
| show_next_prev | 可以是 TRUE 或 FALSE。这会让 CodeIgniter 知道它是否应该显示下一个和上一个的箭头( << 和 >>);点击这些箭头将使日历向前或向后移动一个月。如果设置为 TRUE(在这个菜谱中就是这样),你需要指定 next_prev_url。 |
| next_prev_url | 指定 CodeIgniter 应该使用的 << 或 >> 链接的 URL 代码。 |
接下来,我们加载日历库,并将 $prefs 数组作为第二个参数传递给它。然后,我们检查是否存在第四个 uri 段:
if ($this->uri->segment(4)) {
$year= $this->uri->segment(3);
$month = $this->uri->segment(4);
} else {
$year = date("Y", time());
$month = date("m", time());
}
第三个和第四个 uri 段分别是年份(YYYY)和月份(MM),如果它们不存在,可能是因为第一次加载日历(或者日历不是通过 'next_prev_url' 访问的)。无论如何,因为我们没有第三个或第四个 uri 段传递给我们的模型,我们不得不自己创建它们。但我们应该使用什么?比如当前月份和当前年份(参见前面的高亮代码)?
现在,我们加载模型 App_model 并将我们的 $year 和 $month 变量传递给它:
$this->load->model('App_cal_model');
$appointments = $this->App_cal_model->get_appointments($year, $month);
现在我们来看看模型,看看发生了什么。我们使用时间戳在数据库中存储我们的预约,因为年份和月份被作为字符串 'YYYY' 和 'MM' 传递给 app_cal 控制器,所以我们需要将 'YYYY'、'MM' 字符串转换为时间戳,以便我们可以查询数据库并确定在所选月份的特定一天是否有预约。这意味着我们需要使用 PHP 函数 strtotime。
对于熟悉该函数(或者甚至只是阅读了函数名称)的人来说,会明白 strtotime 将写成的英文字符串转换为 Unix 时间戳,例如,写入 "last Wednesday",strtotime 将返回上一次周三的时间戳。这是一个获取时间戳的好方法,但它确实意味着你需要为要计算的日期生成某种类型的字符串描述。
我们想要获取特定月份的所有预约,这意味着生成一个带有 WHERE 子句的数据库查询,查找 "预约大于代表一个月第一天的日期时间戳,并且小于代表一个月最后一天的日期时间戳"。因此,为了准备这个,让我们看看代码中的以下 $month_as_written 数组:
$month_as_written = array(
'01' => 'January',
'02' => 'February',
'03' => 'March',
'04' => 'April',
'05' => 'May',
'06' => 'June',
'07' => 'July',
'08' => 'August',
'09' => 'September',
'10' => 'October',
'11' => 'November',
'12' => 'December'
);
你会看到数组中每个项目的键都与 $month (MM) 的格式相匹配。这很重要,因为我们将使用 $month 中的值来用英文写出所需的月份名称。
我们将用 '01 ' 预先添加,以表示月初,然后用 $year 后缀,如下所示:
$start_date = '01' . ' ' . $month_as_written[$month] . ' ' . $year;
$start_of_month = strtotime($start_date);
写入的字符串存储在变量 $start_date 中,然后传递给 strtotime(),它反过来返回月份开始的 Unix 时间戳。接下来,我们计算结束日期:
$end_date = days_in_month($month, $year) . ' ' . $month_as_written[$month] . ' ' . $year;
$end_of_month = strtotime($end_date);
接下来,我们使用 CodeIgniter 函数 days_in_month();传入 $month 和 $year 参数,它将返回该月的天数作为一个整数。然后我们将这个值与一个空格 ' ' 和 $month_as_written 数组中的月份名称连接起来,最后以 $year 结尾。然后将这个字符串传递给 strtotime($end_date),它给出了月份结束的 Unix 时间戳;这个值存储在变量 $end_of_month 中。
我们将在数据库查询中使用两个变量 $start_of_month 和 $end_of_month,要求它返回计算月份开始之后的预约,但不超过月份结束:
$this->db->where('app_date > ', $start_of_month);
$this->db->where('app_date < ', $end_of_month);
$query = $this->db->get('appointments');
return $query;
接下来,我们需要构建一个数组来存储预约和 URL。首先,让我们声明这个数组:
$data = array();
现在,我们将遍历 App_model 结果(包含在变量 $appointments 中),在遍历过程中构建数组:
foreach ($appointments->result() as $row) {
$data[(int)date("d",$row->app_date)] = $row->app_url;
}
完成后,数组应该具有以下结构:
array(3) {
[2]=>
string(18) "http://localhost/1"
[4]=>
string(18) "http://localhost/2"
[7]=>
string(18) "http://localhost/3"
}
$data 数组与 $year 和 $month 一起传递给日历库函数 generate():
$data['cal_data'] = $this->calendar->generate($year, $month, $data);
$this->load->view('app_cal/view', $data);
这个结果存储在 $data['cal_data'] 中,然后传递给视图 app_cal/view,从那里渲染到屏幕上。
使用日历库构建预约管理器
之前的食谱使用了 CodeIgniter 日历库来帮助构建交互式日历。然而,您只能查看数据库中已经存在的日历项目。下一步的逻辑是构建一个小型应用程序,允许您通过表单创建日历项目;一个简单的预约管理器就可以做到这一点。我们基于之前的食谱;然而,您不需要回到并完成那个食谱。您需要的所有内容都包含在这个食谱中。
准备工作
我们需要创建一个数据库表来存储我们的预约。如果您已经使用了之前的食谱,您应该已经有了数据库表;如果是这样,请在您的数据库中运行以下代码:
ALTER TABLE `appointments` ADD `app_description` VARCHAR( 255 ) NOT NULL AFTER `app_name`
或者,如果您还没有创建表格,请在您的数据库中运行以下代码:
CREATE TABLE `appointments` (
`app_id` int(11) NOT NULL AUTO_INCREMENT,
`app_date` varchar(11) NOT NULL,
`app_url` varchar(255) NOT NULL,
`app_name` varchar(255) NOT NULL,
`app_description` varchar(255) NOT NULL,
PRIMARY KEY (`app_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
现在数据库已经排序,让我们看看代码:
如何做到这一点…
我们将要创建以下六个文件:
-
/path/to/codeigniter/application/controllers/app_cal.php:这个文件包含运行显示所需的所有代码,包括日历的 HTML 6 模板 -
/path/to/codeigniter/application/models/app_cal_model.php:这个文件包含与数据库交互所需的所有代码 -
/path/to/codeigniter/application/views/app_cal/view.php:这个页面将显示日历 -
/path/to/codeigniter/application/views/app_cal/appointment.php:这个页面将显示一个表单,您可以在其中添加预约 -
/path/to/codeigniter/application/views/app_cal/new.php:这个页面显示一个表单,允许用户创建新的预约 -
/path/to/codeigniter/application/views/app_cal/delete.php:此文件显示删除确认消息
我们需要执行以下步骤来创建这些文件:
-
创建文件
/path/to/codeigniter/application/controllers/app_cal.php并向其中添加以下代码(这是一个相当大的控制器,所以我将分解较大的函数来解释它们的功能):<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class App_cal extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('date'); $this->load->helper('form'); $this->load->model('App_cal_model'); } public function index() { redirect('app_cal/show'); }公共函数
show()将在屏幕上显示日历;它负责决定显示哪个月份和哪一年份。public function show() { if ($this->uri->segment(4)) { $year= $this->uri->segment(3); $month = $this->uri->segment(4); } else { $year = date("Y", time()); $month = date("m", time()); } $tpl = ' {table_open}<table border="1" cellpadding="15" cellspacing="1">{/table_open} {heading_row_start}<tr>{/heading_row_start} {heading_previous_cell}<th><a href="{previous_url}"><<</a></th>{/heading_previous_cell} {heading_title_cell}<th colspan="{colspan}">{heading}</th>{/heading_title_cell} {heading_next_cell}<th><a href="{next_url}">>></a></th>{/heading_next_cell} {heading_row_end}</tr>{/heading_row_end} {week_row_start}<tr>{/week_row_start} {week_day_cell}<td>{week_day}</td>{/week_day_cell} {week_row_end}</tr>{/week_row_end} {cal_row_start}<tr>{/cal_row_start} {cal_cell_start}<td>{/cal_cell_start} {cal_cell_content}'.anchor('app_cal/create/'.$year.'/'.$month.'/{day}', '+').' <a href="{content}">{day}</a>{/cal_cell_content} {cal_cell_content_today}<div class="highlight">'.anchor('app_cal/create/'.$year.'/'.$month.'/{day}', '+').'<a href="{content}">{day}</a></div>{/cal_cell_content_today} {cal_cell_no_content}'.anchor('app_cal/create/'.$year.'/'.$month.'/{day}', '+').' {day}{/cal_cell_no_content} {cal_cell_no_content_today}<div class="highlight">'.anchor('app_cal/create/'.$year.'/'.$month.'/{day}', '+').'{day}</div>{/cal_cell_no_content_today} {cal_cell_blank} {/cal_cell_blank} {cal_cell_end}</td>{/cal_cell_end} {cal_row_end}</tr>{/cal_row_end} {table_close}</table>{/table_close}' ; $prefs = array ( 'start_day' => 'monday', 'month_type' => 'long', 'day_type' => 'short', 'show_next_prev' => TRUE, 'next_prev_url' => 'http://www.your_domain.com/app_cal/show/', 'template' => $tpl ); $this->load->library('calendar', $prefs); $appointments = $this->App_cal_model->get_appointments($year, $month); $data = array(); foreach ($appointments->result() as $row) { $data[(int)date("d",$row->app_date)] = $row->app_url; } $data['cal_data'] = $this->calendar->generate($year, $month, $data); $this->load->view('app_cal/view', $data); }公共函数
create()将处理预约的创建,因此它将显示预约表单,验证输入,并将数据发送到模型以将其插入数据库。public function create() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); $this->form_validation->set_rules('app_name', 'Appointment Name', 'required|min_length[1]|max_length[255]|trim'); $this->form_validation->set_rules('app_description', 'Appointment Description', 'min_length[1]|max_length[255]|trim'); $this->form_validation->set_rules('day', 'Appointment Start Day', 'required|min_length[1]|max_length[11]|trim'); $this->form_validation->set_rules('month', 'Appointment Start Month', 'required|min_length[1]|max_length[11]|trim'); $this->form_validation->set_rules('year', 'Appointment Start Year', 'required|min_length[1]|max_length[11]|trim'); if ($this->uri->segment(3)) { $year = $this->uri->segment(3); $month = $this->uri->segment(4); $day = $this->uri->segment(5); } elseif ($this->input->post()) { $year = $this->input->post('year'); $month = $this->input->post('month'); $day = $this->input->post('day'); } else { $year = date("Y", time()); $month = date("m", time()); $day = date("j", time()); } if ($this->form_validation->run() == FALSE) { // First load, or problem with form $data['app_name'] = array('name' => 'app_name', 'id' => 'app_name', 'value' => set_value('app_name', ''), 'maxlength' => '100', 'size' => '35'); $data['app_description'] = array('name' => 'app_description', 'id' => 'app_description', 'value' => set_value('app_description', ''), 'maxlength' => '100', 'size' => '35'); $days_in_this_month = days_in_month($month,$year); $days_i = array(); for ($i=1;$i<=$days_in_this_month;$i++) { ($i<10 ? $days_i['0'.$i] = '0'.$i : $days_i[$i] = $i) ; } $data['days'] = $days_i; $data['months'] = array('01' => 'January','02' => 'February','03' => 'March','04' => 'April','05' => 'May','06' => 'June','07' => 'July','08' => 'August','09' => 'September','10' => 'October','11' => 'November','12' => 'December'); $data['years'] = array('2013' => '2013'); $data['day'] = $day; $data['month'] = $month; $data['year'] = $year; $this->load->view('app_cal/new', $data); } else { $app_date = mktime(0,0,0,$month,$day,$year); $data = array( 'app_name' => $this->input->post('app_name'), 'app_description' => $this->input->post('app_description'), 'app_date' => $app_date, 'app_url' => base_url('index.php/app_cal/appointment/'.$year.'/'.$month.'/'.$day) ); if ($this->App_cal_model->create($data)) { redirect('app_cal/show/'.$year.'/'.$month); } else { redirect('app_cal/index'); } } }公共函数
delete()负责删除(删除)一个预约。它将加载删除确认表单,验证输入,并将数据传递到模型以从数据库中删除。public function delete() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); if ($this->input->post('app_id')) { $id = $this->input->post('app_id'); } else { $id = $this->uri->segment(3); } $this->form_validation->set_rules('app_id', 'Appointment ID', 'min_length[1]|max_length[11]|is_natural|trim'); if ($this->form_validation->run() == FALSE) { // First load, or problem with form $appointment = $this->App_cal_model->get_single($id); $data['id'] = $id; foreach ($appointment->result() as $row) { $data['app_name'] = $row->app_name; $data['app_date'] = $row->app_date; } $this->load->view('app_cal/delete', $data); } else { if ($this->App_cal_model->delete($id)) { redirect('app_cal/index'); } else { redirect('app_cal/index'); } } } public function appointment() { if ($this->uri->segment(3)) { $year = $this->uri->segment(3); $month = $this->uri->segment(4); $day = $this->uri->segment(5); $data['appointments'] = $this->App_cal_model->get_appointment($year, $month, $day); $this->load->view('app_cal/appointment', $data); } else { } } } -
创建文件
/path/to/codeigniter/application/models/app_cal_model.php并向其中添加以下代码:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class App_cal_model extends CI_Model { function __construct() { parent::__construct(); } function get_appointments($year, $month) { $month_as_written = array( '01' => 'January', '02' => 'February', '03' => 'March', '04' => 'April', '05' => 'May', '06' => 'June', '07' => 'July', '08' => 'August', '09' => 'September', '10' => 'October', '11' => 'November', '12' => 'December' ); $sd = '01' . ' ' . $month_as_written[$month] . ' ' . $year; $start_of_month = strtotime($sd); $ed = days_in_month($month, $year) . ' ' . $month_as_written[$month] . ' ' . $year; $end_of_month = strtotime($ed); $this->db->where('app_date > ', $start_of_month); $this->db->where('app_date < ', $end_of_month); $query = $this->db->get('appointments'); $this->db->last_query(); return $query; } function get_appointment($year, $month, $day) { $start_of_day = mktime(0,0,0,$month,$day,$year); $end_of_day = $start_of_day + 86400; $this->db->where('app_date >= ', $start_of_day); $this->db->where('app_date <= ', $end_of_day); $query = $this->db->get('appointments'); return $query; } function create($data) { if ($this->db->insert('appointments', $data)) { return true; } else { return false; } } function delete($id) { $this->db->where('app_id', $id); if ($this->db->delete('appointments')) { return true; } else { return false; } } function get_single($id) { $this->db->where('app_id', $id); $query = $this->db->get('appointments'); return $query; } } -
创建文件
/path/to/codeigniter/application/views/app_cal/view.php并向其中添加以下代码:<?php echo anchor ('app_cal/create', 'New Appointment') ; ?> <?php echo $cal_data ; ?> -
创建文件
/path/to/codeigniter/application/views/app_cal/appointment.php并向其中添加以下代码:<?php echo anchor ('app_cal/index', 'View Calendar') ; ?> <a href=""></a><h2>Appointments</h2> <?php foreach ($appointments->result() as $row) : ?> <?php echo anchor('app_cal/delete/'.$row->app_id, 'Delete') ; ?><br /> <?php echo date("j-m-Y",$row->app_date) ; ?><br /> <?php echo $row->app_name ; ?><br /> <?php echo $row->app_description ; ?> <hr> <?php endforeach ; ?> -
创建文件
/path/to/codeigniter/application/views/app_cal/delete.php并向其中添加以下代码:<h2>Delete Appointment</h2> <?php if (validation_errors()) : ?> <p><?php echo validation_errors() ;?></p> <?php endif ; ?> <?php echo form_open('app_cal/delete') ; ?> <h4>Are you sure you want to delete the following appointment?</h4> <?php echo $app_name . ' on ' . date("d-m-Y h:i:s", $app_date); ?> <?php echo form_hidden('app_id', $id) ; ?> <br /><br /> <input type="submit" value="Delete" /> or <?php echo anchor ('app_cal', 'Cancel') ; ?> <?php echo form_close() ; ?> -
创建文件
/path/to/codeigniter/application/views/app_cal/new.php并向其中添加以下代码:<h2>New Appointment</h2> <h4>Appointment Name</h4> <?php if (validation_errors()) : ?> <p><?php echo validation_errors() ;?></p> <?php endif ; ?> <?php echo form_open('app_cal/create') ; ?> <?php echo form_input($app_name); ?> <h4>Appointment Description</h4> <?php echo form_input($app_description); ?> <h4>Appointment Date</h4> <?php echo form_dropdown('day', $days, $day); ?> <?php echo form_dropdown('month', $months, $month); ?> <?php echo form_dropdown('year', $years, $year); ?> <br /><br /> <input type="submit" value="Save" /> or <?php echo anchor ('app_cal', 'Cancel') ; ?> <?php echo form_close() ; ?>
它是如何工作的...
大部分功能与之前的菜谱类似,但有一些差异;我们添加了管理预约的支持。因此,让我们首先看看 public function view()。你会注意到我们已经移动了一些代码;从 uri 中获取日期的代码或即时生成日期的代码现在在 $prefs 数组之前——这是由于 $tpl 变量的原因。那么 $tpl 是什么呢?
$tpl 变量字符串的内容,更具体地说,它是日历库使用的日历模板;模板支持用于天数的标签——{day}——但不支持月份或年份。这意味着我们必须手动将这些值插入模板中。但为了做到这一点,我们需要事先知道年份和月份的值;这就是为什么计算月份和日期的代码现在被移动到 $prefs 数组之前。我使用的模板代码是 CodeIgniter 网站用户指南中可用的修改版本:ellislab.com/codeigniter/user-guide/libraries/calendar.html。
从这里开始,其功能与之前菜谱中的 view() 函数相同:我们加载日历库,获取当前月份的所有预约,并将其传递给 app_cal/view.php 视图文件。让我们更详细地了解一下一些新函数:
公共函数 create()
当用户输入新预约的详细信息并提交创建表单时,公共函数create()首先声明新预约的验证规则。然后我们需要从post或get数组中获取特定预约的年、月和日。公共create()函数会检查这一点并将日期值存储在变量中:
if ($this->uri->segment(3)) {
$year = $this->uri->segment(3);
$month = $this->uri->segment(4);
$day = $this->uri->segment(5);
} elseif ($this->input->post()) {
$year = $this->input->post('year');
$month = $this->input->post('month');
$day = $this->input->post('day');
} else {
$year = date("Y", time());
$month = date("m", time());
$day = date("j", time());
}
这里有三个测试,因为公共函数create()可以通过不同的方式访问。第一个测试是查看是否有人通过点击日历网格中的添加预约链接+来访问页面,第二个测试是查看页面是否已提交,第三个(else)是当点击新预约链接时。
接下来,我们检查页面验证是否通过;FALSE可能意味着验证失败,或者create()是第一次被访问。
我们为 CodeIgniter 设置一些表单值以在视图中渲染,并开始构建日期下拉菜单所需的变量:
$days_in_this_month = days_in_month($month,$year);
$days_i = array();
for ($i=1;$i<=$days_in_this_month;$i++) {
($i<10 ? $days_i['0'.$i] = '0'.$i : $days_i[$i] = $i) ;
}
我们随后展示view文件并等待用户提交。在提交成功(即,当表单通过验证时),我们计算预约日期变量(日、月和年)的 Unix 时间戳,并将所有内容打包到$data数组中,以便插入数据库。成功插入后,将用户重定向到他们的预约所在的月份和年份。
公共函数delete()
这相当简单;我们检查get或post数组中是否存在预约 ID:
if ($this->input->post('app_id')) {
$id = $this->input->post('app_id');
} else {
$id = $this->uri->segment(3);
}
我们在get和post中都查找,因为函数可以由第一次点击 URL 的人访问,也可以由第二次点击(当他们点击视图中的确认删除按钮时)的人提交。
如果表单提交时出现错误,或者第一次运行,将从数据库中获取预约详情(前一段代码已传递$id)。
$appointment = $this->App_cal_model->get_single($id);
foreach ($appointment->result() as $row) {
$data['app_name'] = $row->app_name;
$data['app_date'] = $row->app_date;
}
$this->load->view('app_cal/delete', $data);
我们随后从数据库结果中获取app_name和app_date并将它们作为项目存储在$data数组中,以便传递给app_cal/delete.php视图文件。在成功提交(如果没有验证失败)后,调用模型函数delete(),如果发生了删除,用户将被重定向到他们之前删除的预约所在的相同月份和年份。
创建一个助手函数来处理一个人的出生日期
有时你需要一个年龄验证脚本,一个确定用户是否达到一定年龄的方法。根据他们的年龄,他们可能被允许或禁止查看内容,例如,推广成人产品的网站,如酒精或烟草,或者推广特定年龄评级游戏的网站。这个菜谱中的代码帮助你确定用户的年龄,将其与最低年龄要求进行比较,并相应地显示 HTML 文件。
如何做到这一点...
我们将创建五个文件:
-
/path/to/codeigniter/application/controllers/register.php:这是我们的菜谱控制器 -
/path/to/codeigniter/application/helpers/dob_val_helper.php:此文件计算用户的年龄,将其与所需年龄进行比较,并根据结果返回TRUE或FALSE -
/path/to/codeigniter/application/views/register/signup.php:此文件显示年龄验证表单 -
/path/to/codeigniter/application/views/register/enter.php:如果用户可以进入,则显示此文件 -
/path/to/codeigniter/application/views/register/noenter.php:如果用户不能进入,则显示此文件
-
创建控制器文件
register.php,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Register extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('dob_val'); } public function index() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); $this->form_validation->set_rules('year', 'Year', 'required|min_length[4]|max_length[4]|trim'); $this->form_validation->set_rules('month', 'Month', 'required|min_length[2]|max_length[2]|trim'); $this->form_validation->set_rules('day', 'Day', 'required|min_length[2]|max_length[2]|trim'); if ($this->form_validation->run() == FALSE) { $this->load->view('register/signup'); } else { $dob = array( 'year' => $this->input->post('year'), 'month' => $this->input->post('month'), 'day' => $this->input->post('day') ); $at_least = 18; if (are_they_old_enough($dob, $at_least = 18)) { $this->load->view('register/enter'); } else { $this->load->view('register/noenter'); } } } } -
创建辅助程序文件
dob_val_helper.php,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); function are_they_old_enough($dob, $at_least = 18) { $birthday = strtotime($dob['year'].'-'.$dob['month'].'-'.$dob['day']); $diff = floor((time() - $birthday) / (60 * 60 * 24 * 365)); if ($diff >= $at_least) { return true; } else { return false; } } -
创建视图文件
signup.php,并将以下代码添加到其中:<?php echo form_open() ; ?> <?php echo validation_errors() ; ?> Day <input type="text" name="day" size="5" value="<?php echo set_value('day') ; ?>"/> Month <input type="text" name="month" size="5" value="<?php echo set_value('month') ; ?>"/> Year <input type="text" name="year" size="5" value="<?php echo set_value('year') ; ?>"/> <input type="submit" value="go" /> <?php echo form_close() ; ?> -
创建视图文件
enter.php,并将以下代码添加到其中:<p>You are old enough to view page</p> -
创建视图文件
noenter.php,并将以下代码添加到其中:<p>You are NOT old enough to view page</p>
它是如何工作的...
首先,我们来到控制器;控制器加载 URL 辅助程序(因为我们使用 redirect() 函数以及我们将创建的名为 dob_val 的辅助程序):
function __construct() {
parent::__construct();
$this->load->helper('form');
$this->load->helper('dob_val');
}
然后,我们设置表单验证并从 HTML 中设置我们的日、月和年字段的规则。register/signup.php 视图文件被加载,准备用户在三个表单字段中输入他们的出生日期。
用户将点击提交,如果提交通过验证,则三个表单值将被放入 $dob 数组中:
$dob = array(
'year' => $this->input->post('year'),
'month' => $this->input->post('month'),
'day' => $this->input->post('day')
);
我们设置最小年龄(用户必须达到的年龄才能查看受年龄限制的内容)如下:
$at_least = 18;
然后,我们将 $dob 数组以及 $at_least 变量传递给 dob_val 辅助程序:
if (are_they_old_enough($dob, $at_least = 18)) {
$this->load->view('register/enter');
} else {
$this->load->view('register/noenter');
}
辅助程序会计算用户是否超过或低于 $at_least 年龄,如果超过则返回 TRUE,如果没有则返回 FALSE。如果用户符合条件,他们会看到 register/enter 视图文件,如果不满足条件,则会看到 register/noenter 视图文件。
在 CodeIgniter 中处理模糊日期
什么是模糊日期?模糊日期是一种更熟悉和通用的方式来描述数据或时间,而不是精确的时间;它以一种对读者来说更熟悉的方式描述事件。例如,与其说一封电子邮件在 17:41 发送,不如说它是在“不到一分钟前”发送的(假设你在最后一分钟内发送了它)或者甚至是“几分钟前”。某事发生的精确时间被认为是不重要的一—或者至少是不必要的——信息,取而代之的是对日期和时间的更一般、非正式和会话式的描述。
如何实现...
我们将要创建两个文件:
-
/path/to/codeigniter/application/controllers/fuzzy_date.php:此控制器将调用fuzzy_date_helper.php文件,并将一些日期(作为 Unix 时间戳)传递给它以供辅助程序转换。 -
/path/to/codeigniter/application/helpers/fuzzy_date_helper.php:这个辅助程序将由控制器调用,并将转换传递给它的日期,每次都返回一个文字描述。
-
创建控制器文件,
fuzzy_date.php,并向其中添加以下代码:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Fuzzy_date extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('fuzzy_date_helper'););); } public function index() { echo describe_the_time(time() + 30); } } ?> -
创建辅助程序文件,
fuzzy_date_helper.php,并向其中添加以下代码:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); function describe_the_time($time_in) { define('SECOND', 1); define('MINUTE', 60 * SECOND); define('HOUR', 60 * MINUTE); define('DAY', 24 * HOUR); define('MONTH', 30 * DAY); define('YEAR', 12 * MONTH); $past_descriptions = array( 1 => 'about a minute ago', 2 => 'a few minutes ago', 3 => 'within the last hour', 4 => 'earlier today', 5 => 'yesterday', 6 => 'earlier this week', 7 => 'earlier this month', 8 => 'last month', 9 => 'earlier this year', 10 => 'last year', 11 => 'a long time ago', 12 => 'I don\'t know that time' ); $future_descriptions = array( 1 => 'a minute from now', 2 => 'in the next few minutes', 3 => 'in the next hour', 4 => 'later today', 5 => 'tomorrow', 6 => 'later this week', 7 => 'later this month', 8 => 'next month', 9 => 'later this year', 10 => 'next year', 11 => 'a long way off', 12 => 'I don\'t know that time' ); $now = time(); if ($time_in < $now) { if ($time_in > $now - MINUTE) { // About a minute ago return $past_descriptions[1]; } elseif ( ($time_in >= $now - (MINUTE * 5) ) && ($time_in <= $now ) ) { // A few minutes ago return $past_descriptions[2]; } elseif ( ($time_in >= $now - (MINUTE * 60)) && ($time_in <= $now ) ) { // Within the last hour return $past_descriptions[3]; } elseif ( ($time_in >= $now - (HOUR * 24)) && ($time_in <= $now - (MINUTE * 60) ) ) { // Earlier today return $past_descriptions[4]; } elseif ( ($time_in >= $now - (HOUR * 48)) && ($time_in <= $now - (HOUR * 24) ) ) { // Yesterday return $past_descriptions[5]; } elseif ( ($time_in >= $now - (DAY * 7)) && ($time_in <= $now - (HOUR * 48) ) ) { // Earlier this week return $past_descriptions[6]; } elseif ( ($time_in >= $now - (DAY * 31)) && ($time_in <= $now - (DAY * 7) ) ) { // Earlier this month return $past_descriptions[7]; } elseif ( ($time_in >= $now - (DAY * 62)) && ($time_in <= $now - (DAY * 31) ) ) { // Last Month return $past_descriptions[8]; } elseif ( ($time_in >= $now - (MONTH * 12)) && ($time_in <= $now - (MONTH * 31) ) ) { // Earlier this year return $past_descriptions[9]; } elseif ( ($time_in >= $now - (MONTH * 24)) && ($time_in <= $now - (MONTH * 12) ) ) { // Last year return $past_descriptions[10]; } elseif ( ($time_in >= $now - (MONTH * 24) && ($time_in <= $now - (MONTH * 12) ) ) ){ // Last year return $past_descriptions[11]; } else { return $past_descriptions[12]; } } else { if ($time_in < $now + MINUTE) { // A minute from now return $future_descriptions[1]; } elseif ( ($time_in <= $now + (MINUTE * 5) ) && ($time_in >= $now ) ) { // In the next few minutes return $future_descriptions[2]; } elseif ( ($time_in <= $now + (MINUTE * 59)) && ($time_in >= $now ) ) { // In the next hour return $future_descriptions[3]; } elseif ( ($time_in <= $now + (HOUR * 24)) && ($time_in >= $now + (MINUTE * 59) ) ) { // Later today return $future_descriptions[4]; } elseif ( ($time_in <= $now + (HOUR * 48)) && ($time_in >= $now + (HOUR * 24) ) ) { // Yesterday return $future_descriptions[5]; } elseif ( ($time_in <= $now + (DAY * 7)) && ($time_in >= $now + (HOUR * 48) ) ) { // Earlier this week return $future_descriptions[6]; } elseif ( ($time_in <= $now + (DAY * 31)) && ($time_in >= $now + (DAY * 7) ) ) { // Earlier this month return $future_descriptions[7]; } elseif ( ($time_in <= $now + (DAY * 62)) && ($time_in >= $now + (DAY * 31) ) ) { // Last Month return $future_descriptions[8]; } elseif ( ($time_in <= $now + (MONTH * 12)) && ($time_in >= $now + (MONTH * 31) ) ) { // Earlier this year return $future_descriptions[9]; } elseif ( ($time_in <= $now + (MONTH * 24)) && ($time_in >= $now + (MONTH * 12) ) ) { // Last year return $future_descriptions[10]; } elseif ( ($time_in <= $now + (MONTH * 24) ) ) { // Last year return $future_descriptions[11]; } else { return $future_descriptions[12]; } } } ?>
它是如何工作的……
让我们看看fuzzy_date控制器,控制器在构造函数中加载,它反过来加载我们的fuzzy_date_helper,这是将 Unix 时间戳转换为描述性文本的辅助程序:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->helper('fuzzydate_helper');
}
然后,我们加载公共函数index(),它调用fuzzydate_helper,并传递给它一个时间戳(目前,传入的输入设置为time() + 30)。
为什么是time() + 30?好吧,time()是php函数,它返回“现在”的 Unix 时间戳(对你来说,“现在”是什么时候),而+ 30是将 30 秒加到time()返回的当前时间戳值上,意味着“现在加 30 秒”(或“未来 30 秒”)。我将其设置为初始演示,但稍后我将描述如何修改它。
在控制器中,我们将“现在加 30 秒”传递给辅助程序:
echo describe_the_time(time() + 30);
进入函数的传入函数(即我们在控制器中定义的)在辅助程序中本地声明为$time_in:
function describe_the_time($time_in) {
辅助程序获取$time_in变量,查看其值,并计算出时间戳值是否大于或小于辅助程序中定义的$now(即$now = time()):
if ($time_in < $now) {
... // $time_in is in the past
} else {
... // $time_in is in the future
}
由于“现在加 30 秒”大于现在(精确地说,多了 30 秒),辅助程序进入if结构的else部分,然后开始一系列比较,试图找到一个地方,其中在$time_in中定义的值可以匹配。由于“现在加 30 秒”小于“现在加 60 秒”(实际上少了 30 秒),第一个if语句被应用,辅助程序返回$future_descriptions数组中的第一个元素:
if ($time_in < $now + MINUTE) { // A minute from now
return $future_descriptions[1];
}
那将是现在一分钟之后吗?
当然,您可以在控制器中修改传递给辅助程序的时间戳;您可以将其设置为time() + 250(这将现在是 250 秒)。现在,加上 250 秒大于一分钟(60 秒),但小于五分钟(300 秒),这将导致第二个if语句应用并返回文本“在接下来的几分钟内”。
我们还可以传递一个比“现在”更低的时间戳值:time() – 4000(即“现在减去 4000 秒”)。传递一个较低的时间戳将导致辅助程序的行为改变。如何?别忘了最初的if语句?
if ($time_in < $now) {
...
} else {
...
}
这次,我们不会进入它的 else 部分;相反,初始的if部分将被触发,辅助程序将开始处理过去的时间。因此,通过将辅助程序输入设置为time() - 4000(现在是 4000 秒之前,超过一分钟,超过五分钟,超过一小时但不到一天),辅助程序将返回文本“今天早些时候”。
当然,你可以添加更多更多详细的比较和描述;实际上,我鼓励你这样做——尽情发挥吧!
在实际情况中,你会在控制器中修改辅助函数的调用,移除time() +或- number_of_seconds的输入,只保留你想要描述的事物的时戳——无论是博客文章的创建日期、文件上传日期、预约日期……无论什么……我相信你明白了我的意思。
第九章. 扩展核心
在本章中,你将学习:
-
使用 CodeIgniter Sparks
-
使用 DOMPDF Spark 创建 PDF
-
在 CodeIgniter 中创建钩子
-
从数据库中清除无效会话
-
扩展你的控制器
-
使用 FTP 上传文件
-
创建自己的配置文件并使用设置
-
创建库并为它们提供对 CodeIgniter 资源的访问
-
使用语言类 – 在路上切换语言
简介
CodeIgniter 几乎可以做到你需要的所有事情,直接从盒子里出来;但总会有时候你必须扩展或更改标准设置——无论是创建钩子以便你不必修改核心(你真的不想修改核心),还是安装 Sparks 以添加额外的功能和新特性——有许多方法可以扩展和构建 CodeIgniter 以获得更多功能。
使用 CodeIgniter Sparks
不久以前,如果你想为 CodeIgniter 安装一个扩展或一些外部软件,你必须四处寻找。你需要在互联网上搜索你想要的东西,如果你很幸运,你可能会找到一个博客或某个人的个人网站,也许甚至是一个 GitHub 账户或 Google 代码仓库,你可以从中下载并安装一个包。有时它有效,有时则无效,而且你下载的几乎总是需要某种程度的编辑或重写。
快进到现在!幸运的是,我们有 Sparks。将 Sparks 视为可以与 CodeIgniter 一起使用的扩展。它们被保存在一个地方(这样你就不必在互联网上四处寻找),在 www.getsparks.org。
我们在第一章,CodeIgniter 基础中提到了 Sparks,但是,让我们更深入地探讨,并指导你安装和使用一些我在一段时间内发现很有用的 Sparks。
因此,如果你之前还没有安装它,现在就在你的 CodeIgniter 实例中安装 Sparks。你可以前往第一章,CodeIgniter 基础,获取如何操作的说明,或者查看 GetSparks 网站上的说明。
准备工作
首先,你需要导航到你的 CodeIgniter 目录的顶级(或根)目录。
如何做到这一点...
如果你使用的是 MAC 或 Linux,按照以下方式下载 CodeIgniter Sparks:
-
在命令行中,导航到你的 CodeIgniter 应用程序的根目录,然后输入:
php -r "$(curl -fsSL http://getsparks.org/go-sparks)"这将下载并安装 CodeIgniter Sparks 到你的特定 CodeIgniter 实例。
如果你使用的是 Windows,那么你需要手动下载并解压 Sparks。请按照以下说明操作,或者查看 GetSparks 网站上的说明以获取最新版本:
-
在你的 CodeIgniter 目录的顶级(根)目录下创建一个名为
tools的文件夹。 -
访问以下 URL:
getsparks.org/install。 -
前往常规安装部分,下载 Sparks 包。
-
将下载的内容解压到你在第一步中创建的
tools文件夹中。 -
从以下链接下载
Loader类扩展:getsparks.org/static/install/MY_Loader.php.txt。 -
将
MY_Loader.php.txt文件重命名为MY_Loader.php,并将其移动到你的 CodeIgniter 应用程序的application/core/MY_Loader.php目录中。
它是如何工作的...
Sparks 已下载到你的 CodeIgniter 实例的 /path/to/codeigniter/tools 文件夹中,准备使用。你可以通过以下命令安装任何你想要的 Spark:
php tools/spark install [version] spark-name
这里,[version] 是 Spark 的特定版本。它是可选的,如果你省略它,Sparks 将自动选择最新版本。spark-name 是你想要下载的 Spark 的名称。
使用 DOMPDF Spark 创建 PDF
DOMPDF Spark 是一个非常好的工具;它设置简单,可以处理大多数事情。它通过获取 HTML 模板文件的输出(你之前创建的)来工作——你可以像使用普通视图文件一样向 HTML 传递变量——DOMPDF 将从格式化的 HTML 代码创建一个 PDF 文件。
准备工作
由于这个菜谱需要 DOMPDF Spark,我们在做其他任何事情之前需要将其安装到我们的 CodeIgniter 实例中。要安装它,请执行以下步骤:
-
从 getsparks.org 获取 DOMPDF Spark 并安装它。打开一个终端窗口,导航到你的 CodeIgniter 应用程序目录,并输入以下代码:
php tools/spark install dompdf这将下载 DOMPDF Spark。DOMPDF Spark 可能已经被下载,但你需要做一些安装才能使其运行。
在
/path/to/codeigniter/sparks/dompdf/[version-number]/helpers文件夹中(其中[version-number]是 Spark 的版本),有一个文件夹和一个文件。具体如下(加粗):-
/path/to/codeigniter/sparks/dompdf/0.5.3/helpers/dompdf/ -
/path/to/codeigniter/sparks/dompdf/0.5.3/helpers/dompdf_helper.php
-
-
将此文件夹和文件复制到你的 CodeIgniter 应用程序的
helpers文件夹中(/path/to/codeigniter/application/helpers)。 -
我们还需要一个数据库表,以便我们的模型能够工作,所以请继续,并在你的数据库中输入以下代码:
CREATE TABLE `users` ( `user_id` int(11) NOT NULL AUTO_INCREMENT, `user_firstname` varchar(255) NOT NULL, `user_lastname` varchar(255) NOT NULL, `user_email` varchar(255) NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ; INSERT INTO `users` (`user_id`, `user_firstname`, `user_lastname`, `user_email`) VALUES (1, 'Rob', 'Foster', 'rf@domain.com'), (2, 'Lucy', 'Welsh', 'lw@domain.com');
如何操作...
我们将创建以下三个文件:
-
/path/to/codeigniter/application/controllers/makepdf.php:此控制器将加载 DOMPDF 扩展并输出我们的 PDF。 -
/path/to/codeigniter/application/models/makepdf_model.php:我们将使用此模型从数据库获取传递给视图文件view_all_users所必需的信息。 -
/path/to/codeigniter/application/views/makepdf/view_all_users.php:此文件包含我们希望 PDF 看起来的 HTML 和标记。它还包含一些 PHP 代码,这些代码会输出从模型收集的一些数据。
-
首先添加以下代码到
makepdf控制器中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Makepdf extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('file'); $this->load->helper('dompdf'); $this->load->model('Makepdf_model'); } function index() { $data['query'] = $this->Makepdf_model->get_all_users(); $filename = 'List-of-users'; $html = $this->load->view('makepdf/view_all_users', $data, true); pdf_create($html, $filename); } } -
然后将以下代码添加到
makepdf_model模型中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Makepdf_model extends CI_Model { function __construct() { parent::__construct(); } function get_all_users() { $query = $this->db->get('users'); return $query; } } -
最后,将以下代码添加到
makepdf/view_all_users.php视图中:<table> <tr> <td>First Name</td> <td>Last Name</td> <td>Email</td> </tr> <?php foreach($query->result() as $row) : ?> <tr> <td><?php echo $row->user_firstname ; ?></td> <td><?php echo $row->user_lastname ; ?></td> <td><?php echo $row->user_email ; ?></td> </tr> <?php endforeach ; ?> </table>
它是如何工作的……
在我们的makepdf控制器构造函数中,我们加载了dompdf辅助工具。我们将dompdf_helper.php文件和dompdf文件夹放置在我们之前在“准备就绪”部分安装的 CodeIgniter 应用程序的helpers文件夹中。
private function index()然后调用Makepdf_model函数,get_all_users()。这个模型函数在用户表上运行一个简单的选择操作,并将其存储在$data['query']数组中,我们稍后会回到这一点。$filename变量被分配字符串List-of-users,但如果你愿意,你可以将其更改为任何你想要的,你甚至可以添加今天的日期,代码片段如下:
$filename = 'List-of-users-for'.date("d-m-Y", time())
无论如何,回到故事中。我们然后使用标准的 CodeIgniter 方法一个视图文件,并将$data数组传递给它:$html = $this->load->view('makepdf/view_all_users', $data, true)。记住这个$data数组包含我们的用户数据库查询。我们不是让 CodeIgniter 像通常那样渲染视图,而是将现在处理过的 HTML 捕获到$html变量中,该变量被传递到dompdf函数pdf_createalong,以及$filename变量。
如果你将控制器加载到浏览器中,你应该会自动提示下载 PDF。
你可以看到,这可以很容易地适应输出各种广泛的 HTML 设计,从发票到采购订单,再到地址联系人。只需修改模型查询,从数据库中提取你需要知道的信息,并更改 HTML 以显示这些信息,然后就可以完成了。
在 CodeIgniter 中创建钩子
你就在那里,快乐地使用某个框架或 CMS 来运行一个网站,现在有人要求你让它做它最初没有设计的事情。你很不情愿地同意了,并开始研究如何实现所要求的功能。你决定修改你所遇到的任何平台的系统核心文件,因为它既快又简单,而且你的老板使用了诸如“快速胜利”和“成本效益”之类的术语。
无论如何,请求的功能是有效的,每个人都感到高兴——除了那个可怜的人(可能就是你)几个月后回来升级平台到最新版本时……砰!你对该系统核心所做的修改已经丢失,因为升级时被新文件覆盖了。现在,你被要求实现的那件疯狂的事情不再工作,你只能试图回忆你是如何让它工作的。然后,在任何人注意到之前,它就消失了,你只能想着。“哦,上帝,我为什么就不能回家!”
好吧,振作起来!你正在使用 CodeIgniter,这种情况永远不会发生在你身上(当然,前提是你使用钩子)。如果你愿意,你仍然可以修改核心——去做吧,随意修改,看看我是否在乎——但当你需要升级时,你会后悔的。我只是在考虑你的压力水平。
Hooks 允许你在 CodeIgniter 执行的特定点覆盖其行为,而无需修改 CodeIgniter 核心文件。太棒了!这意味着使用 CodeIgniter,你可以实现任何数量的荒谬的管理变更请求,而且升级当天一点也不会受到影响。Hooks 按特定顺序工作。这意味着什么?也就是说,CodeIgniter 按特定顺序工作,也就是说,当 CodeIgniter 运行时,它会按顺序加载其特定的部分。这个顺序始终相同,并且你可以设置一个 Hook 在任何一步执行。
准备工作
首先,你需要告诉 CodeIgniter 它应该允许 Hooks 运行。打开 /application/config/config.php 文件,确保 enable_hooks 的设置是 TRUE,如下面的代码片段所示:
$config['enable_hooks'] = TRUE;
你还必须决定你的 Hook 运行的最佳时间。在 CodeIgniter 的执行过程中有七个点可以设置你的 Hook 运行,如下所示:
-
pre_system:这是你可以设置 Hook 运行的最早位置。在这个阶段,只有基准测试和 Hooks 被引入 -
pre_controller:这在你调用任何控制器之前执行 -
post_controller_constructor:这使你的 Hooks 在控制器构造函数之后但调用任何控制器函数之前运行 -
post_controller:这使得你的 Hook 在控制器执行完毕后运行 -
display_override:这将覆盖 CodeIgniter 的display()函数--这是 CodeIgniter 尝试将视图文件或其他输出渲染到屏幕上的时候 -
cache_override:这覆盖了_display_cache()函数,如果你想要实现自定义缓存功能,可以使用这个 -
post_system:这是在正常操作完成后要调用的(即,在系统完成当前请求的执行后)
如何操作...
-
为你的 Hook 定义一个执行点。一旦你决定了运行 Hook 的点,你将需要告诉 CodeIgniter 何时运行钩子。你通过在
config/hooks.php文件中定义$hook数组中的正确信息来完成此操作。$hook数组的键指定了执行点--即你希望钩子运行的时候(参见本食谱中“准备工作”部分提到的列表)。在下面的示例中,数组键是post_controller,这意味着钩子在控制器执行完毕后执行。现在的$hook数组看起来如下。$hook['post_controller'] = array( 'class' => 'Class_name', 'function' => 'function_name', 'filename' => 'file_name_of_hook.php', 'filepath' => 'path_to_hook', 'params' => array(param1, param2, param3 ... etc) );那么,前面的所有代码意味着什么呢?让我们看一下下面的表格,以更好地理解前面的代码:
数组元素 描述 class它是 filename元素中定义的文件中的类的名称。function它是类中函数的名称。 filename它是包含类的文件的名称。 filepath这是数组元素 filename中定义的文件的路径,通常是hooks文件夹,但如果你愿意,可以添加子文件夹或将它移动到不同的位置。params这是你要传递给 Hook类function元素的任何参数。用逗号分隔每个参数。 -
然后,你需要创建你的钩子类,如下面的代码片段所示:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Class_name { function function_name () { } }你将你的钩子代码放在这个类中,显然需要将
Class_name改为对你更有用的名称,而函数function_name()显然是一个示例——这个名称也需要更改。
如果你需要在钩子中访问 CodeIgniter 资源,你可以通过使用以下代码中的 CodeIgniter get_instance()函数来访问主 CodeIgniter 对象:
function function_name () {
$CI = & get_instance();
$CI->load->thing_to_load();
}
在这里,thing_to_load是你的模型、库等的名称。在那里,你就可以看到钩子实际上很简单,确定一个执行点,创建一个包含你的钩子代码的类,然后就可以开始了!
从数据库中清除无效会话
你可能发现 CodeIgniter 有时无法成功从数据库中的会话表中删除会话。未使用的会话会清除其user_data字段,但行仍然保留在会话表中。
我经常需要编写特定的数据库查询来清除会话。我通常将这些查询放在My_Controller类中(参考本章中的扩展你的控制器配方);然而,我最近开始使用钩子来完成这个任务。这样它总是在后台运行,我无需再考虑它。
准备工作
我们将要修改以下文件:
/path/to/codeigniter/application/config/config.php
打开/application/config/config.php文件,确保enable_hooks的设置是TRUE,如下面的代码片段所示:
$config['enable_hooks'] = TRUE;
如何操作...
我们将创建并修改以下文件:
/path/to/codeigniter/application/hooks/clear_sessions.php
-
创建前面的文件,并向其中添加以下代码:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Clear_sessions { function clear_now() { $CI =& get_instance(); $query = $CI->db->query("DELETE FROM `ci_sessions` WHERE `user_data` = '' "); } }小贴士
我们使用的是默认的 CodeIgniter 会话表名称,即
ci_sessions。在你的应用程序中,你需要将这个值替换为你会话表的名称(假设你已经将其从ci_sessions更改了)。 -
打开
/path/to/codeigniter/application/config/hooks.php文件,并向其中添加以下代码:$hook['post_controller'] = array( 'class' => 'Clear_sessions', 'function' => 'clear_now', 'filename' => 'clear_sessions.php', 'filepath' => 'hooks' );
它是如何工作的...
我们在clear_sessions.php文件中定义了一个类(Clear_sessions),我们将把所有需要用于钩子执行任务的逻辑放入这个类中。在这个类中,我们有一个clear_now()函数。CodeIgniter 知道它需要在clear_sessions.php文件中的Clear_sessions类中运行clear_now()函数,因为我们已经在hooks.php配置文件中定义了这一点,如下面的代码片段所示:
$hook['post_controller'] = array(
'class' => 'Clear_sessions',
'function' => 'clear_now',
'filename' => 'clear_sessions.php',
'filepath' => 'hooks'
);
我们通过前一个数组中的filepath元素告诉 CodeIgniterclear_sessions.php文件的路径。CodeIgniter 将在执行post_controller阶段运行此钩子,也就是说,在运行控制器之后。因此,这就是 CodeIgniter 知道何时执行什么钩子;但钩子的功能是什么?它做了什么?
让我们看看以下代码行:
$CI =& get_instance();
这使我们能够访问主 CodeIgniter 对象,该对象定义为$CI(代表 CodeIgniter)。使用此对象,我们可以访问数据库功能和设置。我们将使用此$CI对象来运行数据库查询,如下所示代码片段所示:
$query = $CI->db->query("DELETE FROM `ci_sessions` WHERE `user_data` = '' ");
上一行代码会删除表中(在此处命名为默认的 CodeIgniter 会话表 ci_sessions)任何user_data为空的会话(不是非空,而是空)。
所有 CodeIgniter 未正确清理的已消耗会话现在应从会话表中消失,而留下活跃的会话。
扩展您的控制器
继承是一件美妙的事情;它允许您定义概念层或应用程序中的级别,也就是说,允许子类共享父类的特性和属性,这使得应用程序能够比标准过程式编程更准确、更直观地模拟现实世界的例子。
在 CodeIgniter 中,您应用程序的默认设计结构是您创建的控制器扩展主 CodeIgniter 控制器。例如,在以下代码中,我们有一个Signin控制器,它扩展了主 CodeIgniter 控制器CI_Controller:
class Signin extends CI_Controller {
}
您的Signin控制器将继承CI_Controller的属性。然而,CodeIgniter 还允许您在CI_Controller和您创建的控制器之间插入一个阶段,您可以插入一个中间层。这个中间步骤被命名为My_Controller。新的 MY_Controller 将扩展 CI_Controller(在扩展过程中继承一切)以及您创建的任何普通应用程序特定控制器(用户、登录、发票等),它们都将继承自新的 MY_Controller。它定义如下代码片段所示:
class MY_Controller extends CI_Controller {
}
现在,您的Signin类必须修改为从您的MY_Controller继承,而不是从 CodeIgniter 的CI_Controller继承,如下所示:
class Signin extends MY_Controller {
}
如何操作...
我们将创建以下两个文件:
-
/path/to/codeigniter/application/core/MY_Controller.php -
/path/to/codeigniter/application/controllers/days.php
-
创建
/path/to/codeigniter/application/core/MY_Controller.php文件,并将以下代码添加到其中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class MY_Controller extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('array'); } } -
接下来,创建
/path/to/codeigniter/application/controllers/days.php文件,并将以下代码添加到其中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Days extends MY_Controller { function __construct() { parent::__construct(); } function index() { $days = array("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"); echo random_element($days); } }
它是如何工作的...
这主要是继承。你的子类正在从父类继承属性。然而,具体来说,我们在这里展示了如何在 MY_Controller 类 中加载数组助手,并让 Days 控制器继承助手资源以执行数组助手函数 random_element()。
这有很多用途,例如,你可以检查用户是否在 MY_Controller 类 中登录,这意味着你只需要写一次,而不是在每个控制器中。或者你可以更进一步,有一个经过身份验证的控制器和一个未经身份验证的控制器扩展 MY_Controller。MY_Controller 文件将调用任何文件并执行对经过身份验证和未经身份验证用户都通用的任何任务,而经过身份验证的控制器将执行登录检查等。
使用 FTP 上传文件
不时地,你会被要求生成一个文件——可能是从数据库导出,或者是一些系统日志。大多数时候,你会被要求将文件通过电子邮件发送到某个位置,但有时,你会被要求将其上传到客户端可以访问的 FTP 文件夹。我们将从一个之前的章节的食谱中提取信息(第六章中的“从数据库结果生成 CSV”,与数据库一起工作),并对其进行调整,以便将其上传到 FTP 位置而不是流式传输输出。
准备工作
-
我们将从一个数据库中提取一些值。为了做到这一点,我们需要一个表来提取数据。以下是该表的架构。将以下内容复制到您的数据库中:
CREATE TABLE `users` ( `user_id` int(11) NOT NULL AUTO_INCREMENT, `user_firstname` varchar(255) NOT NULL, `user_lastname` varchar(255) NOT NULL, `user_email` varchar(255) NOT NULL, PRIMARY KEY (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;# MySQL returned an empty result set (i.e. zero rows). INSERT INTO `users` (`user_id`, `user_firstname`, `user_lastname`, `user_email`) VALUES (1, 'Rob', 'Foster', 'rf@domain.com'), (2, 'Lucy', 'Welsh', 'lw@domain.com');# 2 rows affected. -
前往第六章, 与数据库一起工作,并复制出 从数据库结果生成 CSV 食谱中的代码;然后返回这里获取进一步的指示。
如何做到...
我们将修改 从数据库结果生成 CSV 食谱中的一个文件,即 /path/to/codeigniter/application/controllers/export.php。这是第六章, 与数据库一起工作中的导出控制器。我们将将其作为本食谱的基础。它将从数据库中获取一些数据,最终为我们生成 CSV。
-
打开
export.php进行编辑,并修改它以反映以下代码片段(更改已突出显示):<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Export extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('file'); $this->load->library('ftp'); $this->load->dbutil(); } public function index() { redirect('export/csv'); } public function csv() { $query = $this->db->query("SELECT * FROM users"); $delimiter = ","; $newline = "\r\n"; $data = $this->dbutil->csv_from_result($query, $delimiter, $newline); $filename = 'myfile.csv'; $path = './'.$filename; if (! write_file($path, $data)) { echo 'Cannot write file - permissions maybe?'; exit; } $config['hostname'] = 'your-hostname'; $config['username'] = 'your-username'; $config['password'] = 'your-password'; $config['debug'] = TRUE; $this->ftp->connect($config); $this->ftp->upload($path, '/dir_on_ftp/'.$filename, 'ascii', 0755); $this->ftp->close(); } }这里有一些变量你需要更改以反映你的 FTP 环境设置,例如:
$config['hostname'] = 'ftp.example.com'; $config['username'] = 'your-username'; $config['password'] = 'your-password';...并且字符串
dir_on_ftp在:$this->ftp->upload($path, '/dir_on_ftp/'.$filename, 'ascii', 0755);
它是如何工作的...
正如你所期望的新食谱一样,我们在第六章, 与数据库一起工作中使用的正常导出控制器中做了一些更改。这些更改是必要的,以支持 FTP 助手在将文件上传到 FTP 服务器的工作中的功能。让我们看看发生了什么。
首先,我们在导出控制器构造函数中加载所需的辅助工具--URL、文件和 FTP--以确保这个导出控制器拥有进行文件传输所需的正确支持。
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->helper('file');
$this->load->library('ftp');
$this->load->dbutil();
}
然后一切都将遵循先前控制器(来自第六章,与数据库一起工作)的功能,即从数据库表获取结果集,并使用 CodeIgniter 的dbutil函数csv_from_result()为我们生成文件。它通过write_file()函数使用在$path中定义的位置放置在服务器上。
然后 FTP 功能开始启动。我们为要将文件写入的 FTP 服务器定义登录设置--你可以,也可能应该将这些放在你自己的配置文件中(本章也有解释,见制作自己的配置文件并使用设置配方),但为了现在更容易解释,我们将在这里定义它们。设置相当明显,直到你看到$config['debug']数组设置。debug允许错误报告显示给你,即开发者。显然,在实时生产环境中,你肯定希望将其设置为FALSE以防止任何敏感和重要的信息被显示。
无论如何,我们使用在$config数组中定义的登录设置,尝试连接到 FTP 服务器并尝试上传文件,如下面的代码片段所示:
$this->ftp->connect($config);
$this->ftp->upload($path, '/dir_on_ftp/'.$filename, 'ascii', 0755);
如果一切顺利,文件应该已经上传到你的服务器。使用 FTP 客户端登录并查看它是否在那里--如果不在,请检查你是否使用了正确的 FTP 设置,以及你在 FTP 服务器上写入的路径是否可写并且实际上存在,也就是说,这里用粗体定义的路径确实存在:
$this->ftp->upload($path, '/dir_on_ftp/'.$filename, 'ascii', 0755);
一个有趣的观点是前面upload()函数末尾的两个函数参数:ascii和0755。这些表示我们将文件传输编码为ascii(即纯文本)并设置其文件权限为0755。如果你愿意,这些也可以在配置数组中定义。
创建库并让它们访问 CodeIgniter 资源
CodeIgniter 允许你在不需要或不需要在控制器或模型中放置代码的情况下创建自己的库和辅助工具。为什么要把代码放在库中而不是辅助工具中呢?嗯,有些人对这一点相当激动,我相信如果你足够深入地思考这个问题,你就能想出一套严格的规则来定义何时一段代码是辅助工具或库。但是生活太短暂了。只要代码有良好的文档,并且是可维护的、稳定的和安全的,你可以随心所欲。然而,作为一个一般规则:
图书馆是用于需要访问其他资源的代码,例如需要访问数据库或外部系统(可能通过 cURL),而辅助器是较小的一段代码,用于执行特定任务(例如检查字符串是否为有效的电子邮件或URL,例如)。*
我相信有更好的定义,但这个对我来说是有效的;而且我相信你可能会想要一个辅助器能够访问数据库或其他资源;而且我相信在有些时候,库可能不需要访问这些资源。我的观点是,确保代码有文档和可维护性,不要陷入概念设计细节。
话虽如此,让我们看看如何创建一个库并给它访问 CodeIgniter 资源(因为默认情况下它不会)。
准备工作
我们将通过库访问数据库;然而,为了做到这一点,我们需要一个可以访问的数据库(当然),所以将以下代码复制到你的数据库中:
CREATE TABLE IF NOT EXISTS `person` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`first_name` varchar(50) NOT NULL,
`last_name` varchar(50) NOT NULL,
`email` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=5 ;
INSERT INTO `person` (`id`, `first_name`, `last_name`, `email`) VALUES
(1, 'Rob', 'Foster', 'rfoster@dudlydog.com'),
(2, 'Lucy', 'Welsh', 'lwelsh@cocopopet.com'),
(3, 'Chloe', 'Graves', 'cgraves@mia-cat.com'),
(4, 'Claire', 'Strickland', 'cstrickland@an-other-domain.com');
如何做...
我们将创建以下三个文件:
-
/path/to/codeigniter/application/controllers/call_lib.php -
/path/to/codeigniter/application/libraries/lib_to_call.php -
/path/to/codeigniter/application/models/lib_model.php
-
创建
call_lib.php控制器,并将以下代码添加到其中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Call_lib extends CI_Controller { function __construct() { parent::__construct(); $this->load->library('lib_to_call'); $this->load->database(); } public function index() { $result = $this->lib_to_call->get_users(); echo '<pre>'; var_dump($result->result()); echo '</pre>'; } } -
创建
lib_to_call.php库,并将以下代码添加到其中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Lib_to_call { public function get_users() { $CI =& get_instance(); $CI->load->model('Lib_model'); return $CI->Lib_model->get(); } } -
然后创建
lib_model.php模型,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Lib_model extends CI_Model { function __construct() { parent::__construct(); } function get() { $query = $this->db->get('person'); return $query; } }
它是如何工作的...
在查看如何给你的库访问 CodeIgniter 资源时,我们将运行前面的配方。它是连接到一个模型;然而,它可以连接到任何类型的 CodeIgniter 资源,如钩子或辅助器等。
首先,call_lib.php控制器在其构造函数中加载了lib_to_call库,如下所示:
function __construct() {
parent::__construct();
$this->load->library('lib_to_call');
}
这使得库在整个控制器中可用。
然后我们调用public function index(),它调用库函数get_users(),并将返回的结果存储在$result变量中。让我们看看库函数get_users()正在做什么,这是允许库访问 CodeIgniter 资源的地方。
看看lib_to_call库中以下突出显示的行:
class Lib_to_call {
public function get_users() {
$CI =& get_instance();
$CI->load->model('Lib_model');
return $CI->Lib_model->get();
}
}
我们使用 PHP 函数get_instance()通过引用获取一个副本(这意味着我们使用的是原始对象而不是它的副本)或 CodeIgniter 对象。此对象可以访问整个 CodeIgniter 系统和其资源。我们将此对象存储在名为$CI(代表 CodeIgniter)的局部变量中。现在我们可以调用任何我们喜欢的 CodeIgniter 资源,就像从控制器中调用一样,只不过我们使用的是$CI(而不是在控制器中使用$this)。因此,要在一个控制器中调用模型或辅助器,我们将执行以下操作:
$this->load->helper('helper_name');
$this->load->model('model_name');
但要在库中调用辅助器和模型,我们现在这样做:
$CI->load->helper('helper_name');
$CI->load->model('model_name');
我们现在加载的模型将使用 Active Record 查询数据库,将表中的所有行返回到库中,然后库再将它返回到控制器进行处理。在这种情况下(为了证明它工作),我们使用 var_dump() 输出结果。如果一切顺利,这个菜谱应该会输出如下:
array(4) {
[0]=>
object(stdClass)#19 (4) {
["id"]=>
string(1) "1"
["first_name"]=>
string(3) "Rob"
["last_name"]=>
string(6) "Foster"
["email"]=>
string(20) "rfoster@dudlydog.com"
}
[1]=>
object(stdClass)#20 (4) {
["id"]=>
string(1) "2"
["first_name"]=>
string(4) "Lucy"
["last_name"]=>
string(5) "Welsh"
["email"]=>
string(20) "lwelsh@cocopopet.com"
}
[2]=>
object(stdClass)#21 (4) {
["id"]=>
string(1) "3"
["first_name"]=>
string(5) "Chloe"
["last_name"]=>
string(6) "Graves"
["email"]=>
string(19) "cgraves@mia-cat.com"
}
[3]=>
object(stdClass)#22 (4) {
["id"]=>
string(1) "4"
["first_name"]=>
string(6) "Claire"
["last_name"]=>
string(10) "Strickland"
["email"]=>
string(31) "cstrickland@an-other-domain.com"
}
}
前面的菜谱通过访问数据库来展示如何在库中从内部访问 CodeIgniter 超级对象。然而,辅助工具、钩子和其他元素也可以访问。通过使用 $CI =& get_instance(),我们可以访问主要的 CodeIgniter 对象。通过使用 $CI 而不是 $this,这将使我们能够访问 CodeIgniter 的所有资源,如下面的代码片段所示:
$CI =& get_instance();
$CI->load->model('Lib_model');
return $CI->Lib_model->get();
创建自己的配置文件并使用设置
在同一文件中拥有配置设置是一个很好的主意——好处显而易见——因此,而不是将设置隐藏在控制器、模块、辅助工具、库中,或者(天哪)在视图中,你可以将它们放在一个位置,并从那里引用。CodeIgniter 在 config 文件夹中自带了自己的配置文件;然而,你可以在 config 文件夹中添加自己的文件,并在你的代码中引用它们。这非常方便且易于操作;让我们看看。
如何做到这一点...
我们将创建以下两个文件:
-
/path/to/codeigniter/application/controllers/config_settings.php -
/path/to/codeigniter/application/config/my_config_file.php
-
创建
config_settings.php控制器,打开它进行编辑,并添加以下代码:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Config_settings extends CI_Controller { function __construct() { parent::__construct(); $this->config->load('my_config_file'); } public function index() { echo $this->config->item('first_config_item'); for($i = 0; $i < $this->config->item('stop_at'); $i++) { echo $i . '<br />'; } } } -
创建配置文件,
my_config_file.php,打开它进行编辑,并添加以下代码:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); $config['first_config_item'] = "This is my config setting, there are many like it but this one is mine<br />"; $config['stop_at'] = 10;
它是如何工作的...
说实话,有那么多情况将信息放在配置文件中是有用的,以至于为你们编写一个特定的菜谱是毫无意义的,因为你们需要这个菜谱的可能性极小。所以,这只是一个概念证明;它作为基本两个文件的一个指南存在:一个配置文件和另一个文件(一个控制器)来从中获取信息。
看看控制器中的构造函数,Config_settings。这就是我们定义配置文件名称的地方。你可以随意命名(只要名称没有被另一个配置文件占用)。在这里,我将其命名为 my_config_file;虽然有点像 1995 年,但对于解释来说足够好了,我相信你们已经明白了这个意思。
接下来发生的事情是执行 public function index(),它做两件事:打印出一段文本(文本设置在我们的配置文件中)并遍历一个 for() 循环,直到达到我们在 my_config_file 中指定的值。
这两种方法展示了如何处理配置值:要么在屏幕上输出,要么在某种结构中使用该值。
使用语言类——随时切换语言
一旦您开发了一个支持多种语言的网站,您显然希望允许人们在这些语言之间切换。例如,从英语切换到法语,或从法语切换到德语,或为任何您正在开发的地区或语言。这可以通过几种方式处理,但在这个例子中,我们将使用 CodeIgniter 的 Session 类在不同的语言之间切换。
准备工作
我们将使用 Session 类来存储用户的语言偏好,这意味着我们需要使用 CodeIgniter 会话。这需要一些配置。
我们将编辑以下文件:
-
path/to/codeigniter/application/config/config.php -
path/to/codeigniter/application/config/database.php
以下是一些配置设置:
-
在
path/to/codeigniter/application/config/config.php文件中找到以下配置值,并修改它们以反映以下值:配置项 数据类型 更改为值 描述 $config['sess_cookie_name']字符串ci_session这是写入用户计算机的 cookie 的名称。 $config['sess_expiration']整型7200这是指定会话在无用户活动后应保持活跃的秒数,在此之后变为无效。 $config['sess_expire_on_close']布尔型 (True/False)TRUE这指定了如果用户关闭浏览器,会话将变为无效。 $config['sess_encrypt_cookie']布尔型 (True/False)TRUE这指定了 cookie 是否应在用户计算机上加密。出于安全考虑,应将其设置为 TRUE。$config['sess_use_database']布尔型 (True/False)TRUE这指定了是否将会话存储在数据库中。出于安全考虑,应将其设置为 TRUE。您还需要创建会话表,其代码可在下一页找到。$config['sess_table_name']字符串sessions这指定了用于存储会话数据的数据库表名称。在这个菜谱中,我称会话表为简单会话;然而,您可以保留原始的 i_sessions--只需确保相应地修改 SQL 即可。 $config['sess_match_ip']布尔型 (True/False)TRUE这指定了 CodeIgniter 是否应监控请求的 IP 地址与 session_id的对比。如果传入请求的 IP 与之前的值不匹配,则不允许会话。$config['sess_match_useragent']布尔型 (True/False)TRUE这指定了 CodeIgniter 是否应监控请求的用户代理地址与 session_id的对比。如果传入请求的用户代理与之前的值不匹配,则不允许会话。 -
在
path/to/codeigniter/application/config/database.php文件中找到以下配置值,并修改它们以反映正确的设置,以便您能够连接到您的数据库:配置项 数据类型 更改为值 描述 $db['default']['hostname']Stringlocalhost您数据库的主机名。这通常是 localhost或 IP 地址$db['default']['username']String您用于连接数据库的用户名 $db['default']['password']String连接到您的数据库时使用的密码 $db['default']['database']String您希望连接到的数据库名称 -
我们将在会话中存储用户的语言偏好,会话存储在数据库表
sessions中。以下为会话表的架构。使用您选择的方法(命令行、phpMyAdmin等),将以下 MySQL 架构输入到您的数据库中:CREATE TABLE IF NOT EXISTS `sessions` ( `session_id` varchar(40) COLLATE utf8_bin NOT NULL DEFAULT '0', `ip_address` varchar(16) COLLATE utf8_bin NOT NULL DEFAULT '0', `user_agent` varchar(120) COLLATE utf8_bin DEFAULT NULL, `last_activity` int(10) unsigned NOT NULL DEFAULT '0', `user_data` text COLLATE utf8_bin NOT NULL, PRIMARY KEY (`session_id`), KEY `last_activity_idx` (`last_activity`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
如何做到这一点...
我们将创建以下语言文件:
/path/to/codeigniter/application/language/french/fr_lang.php
...并修改以下两个文件:
-
/path/to/codeigniter/application/controllers/lang.php -
/path/to/codeigniter/application/views/lang/lang.php
-
修改
/path/to/codeigniter/application/controllers/lang.php以反映以下代码片段:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); class Lang extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('language'); $this->load->helper('security'); // Check for empty language values in the session if ($this->session->userdata('filename') == '' || $this->session->userdata('language') == '') { $change_lang = array( 'language' => 'english', 'filename' => 'en', ); $this->session->set_userdata($change_lang); $this->lang->load($this->session->userdata('filename'), $this->session->userdata('language')); } else { // Default language $this->lang->load($this->session->userdata('filename'), $this->session->userdata('language')); } } public function index() { redirect('lang/submit'); } public function submit() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); // Set validation rules $this->form_validation->set_rules('email', $this->lang->line('form_email'), 'required|min_length[1]|max_length[50]|valid_email'); // Begin validation if ($this->form_validation->run() == FALSE) { $this->load->view('lang/lang'); } } public function change_language() { $lang = xss_clean($this->uri->segment(3)); switch ($lang) { case "en": $language = 'english'; $filename = 'en'; break; case "fr": $language = 'french'; $filename = 'fr'; break; default: break; } $change_lang = array( 'language' => $language, 'filename' => $filename, ); $this->session->set_userdata($change_lang); redirect('lang/index'); } } -
修改
/path/to/codeigniter/application/views/lang/lang.php以反映以下代码片段:<html> <body> <?php echo anchor('lang/change_language/fr', 'French'); ?> <?php echo anchor('lang/change_language/en', 'English'); ?> <h2><?php echo $this->lang->line('form_title'); ?></h2> <?php echo form_open('lang/submit'); ?> <?php echo $this->lang->line('form_email'); ?> <?php echo form_input(array('name' => 'email', 'id' => 'email', 'value' => '', 'maxlength' => '100', 'size' => '50', 'style' => 'width:10%')); ?> <?php echo form_submit('', $this->lang->line('form_submit_button')); ?> <?php echo form_close(); ?> </body> </html> -
将以下代码复制到
/path/to/codeigniter/application/system/language/french/fr_lang.php文件中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); $lang['form_title'] = "Titre du formulaire en anglais"; $lang['form_email'] = "Votre E-Mail: "; $lang['form_submit_button'] = "Suggérer"; -
最后,将以下代码添加到
/path/to/codeigniter/application/system/language/english/en_lang.php文件中:<?php if (!defined('BASEPATH')) exit('No direct script access allowed'); $lang['form_title'] = "Form title in English"; $lang['form_email'] = "Email: "; $lang['form_submit_button'] = "Submit";
它是如何工作的...
这是对早期语言菜谱的扩展。我们唯一不同的地方是添加了对 $this->lang->load('', ''); 中值的切换支持。我们使用 CodeIgniter 的会话功能来存储切换的值。因此,当我们通过 URL 中的值传递所需的语言时,我们将想要使用 CodeIgniter 的安全方法 xss_clean() 来减轻跨站脚本攻击的风险。
因此,在构造函数中,我们有以下代码:
// Check for empty language values in the session
if ($this->session->userdata('filename') == '' || $this->session->userdata('language') == '') {
$change_lang = array(
'language' => 'english',
'filename' => 'en',
);
$this->session->set_userdata($change_lang);
$this->lang->load($this->session->userdata('filename'), $this->session->userdata('language'));
} else { // Default language
$this->lang->load($this->session->userdata('filename'), $this->session->userdata('language'));
}
这将检查是否有任何语言变量在会话中设置。如果没有,我们在 $change_lang 数组中定义语言并将其传递给 $this->lang->load('', ''); 函数,如下所示:
$this->lang->load($this->session->userdata('filename'), $this->session->userdata('language'));
从那里,public function index() 被加载并立即重定向到 public function submit(),显示 HTML 表单。
我们已修改了 HTML 表单,添加了以下两个锚点标签:
<?php echo anchor('lang/change_language/fr', 'French'); ?>
<?php echo anchor('lang/change_language/en', 'English'); ?>
如果用户在浏览器中点击这些链接(法语或英语)中的任何一个,将执行 public function change_language()。然后我们通过 $lang = xss_clean($this->uri->segment(3)); 获取并清理 URL 的第三个参数,并通过 PHP Switch/Case 语句查找 en 或 fr,在过程中将 $language 和 $filename 变量分配为正确的详细信息(默认加载英语)。
接下来,我们将 $language 和 $filename 变量加载到 $change_lang 数组中,并使用 $this->session->set_userdata($change_lang); 写入会话。最后,我们通过重定向回 public function index() 来结束,这将带我们回到起点,并加载新的语言。
第十章. 处理图片
在本章中,你将学习:
-
在 MAC 上使用 Cactuslab 安装 ImageMagick
-
使用 CodeIgniter 上传图片
-
生成缩略图 – 调整大小
-
旋转图片
-
裁剪图片
-
使用文字添加水印
-
使用图像叠加添加水印
-
使用 CodeIgniter CAPTCHA 提交表单
简介
CodeIgniter 提供了一系列有用的工具来帮助你以图像处理类的方式操作和修改图片;它不是 Photoshop,但对于日常 Web 开发中的大多数需求来说已经足够好了。它具有帮助您上传图片、调整大小、创建缩略图、添加水印、裁剪和旋转的功能——这些都是非常有用的事情,正是你在开发环境中所追求的。以下是操作方法。
在 MAC 上使用 Cactuslab 安装 ImageMagick
CodeIgniter 图像处理类的某些功能需要 GD2,然而,其他功能需要 ImageMagick。如果你在 MAC 上使用 MAMP,那么默认情况下你很可能没有安装它。Cactuslab 已经制作了一个安装程序,它会为你完成这项工作。
如何操作...
-
访问 URL
www.cactuslab.com/imagemagick。 -
下载安装程序。在撰写本文时,最新版本是适用于 Mac OS X 10.5 - 10.8 的 ImageMagick 6.8.6-3。
-
运行安装程序,如果一切顺利,你现在应该已经安装了 ImageMagick。你需要将
$config['library_path']的值设置为/opt/ImageMagick/bin,如下所示:'library_path' => '/opt/ImageMagick/bin'
它是如何工作的...
安装程序会为你处理所有事情:这是巫术!
使用 CodeIgniter 上传图片
这与书中前面提到的文件上传配方类似;然而,它与它不同,因为我们正在使 CodeIgniter 上传图片(而不是上传任何文件类型)并在图片上执行特定任务,这些任务与书中其他上传示例不相关。因此,请将其视为一个单独的上传脚本。此脚本是该章中其他配方的基础脚本(除了 CAPTCHA 配方之外),也就是说,旋转、水印、调整大小等配方需要此基础脚本才能运行。
如何操作...
我们将创建以下两个文件:
-
/path/to/codeigniter/application/controllers/upload.php -
/path/to/codeigniter/application/views/upload/upload.php
-
创建控制器文件,
upload.php,并将以下代码添加到其中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Upload extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); } function index() { $this->load->view('upload/upload', array('error' => ' ' )); } function do_upload() { $config['upload_path'] = '/path/to/upload/dir/'; $config['allowed_types'] = 'gif|jpg|png'; $config['max_size'] = '10000'; $config['max_width'] = '1024'; $config['max_height'] = '768'; $this->load->library('upload', $config); if (! $this->upload->do_upload()) { $error = array('error' => $this->upload->display_errors()); $this->load->view('upload/upload', $error); } else { $data = array('upload_data' => $this->upload->data()); echo '<pre>'; var_dump($data); echo '</pre>'; } } } -
创建
/path/to/codeigniter/application/views/upload/upload.php文件,并将以下代码添加到其中:<?php if ($error) : ?> <?php echo $error ; ?> <?php endif ; ?> <?php echo form_open_multipart('upload/do_upload');?> <input type = "file" name = "userfile" size = "20" /> <br /><br /> <input type = "submit" value = "upload" /> <?php echo form_close() ; ?>
它是如何工作的...
当在浏览器中运行upload控制器时,用户会看到表单(该表单位于视图文件views/ipload/upload.php中)。用户选择一个图片并按下提交按钮,然后调用公共函数do_upload()。我们立即定义一些设置,上传的图片将根据这些设置进行检查,例如允许的图片类型、最大大小和尺寸,这些是:
$config['upload_path'] = '/path/to/upload/dir';
$config['allowed_types'] = 'gif|jpg|png';
$config['max_size'] = '10000';
$config['max_width'] = '1024';
$config['max_height'] = '768';
如果上传的图片符合这些要求,图片可以被移动到由 $config['upload_path'] 指定的地方进行存储,以便在需要时使用。
生成缩略图 – 调整大小
显然,拥有生成缩略图的功能是非常有用的。大多数网络开发者都曾有过在当前上传的图片或之前上传的图片中生成缩略图的需求。通常,这种处理会直接使用 PHP 或你可能使用的任何编程语言来完成;但 CodeIgniter 给你提供了轻松创建缩略图的能力,这就是你如何做到的。
准备工作
我们将使用自己的库来完成这项工作。如果你还没有这样做(在本章的其他菜谱中),创建以下文件:
/path/to/codeigniter/application/libraries/image_manip.php
-
确保按照以下方式定义
Image_manip库类:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Image_manip { } -
还要确保你已经安装了图像库 GD2,并且已经复制并准备好了本章的“基础”菜谱——即 使用 CodeIgniter 上传图片——因为本菜谱使用 使用 CodeIgniter 上传图片 的代码作为基础菜谱。
如何操作...
我们将修改之前菜谱中的以下文件,使用 CodeIgniter 上传图片:
-
/path/to/codeigniter/application/controllers/upload.php -
/path/to/codeigniter/application/libraries/image_manip.php
-
将以下行(粗体)添加到构造函数中,使整个构造函数看起来像以下代码片段:
function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->library('image_manip'); } -
修改
do_upload()函数,将其更改为以下内容:if ( ! $this->upload->do_upload()) { $error = array('error' => $this->upload->display_errors()); $this->load->view('upload/upload', $error); } else { $data = array('upload_data' => $this->upload->data()); $result = $this->upload->data(); $origional_image = $result['full_path']; $data = array( 'image_library' => 'gd2', 'source_image' => $origional_image, 'create_thumb' => TRUE, 'maintain_ratio' => TRUE, 'width' => '75', 'height' => '50' ); if ($this->image_manip->resize_image($data)) { echo 'Image successfully resized<br /><pre>'; var_dump($result); echo '</pre>'; } else { echo 'There was an error with the image processing.'; } } -
修改
image_manip库,添加以下函数:function resize_image($data) { $CI =& get_instance(); $CI->load->library('image_lib', $data); if ($CI->image_lib->resize()) { return true; } else { echo $CI->image_lib->display_errors(); } }
它是如何工作的...
当在浏览器中运行 upload 控制器时,用户会看到一个表单(该表单位于视图文件 views/ipload/upload.php 中)。用户选择一张图片,然后按下 提交 按钮,之后调用 public function do_upload()。我们立即定义一些设置,用于检查上传的图片,例如允许的图片类型、最大尺寸和维度——就像我们在 使用 CodeIgniter 上传图片 菜谱中所做的那样。假设上传成功且没有错误,我们调用 image_manip 库中的 resize_image() 函数:
$this->image_manip->resize_image($data);
resize_image() 函数获取主 CodeIgniter 对象 $CI 并加载其自己的 image_lib 库,如下面的代码片段所示:
$CI = & get_instance();
$CI->load->library('image_lib', $data);
image_lib 库将由 CodeIgniter 使用,通过我们在 $data 数组中提供的参数对图片进行更改。
我们调用 $CI->image_lib->resize(),检查返回的 TRUE 值。如果返回 FALSE,我们则返回操作中的任何错误消息。否则,将创建一个缩略图,如下面的代码片段所示:
if ($CI->image_lib->resize()) {
return true;
} else {
echo $CI->image_lib->display_errors();
}
旋转图片
CodeIgniter 允许旋转图片;如果你需要垂直或任何其他方向的翻转,这很有用。以下是操作方法。
准备工作
我们将要使用我们自己的库来完成这个任务。如果你还没有这样做(在本章的其他菜谱中),创建以下文件:
/path/to/codeigniter/application/libraries/image_manip.php
-
确保将
image_manip库类定义为以下形式:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Image_manip { } -
还要确保你已经安装了图像库 ImageMagik,并且已经将本章的“基础”菜谱——即使用 CodeIgniter 上传图片——复制并准备好,因为这个菜谱使用使用 CodeIgniter 上传图片的代码作为基础菜谱。
如何操作...
我们将要修改以下两个文件:
-
/path/to/codeigniter/application/controllers/upload.php -
/path/to/codeigniter/application/libraries/image_manip.php
-
将以下函数添加到
image_manip.php库文件中:function rotate($data) { $CI = & get_instance(); $CI->load->library('image_lib', $data); $CI->image_lib->initialize($data); if ($CI->image_lib->rotate()) { return true; } else { echo $CI->image_lib->display_errors(); } } -
将以下行(加粗)添加到构造函数中,使整个构造函数看起来如下:
function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->library('image_manip'); } -
修改
do_upload()函数,将其修改为以下内容:if ( ! $this->upload->do_upload()) { $error = array('error' => $this->upload->display_errors()); $this->load->view('upload/upload', $error); } else { $data = array('upload_data' => $this->upload->data()); $result = $this->upload->data(); $original_image = $result['full_path']; $data = array( 'image_library' => 'imagemagick', 'library_path' => '/opt/ImageMagick/bin', 'source_image' => $original_image, 'rotation_angle' => 'vrt' ); if ($this->image_manip->rotate($data)) { echo 'Image successfully rotated<br /><pre>'; var_dump($result); echo '</pre>'; } else { echo 'There was an error with the image processing.'; } }
它是如何工作的...
当在浏览器中运行上传控制器时,用户会看到一个表单(该表单位于视图views/ipload/upload.php文件中)。用户选择一个图片并点击提交按钮,然后调用public function do_uplaod()。我们立即定义一些设置,用于检查上传的图片,例如允许的图片类型、最大大小和尺寸——就像我们在使用 CodeIgniter 上传图片菜谱中所做的那样。假设上传成功且没有错误,我们从上传数据中获取full_path:
$original_image = $result['full_path'];
将其分配为局部变量$original_image。然后我们定义一个数组($data),包含所有 CodeIgniter 需要裁剪图片的配置设置(确保获取library_path正确)。我们将这个$data数组传递给库函数rotate:
$this->image_manip->rotate($data);
这将在图片上执行旋转操作。
裁剪图片
这将最有用且相关,当与前端机制结合使用时,允许用户选择图片的一个区域;然而,我包括这段代码在这里,因为你可能需要它。你永远不知道!
准备工作
我们将要使用我们自己的库来完成这个任务。如果你还没有这样做(在本章的其他菜谱中),创建以下文件:
/path/to/codeigniter/application/libraries/image_manip.php
-
确保将
image_manip库类定义为以下形式:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Image_manip { } -
还要确保你已经安装了图像库 ImageMagik。
注意
如果您在 MAC 上使用 MAMP,那么您默认可能没有安装 ImageMagick。有一个过程可以在 MAMP 上安装 ImageMagick;然而,有一个更快的方法。Cactuslab 提供了一个安装程序,网址为
www.cactuslab.com/imagemagick,它工作得很好。本章中也有一个使用 Cactuslab 在 MAC 上安装 ImageMagick的食谱,它解释了安装过程。 -
确保你已经复制并准备好这个章节的“基础”食谱,即使用 CodeIgniter 上传图片,因为这个食谱使用使用 CodeIgniter 上传图片作为基础食谱。
如何做...
我们将修改以下两个文件:
-
/path/to/codeigniter/application/controllers/upload.php -
/path/to/codeigniter/application/libraries/image_manip.php
-
将以下行(加粗)添加到构造函数中,使整个构造函数看起来如下:
function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->library('image_manip'); } -
修改
do_upload()函数,改为以下内容:if ( ! $this->upload->do_upload()) { $error = array('error' => $this->upload->display_errors()); $this->load->view('upload/upload', $error); } else { $data = array('upload_data' => $this->upload->data()); $result = $this->upload->data(); $original_image = $result['full_path']; $data = array( 'source_image' => $original_image, 'image_library' => 'imagemagick', 'library_path' => '/opt/ImageMagick/bin', 'x_axis' => '100', 'y_axis' => '60' ); if ($this->image_manip->crop_image($data)) { echo 'Image successfully cropped<br /><pre>'; var_dump($result); echo '</pre>'; } else { echo 'There was an error with the image processing.'; } } -
修改
image_manip库中的以下函数:function crop_image($data) { $CI =& get_instance(); $CI->load->library('image_lib', $data); $CI->image_lib->initialize($data); if ($CI->image_lib->crop()) { return true; } else { echo $CI->image_lib->display_errors(); } }
它是如何工作的...
当在浏览器中运行upload控制器时,用户会看到一个表单(位于视图views/ipload/upload.php文件中)。用户选择一个图片并点击提交按钮,然后调用public function do_uplaod()。我们立即定义一些设置,用于检查上传的图片,例如允许的图片类型、最大大小和尺寸——就像我们在使用 CodeIgniter 上传图片食谱中所做的那样。假设上传成功且没有错误,我们从上传数据中获取full_path:
$original_image = $result['full_path'];
我们将其分配为一个局部变量$original_image。然后我们定义一个数组$data,包含所有 CodeIgniter 要求裁剪图片的配置设置(确保获取library_path变量正确)。我们将这个$data数组传递给库函数crop_image,如下面的代码片段所示:
$this->image_manip->crop_image($data);
这将在图片上执行裁剪操作。
可能的错误
在编写这个食谱的过程中,您可能会看到一些或所有(或者可能是完全不同的)错误信息。以下是一些错误及其可能的解决方案(如果其他方法都失败了,请尝试 Google 搜索):
错误:图片处理失败。请验证您的服务器支持所选协议,并且您的图片库路径正确。
可能的解决方案:可能是您的图片库路径不正确或图片库的配置设置错误。请验证您是否安装了正确的库并且路径正确。为此,请执行以下步骤:
-
在您的终端中输入
cd /usr/X11R6/bin。 -
然后输入
ls(或者在 Windows 上输入dir)。 -
在那里寻找 ImageMagick 库。如果您看不到它,那么它可能尚未安装,您需要安装它才能执行裁剪操作。
你如何安装 ImageMagick?嗯,互联网上有许多说明和教程可以帮助你。然而,如果你使用 MAMP,请访问本章中的在 MAC 上使用 Cactuslab 安装 ImageMagick配方,并使用安装程序来帮助安装 ImageMagick 并完成配置工作。
然而,请注意,库路径不会像 CodeIgniter 文档中那样是/use/X11R6/bin/(带有尾随斜杠),而是/opt/ImageMagick/bin(不带尾随斜杠)。
使用文本添加水印
添加水印可以是一个有用的方式来标记图片上的版权(只是确保你是版权所有者)。CodeIgniter 提供了一个简单的方法来给图片添加水印。水印可以是文本或图像叠加,并且可以放置在原始图片的任何位置。以下是如何添加文本水印的描述。
准备工作
我们将使用我们自己的库来完成这项工作。如果您还没有这样做(在本章的其他配方中),请创建以下文件:
/path/to/codeigniter/application/libraries/image_manip.php
-
确保将
image_manip库类定义为以下形式:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Image_manip { } -
还要确保您已经安装了图像库,GD2。
-
确保您已经复制并准备好了本章的基础配方使用 CodeIgniter 上传图片,因为这个配方使用使用 CodeIgniter 上传图片作为基础配方。
如何实现...
我们将修改以下两个文件:
-
/path/to/codeigniter/application/controllers/uplod.php -
/path/to/codeigniter/application/libraries/image_manip.php
-
将以下函数添加到
image_manip.php库文件中:function do_watermark($data) { $CI =& get_instance(); $CI->load->library('image_lib', $data); $CI->image_lib->initialize($data); if ($CI->image_lib->watermark()) { return true; } else { echo $CI->image_lib->display_errors(); } } -
将以下行(加粗)添加到构造函数中,使整个构造函数看起来如下:
function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->library('image_manip'); } -
修改
do_upload()函数,将其更改为以下内容:if ( ! $this->upload->do_upload()) { $error = array('error' => $this->upload->display_errors()); $this->load->view('upload/upload', $error); } else { $data = array('upload_data' => $this->upload->data()); $result = $this->upload->data(); $original_image = $result['full_path']; $data = array( 'image_library' => 'GD2','source_image' => $original_image,'wm_text' => 'Copyright (c) ' . date("Y",time()) . ' - YOUR NAME','wm_type' => 'text','wm_font_path' => './system/fonts/texb.ttf', 'wm_font_size' => '16','wm_font_color' => 'ffffff','wm_vrt_alignment' => 'middle','wm_hor_alignment' => 'left','wm_padding' => '20' ); if ($this->image_manip->do_watermark($data)) { echo 'Image successfully watermarked<br /><pre>'; var_dump($result); echo '</pre>'; } else { echo 'There was an error with the image processing.'; } }
工作原理...
当在浏览器中运行upload控制器时,用户会看到一个表单(该表单位于视图文件views/ipload/upload.php中)。用户选择一张图片并点击提交按钮,随后调用public function do_uplaod()。我们立即定义一些设置,用于检查上传的图片,例如允许的图片类型、最大尺寸和维度,就像我们在使用 CodeIgniter 上传图片配方中所做的那样。假设上传成功且没有错误,我们从上传数据中获取full_path:
$original_image = $result['full_path'];
将其分配为局部变量,$original_image。接下来,我们将定义一个数组($data),包含所有必要的设置,以允许 CodeIgniter 在我们的上传图片上执行水印叠加。以下表格中我将介绍一些有趣的设置:
| 设置 | 选项 | 我们如何应用它 |
|---|---|---|
wm_type |
text, overlay |
在这个菜谱中,它设置为 text,这告诉 CodeIgniter 它必须在图像上写文本,而不是调用图像作为叠加。在下一个菜谱中,我们将查看叠加水印。 |
wn_vrt_alignment |
top, middle, bottom |
我们告诉 CodeIgniter 它应该将文本放置在上传图像的中间。 |
wn_hor_alignment |
left, center, right |
我们告诉 CodeIgniter 它应该将文本放置在上传图像的左侧。 |
wm_font_color |
任何十六进制值(有关有用 URL 的提示,请参阅以下内容) | 我们将文本写成白色,没有其他原因,只是因为我用来测试这段代码的图像相当暗——一个浪漫的日落(啊)——但您当然可以将其更改为您想要的任何十六进制值。 |
wm_font_path |
./system/fonts/texb.ttf |
这是 CodeIgniter 附带的自带字体;它有点工业风格,您可能想更换另一个,要么将不同的真型字体复制到./system/fonts/目录中,要么链接到该目录之外的字体。 |
以下 URL 有一个十六进制颜色值的列表:www.w3schools.com/html/html_colors.asp。
添加图像叠加水印
CodeIgniter 可以根据前面的菜谱以文本形式添加水印,但 CodeIgniter 还可以通过在基础图像上叠加水印图像来实现。以下是操作方法...
准备工作
我们将使用自己的库来完成这个任务。如果您还没有这样做(在本章的其他菜谱中),请创建以下文件:
/path/to/codeigniter/application/libraries/image_manip.php
-
确保将
image_manip库类定义为以下内容:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Image_manip { } -
这个菜谱基于添加文本水印菜谱。请确保您已经遵循了那个菜谱。我们将对其进行一些代码更改,以帮助我们实现水印叠加。
如何做...
我们将要修改以下文件:
-
/path/to/codeigniter/application/controllers/uplod.php -
/path/to/codeigniter/application/libraries/image_manip.php
-
将以下函数添加到库文件
image_manip.php中:function do_watermark($data) { $CI =& get_instance(); $CI->load->library('image_lib', $data); $CI->image_lib->initialize($data); if ($CI->image_lib->watermark()) { return true; } else { echo $CI->image_lib->display_errors(); } } -
将以下行(加粗)添加到构造函数中,以便整个构造函数看起来像这样:
function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->library('image_manip'); } -
修改
do_upload()函数,将其更改为以下内容:if (! $this->upload->do_upload()) { $error = array('error' => $this->upload->display_errors()); $this->load->view('upload/upload', $error); } else { $data = array('upload_data' => $this->upload->data()); $result = $this->upload->data(); $original_image = $result['full_path']; // Create Watermark $data = array( 'image_library' => 'gd2','source_image' => $original_image, 'wm_type' => 'overlay','wm_overlay_path' => $config['upload_path'] . '/overlay_image_name', 'wm_font_path' => './system/fonts/texb.ttf','wm_font_size' => '16','wm_font_color' => 'ffffff','wm_vrt_alignment' => 'middle','wm_hor_alignment' => 'left','wm_padding' => '20' ); $this->image_manip->do_watermark($data); }
它是如何工作的...
这与添加文本水印菜谱的基本功能相同。然而,与wm_type设置为text不同,我们将其设置为overlay。我们添加了配置数组项wm_overlay_path,并将其设置为存储叠加图像的位置(在这种情况下,我们将叠加图像放置在上传文件夹中;当然,您可以在系统上的任何位置移动它,但这里是为了保持简单)。我们还删除了数组项wm_text,现在它不再需要(然而,如果您愿意,可以保留它,它不会干扰图像叠加)。
使用 CodeIgniter CAPTCHA 提交表单
有时,除了转义和验证用户输入之外,还需要在表单中添加更多的安全性;有时你可能希望确保输入数据并提交表单的是人类而不是某些脚本或机器人。
一种经过验证和测试的方法是CAPTCHA。CAPTCHA 有其他替代方案;例如,一个数学问题(比如 10 + 7 是什么)在你的应用程序中相当容易构建。一种新方法是让用户玩一个简短的游戏。根据他们的表现,他们会被评估为人类或机器人;areyouahuman.com 是一个很好的资源。但到目前为止,我们将专注于 CodeIgniter 的 CAPTCHA 功能,为我们创建一个受 CAPTCHA 保护的表单。
准备工作
我们将把 CodeIgniter 为我们生成的 CAPTCHA 信息存储在数据库中的一个表中。为了做到这一点,我们首先需要创建这个表。以下是在数据库中执行此操作的 MySQL 代码。将以下内容复制到您的数据库中:
CREATE TABLE `captcha` (`captcha_id` bigint(13) unsigned NOT NULL AUTO_INCREMENT,`captcha_time` int(10) unsigned NOT NULL,`ip_address` varchar(16) NOT NULL DEFAULT '0',`word` varchar(20) NOT NULL,PRIMARY KEY (`captcha_id`),KEY `word` (`word`)) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
如何实现...
我们将创建以下四个文件:
-
/path/to/codeigniter/application/controllers/comments.php: 这是一个控制器,它帮助处理用户提交的姓名、电子邮件地址和评论,并且还会调用一个助手来处理 CAPTCHA 数据 -
/path/to/codeigniter/application/helpers/make_captcha_helper.php: 这个助手包含了生成 CAPTCHA 图像所需的代码 -
/path/to/codeigniter/application/models/captcha_model.php: 这个模型用于检查用户提交的 CAPTCHA 值与数据库中存储的值是否匹配 -
/path/to/codeigniter/application/views/comments/post_form.php: 这个视图文件将显示表单(姓名、电子邮件、评论等)和 CAPTCHA 图像
-
创建
controllers/comments.php控制器文件,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Comments extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('form'); $this->load->helper('url'); $this->load->helper('make_captcha'); $this->load->model('Captcha_model'); } public function index() { $this->load->library('form_validation'); $this->form_validation->set_error_delimiters('', '<br />'); $this->form_validation->set_rules('name', 'Name', 'required|max_length[225]'); $this->form_validation->set_rules('email', 'Email', 'required|max_length[225]'); $this->form_validation->set_rules('message', 'Message', 'required|max_length[225]'); $this->form_validation->set_rules('captcha', 'Captcha', 'required|max_length[225]'); // Begin validation if ($this->form_validation->run() == FALSE) { $data['img'] = make_captcha(); $this->load->view('comments/post_form', $data); } else { $expiration = time() - 7200; $this->Captcha_model->delete_expired($expiration); $data = array( 'captcha' => $this->input->post('captcha'), 'ip_address' => $this->input->ip_address(), 'expiration' => $expiration); $num_rows = $this->Captcha_model->does_exist($data); if ($num_rows == 0) { $data['error'] = "Type the word in the image."; $data['img'] = make_captcha(); $this->load->view('comments/post_form', $data); } else { echo 'CAPTCHA OKAY - HERE IS YOUR POST:' . '<br />'; echo $this->input->post('name') . '<br />'; echo $this->input->post('email') . '<br />'; echo $this->input->post('message') . '<br />'; } } } } -
接下来创建
helpers/make_captcha_helper.php助手文件,并将以下代码添加到其中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); function make_captcha() { $CI =& get_instance(); $CI->load->helper('captcha'); $vals = array( 'img_path' => '/file/path/to/image/captcha/', 'img_url' => 'http://url/to/image/captcha/', 'font_path' => './system/fonts/texb.ttf', 'img_width' => '150', 'img_height' => 30, 'expiration' => 7200 ); $cap = create_captcha($vals); $data = array( 'captcha_time' => $cap['time'], 'ip_address' => $CI->input->ip_address(), 'word' => $cap['word'] ); $query = $CI->db->insert_string('captcha', $data); $CI->db->query($query); return $cap['image']; }小贴士
img_path和img_url是这里两个有趣的设置。img_path应该是文件系统上图片文件夹的路径,而img_url应该是图片在网页浏览器中显示的路径。CodeIgniter 使用img_url来构建一个 HTMLimg标签,并且这个标签会被发送到post_form作为$data['img']。 -
创建
models/captcha_model.php模型文件,并将以下代码添加到其中:<?php if (! defined('BASEPATH')) exit('No direct script access allowed'); class Captcha_model extends CI_Model { function __construct() { parent::__construct(); } public function delete_expired($expiration) { $this->db->where('captcha_time < ',$expiration); $this->db->delete('captcha'); } public function does_exist($data) { $this->db->where('word', $data['captcha']); $this->db->where('ip_address', $data['ip_address']); $this->db->where('captcha_time > ', $data['expiration']); $query = $this->db->get('captcha'); return $query->num_rows(); } } -
然后创建
comments/post_form.php视图文件,并将以下代码添加到其中:<?php echo form_open() ; ?> <?php echo validation_errors() ; ?> <?php if (isset($errors)) { echo $errors ; } ?> <br /> Name <input type = "text" name = "name" size = "5" value = "<?php echo set_value('name') ; ?>"/><br /> Email <input type = "text" name = "email" size = "5" value = "<?php echo set_value('email') ; ?>"/><br /> Message <br /> <textarea name = "message" rows = "4" cols = "20" /><?php echo set_value('message') ; ?></textarea><br /> Please enter the string you see below <input type = "text" name = "captcha" value = "" /> <br /> <?php echo $img ; ?> <br /> <input type = "submit" value = "Submit" /> <?php echo form_close() ; ?>
它是如何工作的...
评论控制器加载 public function index(),它设置了当用户提交表单时的验证环境。由于 $this->form_validation->run() 将等于 FALSE(因为表单尚未提交),所以会调用 make_captcha_helper 函数,即 make_captcha(),并将它的返回值发送到 comments/post_form 视图,如下面的代码片段所示:
if ($this->form_validation->run() == FALSE) {
$data['img'] = make_captcha();
$this->load->view('comments/post_form', $data);
} else {
... etc
make_captcha_helper 函数,make_captcha(),将获取主要的 CodeIgniter 对象,如下面的代码片段所示:
$CI = & get_instance();
$CI->load->helper('captcha');
它定义了构建 CAPTCHA 图像所需的价值,如下面的代码片段所示:
$vals = array(
'img_path' => '/file/path/to/image/',
'img_url' => 'http://url/to/image/',
'font_path' => './system/fonts/texb.ttf',
'img_width' => '150',
'img_height' => 30,
'expiration' => 7200
);
这些值存储在 $data 数组中,该数组传递给 CodeIgniter 辅助程序 captcha,它返回给我们 $cap 数组。$cap 传递给数据库函数 insert_string(),以便将 CAPTCHA 信息保存到数据库中,如下面的代码片段所示:
$cap = create_captcha($vals);
$data = array(
'captcha_time' => $cap['time'],
'ip_address' => $CI->input->ip_address(),
'word' => $cap['word']
);
$query = $CI->db->insert_string('captcha', $data);
我们将使用数据库中的这一行与用户提交表单时输入的数据进行比较。
最后,make_captcha 辅助程序返回一个 HTML img 标签字符串到我们的评论控制器。这被保存在 $data 数组中,并传递到 post_form 视图文件。
然后用户会看到一个 HTML 表单,其中包含姓名、电子邮件、评论输入,以及一个 CAPTCHA 图像和一个用于输入看到的 CAPTCHA 字符串的文本框。用户完成表单,仔细输入他们的数据以及 CAPTCHA 图像中的字符串,然后点击 提交 按钮。
假设验证通过(没有表单错误),则评论控制器将开始将用户输入的 CAPTCHA 字符串与由 make_captcha 辅助程序在数据库中创建的字符串进行比较。
它通过首先清理数据库中的旧 CAPTCHA 行(在这个例子中,旧的是任何超过两小时的数据)来启动这个过程;它是通过定义当前时间(作为一个 Unix 时间戳)减去两小时(或 7200 秒)来做到这一点的,这被设置为 $expiration 时间,如下面的代码片段所示:
$expiration = time() - 7200;
$this->Captcha_model->delete_expired($expiration);
调用 Captcha_model 函数 delete_expired(),并将过期时间传递给它。这个模型函数将删除数据库中 captcha_time 小于过期时间的行,如下面的代码片段所示:
public function delete_expired($expiration) {
$this->db->where('captcha_time < ',$expiration);
$this->db->delete('captcha');
}
一旦从数据库中删除了旧的 CAPTCHA,就创建并填充了包含用户的 CAPTCHA 输入、他们的 IP 地址以及再次的 $expiration 时间(我们用来删除旧行的那个)的 $data 数组。这个 $data 数组传递给 Captcha_model 函数 does_exist()。这个模型函数将检查用户输入的 CAPTCHA 字符串是否存在于数据库中,如果是,则有效(即小于两小时且与提供的 IP 地址匹配)。模型函数返回找到的行数,如下面的代码片段所示
public function does_exist($data) {
$this->db->where('word', $data['captcha']);
$this->db->where('ip_address', $data['ip_address']);
$this->db->where('captcha_time > ', $data['expiration']);
$query = $this->db->get('captcha');
return $query->num_rows();
}
如果没有行存在,则 $data['errors'] 被赋予一个错误消息。再次调用 make_captcha(),生成一个新的 CAPTCHA 图像并发送到 post_form 视图,错误消息显示在新的 CAPTCHA 图像上方。然后系统等待用户再次填写表格并再次尝试。
然而,如果结果不是零,那么用户输入的 CAPTCHA 字符串就是正确的,因此我们会向他们显示一条快速消息并回显他们的输入。实际上,你可以在这里做你喜欢的事情,比如处理他们的消息并将其保存到博客订阅源,或者将他们重定向到网站上的另一个区域,无论你想要什么。
第十一章:SEO、缓存和记录
在本章中,你将学习:
-
在 CodeIgniter 中使用 SEO 友好的 URL
-
使用 CodeIgniter 缓存
-
使用 CodeIgniter 记录错误
-
对你的应用程序进行基准测试
简介
在本章中,我们将查看各种可用于 CodeIgniter 的缓存配方。我们将使用 CodeIgniter 的缓存功能来帮助我们存储数据源和数据库结果——尽管数据源和数据库结果实际上只是作为向你展示如何将数据输入到 CodeIgniter 中,以便它可以进行缓存的一种手段——数据输入可以是任何你希望的内容。我们还将探讨使用 CodeIgniter 路由实现 SEO 友好 URL 的方法。
在 CodeIgniter 中使用 SEO 友好的 URL
在某个时候,你可能想要改变 CodeIgniter 处理 URL 到控制器路由的方式。默认情况下,CodeIgniter 将 URL 分割成几个不同的部分。显然,URL 中有域名部分(www.domain.com),但之后通常(但不总是)还有三个更多项目,每个项目之间由正斜杠分隔。第一个项目是控制器,第二个是控制器中的函数(或方法,如果你愿意),第三个是你将传递给控制器中函数的参数。
因此,CodeIgniter 中的一个标准 URL 可能看起来像www.domain.com/users/edit/1。所以,用户编号1正在使用users控制器中的edit函数进行编辑——这看起来很简单,我相信你很熟悉它。
然而,可能会有时候你希望它改变。在config/routes.php文件中设置一个规则,可以将 URL 映射到控制器,但隐藏在地址栏中显示的内容;例如,你可能有一个名为bulletin的控制器,你希望它在 URL 中显示为news。
如何操作...
我们将创建一个文件,并修改另一个文件,通过以下步骤进行:
-
创建
/path/to/codeigniter/application/controllers/shop.php文件,并将其中的以下代码添加到该文件中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Shop extends CI_Controller { function __construct() { parent::__construct(); } public function index() { echo 'Controller: ' . __CLASS__ . ', Method: ' . __FUNCTION__; } public function product() { echo 'Controller: ' . __CLASS__ . ', Method: ' . __FUNCTION__; echo '<br />'; echo 'Product ID: ' . $this->uri->segment(2); } public function all() { echo 'Controller: ' . __CLASS__ . ', Method: ' . __FUNCTION__; } } -
在你的编辑器中打开
/path/to/codeigniter/config/routes.php文件,并相应地进行修改(更改已突出显示):$route['default_controller'] = "default_controller"; $route['404_override'] = ''; $route['item/(:any)'] = "shop/product"; $route['item'] = "shop/all";
它是如何工作的...
查看以下config/routes.php文件中的以下行:
$route['item/(:any)'] = "shop/product";
$route['item'] = "shop/all";
现在,想象它们由左右两部分组成,即在=符号之前是左部分,在=符号之后是右部分;左部分映射到右部分,左部分的值将映射到右部分的值。
在前面的例子中,任何以item开头的控制器名称的 URL 都将映射到shop控制器。让我们逐一查看每个规则:
通过在浏览器中输入路由的名称(例如,http://www.your_web_site_name.com/item),item 命令将导致 CodeIgniter 调用 shop 控制器,并在该控制器中调用 public function all() 方法。因为我们已经在路由文件中定义了它,所以你可以在浏览器中通过输入 item 到 URL 中看到这一点。CodeIgniter 将请求的 URL 映射到路由规则中定义的路径,并且(在我们的例子中)调用 shop 控制器,它使用 __CLASS__ 和 __FUNCTION__ 将以下输出写入浏览器:

现在,看看浏览器地址栏中的 URL,它仍然会显示 item,但屏幕上的文本说正在调用的是 shop 控制器。我们现在已经将 URL 映射到控制器,而用户却毫无察觉。
现在,让我们看看其他的路由规则--:any。通过在浏览器中输入路由的名称(例如,http://www.your_web_site_name.com/item/123456),item/123456 命令将导致 CodeIgniter 调用 shop 控制器和 public function product(),因为我们已经在路由文件中定义了它。在 public function product() 中,我们不仅输出了 __CLASS__ 和 __FUNCTION__ 名称,还输出了 URI 的第二个部分:
$this->uri->segment(2);
在这种情况下,是产品的 ID (12356),所以你将在浏览器中看到以下截图:

这条路由的关键在于路由映射规则中的 (:any) 标志;(:any) 告诉 CodeIgniter,URI 的第二个部分,无论是什么,都应该映射到 shop 控制器的 product 函数。因为我们是在编写代码,所以我们会知道 URL 的第二个 URI 段是产品 ID,我们可以用它来查询数据库以获取该产品的详细信息。
使用 CodeIgniter 缓存
你可以使用 CodeIgniter 缓存来缓存(或临时存储)几乎任何东西。作为一个使用 CodeIgniter 的缓存示例,我们将缓存一个 RSS 源。我们当然可以缓存我们想要的任何东西;然而,缓存一个 RSS 源是一个好的开始。处理 RSS 非常简单,这个配方可以很容易地转换为缓存来自其他来源的源,例如调用 Twitter 等。
如何操作...
我们将创建 /path/to/codeigniter/application/controllers/rss_cache.php 文件。
-
创建前面的文件,并向其中添加以下代码:
<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Rss_cache extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('xml'); $this->load->driver('cache', array('adapter' => 'apc')); } public function index() { $raw_feed = '<?xml version = "1.0" encoding = "ISO-8859-1" ?> <rss version="2.0"> <channel> <title>RSS Feed</title> <link>http://www.domain.com</link> <description>General Description</description> <item> <title>RSS Item 1 Title</title> <link>http://www.domain1.com/link1</link> <description>Description of First Item</description> </item> <item> <title>RSS Item 2 Title</title> <link>http://www.domain2.com/link2</link> <description>Description of Second Item</description> </item> <item> <title>RSS Item 3 Title</title> <link>http://www.domain3.com/link3</link> <description>Description of Third Item</description> </item> </channel> </rss>'; $feed = new SimpleXmlElement($raw_feed); if (!$cached_feed = $this->cache->get('rss')) { foreach ($feed->channel->item as $item) { $cached_feed . = $item->title . '<br />' . $item->description . '<br /><br />'; } $this->cache->save('rss', $cached_feed, 7); } echo $this->cache->get('rss'); } public function clear_cache() { $this->cache->clean(); redirect('rss_cache'); } } ?>
它是如何工作的...
首先,让我们看看构造函数。我们正在加载一个助手和一个驱动程序,如下面的代码片段所示:
function __construct() {
parent::__construct();
$this->load->helper('url');
$this->load->driver('cache', array('adapter' => 'apc'));
}
小贴士
要使用 APC,你需要确保你正在工作的环境中安装了 APC。如果没有,你需要安装它。为此,请访问 www.php.net/manual/en/apc.setup.php 获取更多详细信息。
由于我们使用了redirect()函数,所以存在 URL 辅助器;缓存驱动器用于提供支持以帮助我们缓存数据,在这种情况下,它帮助我们缓存来自 RSS 源的数据--然而,它实际上可以是任何东西。
public function index()首先使用硬编码的 RSS 源定义了$rss_feed变量;这只是为了说明。实际上,你将使用以下方式获取源:
$raw_feed = file_get_contents('http://www.domain.com/rss_feed_url');
然而,在这个菜谱中硬编码它是方便的,因为通过硬编码,你可以看到源的结构,并知道它应该“看起来”是什么样子
该源具有简单的结构,只包含三个项目。$raw_feed变量被传递给 PHP 的SimpleXMLElement类,它为我们返回一个对象($feed),我们可以用它来工作。
然后,我们使用 CodeIgniter 检查缓存中是否存在名为rss的项;如果没有,我们将遍历$feed对象,从 RSS 源中的每个项目提取标题和描述,并将它们连接成一个字符串,该字符串命名为$cached_feed。
if (!$cached_feed = $this->cache->get('rss')) {
foreach ($feed->channel->item as $item) {
$cached_feed .= $item->title . '<br />' . $item->description . '<br /><br />';
}
$this->cache->save('rss', $cached_feed, 30);
}
$cached_feed字符串被保存到缓存中,名称为rss,持续时间为 30 秒(有关缓存持续时间的更多信息,请参阅以下代码):
$this->cache->save('rss', $cached_feed, 30);
一旦 RSS 源中的所有项目都已被处理,我们将输出缓存项rss,如下代码片段所示:
echo $this->cache->get('rss');
你应该在浏览器中看到以下类似的输出:
RSS Item 1 Title
Description of First Item
RSS Item 2 Title
Description of Second Item
RSS Item 3 Title
Description of Third Item
为什么是 30 秒?因为这将是足够的时间让你去浏览器运行脚本,查看前面的输出,然后快速回到你的文本编辑器中的代码,更改源中的某些内容(例如第三项的标题元素改为Gigantic Elephants)。点击保存,然后回到浏览器刷新,这应该在第一次运行rss_cache后的 30 秒(30 秒)给你以下输出:
RSS Item 1 Title
Description of First Item
RSS Item 2 Title
Description of Second Item
Gigantic Elephants
Description of Third Item
如果你觉得 30 秒的等待时间太长,你可以通过运行public function clear_cache()手动清除缓存,这将调用 CodeIgniter 的$this->cache->clean()函数,并将我们重定向回rss_cache控制器,整个过程将重新开始。
或者,你可以将缓存项有效的时间长度从 30 秒减少到,比如说,10 秒(但现实中,你可能希望它是一个适合你服务器或数据的时间长度)。
因此,现在你可以看到如何在缓存中存储数据,它不一定是 XML 或 RSS 源,它实际上可以是来自任何来源的数据:数据库查询(尽管 CodeIgniter 为此提供了特定的数据库缓存方法),来自社交链接的源(例如 Twitter 源或 Instagram),甚至是每 5 分钟获取一次的金融数据,如 FTSE 的价值(或者你设置的任何时长)。
你可能遇到的问题
如果您在 MAC 上使用 MAMP 进行开发,那么默认的缓存方法很可能是 XCache。CodeIgniter 没有与 XCache 一起工作的驱动程序,您可能需要编写自己的驱动程序(勇敢地去做),或者(像我一样)将缓存引擎更改为 APC。
小贴士
APC(替代 PHP 缓存)是一种缓存服务,在这种情况下由 MAMP 提供。
您需要告诉 MAMP 使用 APC 而不是 XCache。为此,打开您的 MAMP 控制面板,点击偏好设置,然后点击PHP选项卡。现在您应该看到一个名为PHP 扩展的部分。在该部分中应该有一个下拉列表(这可能是设置为 XCache);从该列表中选择 APC,然后点击确定按钮。MAMP 将重新启动,在此之后,您应该可以正常使用。
使用 CodeIgniter 记录错误
在您的 CodeIgniter 应用程序中记录发生的错误不必仅限于查看 PHP 或 Apache 日志;您可以通过 CodeIgniter 的日志功能启用 CodeIgniter 在代码的某些点处理和记录错误以及其他行为和事件。这个功能(如果设置正确)可以特别有用,用于跟踪用户的系统旅程和进度,如果他们正在执行的操作出现任何问题,您可以在日志中查找并追踪他们所做的一切以及何时发生,从而更好地了解(如果有的话)发生了什么,并希望考虑如何防止其再次发生。
在本配方中,我们将探讨在 CodeIgniter 中使用日志功能以及跟踪操作中是否出现错误。
准备工作
我们需要在配置文件中设置日志报告级别,以便 CodeIgniter 知道要报告哪种级别的日志消息。日志文件夹也应具有写权限,因此请执行以下步骤:
-
打开
/path/to/codeigniter/application/config/config.php文件,找到以下行:$config['log_threshold'] = 0;您可以将
log_threshold配置数组元素的值更改为以下五种状态之一,具体如下表所示:状态 用途 描述 0- CodeIgniter 将不记录任何内容。 1log_message('error', 'Some Text')CodeIgniter 将记录错误信息。 2log_message('debug', 'Some Text')CodeIgniter 将记录调试信息。 3log_message('info', 'Some Text');CodeIgniter 将记录信息消息。 4对于我们的配方,我已将值设置为
4,因为我希望记录 CodeIgniter 可能为我生成的任何错误消息或信息消息。 -
查找以下行:
$config['log_path'] = '/path/to/log/folder/';.确保在
$config['log_path']中正确设置了/path/to/log/folder/,并且指定的文件夹具有写权限(否则,CodeIgniter 无法将该文件夹中的日志文件写入)。
如何操作...
我们将使用(因为它很方便)本章前面提到的 使用 CodeIgniter 缓存 菜谱,并对其进行修改以应用 CodeIgniter 日志。在这个菜谱中,我们将它的名称更改为 cache_log.php(以保持与 rss_cache.php 分离)。所以,如果您还没有这样做(不要忘记更改名称,以下代码中突出显示):
-
创建
/path/to/codeigniter/application/controllers/cache_log.php文件,并将以下代码添加到其中(更改已在代码中突出显示):<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Cache_log extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->helper('xml'); $this->load->driver('cache', array('adapter' => 'apc')); } public function index() { $raw_feed = '<?xml version="1.0" encoding="ISO-8859-1" ?> <rss version="2.0"> <channel> <title>RSS Feed</title> <link>http://www.domain.com</link> <description>General Description</description> <item> <title>RSS Item 1 Title</title> <link>http://www.domain1.com/link1</link> <description>Description of First Item</description> </item> <item> <title>RSS Item 2 Title</title> <link>http://www.domain2.com/link2</link> <description>Description of Second Item</description> </item> <item> <title>RSS Item 3 Title</title> <link>http://www.domain3.com/link3</link> <description>Description of Third Item</description> </item> </channel> </rss>'; $feed = new SimpleXmlElement($raw_feed); if (!$feed) { log_message('error', 'Unable to instantiate SimpleXmlElement.'); } else { log_message('info', 'SimpleXmlElement was instantiated correctly.'); } if (!$cached_feed = $this->cache->get('rss')) { foreach ($feed->channel->item as $item) { $cached_feed .= $item->title . '<br />' .$item->description . '<br /><br />'; } $this->cache->save('rss', $cached_feed, 7); log_message('info', 'Cache item saved.'); } echo $this->cache->get('rss'); } public function clear_cache() { if (!$this->cache->clean()) { log_message('error', 'Unable to clear Cache.'); } else { log_message('info', 'Cache cleared.'); } redirect('rss_cache'); } } ?>如果一切顺利,您不应该有任何错误,但在配置文件中应该有一些
DEBUG数据。当您打开该文件时,您应该看到类似以下内容:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); ?> DEBUG - 2013-09-24 19:46:11 --> Config Class Initialized DEBUG - 2013-09-24 19:46:11 --> Hooks Class Initialized DEBUG - 2013-09-24 19:46:11 --> Utf8 Class Initialized DEBUG - 2013-09-24 19:46:11 --> UTF-8 Support Enabled DEBUG - 2013-09-24 19:46:11 --> URI Class Initialized DEBUG - 2013-09-24 19:46:11 --> Router Class Initialized DEBUG - 2013-09-24 19:46:11 --> Output Class Initialized DEBUG - 2013-09-24 19:46:11 --> Security Class Initialized DEBUG - 2013-09-24 19:46:11 --> Input Class Initialized DEBUG - 2013-09-24 19:46:11 --> XSS Filtering completed DEBUG - 2013-09-24 19:46:11 --> XSS Filtering completed DEBUG - 2013-09-24 19:46:11 --> XSS Filtering completed DEBUG - 2013-09-24 19:46:11 --> CRSF cookie Set DEBUG - 2013-09-24 19:46:11 --> Global POST and COOKIE data sanitized DEBUG - 2013-09-24 19:46:11 --> Language Class Initialized DEBUG - 2013-09-24 19:46:11 --> Loader Class Initialized DEBUG - 2013-09-24 19:46:11 --> Database Driver Class Initialized DEBUG - 2013-09-24 19:46:11 --> Session Class Initialized DEBUG - 2013-09-24 19:46:11 --> Helper loaded: string_helper DEBUG - 2013-09-24 19:46:11 --> Encrypt Class Initialized DEBUG - 2013-09-24 19:46:11 --> Session routines successfully run DEBUG - 2013-09-24 19:46:11 --> XML-RPC Class Initialized DEBUG - 2013-09-24 19:46:11 --> Controller Class Initialized DEBUG - 2013-09-24 19:46:11 --> Helper loaded: url_helper DEBUG - 2013-09-24 19:46:11 --> Helper loaded: xml_helper INFO - 2013-09-24 19:46:11 --> SimpleXmlElement was instantiated correctly. INFO - 2013-09-24 19:46:11 --> Cache item saved. DEBUG - 2013-09-24 19:46:11 --> Final output sent to browser DEBUG - 2013-09-24 19:46:11 --> Total execution time: 0.0280您可以在日志中看到我们在代码中设置的 info 项;我已经突出显示它们以便它们突出显示。
它是如何工作的...
您可以看到我们在控制器执行的各个阶段添加了一些条件语句,检查某些 CodeIgniter 函数的返回值(更改已在之前的代码中突出显示)。根据返回值(TRUE 或 FALSE),我们将使用 CodeIgniter 的 log_message() 函数将信息写入日志,但让我们更仔细地看看这些消息以及它们何时被触发。
首先,我们将尝试实例化一个新的 SimpleXmlElement() 对象。如果我们得到返回的对象,日志中会写入一条信息消息(SimpleXmlElement() 实例化正确)。如果有错误,我们将错误消息写入日志(无法实例化 SimpleXmlElement());请看以下代码片段:
$feed = new SimpleXmlElement($raw_feed);
if (!$feed) {
log_message('error', 'Unable to instantiate SimpleXmlElement.');
} else {
log_message('info', 'SimpleXmlElement was instantiated correctly.');
}
您可以看到我们正在使用 CodeIgniter 的日志功能将消息写入日志文件,并将这些消息定义为错误或信息;这有助于调试用户的旅程,因为您将知道什么是真正的错误,以及您输入到日志中的信息。
日志风格
我发现将日志消息写成以下代码片段很有用:
log_message('info', ' **** ' . __LINE__ . ' – This is a message.');
在前面的代码片段中,我们将消息定义为信息,但消息以四个星号(****)开始。这将使消息在查看日志时突出显示,接下来是 __LINE__ 参数(让您知道在脚本中的触发位置),然后是实际的消息--这里是非常无趣的--' - This is a message'。
您可能希望根据需要添加 __FILE__、__CLASS__ 或 __FUNCTION__ 以提高准确性。
应用程序的基准测试
基准测试对你来说可能很有用,因为它可以让你了解你的应用程序如何处理计算所有代码的任务。它可以让你知道在你的应用程序中哪里可能存在速度慢的问题,无论是由于内存限制,还是可能是因为一段特别计算密集的代码块。利用这些信息,你可以确定是否存在任何瓶颈,并且如果你能够清除它们,可能通过重新编程或分配额外资源。下面是如何进行的。
准备工作
许多网络应用程序都会链接到某种数据库,作为基准测试数据库连接性的一个例子,我们将查询一个数据库。为了做到这一点,我们显然需要一个数据库来连接。将以下 MySQL 代码复制到你的数据库中:
CREATE TABLE `bench_table` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`firstname` varchar(50) NOT NULL,
`lastname` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=3 ;
INSERT INTO `bench_table` (`id`, `firstname`, `lastname`) VALUES
(1, 'Rob', 'Foster'),
(2, 'Lucy', 'Welsh');
如何操作...
我们将创建以下两个文件:
-
/path/to/codeigniter/application/controllers/bench.php -
/path/to/codeigniter/application/models/bench_model.php
-
创建
bench.php控制器文件,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Bench extends CI_Controller { function __construct() { parent::__construct(); $this->load->helper('url'); $this->load->model('bench_model'); $this->load->database(); } public function index() { // Who's in the database? $this->benchmark->mark('bm1_start'); foreach ($this->bench_model->get_people()->result() as $row) { echo $row->firstname . ' ' . $row->lastname . '<br />'; } $this->benchmark->mark('bm1_end'); // Write some more people to the database $this->benchmark->mark('bm2_start'); $data = array( array('firstname' => 'George', 'lastname' => 'Foster'), array('firstname' => 'Jackie', 'lastname' => 'Foster'), array('firstname' => 'Antony', 'lastname' => 'Welsh'), array('firstname' => 'Rowena', 'lastname' => 'Welsh'), array('firstname' => 'Peter', 'lastname' => 'Foster'), array('firstname' => 'Jenny', 'lastname' => 'Foster'), array('firstname' => 'Oliver', 'lastname' => 'Welsh'), array('firstname' => 'Harrison', 'lastname' => 'Foster'), array('firstname' => 'Felicity', 'lastname' => 'Foster') ); $result = $this->bench_model->add_to_db($data); $this->benchmark->mark('bm2_end'); if ($result) { // Who's in the database now? $this->benchmark->mark('bm3_start'); foreach ($this->bench_model->get_people()->result() as $row) { echo $row->firstname . ' ' . $row->lastname . '<br />'; } $this->benchmark->mark('bm3_end'); } else { echo 'Cannot write to database.'; } echo '<br /> ---- BENCHMARK POINT STATS ---- <br />'; echo 'BM1 (S) to BM1 (E): ' . $this->benchmark->elapsed_time('bm1_start','bm1_end') . '<br />'; echo 'BM2 (S) to BM2 (E): ' . $this->benchmark->elapsed_time('bm2_start','bm2_end') . '<br />'; echo 'BM3 (S) to BM3 (E): ' . $this->benchmark->elapsed_time('bm3_start','bm3_end') . '<br />'; } } ?> -
创建
bench_model.php模型文件,并将以下代码添加到其中:<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed'); class Bench_model extends CI_Model { function __construct() { parent::__construct(); } function get_people() { $query = $this->db->get('bench_table'); return $query; } function add_to_db($data) { if ($this->db->insert_batch('bench_table', $data)) { return TRUE; } else { return FALSE; } } }
它是如何工作的...
如果你通过浏览器运行控制器 bench,你应该在屏幕上看到以下输出:
Rob Foster
Lucy Welsh
Rob Foster
Lucy Welsh
George Foster
Jackie Foster
Antony Welsh
Rowena Welsh
Peter Foster
Jenny Foster
Oliver Welsh
Harrison Foster
Felicity Foster
---- BENCHMARK POINT STATS ----
BM1 (S) to BM1 (E): 0.0004
BM2 (S) to BM2 (E): 0.0010
BM3 (S) to BM3 (E): 0.0005
输出是 bench_table 数据库表的表内容循环,随后是基准测试统计信息。我已经突出了 batch_insert() 操作的统计数据。
BM1 (S) 是 BM1 基准测试的开始,而 BM1 (E) 是 BM1 基准测试的结束。
你可以看到,执行 batch_insert() 操作比两个读取操作(在你的应用程序中实际的处理时间可能会有所不同,而且每次运行控制器时也可能会有所不同;然而,BM2 项几乎总是更长时间,但时间会根据你使用的系统而有所不同)花费的时间要长得多。
如果这是一个更复杂的情况,我们可以使用这些信息来定位代码中的瓶颈,并希望修复它们以确保应用程序更加流畅。
那么,代码中发生了什么?由于基准系统总是由 CodeIgniter 加载并且总是可用,因此没有库或其他资源需要加载。
当我们运行基准控制器时,会调用 public function index() 并立即运行 bench_model 的 get_people() 函数。这在对 bench_table 数据库表的 Active Record SELECT 操作,将结果对象返回给控制器。然后我们遍历这个循环,并输出每一行以显示在 batch_insert() 操作之前数据库中的行列表:
$this->benchmark->mark('bm1_start');
foreach ($this->bench_model->get_people()->result() as $row) {
echo $row->firstname . ' ' . $row->lastname . '<br />';
}
$this->benchmark->mark('bm1_end');
眼尖的你们中的一些人也会注意到突出显示的行,我们为 CodeIgniter 定义了关注的开头和结尾点。第一个我们命名为 bm1_start,第二个我们命名为 bm1_stop。我们可以称它们为任何我们喜欢的名字,但这是我决定要叫的名字。
我们随后执行 batch_insert 操作,如下代码片段所示:
$this->benchmark->mark('bm2_start');
$data = array(
array('firstname' => 'George',
'lastname' => 'Foster'),
array('firstname' => 'Jackie',
'lastname' => 'Foster'),
array('firstname' => 'Antony',
'lastname' => 'Welsh'),
array('firstname' => 'Rowena',
'lastname' => 'Welsh'),
array('firstname' => 'Peter',
'lastname' => 'Foster'),
array('firstname' => 'Jenny',
'lastname' => 'Foster'),
array('firstname' => 'Oliver',
'lastname' => 'Welsh'),
array('firstname' => 'Harrison',
'lastname' => 'Foster'),
array('firstname' => 'Felicity',
'lastname' => 'Foster')
);
$result = $this->bench_model->add_to_db($data);
$this->benchmark->mark('bm2_end');
我们正在定义一个包含我们想要添加到数据库中的人的详细信息的多维数组,并将其发送到 bench_model 的 insert_batch() 函数;现在,那些敏锐的眼睛中的人会再次注意到高亮行。这些是 bm2 的开始和结束点。如果 batch_insert() 操作返回 TRUE(正确插入到数据库中),我们随后再次调用 get_people() 模型函数,这将返回数据库中的所有记录:
if ($result) {
// Who's in the database now?
$this->benchmark->mark('bm3_start');
foreach ($this->bench_model->get_people()->result() as $row) {
echo $row->firstname . ' ' . $row->lastname . '<br />';
}
$this->benchmark->mark('bm3_end');
} else {
echo 'Cannot write to database.';
}
再次,这里我们定义(如前一段代码中高亮显示的)bm3 的开始和结束点。这完成了我们的数据库操作,然后我们转向基准测试的汇报。
我们要求 CodeIgniter 告诉我们两个点之间的执行时间:
echo '<br /> ---- BENCHMARK POINT STATS ---- <br />';
echo 'BM1 (S) to BM1 (E): ' . $this->benchmark->elapsed_time('bm1_start','bm1_end') . '<br />';
echo 'BM2 (S) to BM2 (E): ' . $this->benchmark->elapsed_time('bm2_start','bm2_end') . '<br />';
echo 'BM3 (S) to BM3 (E): ' . $this->benchmark->elapsed_time('bm3_start','bm3_end') . '<br />';
对于每个 bm1、bm2 和 bm3,我们想要知道使用 $this->benchmark->elapsed_time() 函数指定的点之间的时间。这个函数接受两个参数:一个起始点和结束点。对于这个菜谱,我们已经要求 CodeIgniter 报告每个 bm# 点(其中 # 是数字 1、2 或 3)之间经过的时间,但如果我们愿意,我们可以这样写:
echo 'BM1 (S) to BM2 (E): ' . $this->benchmark->elapsed_time('bm1_start','bm2_end') . '<br />'
之前的代码将报告 bm1_start 和 bm2_end 之间的时间差(或者从第一个 get_people() 查询的开始到 batch_insert() 查询的结束)。
将每个 $this->benchmark->mark('bm2_end'); 视为一个检查点,并且你可以使用 $this->benchmark->elapsed_time('checkpoint_1','checkpoint_2') 来返回它们之间的时间差。


浙公网安备 33010602011771号