[2009/04/16]发布《Drupal项目实战-公司订餐系统(四)》本博客内容均为原创(Original),如有雷同,纯属巧合。转载请注明出处。同时欢迎学术探讨与批评。

2009年1月25日星期日

方医生恭祝大家牛年快乐!

值此新春到来之际,我代表我和我的家人,对在过去的一年来对我给予极大支持的同学们、朋友们,以及广大的Drupal爱好者们致以最诚挚的问候:祝愿大家在新的一年里,万事如意,心想事成!在牛年里,无论是学业、事业还是家庭,都牛气冲天。特别感谢美丽可爱的Ballet网页设计,也祝福Drupal在牛年发展的更好。

2009年1月17日星期六

让Drupal展示绚烂的图表-Open Flash Chart

有朋友问起关于在Drupal中生成图表的功能,并推荐了一个Charts模块。但经我的试用,发现这个模块文档奇缺,无法简单的安装成功。因此找到了另外一个模块:Open Flash Chart API。
Open Flash Chart 是非常有名的一个开源的、免费的Flash图表生成程序,它的网址是:http://teethgrinder.co.uk/open-flash-chart/index.php。通过它可以生成很酷很炫的各种图表。如下图所示。


为了方便大家的使用,我将做一个小视频,欢迎收看,敬请关注。

Drupal项目实战-公司订餐系统(三)

上节回顾

上一节的进展为: 

  • 创建了Food的Content type
  • 使用CCK创建了Food的相关字段
  • 修改了node-food.tpl.php

目前,简单的菜单管理功能模块可以说基本上完成了,除了权限控制部分。我认为权限控制应该在所有系统功能完成后再统一考虑,目前还是先实现功能为主。那么接下来,就可以进入订餐功能模块的开发了。本文我们就开始订餐功能的设计和开发。

订餐功能模块分析

公司员工在浏览了菜单后,可以选择订购此午餐,同时设定购买数量。由于菜单是一个套餐,因此通常一个员工只会选择一种套餐。但是,此处我认为应该在一定程序上考虑系统的可扩展性,也就是说,应该考虑一个用户订两个或多个“套餐”的情况。因为很显然的是,员工小A想替小B和小C订餐的话,那么使用小A的帐户就需要同时订购三种不同的套餐。

其实此处就存在一个实际的业务模型和系统模型间的映射关系。如果只是从“一个员工中午只吃一种套餐”的常识来想,将每个“帐户”设定为“只允许订购一种套餐”的话,做成的系统就明显不适合使用了。这点也提醒做系统设计的朋友们要注意。

由于目前本系统只限于某公司员工内部使用,因此不存在需要填写送餐地址的信息。同时为了方便起见,暂时不考虑网上支付。这样问题就简化了。另外,用户在订餐时,通常会按照个人口味提出一些建议,因此系统还需要提供一个文本框,用于用户对订餐进行一些“补充说明”。

总结一下,订饭的流程如下:

  1. 浏览菜单并选择“订购”某套餐
  2. 设置订购数量
  3. 输入补充信息
  4. 查看购物车并确认生成订单
  5. 查看订单状态(已收到订单、已发货、收货确认)

图!图!图!

我认为任何语言的描述都不如页面草图来的直接。UML和简洁的文字都不是和客户交流的最好方式。看得见的页面图才是最有效的办法。

上面几张图演示了在浏览套餐菜单后,可直接订餐的全部过程。
(未完待续)

如何升级Drupal 6

使用Drupal 6.0后,可以在后台管理的“报告”中收到Drupal最新更新的提示。如下图:
比如,我现在使用的是D6.8,Drupal提示我要升级为D6.9。那么如何升级呢?

首先直接下载D6.9的安装包,其中有一个UPGRADE.txt文件,用Editplus或其它高级点的notepad打开(因为是UNIX换行,所以在Windows的记事本中看是一团文字。),里面便是Drupal的升级建议,其中首要的几条是:

  1. 确定你的系统符合Drupal新版的要求。要求(Requirements)可以在http://drupal.org/requirements中查看。
  2. 备份所有的相关数据
  3. 检查自定义模块和主题的兼容性
  4. 确保你阅读了整个文件内容

第1条没的说,只要是LAMP或能运行PHP和MySQL即可。推荐使用APACHE。
第2条比较重要,我一般是备份一下整个数据库,以及sites/all下面的我自己安装的modules和themes。要不怎么说不要轻易更改Drupal内核文件呢,现在就看出好处来了。如果你开发时偷懒,不使用Drpual API和Hook来开发自定义模块,要升级系统时就很郁闷了,你需要记住你修改了哪些内核文件,而且还要和新版的文件对比再合并,so troubled。另外,建议大家读一下新版有哪些升级,是否对数据库有大的改动。
同时,还有sites/default/下的settings.php配置文件、.htaccess以及robot.txt文件等会被开发者修改的文件,都要留意的备份一下。OK,总结一下,列个表:

  1. 数据库
  2. sites/all下面的所有文件
  3. sites/default/settings.php配置文件。如果是多站点,还需要备份每个站点settings.php文件。如sites/example.com/settings.php。
  4. .htaccess
  5. robots.txt

第3条其实也很简单,去你使用的module和theme的project首页,检查一下是否可以使用在新版本中。一般情况,module或theme都是默认适用于同一版本系列的,比如5.x或6.x,微小的版本升级都可直接使用。不过对于像D5到D6这样的重大升级,还是要先看看是否支持D6,然后去下载新版本。
第4条写给英语好的朋友们,UPGRADE.txt文档里列出了升级的步骤,还是值得参考的。下面是我整理的步骤:

  1. 备份。备份所有能备份的东西。
  2. 获得超级管理员帐户。就是UserID = 1的用户。
  3. 将站点设置为off-line即下线状态,这样可以防止在升级时有用户访问。
  4. 将主题切换至默认主题——Garland。
  5. 关闭所有的第三方模块和自定义模块。
  6. 将Drupal自带的安装目录和程序全删除。这里指drupal根目录中除files、sites外其它的任何目录。
  7. 将新的Drupal程序包解压缩至相同位置。
  8. 设置好配置文件,如settings.php等。并且要确认是正确的。
  9. 关键的步骤:运行站点下的update.php,如http://www.example.com/update.php。如果你没权限运行,找到settings.php文件,将里面的$update_free_access设置为TRUE。然后在安装结束后再设置为FALSE。
  10. 运行update.php成功后,开启自定义和第三方模块modules。
  11. 再次运行update.php。
  12. 成功后,将站点设置为“上线on-line”。
  13. 更新结束。

欢迎朋友们交流升级心得,分享遇到的问题。

2009年1月12日星期一

Drupal Views 2攻略-自定义View模板

上个视频主要讲解了Views 2模块的基本使用技巧。本次视频的主要内容是:如何创建自定义模板文件来定制View样式。敬请收看!

Youku网址为:http://v.youku.com/v_show/id_XNjU2MzcyMjQ=.html



网速快的朋友可去Youtube看,地址为:
PS: Youtube生成高清版本可能需要一段时间。

2009年1月11日星期日

Drupal地心游记:神秘的羊皮纸

夜深人静。我独自一人收拾行囊,将探险所需要的各种工具装在包里。由于长期的奔波与不安定的生活,没有多少钱和家当,而且我估计在探索Drupal地心深处的过程中,钱也派不上什么用场。唯一可用的,就是几本经常翻看的手册,如PHP、HTML、CSS、JavaScript,以及MySQL等,它们陪伴了我很多年。这些手册应该是Drupal地心中最重要的参考依据了,少了它们,有些谜团就不好解答了。

动身之前,有一件非常重要的事,那就是有一样东西必须要弄到——地图。这个地图并不是去Drupal城的地图,而是探索Drupal地心的路线图。前年夏天,我辗转获得了Drupal城缔造者Dries Buyteart写的一本厚厚的书,每页的内容是被称为“程序源代码”的文字。这本书详细记录了Drupal城地下的构造及运转机制,显然是探险的依据,但还有一个重要的问题没有解决:入口的位置以及探索的路线。

我不停的翻看那本厚厚的书,试图从中找到答案。突然,我觉得这本书中有一页与其它纸的质地不同。其它的纸就像我们日常用的普通的纸,而这张纸的手感却不一样,很有韧性,而且上面的字迹比其它纸上的更为清楚且浑厚。原来是一张羊皮纸!



在很多西方的探险小说中,羊皮纸通常是非常重要的道具,因为它的上面会记载着通向神秘之国以及宝藏的路线图。同样,要探索Drupal,也一定会有一个“羊皮纸”,上面应该标注着地图或神秘的密码,这样才使探险者不会迷失方向。我翻看了很多次这本书,才将目光集中在这一页羊皮纸上。在这个页上有一行中的字迹比其它的更厚重——index.php。

经过我仔细的阅读,确定它就是(Drupal根目录下)index.php的源代码。不难发现,Drupal城中的所有建筑以及景观(页面),都是由index.php来生成的。那么index.php的源码中,也一定蕴含着探索Drupal地心的路线图了。

在Drupal中,index.php负责所有URL的解析和所有页面的生成工作。比如要访问增加内容的页面,它的URL是http://localhost/drupal/index.php?q=node/add;如果要访问管理页面,那就是index.php?q=admin。由此可见,index.php是整个Drupal系统的路由(route),用于接收URL请求,并跟据请求,调用不同的函数,最终生成整个页面。

即使我们开启了Clean URL,用简洁的方式(如http://localhost/drupal/admin来访问管理页面)访问页面,其原理只是利用Apache的Rewrite功能,将Clean URL转义为index.php?q={},调用index.php来生成页面。

据此分析,Index.php就是“Drpual城地心”的入口,沿着它的指引的道路(脚本的执行顺序),应该就可以走完整个探险之旅,最后平安回到入口。否则就有可能迷失于Drupal地心深处,等待我的只有未知的生物和无尽的黑暗了......






2009年1月6日星期二

Drupal 缓存基础

关键字:DRUPAL,缓存,API

方医生注

本文介绍了Drupal的缓存机制,主要是自定义模块开发时,如何使用缓存的问题。英文原文地址为:http://www.lullabot.com/articles/a_beginners_guide_to_caching_data。不过我在翻译时,并没有100%按照原文,进行了一些改进,使其读其来更像中文文章。如果大家觉得有不明白的地方,欢迎指出。

正文开始

在Drupal中创建复杂的、动态的内容是很easy的,但需要做出一定的牺牲的。现在很多Web 2.0的特性的加入,使得网站在变得很cool的同时,也坠入了“性能恶梦(performance nightmare)”的深渊中。每一个节点的加载或一个页面的加载,都会引起高负载、大量的数据库访问、复杂的计算和大量的客户端脚本的执行。

一个解决方案是在Drupal的管理界面开启页面缓存。这个可以使匿名用户(即不登录的情况下)访问速度增加,因为Drupal会把每一个要输出的页面预先缓存(方医生注:请注意!不是缓存成HTML页面,而是把整个页面的HTML内容序列化为字符串,然后存入数据库),这样可以大大的减少数据库的查询次数。但是,这个对于登录的用户是没有任何效果的:因为页面级的缓存是一种“要么全是要么就一点都没有(all-or-nothing)”的缓存方式,它只有在很标准的情况下才有效,并且所有用户看到的视图都是一样的。

不过,早晚有一天你需要挖掘你的代码,找出数据库的查询访问热点(hot spots),然后自己对程序进行缓存。幸运的是(我最喜欢看到Fortunately这个单词),Drupal有一些内建的关于缓存的API和一些指导性的文档,使得我们可以很easy的自定义Drupal程序的缓存。

基础

做优化和缓存的第一条(The first rule)就是:永远不要使消耗时间的操作执行两次!要充分利用第一次执行的结果,并重用它们(Never do something time consuming twice if you can hold onto the results and re-use them.)下面我们看一个简单的示例:

<?php

function my_module_function($reset = FALSE) {
  static $my_data;
  if (!isset($my_data) || $reset) {
    // 在些进行复杂的计算,并使用正确的内容生成$my_data变量
  }
  return $my_data;
}

?>

这段代码中最重要的部分,就是这个函数创建了一个静态变量——$my_data.静态变量的好处在于,当其第一次被赋值后,就可以被持续的使用,即使这个函数被重新调用。也就是说,我们先检查这个变量是否被赋值,如果已经有值了,那就不需要重新计算,只需要直接返回其值即可。

这种模式经常在Drupal中使用——其中包括很多关键的函数,如node_load。这个函数在被第一次调用时,根据传入的节点ID,查询数据库得到节点对像,并赋值于一个静态变量;然后如果这个节点再在一个Block中被调用,或被列表中调用,就不会再次查询数据库了。

另一个重要的特性是使用$reset参数。缓存确实很好,但偶尔你也需要确保用户访问的页面所得到的数据是绝对新鲜的(absolute freshest)。这时$reset变量就派上用场了。我们可以在需要更新缓存的时候,将$reset设置为TRUE。

Drpual的缓存函数

你可能注意到,静态变量只能在一个单独的页面生存周期内有效。如果需要更好的性能,我们可以使缓存的数据更持久……

<?php

function my_module_function($reset = FALSE) {
  static $my_data;
  if (!isset($my_data) || $reset) {
    if (!$reset && ($cache = cache_get('my_module_data')) && !empty($cache->data)) {
      $my_data = unserialize($cache->data);
    }
    else {
      // 处理复杂计算,生成$my_data变量
      cache_set('my_module_data', 'cache', serialize($my_data));
    }
  }
  return $my_data;
}
?>
这个版本的函数仍然使用静态变量,但是它增加了新的一个层(layer):数据缓存层。Drupal的API提供三个关键的函数你需要了解:cache_get(), cache_set()和cache_clear_all()。接下来让我们看看如何使用它们。

回到上面的函数,在对静态变量做初始检查后,这个函数就会通过一个键来查找Drupal缓存(数据库中的缓存)。如果能够查到,并且$cache->data元素是非空的,函数公反序列化缓存数据,并将其存入$my_data变量中。

如果没有缓存被找到(或如果$reset == TRUE),函数就会实际生成数据。然后它会将其序列化再存入数据库,这样以后就可以在将来被使用了。缓存的键值(key)的命名最好以你自定义模块的名称为前缀,这样便于标识。

最终的结果是什么?一个很聪明很轻便的函数可能节省很多的时间——首先检查内存中是否已有变量存在(静态变量),然后再检查数据库缓存,如果这些都没有那就重新生成。如果你查看很多针对数据库查询的模块的话,会发现很多模块都应用了这种模式。

保持最新

发生了什么呢?如果你缓存的数据过期了,并且需要重新计算呢?默认的情况下,缓存的信息将会一直存在直到某模块执行了cache_clear_all()函数,清空所有的数据。如果你的数据偶尔的被更新,那么当每次你需要做一些更改时你或许可以考虑简单的调用cache_clear_all(‘my_module_data’, ‘cache’)。如果你缓存了很多数据(比如你为一个模块存储了n个缓存,或为每个角色都做了个缓存),那么可以传入第三个参数给cache_clear_all函数,以使其全部清空:

<?php

cache_clear_all('my_module', 'cache', TRUE);

?>

上面的函数将清空所有以“my_module”为前缀的键值的缓存。

如果你不需要使你的缓存“时刻保持最新“,但又需要使其“适当的”新鲜(reasonably fresh),你可以给cache_set函数传入一个过期时间。比如:

<?php
cache_set('my_module_data', 'cache', serialize($my_data), time() + 360);
?>

最后一个参数是一个UNIX时间戳,用来表示过期时间。最简单的方法是使用time()函数,再加上这个缓存的生存时间(以秒为单位)。过期的缓存会被自动的清除(应该是通过cron函数来实现)。

高级缓存

你可能注意到了cache_set()函数的第二个参数是“cache”——这是Drupal用于存放缓存的默认的数据表的名称。如果你要存储大量的数据在缓存中,你可以设置一个自定义的单独的缓存表,并通过这个参数传递给Drupal。这样可以使缓存更快一些,因为可以与其它的模块的缓存表分开。我们熟悉的Views模块就是使用的这种模式(单独创建缓存数据表),尤其当需要清除所有缓存时,单独的缓存表可以避免删除其它模块的缓存数据。

如果你真的希望充分利用服务器的每一个资源,Drupal还提供了可以使用其它缓存系统的机制。只需要改settings.php的一行代码,你就可以使三个标准函数cache_set(), cache_get(),和cache_clear_all()函数有不同的实现。基于文件的缓存(File-based caching),与memcached集成,或使用其它的缓存系统都成为可能。而且,只要你使用了Drupal的标准的缓存函数(就是上面提到的三个),那么你不需要在你的模块中更改任何代码,就可以实现缓存系统的移植。

一些忠告

过犹不及(Like all good things, it’s possible to overdo it)。有时用缓存是不需要的——如果你只是查找一条数据的话,把它存入缓存数据表就是很愚蠢的行为了。可以使用Devel模块来定位哪些查询降低了加载的性能。它可以高亮一些查询时间较长的查询,以及那些被反复执行的查询。

在其它时间,你使用的数据可能会不适合标准的缓存系统。如果你需要在SQL查询中连接缓存数据,比如,cache_set()将会把数据字符串序列化,有时就会造成麻烦(因为不能直接使用)。在这种情形下,你需要找一个针对你的模块的缓存解决方案。VotingAPI模块维护了一个数据表用于存放每个用户的投票结果,另一个数据表用于存储计算结果(如平均值、总和等),这样可以在SQL查询时,快速的进行连接排序和过滤节点。

最后,一定要记住:缓存不是一个长久的存储方式!因为如果其它的模块调用了cache_clear_all()函数的话,那么所有的缓存数据就都会消失。因此如果你要存放某些只能计算一次的数据的话,还是找个安全点的地方放比较好。

去西部吧,年青的Drupaler!

祝贺你:你现在拥有一套加速你的代码的强有力的工具了!前进,优化你的代码吧。

后记

本文的几个示例函数比较有参考价值,大家在写自定义模块时可以参考一下。缓存对网站的性能影响是相当的大,一定要在适当的条件下使用适当的缓存机制。

2009年1月3日星期六

Drupal视频讲座第一辑—Views 2攻略

Views 2 是Drupal 6最重要的模块,它可以生成Drupal站点内容的各种列表,而且不需要写一行代码。同时,它的作者又荣获2008年度开源CMS最有价值专家。由此可见Views 2对于Drupal的重要性。我也制作了一系列关于Views 2的视频讲座,旨在与大家分享Views 2的使用经验。欢迎大家收看并与我交流。相关视频还会陆续发布,敬请关注。

Views 2:基础
本节视频主要包含了Views 2的相对于Views 1的改进,一些基本概念和配置,并创建了一个实例区块来展示区块列表。
如果你的浏览器无法看到视频,可直接访问:http://v.youku.com/v_show/id_XNjM4NjE3NzI=.html。


可能视频中的截图不是很清楚,下面是两个主要的页面,大家可以参考着看(点击看大图):



Views 2相关视频还会陆续推出....Coming Soon....