Magento-1-8-开发者秘籍-全-
Magento 1.8 开发者秘籍(全)
原文:
zh.annas-archive.org/md5/9fa4554690d615ba4cbbe65c64c8aa3d
译者:飞龙
前言
Magento 是市场上最受欢迎的电子商务平台之一,因为它免费、稳定,并提供了许多功能。许多电子商务网站都是用 Magento 构建的。在 Magento 中开发并不像您预期的那么简单。当您想开始使用 Magento 时,一本展示最佳实践的指南在学习 Magento 开发时将非常有帮助,这正是本书旨在做到的。
在 Magento 1.8 开发食谱 中,我们将发现所有有助于您成为优秀 Magento 开发者的主题,然后我们从基础知识开始,并在本书的结尾讨论更高级的主题。这将是一个很好的指南,解释了您必须采取的每个步骤或行动来完成本书的食谱。
我们将使用合适的工具从创建一个良好的开发环境开始这本书。我们将创建一个包含示例产品的 Magento 安装所在的服务器。我们将创建一个自定义主题来改变网店的外观和感觉。本书的重点将是开发部分。我们将创建一个遵循 Magento 最佳实践的定制模块。我们将使用许多在 Magento 项目中常用的常见功能来定制此模块,例如额外的控制器页面、数据库集成、自定义运输方法和额外的后端界面。本书的结尾,我们将看到如何提高我们的 Magento 安装的性能。最后,我们将看到一些调试技术,如 xDebug,并使用 PHPUnit 创建单元测试。
本书涵盖的内容
第一章, 入门 Magento,为您提供了介绍并展示了如何使用合适的工具创建开发环境,如何安装 Magento,以及如何在版本控制系统中处理代码。
第二章, 主题,解释了您可以做什么来自定义商店的外观和感觉。
第三章, 处理产品,为您提供了更多关于在您的商店中展示产品以及使用 Facebook 喜欢按钮自定义产品页面的信息。
第四章, 创建模块,描述了如何创建一个基本的 Magento 模块,如何通过自定义页面、翻译、块等配置来扩展该模块,以及如何重写现有类。
第五章, 数据库概念,展示了 Magento 如何与数据库连接,表格如何与 Magento 实体关联,以及 EAV 系统的工作原理。它还展示了如何创建主/从设置。
第六章, 数据库和模块,教你如何通过创建一个安装脚本来扩展一个 Magento 模块,该脚本安装一个数据库表和实体,它们将与该数据库表交互。
第七章, Magento 后端,涵盖了当你将模块与 Magento 后端集成时应了解的主题,例如添加额外的配置页面,创建概览页面,以及扩展管理菜单。
第八章, 事件处理和 Cron 作业,描述了在 Magento 中实现事件驱动架构的方式,以及你如何在模块中使用它。在本章的后面部分,你将学习如何在模块中创建 cron 作业以及如何测试它们。
第九章, 创建一个运输模块,展示了如何创建一个带有新运输方法所需配置的自定义模块。
第十章, 创建产品滑块小部件,解释了如何创建一个带有自定义小部件的模块,如何构建后端界面,以及如何在小部件的前端提供良好的 UI。
第十一章, 性能优化,描述了如何通过不同的技术(如 Web 服务器优化和缓存系统(APC 和 Memcached))来基准测试你的网站,了解其限制并提高其性能。
第十二章, 调试和单元测试,展示了如何使用 PHP 调试器 xDebug,如何在 Magento 中使用 FirePHP,以及如何使用 PHPUnit 创建一个简单的单元测试。
你需要这本书的内容
-
Magento 1.8 源代码
-
Ubuntu 版本 13.10 或更高
-
Apache2
-
PHP 版本 5.3 或更高
-
MySQL 服务器 5
-
NetBeans IDE
-
phpMyAdmin
-
Wiz 命令行工具
-
Firebug(Firefox 的附加组件)
-
FirePHP(Firefox 的附加组件)
-
xDebug
-
PHPUnit
-
一个标准的网络浏览器
-
Siege(基准测试工具)
-
ApacheBench(另一个基准测试工具)
-
YSlow(Firefox 的附加组件)
-
Git 版本控制系统
-
jQuery
-
carouFredSel(一个用于创建 jQuery 轮播图的 jQuery 库)
这本书面向谁
如果你了解一些 PHP 编程知识并想开始进行 Magento 开发,这本书有你需要的内容。开始使用这本书的食谱不需要了解 Magento。需要基本的 PHP 和 Web 开发知识。这本书从 Magento 开发的基础开始,到更高级的主题结束。即使你对 Magento 开发有所了解,但需要一本好的指南,这本书也有适合你的内容。
习惯用法
在这本书中,你会发现许多不同风格的文本,用于区分不同类型的信息。以下是一些这些风格的示例,以及它们含义的解释。
文本中的代码单词、数据库表名、文件夹名、文件名、路径名、虚拟 URL 和用户输入按以下方式显示:“widget.xml
文件用于在 Magento 安装中定义小部件。”
代码块设置如下:
<category_id>
<label>Category ID</label>
<type>text</type>
<required>1</required>
<sort_order>20</sort_order>
<visible>1</visible>
</category_id>
当我们希望将您的注意力引向代码块中的特定部分时,相关的行或项目将以粗体显示:
<category_id>
<label>Category ID</label>
<type>text</type>
<required>1</required>
<sort_order>20</sort_order>
<visible>1</visible>
</category_id>
任何命令行输入或输出都按以下方式编写:
sudo apt-get install apache2
新术语和重要词汇以粗体显示。例如,您在屏幕上、菜单或对话框中看到的单词,在文本中会这样显示:“点击完成,您的 NetBeans 项目就准备好了。”
注意
警告或重要注意事项以这种方式显示在框中。
技巧
技巧和窍门看起来像这样。
读者反馈
我们始终欢迎读者的反馈。请告诉我们您对这本书的看法——您喜欢什么或可能不喜欢什么。读者反馈对我们开发您真正能从中获得最大收益的标题非常重要。
要向我们发送一般反馈,只需发送一封电子邮件到<feedback@packtpub.com>
,并在邮件主题中提及书名。
如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请参阅我们的作者指南www.packtpub.com/authors。
客户支持
现在,您已经是 Packt 图书的骄傲拥有者,我们有一些事情可以帮助您从您的购买中获得最大收益。
下载示例代码
您可以从您在www.packtpub.com
的账户下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support
并注册,以便将文件直接通过电子邮件发送给您。
勘误
尽管我们已经尽一切努力确保内容的准确性,但错误仍然可能发生。如果您在我们的书中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以节省其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata
来报告它们,选择您的书,点击勘误提交表单链接,并输入您的勘误详情。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站,或添加到该标题的勘误部分下的现有勘误列表中。任何现有勘误都可以通过从www.packtpub.com/support
选择您的标题来查看。
侵权
在互联网上,版权材料的盗版问题是一个持续存在于所有媒体中的问题。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现我们作品的任何非法副本,无论形式如何,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。
请通过 <copyright@packtpub.com>
联系我们,并提供涉嫌盗版材料的链接。
我们感谢您在保护我们作者和我们为您提供有价值内容的能力方面的帮助。
问题
如果您在本书的任何方面遇到问题,可以通过 <questions@packtpub.com>
联系我们,我们将尽力解决。
第一章. Magento 入门
在本章中,我们将涵盖以下主题:
-
准备开发环境
-
使用示例数据安装 Magento
-
将版本控制添加到源代码
-
配置开发工具
简介
你可能听说过 Magento。它是一个开箱即用的电子商务平台,具有许多功能,例如目录导航、促销规则、RSS 源、产品搜索、产品标签、产品评论、税务规则、报告和订单管理。
Magento 背后的公司是 Varien。2007 年,他们开始考虑一个开源的电子商务平台,该平台将针对大量用途进行设计。经过一年的开发,2008 年推出了第一个稳定版本 Magento 1.0。当 Magento 在后来的几年中变得更加流行时,eBay 对其产生了兴趣,现在拥有 100% 的 Magento。
如果您安装并配置了 Magento,那么您就可以开始在线销售产品。但是,当您需要自定义主题、额外的运输方式和其他功能时,您必须根据 Magento 的规则扩展软件。如果您对 PHP 编程有所了解,请按照食谱中描述的步骤操作,您就可以开始编辑您的 Magento 商店。
我们将创建一个开发环境,您可以从中开始工作。我们将安装示例数据,以便商店不会空空如也,并准备好使用。如果您是新手,您可以从这里开始。如果您是专业人士,您仍然可以从这里开始,因为我们遵循 Magento 开发的最佳实践。让我们开始吧。祝你好运!
准备开发环境
我们将设置一个包含 Magento 的开发环境。为此,我们需要设置一个 LAMP (Linux, Apache, MySQL, PHP) 环境。在那个 LAMP 环境中,我们将创建一个 完全限定域名 (FQDN) 和一个虚拟主机。
准备就绪
我们需要设置一个开发服务器,我们将使用它来运行 Magento。当我们有一个 Ubuntu 桌面环境时,我们必须安装以下软件的最新版本:
-
Apache2
-
PHP
-
MySQL 服务器
-
额外的 PHP 库
我们可以通过在 CLI 界面上运行以下命令来安装这些软件。这些命令基于基于 Ubuntu 的 Linux 发行版。要在带有 Ubuntu 操作系统的桌面电脑上运行这些命令,请启动 终端 程序:
-
要安装 Web 服务器 Apache2,请运行以下命令:
sudo apt-get install apache2
-
安装 PHP,请运行以下命令:
sudo apt-get install php5
-
要安装 MySQL 服务器,请运行以下命令:
sudo apt-get install mysql-server
-
要安装 Magento 所需的 PHP 扩展,例如 MySQL 桥接,请运行以下命令:
sudo apt-get install php5-mysql php5-curl php5-gd php-pear
如何操作...
当一切安装完毕后,我们将创建一个带有 FQDN 的虚拟主机。我们希望我们的开发环境可以通过 http://magento-dev.local/
访问。为此,我们必须创建一个带有此域名的虚拟主机。此域名指向我们之前创建的 Web 服务器的 IP 地址。
以下步骤描述了如何创建一个带有 FQDN 的虚拟主机:
-
在
/etc/apache2/sites-available/
目录中创建一个magento-dev.local
文件。 -
要创建和编辑文件,请运行以下命令:
sudo nano /etc/apache2/sites-available/magento-dev.local
-
将以下内容粘贴到该文件中:
<VirtualHost *:80> # ServerName (domain) and admin email ServerAdmin webmaster@magento-dev.local ServerName magento-dev.local DocumentRoot /var/www/magento-dev.local/public # Folder of the site. We have to create this # Log file locations LogLevel warn ErrorLog /var/log/apache2/magento-dev.error.log CustomLog /var/log/apache2/magento-dev.access.log combined </VirtualHost>
-
在终端中运行以下命令以创建
www
根目录:-
要创建站点文件夹(文档根目录),请运行以下命令:
sudo mkdir /var/www/magento-dev.local/public sudo chown -R www-data:www-data /var/www/magento-dev.local/
-
要启用站点,请运行以下命令:
sudo a2ensite magento-dev.local
-
要重新加载 Apache 服务器,请运行以下命令:
sudo service apache2 reload
-
要测试站点,我们必须在我们的主机文件(
/etc/hosts
)中添加以下行:127.0.0.1 magento-dev.local
这将使域名
magento-dev.local
指向 IP 地址 127.0.0.1。这是本地 web 服务器的 IP 地址(localhost),因此向此 URL 发出的请求不会进入互联网,而是进入本地 web 服务器。
-
它是如何工作的...
本食谱描述了如何从 CLI 界面安装 web 服务器。如果您已经有一个具有特定域名的 web 服务器,您可以跳过这一章。
Magento 文件将安装在公共目录中。当对域名发出请求时,www-data
用户将执行请求。因此,最好所有文件和文件夹都由 www-data
用户和组拥有,以避免权限问题。
在 Linux 服务器上,每个文件和文件夹都有三种类型的权限。这些权限是 读取、写入和执行(rwx)。您可以在三个范围内设置这些权限:所有者、组和其他人。在每次文件请求时,Linux 根据权限决定用户是否可以读取、写入或执行某个文件。
对于 HTTP 请求,将使用 www-data
用户来执行请求。因此,确保 www-data
用户有足够的文件权限来运行应用程序非常重要。要更改文件权限,可以使用 chmod
命令。要更改所有者和组,可以使用 chown
命令。
还有更多...
也可能在其他操作系统上运行 Magento 和 web 服务器。要运行 web 服务器,我们需要 Apache、MySQL 和 PHP。这些软件可以在 Windows 或 Mac 设备上安装。
Windows 操作系统的变体是 WAMP(Windows, Apache, MySQL, and PHP)。您可以在 WAMP 网站上找到有关此变体的更多信息,网址为 www.wampserver.com
。
对于 Mac OS,另一种选择是 MAMP。有关 MAMP 的更多信息,请访问 www.mamp.info
。
跨平台 web 服务器是 XAMP。您可以从他们的网站下载 Linux、Windows 或 Mac 的安装程序,网址为 www.apachefriends.org/en/xampp.html
。
使用示例数据安装 Magento
当您安装 Magento 时,您可以从一个空数据库或包含一些示例产品和配置的数据库开始。对于我们的开发环境,最佳做法是从示例数据开始,因为我们可以在安装后立即开始使用 Magento。
对于一个新的网店,建议从空数据库开始,自行进行配置。要安装 Magento,我们需要以下数据:
-
Magento 代码
-
示例数据
准备工作
您可以从 Magento 的网站下载 Magento 和示例数据(www.magentocommerce.com/download
)。下载最新的完整版本和示例数据。本书的食谱基于 Magento 版本 1.8.1.0。对于示例数据,您可以使用 1.6.1.0 版本。
如何操作...
以下步骤显示了如何安装带有示例数据的干净 Magento 网店:
-
将代码提取到我们 Web 服务器适当的文件夹中。在我们的例子中,这是
/var/www/magento-dev.local/public
。通过在网站的根文件夹中运行ls -la
命令查看文件夹结构。我们必须注意隐藏文件是否包含在文件夹中:ls -la drwxrwxr-x . drwxrwxr-x .. -rwxrw-r-- .htaccess -rw-rw-r-- .htaccess.sample -rw-rw-r-- api.php drwxrwxr-x app -rw-rw-r-- cron.php -rw-rw-r-- cron.sh drwxrwxr-x downloader drwxrwxr-x errors -rw-rw-r-- favicon.ico -rw-rw-r-- get.php -rw-rw-r-- .htaccess -rw-rw-r-- .htaccess.sample drwxrwxr-x includes -rw-rw-r-- index.php -rw-rw-r-- index.php.sample -rw-rw-r-- install.php drwxrwxr-x js drwxrwxr-x lib -rw-rw-r-- LICENSE_AFL.txt -rw-rw-r-- LICENSE.html -rw-rw-r-- LICENSE.txt -rw-rw-r-- mage drwxrwxr-x media -rw-rw-r-- php.ini.sample drwxrwxr-x pkginfo -rw-rw-r-- RELEASE_NOTES.txt drwxrwxr-x shell drwxrwxr-x skin drwxrwxr-x var
提示
在 Linux 中,隐藏文件或文件夹以点(.)开头,例如
.htaccess
文件。我们使用的ls
命令的-a
选项显示了所有文件和文件夹,包括隐藏的。重要的是要看到.htaccess
文件在目录中,因为这个文件包含 URL 重写和其他服务器配置的配置。 -
当您解压示例数据存档时,您会看到一个媒体文件夹和一个 SQL 文件。SQL 文件包含数据库、媒体文件夹和图像。要安装媒体文件夹,请将此文件夹与网站的根媒体文件夹合并。
提示
在运行 Magento 安装向导之前安装示例数据非常重要。如果 Magento 在数据库中找不到示例数据,向导将继续使用不带示例数据的空数据库。
-
要安装数据库,您必须运行以下命令:
-
要创建数据库,请运行以下命令:
mysql -u root -p create database magento_dev; exit;
-
要导入 SQL 文件,请运行以下命令:
mysql -u <<username>> -p magento_dev < "path_to_sample_data.sql"
提示
为了避免权限问题,请确保所有文件和文件夹都有正确的权限。出于安全考虑,建议所有文件只拥有足够的权限,以便只有正确的用户可以访问正确的文件。当您赋予所有权限(777)时,您不会有权限问题;但是,服务器上的每个用户都可以读取、写入和执行您的应用程序中的每个文件。
-
-
下一步是运行 Magento 安装向导。转到网站的 URL,安装程序将如以下截图所示显示:
-
通过接受条款和条件继续安装过程。
-
在下一屏,选择您商店的正确语言、区域设置和货币。
-
在配置页面,用正确的数据填写表单:
-
数据库类型:在此字段中输入
MySQL
。 -
主机:在此字段中输入
localhost
。 -
数据库名称:在此字段中输入
magento_dev
(您的 Magento 数据库名称)。 -
用户名:在此字段中输入
root
(您的 Magento 数据库用户名)。 -
用户密码:在此字段中输入
root
(你的数据库用户密码)。留空是可能的。 -
表前缀:留此字段为空(如果填写,所有表都将具有在此处输入的前缀)。
-
基础 URL:在此字段中输入
http://magento-dev.local/
(你的网店 URL)。 -
管理路径:在此字段中输入
admin
(后端路径)。 -
启用图表:此复选框必须勾选。
-
在下一步之前跳过基础 URL 验证:此复选框必须取消勾选(如果勾选,向导将在处理此表单时检查有效的 URL)。
-
使用 Web 服务器(Apache)重写:此复选框必须取消勾选。
-
使用安全 URL(SSL):此复选框必须取消勾选。
-
-
提交表单并继续到下一步。在这一步中,你可以配置管理员账户。填写正确的数据,并记住登录名和密码,因为这是管理商店所必需的。留加密密钥为空。
-
提交此表单后,安装向导终止。设置完成。你现在可以访问你的商店,并使用在安装向导中创建的用户名进入后端。
工作原理...
我们刚刚创建了一个完全功能的 Magento 商店。技术上,你可以开始销售产品。
我们从示例数据开始。我们插入数据库的 SQL 文件包含示例网店的所有数据。当你想创建一个空网店时,你可以从一个空数据库开始。
当安装一个新的商店时,始终遵循安装向导。安装向导创建app/etc/local.xml
文件,并安装你在安装向导中设置的值。这些值包括货币、时区、后端用户等。当app/etc/
文件夹中没有local.xml
文件时,安装向导会出现。在其他所有情况下,网店会出现。
在app/etc
文件夹中有一个完整的local.xml
文件,你可以安装 Magento。然而,这并不推荐,因为你没有后端用户和其他配置。
将版本控制添加到源代码
使用版本控制,你可以管理对计算机文件所做的更改。当你将其添加到编程代码中时,你可以跟踪从初始化以来对代码所做的所有更改。市场上有一些非常流行的版本控制系统,例如 Subversion (SVN)、Mercurial SCM、CVS 和 Git SCM。
在这个菜谱中,我们将使用 Git SCM 为之前创建的 Magento 代码添加版本控制。我们将初始化一个空仓库。之后,我们将进行几次提交,将所有 Magento 代码添加到仓库中。
准备工作
前往你的命令行,通过在终端运行以下命令在你的本地服务器上安装 Git:
sudo apt-get install git
如何操作...
以下步骤展示了你如何将 Magento 代码添加到版本控制系统 Git 中:
-
我们必须初始化 Git 仓库。为此,导航到源文件夹并输入以下命令:
git init
-
运行以下命令,你会看到所有文件都被标记为未跟踪:
git status
在接下来的步骤中,我们将把其他文件夹添加到仓库中。
-
通过运行以下命令将
app/
文件夹添加到仓库中:git add app/
这将添加
app/
目录中的所有文件。当你运行git status
命令时,你会看到所有文件的列表。 -
运行以下命令从仓库中删除
local.xml
文件,但不从驱动器中删除:git rm --cached app/etc/local.xml
-
运行以下命令以创建第一个提交:
git commit –m "add app folder"
-
运行以下命令以跟踪文件的状态:
git status
-
在根目录中创建一个
.gitignore
文件,并在其中添加以下内容:app/etc/local.xml
-
再次运行
git status
命令,你会看到local.xml
被忽略。 -
通过运行以下命令提交
.gitignore
文件:git commit .gitignore –m "add gitignore file"
-
通过运行命令
git add
<文件夹或文件名
> 将其他应用程序文件和文件夹添加到仓库中。为以下文件夹运行此命令:-
downloader
(用于安装插件) -
errors
(关于错误处理的配置) -
js
(JS 库文件夹) -
lib
(包含 PHP 库) -
pkginfo
(关于升级的信息) -
shell/
(包含 PHP CLI 脚本) -
skin/
(CSS 和图像文件夹) -
api.php
-
cron.php
-
cron.sh
-
favicon.ico
-
get.php
-
index.php
-
index.php.sample
-
install.php
-
LICENCE_AFL.txt
-
LICENCE.html
-
LICENCE.txt
-
mage
-
php.ini.sample
-
RELEASE_NOTES.txt
-
-
运行新的
git commit
命令:git commit –m "add additional files"
-
忽略其他非静态的 Magento 文件和文件夹。在
.gitignore
文件中添加以下内容:/app/etc/local.xml /errors/local.xml /media/css/ /media/dhl /media/downloadable /media/import/ /media/js/ /media/catalog/ /media/customer /media/upload/ /media/wysiwyg/ /media/captcha/ /media/tmp/ /media/xmlconnect /var/import/ /var/export/ /var/cache/ /var/log/ /var/session/ /var/locks/ /var/package /var/report/ /var/resource_config.json sitemap.xml
-
通过以下命令将
media/
和var/
文件夹添加到仓库中:git add media/.htaccess git add var/.htaccess
我们只需要
.htaccess
文件,其他文件被忽略,因为它们是动态的。
它是如何工作的...
当使用版本控制系统时,你必须记住,克隆项目的另一个人可以设置一个包含数据库和 Git 中的代码的环境。这个环境必须与你提交的环境相同。
非常重要的是,每个 Magento 核心文件和你的定制文件都必须在 Git 仓库中。你不需要将配置文件,如 app/etc/local.xml
和 errors/local.xml
,添加到版本控制中。当你在另一台服务器上运行你的代码时,配置文件中的设置通常与服务器上的设置不同。
动态生成的文件,如缓存文件和用户图像,存储在 media
和 var
文件夹中,所以我们不需要这些文件夹的内容。
这些文件夹中唯一重要的文件是 .htaccess
文件,它包含限制 var
文件夹和 media
文件夹的配置。
配置开发工具
当你开始定制你的 Magento 店铺时,一个包含以下组件的良好开发环境可以节省时间:
-
代码编辑器(IDE)
-
MySQL 客户端(phpMyAdmin 或 MySQL Workbench)
-
命令行工具
准备工作
NetBeans 是一个开源 IDE,可用于许多编程语言。PHP 支持维护良好,并且与其他系统(Git、SVN 和 MySQL)有很多集成。
要安装 NetBeans,您必须从他们的网站 netbeans.org
下载并运行安装程序。确保在下载时,您选择 PHP 版本或完整版本。
如何操作...
以下步骤展示了如何使用 Magento 文件作为文档根创建 NetBeans 项目:
-
要创建项目,打开 NetBeans 并导航到 文件 | 新建项目。
-
在对话框窗口中,点击 PHP 应用程序(使用现有源),如图所示:
-
点击 下一步 并配置以下设置:
-
源文件夹: 此字段设置为您的 Magento 代码位置
-
项目名称:NetBeans 项目的名称在此字段中输入
-
PHP 版本: 此字段设置为 PHP 5.3 或更高版本
-
默认编码:此字段设置为 UTF-8
以下截图给出了这些设置的示例值:
小贴士
当您使用版本控制系统(SVN、Git 或其他)时,建议勾选选项 将 NetBeans 元数据放入单独的目录。如果没有勾选,将在您的 Magento 根目录中创建一个
.nbproject
文件夹,您将不会在版本控制系统中拥有它。另一种可能性是在.gitignore
文件中添加.nbproject
文件夹。 -
-
点击 下一步。
-
配置以下设置:
-
运行方式:在我们的案例中,此字段设置为本地网络服务器
-
项目 URL: 在此字段中输入
http://magento-dev.local
,这是我们为 Magento 配置的 URL -
索引文件: 此字段设置为
index.php
以下截图给出了这些设置的示例值:
-
-
点击 完成,您的 NetBeans 项目就准备好了。
它是如何工作的...
当项目启动时,您会在窗口的左侧列中看到文档根。当您双击一个文件时,该文件将在主窗口中打开。在此窗口中,您可以查看文件的历史记录、格式化代码、重构变量名以及更多操作。
更多内容...
在 IDE 的开发环境背后,数据库客户端和命令行工具在开发 Magento 时非常有用。
MySQL 客户端 phpMyAdmin
phpMyAdmin 是一个作为网络应用程序运行的 MySQL 客户端。它是一个易于使用的工具,可以直接在数据库中输入 SQL 语句。
-
要安装 phpMyAdmin,打开您的终端并运行以下命令:
sudo apt-get install phpmyadmin
-
按照安装程序的说明操作。
-
编辑
/etc/apache2/apache.conf
文件,并在文件末尾添加以下代码:Include /etc/phpmyadmin/apache.conf
-
使用以下命令重新加载 Apache 服务器:
sudo service apache2 reload
-
前往
http://magento-dev.local/phpmyadmin
,你将看到登录屏幕。你可以使用你的 Magento 数据库的数据库用户凭据登录。
命令行工具 Wiz
Magento 在开发过程中有许多系统任务需要执行。所有这些操作都可以在后台触发。然而,由于这是一项耗时的工作,shell 接口可以为你节省大量时间。
Wiz 是一个可以从 github.com/classyllama/Wiz
下载的命令行工具。
按照他们网站上的安装说明进行操作。安装后,你必须将你的 shell 目录更改为 Magento 根目录。如果你在 Magento 根目录中,你运行的所有命令都将针对该 Magento 安装执行。
以下是一些有用的命令:
-
wiz admin-resetpass
:当你忘记管理员密码时,可以使用此命令 -
wiz admin-createadmin
:此命令从头创建一个新的管理员用户 -
wiz cache-clear
:此命令清除所有缓存或特定缓存 -
wiz devel-showhints
:此命令启用或禁用前端提示 -
wiz module-list
:此命令列出所有已安装的模块 -
wiz sql-cli
:此命令打开 MySQL 命令行
第二章 主题化
在本章中,我们将涵盖以下主题:
-
配置 Magento 主题和包
-
创建您的第一个主题
-
向您的主题添加额外文件
-
添加 jQuery 支持
-
更改页面标题
-
与翻译一起工作
-
理解主题块系统
-
将小部件添加到布局中
简介
当您想使您的示例 Magento 网站准备就绪时,大多数人首先想要改变的是商店的外观和感觉。访客对您的网站的第一印象就是外观和感觉。
在本章中,我们将介绍您可以使用 Magento 主题做的最重要的事情。自定义标准主题并不难,但从头开始构建一个主题则需要大量工作。
配置 Magento 主题和包
Magento 与多个主题一起工作。您可以在同一商店中配置更多主题。一个常见的情况是您有一个商店主题,以及基于商店主题的一些继承主题。
准备工作
登录到后端,前往您可以在 系统 | 配置 | 设计 下找到的主题配置部分。
如何操作...
以下说明描述了如何管理网店的不同 Magento 主题:
-
将主题字段更改为
modern
并清除缓存。前往您的前端,您的商店将如下所示:您现在已配置了
default
包中的modern
主题。此主题将扩展default
主题中的内容,在最后的情况下,从base/default
主题扩展。这个过程被称为 主题回退系统。 -
将主题字段留空,并添加以下配置:
-
重新加载前端:
您的商店将看起来像
default
主题,但有一些不同的 CSS 样式。我们已经配置了default
主题,但使用了包含 CSS 文件的另一个skin
文件夹。
工作原理...
Magento 中有三种主题级别:基础主题、包的默认主题和包中的其他主题。
以下图表显示了配置主题时主题回退系统的工作原理:
当配置 winter
主题时,Magento 将按以下顺序查看文件:
-
主题 (
packt/winter
) -
包含的默认主题 (
packt/default
) -
基础主题 (
base/default
)
您可以在配置页面配置默认主题,通过导航到 系统 | 配置 | 设计。如果已设置(例如,packt/summer
),回退系统将按以下顺序遍历主题:
-
主题 (
packt/winter
) -
配置的默认主题 (
packt/summer
) -
包含的默认主题 (
packt/default
) -
基础主题 (
base/default
)
存储主题的目录是 app/design/frontend
(模板文件,渲染 HTML)和 skin/frontend
(静态文件,CSS、图像、字体等)。
实际上,包的默认主题将包含网店的主题。该包中的子主题大多具有相同的布局,但在一些小细节上有所不同。例如,冬季主题有雪的背景,而夏季主题有海滩的背景。其余的主题完全相同,因为这是使用默认主题的相同文件。
注意
当你的主题不在默认包中时,安装 Magento 扩展后前端不会发生任何变化。
原因可能是模块的主题文件位于默认包中。Magento 只查看配置和基本包。默认包就像其他任何包一样,例如 packt
或 music
。当模块有自定义主题文件时,最佳实践是将这些文件存储在基本主题中,以避免这个问题。
你可以在不同的级别配置 Magento 主题,以便在前端显示。在大多数情况下,主题设置是在配置页面上配置的,通过导航到 系统 | 配置 | 设计。
你可以通过导航到 系统 | 设计 在页面上添加例外。在此页面上,你可以为具有开始日期和结束日期的商店配置主题。
也可以为产品、类别或 CMS 页面配置特定的主题。你可以在产品、类别或 CMS 页面的编辑页面上更改设置。
创建你的第一个主题
我们将创建一个包含默认主题的包。在后续阶段,我们可以在该包中添加更多主题,这些主题将扩展我们在本章中创建的默认主题。
准备工作
打开你的 IDE 并导航到主题文件夹 (app/design/frontend and skin/frontend/default
)。
如何操作...
以下步骤展示了创建自定义主题所需的操作:
-
创建以下文件夹:
-
app/design/frontend/packt/default
-
app/design/frontend/packt/default/layout
-
app/design/frontend/packt/default/template
-
skin/frontend/packt/default
-
-
将
skin/frontend/default/blank/
文件夹的内容复制到skin/frontend/packt/default
。文件夹看起来如下: -
在后端配置新主题和包。导航到 系统 | 配置 | 设计 并按以下方式配置:
小贴士
下载示例代码
你可以从你购买的所有 Packt 书籍的账户中下载示例代码文件。
www.packtpub.com
。如果你在其他地方购买了这本书,你可以访问www.packtpub.com/support
并注册,以便直接将文件通过电子邮件发送给你。 -
保存配置后,你的网站将看起来如下:
主题现在已安装并准备好进一步开发。
工作原理...
我们刚刚创建了一个空白主题;一个只有结构化 CSS 的主题。当我们想用 CSS 自定义外观和感觉时,我们必须在以下文件夹和文件中工作:
-
skin/frontend/packt/default/css
-
skin/frontend/packt/default/css/styles.css (主要 CSS 文件)
-
skin/frontend/packt/default/images (CSS 图像文件夹)
使用这些文件,您可以更改商店的 CSS 外观和感觉。如果您想更改一些 HTML 代码,您必须在app/design/frontend/packt/default
文件夹中工作。
此文件夹是从存储标准文件的app/design/frontend/base/default
文件夹继承而来的。如果您想更改base
文件夹中的文件,您需要从base
文件夹复制具有相同文件夹结构的文件到您的主题。
小贴士
修改基础包文件的代码也会起作用,但这样做不推荐,因为这些文件是 Magento 核心的一部分。当这些文件被更改时,您的更改将在 Magento 升级后丢失,并且您的代码可维护性会降低。
如果您想更改位于app/design/frontend/base/default/template/page/html/header.phtml
的header.phtml
文件,您必须将其复制到app/design/frontend/packt/default/template/page/html/header.phtml
。
当它位于那里时,清除您的缓存。现在,您的主题文件已加载,而不是基本文件夹中的文件。
小贴士
在 Magento 开发过程中,您必须清除缓存以查看您的更改。您还可以禁用缓存。如果这样做,在开发过程中不需要多次清除缓存,但页面加载时间会更长。
还有更多…
要查看每个文件是从哪里加载的,您可以开启模板路径提示。为此,请转到后端并导航到系统 | 配置 | 开发者。
在左上角导航到您的商店视图,并设置以下配置:
当此设置生效时,导航到前端,您将看到每个块周围都有红色列。
将额外文件添加到您的主题中
在前面的菜谱中,我们使用一个 CSS 文件设置了我们的主题。通常,您可能还想添加一些其他文件(当您使用 JavaScript 插件或其他东西时)。
准备工作
我们将向我们的主题添加额外的 CSS 和 JavaScript 文件。为此,我们必须在以下两个目录中工作:
-
app/design/frontend/packt/default/layout
-
skin/frontend/packt/default
如何操作...
在以下步骤中,我们将看到如何向主题添加额外文件:
-
复制
page.xml
文件。所有需要包含的 CSS 和 JavaScript 文件的声明都存储在app/design/frontend/base/default/layout/page.xml
文件中。小贴士
可能还有其他 CSS 和 JavaScript 声明在其他布局 XML 文件中。
page.xml
文件用于设置默认布局结构。 -
我们需要添加一些内容。因此,我们需要将这个文件及其相同的文件夹结构复制到我们的主题文件夹中。将文件复制到
app/design/frontend/packt/default/layout/page.xml
。 -
添加 CSS 声明。
-
如果你打开
page.xml
文件并查看<default>
标签,你将看到很多带有"addCss"
的行。我们需要在该部分添加以下行:<action method="addCss"><stylesheet>css/packt.css</stylesheet></action>
注意
我们在这里指定的路径是从我们的主题根文件夹开始的路径,因此它是
css/packt.css
。Magento 将使用主题回退来在theme
、package
或base
文件夹中查找文件。 -
不要忘记创建 CSS 文件。在
skin/frontend/packt/default/css/
文件夹中创建packt.css
文件。 -
清除 Magento 缓存。
小贴士
当你在 XML 文件中更改某些内容时,你将始终需要清除缓存以查看更改的效果。也可以禁用缓存。目的是你不需要在页面加载较慢时多次清除缓存。
-
重新加载前端并查看 HTML 源代码。你的文件应该被添加。
它是如何工作的...
addCss
函数是 Magento 头部块中的一个函数。此块将生成页面的 HTML 头部部分。
当处理额外的 CSS 和 JS 文件时,以下函数很有用:
-
addJs
:这个函数将js
文件夹(根目录中的文件夹)中的 JS 文件添加到页面头部。 -
addItem
:这是一个将 JS 或 CSS 文件添加到页面头部的函数。该函数有两个必需的参数:类型和文件名。
类型可以有以下值:
-
skin_js
:这是一个位于skin
文件夹中的 JavaScript 文件 -
js_css
:这是一个位于js
文件夹中的 CSS 文件 -
js
:这是一个位于js
文件夹中的 JavaScript 文件 -
css
:这是一个位于skin
文件夹中的 CSS 文件 -
rss
:这创建了一个带有type="application/rss+xml"
属性的<link>
HTML 标签
addItem
函数有三个其他可选参数,用于自定义head
标签中标签的输出。
更多内容...
当你使用正确的函数来包含图片、JavaScript 和 CSS 时,与在.phtml
文件中添加 HTML 代码相比,你将获得以下优势:
-
当你配置时,你可以将 CSS 和 JavaScript 文件合并到一个文件中
-
它始终包含到文件的正确绝对 URL(它包含正确的域名和协议)
-
它使用主题回退系统
添加 jQuery 支持
Magento 1.8 及更早版本在前端和后端都没有使用 jQuery。Magento 用于 JavaScript 功能的库是 Prototype。当你想要使用一些 jQuery 代码时,你需要将 jQuery 库添加到你的主题中。
准备工作
前往 jQuery 网站并获取最新版本。
如何操作...
默认情况下,Magento 加载 Prototype 库。排除它将在每个页面上引起 JavaScript 错误,因此我们需要使用这两个库。jQuery 和 Prototype 的问题是它们都使用美元符号($
)作为它们函数的命名空间。
为了避免冲突,我们必须在 noconflict 模式下使用 jQuery:
-
将 jQuery 库放在
js/jquery
文件夹中。 -
我们将在主题文件夹中创建一个
local.xml
文件来添加额外的布局指令。创建app/design/frontend/packt/default/local.xml
文件。 -
在该文件中添加以下内容:
<?xml version="1.0" encoding="UTF-8"?> <layout> <default> <reference name="head"> <action method="addJs"> <js>jquery/jquery.js</js> </action> </reference> </default> </layout>
-
在
js/jquery
文件夹中创建一个jquery.noconflict.js
文件,内容如下:$.noConflict();
-
在添加 jQuery 文件后,将此文件添加到您的
local.xml
文件中。该文件包含以下代码片段:<?xml version="1.0" encoding="UTF-8"?> <layout> <default> <reference name="head"> <action method="addJs"> <js>jquery/jquery.js</js> </action> <action method="addJs"> <js>jquery/jquery.noconflict.js</js> </action> </reference> </default> </layout>
-
清除您的缓存并查看您的前端源代码。
-
jQuery 文件和
noconflict
文件应添加到头部部分。 -
您现在可以在您的网店中使用 jQuery 了。
小贴士
请记住,您必须使用
jQuery
命名空间而不是$
命名空间来调用 jQuery。还要注意,Magento 的第三方扩展可能包含前端中的 jQuery。请确保版本与您的代码兼容。
-
我们通常使用以下代码片段:
$(document).ready(function() {});
但是,我们将使用以下带有 jQuery 命名空间的行:
jQuery(document).ready(function () {});
它是如何工作的...
我们向主题添加了包含 jQuery 库的两个文件。我们必须使用 noconflict 模式来避免与 prototype 库的冲突。
要将文件添加到布局中,我们创建了一个local.xml
文件。在该文件中,我们添加了将 JavaScript 文件添加到 HTML 代码头部的指令。
使用local.xml
文件进行此操作的主要优势是您不必从基础文件夹覆盖整个 XML 文件,只需添加两行代码。
更改页面标题
在上一章中,我们向 HTML 的<head>
标签添加了一些 CSS 和 JavaScript 文件。要更改页面标题,我们必须以类似的方式进行操作。
我们将更改位于http://magento-dev.local/contacts
的联系人页面的页面标题。
如何操作...
要更改联系页面标题,请查看以下步骤:
-
前往前端中的联系人页面。这是在
http://magento-dev.local/contacts
可用的。 -
您可以看到页面标题为联系我们。我们将将其更改为给我们留言。
-
将
layout/contacts.xml
文件从基础主题复制到我们的主题,并打开该文件。在该文件中,有一个 xml 标签<contacts_index_index>
:<contacts_index_index translate="label"> <label>Contact Us Form</label> <reference name="head"> <action method="setTitle" translate="title" module="contacts"><title>Contacts Us</title></action> </reference> <reference name="root"> <action method="setTemplate"><template>page/2columns-right.phtml</template></action> <action method="setHeaderTitle" translate="title" module="contacts"><title>Contact Us</title></action> </reference> <reference name="content"> <block type="core/template" name="contactForm" template="contacts/form.phtml"/> </reference> </contacts_index_index>
-
在
<reference name="head">
标签中,您将看到一个setTitle
方法。将此行设置为以下内容:<action method="setTitle" translate="title" module="contacts"><title>Give us a message</title></action>
-
清除您的缓存并重新加载页面。现在您的浏览器中的标题已更改为给我们留言。
它是如何工作的...
HTML 页面中的<title>
标签位于头部部分。此头部部分由 Magento 头部块生成。
在模板文件中,代码如下所示:
<title><?php echo $this->getTitle() ?></title>
这意味着 Magento 将在标签中打印该对象的标题。要设置该对象的标题,我们使用setTitle
函数。
翻译工作
Magento 具有在多种语言中运行多个商店的能力。在示例商店中,您可以查看英语、法语和德语商店视图。
准备工作
打开后台并转到常规配置部分(系统 | 配置 | 常规)。我们将配置商店视图的语言并进行一些内联翻译。
如何操作...
以下步骤展示了如何在 Magento 商店中翻译字符串:
-
为每个商店视图配置语言。
-
在左上角,你会看到一个商店切换器,你可以切换商店的配置。为法语商店视图设置法语语言,为德语商店视图设置德语语言。
-
清除缓存并重新加载你的前端。
小贴士
你不会看到任何变化,因为没有为法语和德语安装默认的语言包。语言包可在 Magento Connect 中找到。
-
启用内联翻译。我们将使用可以从 系统 | 配置 | 开发者(最后一个选项卡)配置的内联翻译工具来翻译我们的商店。在默认配置范围内,配置以下内容:
-
重新加载你的前端。你会在前端看到很多字符串周围有红色边框。当你悬停并点击图标时,会弹出一个窗口显示翻译表单:
使用这个翻译表单,你可以为当前商店的语言创建自己的翻译。
-
点击提交,清除缓存,并重新加载页面。
如果一切顺利,你会看到文本已翻译成你刚刚输入的字符串。确保在编辑翻译时始终清除缓存。
它是如何工作的...
Magento 有一个非常强大的翻译功能。要在翻译函数中创建字符串,你必须使用以下语法:
Mage::helper('core')->__('Text to translate');
这种语法总是有效的,但在处理模板文件时,你也可以使用更短的语法:
$this->__('Text to translate');
翻译函数将按以下方式搜索字符串:
-
首先,Magento 将查看数据库中的
core_translate
表。在这个表中,只有那些你已内联翻译的翻译被添加(就像我们在本食谱中所做的那样)。 -
如果在
core_translate
表中没有找到匹配的字符串,Magento 将查看主题中的translate.csv
文件。此文件位于app/design/frontend/<<my_package>>/<<my_theme>>/locale/<<language>>/translate.csv
。 -
如果字符串在主题翻译文件中未找到,Magento 将在
app/locale/<<language>>
文件夹中搜索该字符串。在这个文件夹中,你可以找到所有的翻译文件。一个翻译包将包含一些模块的翻译文件(.csv
)。 -
如果在模块的翻译文件中没有找到字符串,Magento 将打印传递给翻译函数的原始字符串。
理解主题块系统
Magento 前端不是一个单独的文件。它是嵌套在一起的块的组合。每个块都是一个你可以与之交互的对象。
准备工作
在你的商店中打开一个分类页面,你会看到很多框架,它们都代表一个块。一些块是结构性的(左侧列、右侧列、内容、页脚等),而其他块包含内容(产品列表、购物车、导航等)。
如何做...
在接下来的两个步骤中,我们将启用前端提示,以了解页面上的使用块。
-
启用你的前端 模板路径提示。转到 系统 | 配置 | 开发者 并启用模板和块名称。
-
重新加载前端。当前端提示启用时,前端页面将围绕每个块有红色框架。
每个带有模板的块都在一个红色框架中。白色标题是模板文件,蓝色标题是块对象的类名。
当你想编辑块的內容时,你将不得不查看显示在红色框架中的文件。确保当模板文件位于基本主题中时,你将其复制到你的主题中。
它是如何工作的...
所有块都在布局 XML 文件中声明。这些布局文件存储在主题的布局文件夹中。
当你打开布局文件时,所有内容都存储在布局 XML 标签中。此标签的子标签是布局处理器。
布局处理器是一组可以加载到页面上的布局指令。当你查看 contacts.xml
文件时,你会看到两个处理器。布局处理器始终位于 <layout>
XML 树的第二级:
-
default
-
contacts_index_index
默认处理器将加载到每个页面上。contacts_index_index
处理器将加载到联系人页面上。
当你想在页面上加载布局指令时,你必须按照以下方式命名你的处理器:
<modulename>_<controllername>_<actionname>
对于联系人页面,你必须输入以下值:
-
模块:
contacts
-
控制器:
IndexController
-
动作:
indexAction
对于登录页面,如下所示:
-
模块:
customer
-
控制器:
AccountController
-
动作:
loginAction
因此,处理器的名称是 customer_account_login
。
你可以在第四章 添加新页面 的配方中找到有关控制器及其操作的信息,创建模块。
当你查看主题中的 customer.xml
文件时,你会看到这个处理器以及登录页面的指令。当你查看处理器内部时,你会看到 <reference>
标签。在这些标签内部,你会看到 <block>
标签。
引用是一个块。当你有 <reference name="left">
时,你正在对名为 left
的块执行操作。这个名为 left
的块在主题的 page.xml
文件中声明。
你可以执行的操作如下:
-
使用
<action method="methodName">
在块对象上调用方法 -
添加子块
-
移除子块
-
设置模板
Magento 前端是用块构建的。一切开始的主要块被称为根。如果你查看我们主题中的 page.xml
文件,在默认处理器中,你会看到结构块是如何初始化的。
将小部件添加到布局中
在上一个菜谱中,我们描述了 Magento 布局块。编写布局 XML 文件并不像添加块到布局中的图形界面那样简单。这个界面被称为小部件。
准备工作
我们将在分类页面的左侧列添加一个产品链接。转到后端,导航到 CMS | 小部件。
如何操作...
在以下步骤中,我们将为分类页面配置一个小部件。
-
选择添加新小部件实例。
-
在下一个表格中,选择以下截图所示的配置:
-
点击继续,随后屏幕显示如下:
-
使用以下值填写表格:
-
小部件实例标题:
Widget-category-left-product
这是后端中该小部件的标题。当与许多小部件一起工作时,结构名称很容易确定。
-
分配到商店视图:所有商店视图
在小部件选项选项卡中,您必须选择产品。
-
-
保存小部件。点击保存并继续编辑。小部件实例现在已保存,但在前端不会显示任何内容,因为没有设置布局更新。
-
要在前端显示小部件,我们必须通过点击添加布局更新按钮来创建一个布局更新。
-
按照以下方式填写表格:
-
清除缓存并转到分类页面。左侧列将显示一个产品链接。
工作原理...
在左侧列中,向前端添加了一个新块。就像前端上的每个块一样,这个小部件也有一个块类和一个与其他块类似的模板。
唯一的区别是,这个块不是由 XML 文件生成的,而是由数据库中的 XML 布局指令生成的。
小部件界面将生成一个存储在数据库中的布局 XML。块类和模板与 XML 文件中的其他块类似。
第三章. 与产品协作
在本章中,我们将涵盖:
-
设置目录默认值
-
与属性集协作
-
与产品类型协作
-
添加 Facebook“喜欢”按钮
-
通过查询字符串添加产品到购物车
-
嵌入 YouTube 视频
-
更改产品页面 URL
简介
产品选择以及它们在前端如何显示对于创建一个用户体验良好的网店非常重要。让访客购买东西是每位店主的主要目标。
我们必须以这种方式设置产品,以便访客可以快速找到他们想要的东西。如果这样做,并且商店看起来很可靠,访客更有可能购买东西。
本章将解释你可以使用一组产品做什么,以及如何添加额外的东西,如视频或“喜欢”按钮。
本章的目标是在不进行大量开发的情况下使你的商店更加用户友好。
设置目录默认值
第一步是配置目录设置到首选值。我们将涵盖标准安装中所有可能的配置值。
准备工作
在浏览器的一个标签页中打开前端。在第二个标签页中,打开后端并登录。
如何操作...
在接下来的步骤中,我们将配置目录(分类和产品)页面的设置:
-
前往系统中的配置部分,然后点击目录标签。你将看到以下屏幕:
-
打开前端部分并设置以下值:
-
列表模式:网格(默认以网格或列表显示产品)
-
网格页面上每页产品允许的值:12,24,36
-
网格页面上每页产品默认值:24
注意
在更改网格页面的允许和默认值时,确保数字可以被每行的产品数量整除。否则,页面上的产品数量将无法适应网格。
-
列表页面上每页产品允许的值:10,20,30,40
-
列表页面上每页产品默认值:10
-
每页允许所有产品:否
注意
当你有大量产品时,不建议将允许每页所有产品选项设置为是。当你有 2,000 个产品,并且你想要在单页上显示所有产品时,你会生成大量的 HTML 输出,这可能导致内存问题。
-
产品列表排序方式:价格
-
使用扁平目录分类:否
-
使用扁平目录产品:否
注意
前面两个设置(使用扁平目录分类和使用扁平目录产品)的目的是在第五章中解释的使用 EAV 表配方中,数据库概念。
-
在产品和分类中允许动态媒体 URL:是
-
-
在网站地图部分设置以下值:
-
使用类似树状结构的分类网站地图:是
-
每页最小行数:30
此设置将更改网站地图页面的外观。您可以在
catalog/seo_sitemap/category
中找到它。 -
-
启用访客的产品评论。这允许每个人都可以对产品进行评论。当启用时,评论表单将出现在产品评论页面上。
-
打开产品警报设置部分以配置当价格或库存变化时发送的产品警报电子邮件。
我们将配置以下设置的库存警报:
-
允许当产品价格变化时发出警报:否
-
允许当产品库存变化时发出警报:是
注意
之前的配置将发送库存警报电子邮件(当产品有库存时触发库存警报)到订阅的电子邮件地址。
-
-
我们可以在下一节中设置产品警报运行设置的值。我们将配置一个每天凌晨 4 点发送警报电子邮件的日常任务:
-
频率:每日
-
开始时间:04:00:00
-
-
保持产品图像占位符不变。在这里,我们可以设置一个默认图像,当产品没有图像或图像未找到时将显示该图像。最佳方式是在主题中设置占位符图像。
-
在最近查看/比较的产品选项卡中,设置以下值:
-
显示当前:网站
注意
这将显示您在网站的所有商店和商店视图中最近查看的产品。
-
默认最近查看计数:5
-
默认最近比较计数:5
-
-
在价格选项卡中,将目录价格范围设置为全局。对于本教程,我们不需要为每个商店视图设置不同的价格。当价格范围设置为全局时,我们只能为不同商店视图中的产品配置一个价格。
-
在分层导航部分,我们将修改一些设置以自定义类别页面的左侧导航。
-
显示产品计数:是
-
价格范围:均衡价格范围
通过设置此选项,价格阶梯将始终具有相同的增量。
-
-
将顶级导航类别设置为3。这意味着导航的最大深度为三个级别。
-
跳过搜索引擎优化选项。我们将在更改产品页面 URL菜谱中查看它
-
按以下方式配置目录/搜索部分:
-
最小查询长度:3
-
最大查询长度:128
-
最大查询词数:10
-
搜索类型:类似
您可以将此设置为全文,以获得更好的结果,但性能可能不是很好。之前的设置配置了 Magento 的搜索行为。
-
-
不要忘记保存配置。
它是如何工作的...
所有这些设置都保存在 Magento 的配置表中。目录页面的前端文件将获取这些设置并根据这些设置渲染输出。
当你在类别页面添加额外功能时,你可以通过额外参数轻松扩展配置。有关扩展配置的更多信息,请参阅第七章中的扩展系统配置配方,Magento 后端。
与属性集一起工作
Magento 有一个灵活的系统来处理产品。例如,当你出售一个桌面游戏和一台电脑时,每个产品的规格都不同。假设我想为桌面游戏配置一个年龄,为电脑配置一个屏幕分辨率。在这种情况下,这些事情由属性和属性集来处理。
当你创建产品时,你必须首先选择一个属性集(具有特定属性),然后才能输入其他数据。
准备工作
前往后端,并导航到目录 | 属性 | 管理属性和目录 | 属性 | 管理属性集。
我们将在这些页面上创建一些额外的属性和一个属性集。
如何操作...
在接下来的步骤中,我们将创建额外的产品属性,我们可以使用属性集来使用这些属性:
-
在管理属性页面上创建一个新的属性。
-
在点击新建属性后,按照以下方式填写表单:
-
属性代码:available_from(在数据库和函数中使用的属性代码)
-
范围:商店视图(此设置添加了为每个商店视图指定单独值的能力)
-
商店所有者目录输入类型:日期(这是属性的类型)
-
默认值:空
-
唯一值:否
-
值必需:否
-
应用至:所有产品类型
-
在快速搜索中使用:否(此选项在执行快速搜索时搜索属性值)
-
在高级搜索中使用:否(此选项使属性在高级搜索选项中可供搜索)
-
前端可比较:否(此选项在产品比较工具中显示属性)
-
用于促销规则条件:否
-
位置:空
-
前端产品视图页面可见:是(此选项在产品页面上显示属性值)
-
用于产品列表:否(此选项使属性值在类别页面上可用)
-
用于产品列表排序:否(此选项使属性在类别页面上可用于排序)
-
-
在管理标签/选项字段中,填写前端标签。
-
点击保存属性,属性将被保存。
-
通过导航到目录 | 属性 | 管理属性集来创建一个新的属性集。
-
按照以下方式填写表单并点击保存属性集:
-
属性集概述打开。在此页面上,你可以将属性拖放到适当的组中。
-
创建一个名为
Game specific data
的组,并将available_from、memory和processor属性拖放到其中。概览将如下所示:
-
保存属性集。
-
在目录下的管理产品中创建一个新的产品,并输入以下配置:
-
属性集:游戏
-
产品类型:简单产品
-
-
点击继续。
你将看到特殊游戏属性现在在游戏特定数据选项卡中可用:
工作原理...
当你处理多个产品系列时,会使用属性和属性集。在我们的商店样本数据中,有更多属性集可供相机、电脑、衬衫、鞋子等使用。
通过属性集,你可以将一个系列的属性分组在一起。
在创建新属性时,你必须指定属性的类型。以下类型具有不同的输入和输出:
-
文本字段
-
文本区域(多行文本字段)
-
日期
-
是 / 否
-
多选
-
下拉菜单
-
价格
-
媒体图片
-
固定产品税(这用于额外的税,如作者税)
小贴士
当你在分类页面的左侧导航中使用属性作为过滤器时,此属性必须具有下拉菜单、多选或价格类型。
处理产品类型
在 Magento 中,你可以创建不同类型的产品。标准产品是简单产品。此类产品用于销售基本产品。然而,还有其他产品类型,你可以选择选项或尺码。
准备工作
我们将创建一个可配置的产品。例如,你想购买一双鞋,可以选择它们的尺码和颜色。打开你的后端,导航到目录 | 管理产品。
如何操作...
在以下步骤中,我们将创建一个产品(鞋子),我们可以在产品详情页上指定它们的尺码:
-
当你点击添加产品按钮时,选择以下配置:
-
当我们点击下一步时,我们必须选择要配置的属性。此产品必须在鞋码属性上可配置。选择复选框并点击继续。
-
填写所需的属性,并通过点击保存并继续编辑来保存产品。
小贴士
要使产品在商店中可见,请检查网站、状态、可见性、分类和库存属性。
-
导航到关联产品选项卡,我们将创建子产品。
-
通过在快速简单产品创建部分添加以下配置,我们将添加五个子产品:
-
点击快速创建按钮,并重复四次,每次使用不同的鞋码。
-
当一切顺利时,超级产品属性配置部分将如下所示:
-
点击保存并继续按钮,产品将被保存。
-
导航到前端并搜索我们刚刚创建的产品。当我们处于产品详情页面时,您将看到一个下拉菜单,您可以在其中选择我们刚刚创建的选项。
它是如何工作的...
可配置产品是一种在添加到购物车之前必须配置选项的产品。客户将在购物车中看到可配置产品,但在后台也会添加一个简单产品到购物车中。
正是因为这个原因,我们必须创建简单产品来作为可配置产品的选项。可配置产品是父产品,用于在前端显示子产品。当产品售出时,所选子产品的 SKU 将被用于处理订单。这就是为什么库存是在子产品上配置的原因。
更多内容...
在 Magento 中,您可以创建六种类型的产品。以下部分简要介绍了不同产品类型可以实现的功能。
简单产品
简单产品就是您可以在您的网店中销售的产品。在 Magento 中,每个产品都有一个唯一的 ID(SKU),它通常与供应商的商品代码具有相同的值。
可配置产品
在这个菜谱中,我们创建了一个可配置产品。该产品有子产品,您可以在产品详情页面上进行配置(例如,配置大小)。子产品是简单产品。
捆绑产品
捆绑产品类似于可配置产品,但您可以使用更多的(可选)选项。要找到一个捆绑产品的良好例子,您可以访问电子产品 | 电脑 | 自行组装。这个分类中的产品是捆绑产品。
组合产品
组合产品是一种表示一组产品的产品,您可以指定子产品的数量。一个很好的例子可以在家具 | Magento 红家具套装中找到。
虚拟产品
虚拟产品就像一个简单的产品,但它不是实物。它没有库存,也不能发货。在样本数据中,样本商店捆绑产品的保修就是虚拟产品的良好例子。
可下载产品
可下载的产品是一种非实物产品。当客户购买此类产品时,他们将收到一个下载链接,可以下载他们的产品,如 PDF、ZIP、MP3 或其他类型的文件。
添加 Facebook 点赞按钮
这些天,在您的网站上显示 Facebook like
按钮已成为一种时尚,这样访客就可以与他们的朋友分享页面。
在这个菜谱中,我们将把like
按钮添加到每个产品页面。
准备工作
打开您的浏览器并转到产品页面。在您的 IDE 中打开您的配置主题文件夹。
如何操作...
在接下来的步骤中,我们将生成并添加 Facebook like
按钮的代码到产品详情页面:
-
从您的主题中打开
catalog/product/view.phtml
模板。如果它不在主题中,请从app/design/frontend/base/default
文件夹中复制它。 -
从
developers.facebook.com/docs/reference/plugins/like
进入,您可以使用配置器页面来定制您页面的“赞”按钮,如图所示: -
将代码粘贴到您的
view.phtml
文件中,使其更易于阅读;例如,在文件的末尾作为div
标签的子标签,该标签具有product-collateral
类。 -
重新加载产品页面,将显示“赞”按钮。
-
要更改“赞”按钮的 URL,请更改 HTML5 代码中的
data-href
属性。以下代码显示了如何使用产品页面 URL 进行此操作:<div class="fb-like" data-href="<?php echo Mage::helper('core/url')->getCurrentUrl() ?>" data-layout="standard" data-action="like" data-show-faces="true" data-share="true"> </div>
-
在使用 HTML5 代码时,不要忘记包含 JavaScript SDK。
它是如何工作的...
Facebook 的“赞”按钮是您可以嵌入您网站中的社交插件之一。在 Facebook 上,每个 URL 都是一个您可以点赞或评论的对象。您可以从以下 URL 找到有关可用社交插件的更多信息:
developers.facebook.com/docs/plugins/
在本教程中,我们介绍了 Facebook“赞”按钮的添加,但当你想要添加其他社交媒体网站(如 Twitter 或 Google Plus)的按钮时,您可以访问以下 URL:
还有更多...
当您喜欢您网站的页面时,Facebook 将根据页面的 HTML 标记生成一个墙贴。当您喜欢一个页面时,Facebook 将在您的墙上发布该项目的图片、标题和描述。
使用Open Graph(og)meta
标签,您可以自定义这些内容。有关可用 og 标签及其使用方法的概述,请参阅developers.facebook.com/docs/opengraph/using-objects/
。
通过查询字符串添加产品到购物车
在某些用例中,当访客直接将产品添加到购物车时,您将链接访客到您的网站;例如,当您在外部网站上有一个带有立即购买按钮的活动时。
准备工作
使用您的商店打开浏览器,并在后端导航到目录 | 管理产品。
如何操作...
在接下来的步骤中,我们将创建一个 URL,在该 URL 中,产品将被添加到购物车:
-
在您的前端找到您想要添加到购物车的产品。例如,导航到家具 | 客厅 | 脚凳。
-
在后端找到此产品并记住其 ID。
-
要添加产品,我们必须使用产品 ID 作为
GET
参数调用checkout/cart/add
URL。因此,最终的 URL 将是http://magento-dev.local/checkout/cart/add?product=51
。 -
在调用此 URL 时,您将被重定向到包含此产品一个项目的购物车页面。
-
当你在查询中添加
qty
参数时,你可以将同一产品的多个项目添加到购物车中。调用http://magento-dev.local/checkout/cart/add?product=51&qty=3
URL,你将看到有三个产品被添加到购物车中。
小贴士
无法将两个或更多不同产品添加到购物车中,并使用相同的查询。如果你想这样做,你可以创建一个分组产品。
它是如何工作的...
如果我们监控我们商店中的添加到购物车
动作,我们将看到前端对checkout/cart/add
动作的GET
或POST
请求。如果请求有效,这个动作将转发到购物车页面。
更多内容...
添加一个简单产品很容易,但如果我们想添加一个分组、可配置或捆绑产品,查询字符串就会稍微复杂一些。
要知道必须发送到add
动作的参数,我们必须使用 Firebug 进行调试。在网络面板中,如图所示,你可以调试请求:
上一张截图显示了可配置产品的POST
参数。
嵌入 YouTube 视频
在产品描述中,我们可以添加 HTML 标签,这样我们就可以使用<object>
标签将 YouTube 视频嵌入到产品的描述中。
准备工作
前往www.youtube.com
,选择你想要添加到产品描述中的视频。
如何操作...
下一个步骤将展示如何在产品详情页面上嵌入 YouTube 视频:
-
在 YouTube 视频页面上,点击嵌入按钮。当你点击这个按钮时,以下屏幕会出现:
-
复制 HTML 代码并将其粘贴到产品的描述中。
-
保存产品。
-
前往前端的产品页面。你将在产品页面上看到视频。
它是如何工作的...
在产品描述中使用 HTML 标签的能力为这个字段提供了很多灵活性。可以使用所见即所得编辑器来编辑内容,因为这允许我们使用如 YouTube 视频或其他第三方小部件等小部件。
更改产品页面的 URL
当你在产品页面上时,每个产品的 URL 总是看起来很干净。URL 中的名称使其非常 SEO 友好。
在这个食谱中,我们将探讨 Magento 中URL 重写的可能性以及它是如何工作的。
准备工作
从后端的产品管理页面进入目录。打开一个产品并查看URL 键属性。这个食谱基于HTC Touch Diamond产品。
如何操作...
在接下来的步骤中,展示了你如何更改产品页面的 URL:
-
在前端找到合适的产品并查找 URL。在这种情况下,这将是一个
/htc-touch-diamond.html
。 -
在后端,将URL 键属性更改为buy-now-htc-touch-diamond。
-
在前端重新加载产品。URL 将更改为我们在后端输入的新 URL。
小贴士
当你选择为旧 URL 创建永久重定向复选框时,Magento 将为产品的旧 URL 创建一个永久 301 重定向响应。该复选框位于产品编辑页面下的URL 键属性中。
-
在后端清空URL 键属性并再次保存产品。你会发现 Magento 会根据产品的名称自动生成URL 键值。
-
在后端,转到系统 | 配置 | 目录 | 搜索引擎优化。清空产品 URL 后缀字段并保存配置。
-
通过导航到系统 | 缓存管理来清除缓存。
-
通过导航到系统 | 索引管理来重新索引产品 URL 重写索引。在重新索引过程中,URL 将在
core_url_rewrite
数据库表中生成。 -
在前端重新加载产品,你会看到
.html
后缀已经消失。
它是如何工作的...
在 Magento 中,有一个 URL 重写系统,它将 SEO 友好的 URL 映射到该请求的系统 URL。你可以在后端看到所有的 URL 重写。导航到目录 | URL 重写管理,你将看到 Magento 将为控制器操作映射的完整 URL 列表。例如,当我们搜索htc-touch-diamond
时,我们将看到一个包含所有这些键的 URL 列表。
我们看到的是以下内容:
-
永久 301 重定向响应(选项列中的值为RP的行。RP 代表永久重定向。)
-
产品 URL。
-
类别产品 URL。
每个商店视图都会重新生成所有 URL。当一个产品在多个商店中启用时,一个产品有多个 URL 是正常的。
还有更多...
在 URL 重写页面上,也可以添加自定义 URL 重写;例如,为联系页面添加 URL 重写。
在添加如下截图所示的配置时,你将为英文商店视图创建/sitemap.html
的重写:
-
在商店下拉菜单中,你可以配置 URL 重写的商店。
-
在ID 路径字段中,设置了 URL 重写的 ID。你必须为这个输入一个唯一的名称。
-
请求路径字段中的值是你想要重写的路径。在这种情况下,我们想要在
/sitemap.html
路径上重写某些内容。 -
目标路径字段中的值是请求将结束的路径。在这种情况下,它是联系页面。
-
如果重定向下拉菜单中的值设置为是,你可以选择将 301 重定向到目标。当它设置为否时,目标页面将在请求路径上渲染(因此页面的 URL 不会改变)。
第四章. 创建模块
在本章中,我们将涵盖以下主题:
-
创建文件
-
注册块、助手和模型
-
添加新页面
-
添加布局文件
-
添加翻译文件
-
添加新产品的块
-
重写核心类
简介
当你查看 Magento 的核心代码时,你会看到模块化架构。每个概念都存储在一个模块中。Magento 是所有核心模块的组合。模块化架构的优势在于其可扩展性。添加扩展或重写核心的模块很容易。
在本章中,我们将创建一个模块,使用你扩展 Magento 所需了解的最重要的事项。
创建文件
当你想创建一个模块时,第一步是创建注册模块所需的文件和文件夹。在本食谱的末尾,我们将有一个已注册但没有任何功能的模块。
准备工作
打开你的 IDE 并导航到 app/code/local
文件夹。如果本地文件夹不存在,创建 app/code/local
文件夹。
如何操作...
在以下步骤中,我们将创建注册 Magento 模块所需的文件。
-
在
app/code/local
中创建模块,通过创建以下文件夹:-
app/code/local/Packt
-
app/code/local/Packt/Helloworld
-
app/code/local/Packt/Helloworld/etc
Packt
是模块的命名空间,Helloworld
是模块的名称。 -
-
通过在
app/etc/modules
文件夹中创建Packt_Helloworld.xml
文件来注册模块。在此文件中添加以下内容:<?xml version="1.0"?> <config> <modules> <Packt_Helloworld> <active>true</active> <codePool>local</codePool> </Packt_Helloworld> </modules> </config>
-
在模块的
etc
文件夹中创建主配置文件config.xml
。在文件中添加以下内容:<?xml version="1.0" encoding="UTF-8"?> <config> <modules> <Packt_Helloworld> <version>0.0.1</version> </Packt_Helloworld> </modules> </config>
-
通过访问后端检查模块的注册情况。转到 系统 | 配置 | 高级 并检查模块是否在列表中。确保你已经清除了 Magento 缓存。
小贴士
当你在命令行中运行 wiz module-list
命令时,你会看到所有模块的列表,包括版本、代码池等。你可以在 第一章 的 配置开发工具 食谱中找到有关 Wiz 命令行工具的更多信息。
工作原理...
Magento 有三个代码池。它们如下所示:
-
核心:核心池包含 Magento 代码的模块和类。不建议更改此目录中的文件,因为当 Magento 升级时,这些文件将被覆盖。
-
社区:社区池是为社区提供的 Magento 模块。它们大多是你可以从 Magento Connect 下载的免费模块。
-
本地:本地池是为自定义模块准备的,例如我们在这个食谱中创建的模块。
Magento 将首先在本地代码池中搜索代码。接下来,它将在社区代码池中查找,最后它将在核心代码池中查找。
注册块、辅助和模型
当我们想要在我们的模块上执行操作时,我们必须使用块、模型和辅助。在这个菜谱中,我们将使用正确的类前缀注册这些对象类型。
准备工作
打开Packt_Helloworld
模块的config.xml
文件。此文件位于app/code/local/Packt/Helloworld/etc/config.xml
。
如何操作...
在以下步骤中,我们将向模块添加配置以注册块、辅助和模型:
-
通过在
config.xml
文件中添加以下配置来注册块。将以下 XML 代码作为<config>
标签的子标签添加:<global> <blocks> <helloworld> <class>Packt_Helloworld_Block</class> </helloworld> </blocks> </global>
-
创建
app/code/local/Packt/Helloworld/Block
文件夹。 -
重复步骤 1 和步骤 2 以注册辅助和模型。你的全局标签将类似于以下代码所示:
<global> <blocks> <helloworld> <class>Packt_Helloworld_Block</class> </helloworld> </blocks> <helpers> <helloworld> <class>Packt_Helloworld_Helper</class> </helloworld> </helpers> <models> <helloworld> <class>Packt_Helloworld_Model</class> </helloworld> </models> </global>
-
创建
app/code/local/Packt/Helloworld/Helper
和app/code/local/Packt/Helloworld/Model
文件夹。 -
通过将以下内容添加到
app/code/local/Packt/Helloworld/Helper/Data.php
文件中,创建模块的辅助类:<?php class Packt_Helloworld_Helper_Data extends Mage_Core_Helper_Abstract { }
辅助类总是从核心辅助类扩展,以使用该类中声明的函数。
-
使用以下命令通过 Wiz 命令行工具测试您的配置:
wiz devel-models
你将看到你的注册模型在 Magento 安装中可用。
小贴士
当你在配置 XML 文件中更改某些内容时,确保在测试配置之前清除 Magento 缓存。否则,更改将没有效果,因为旧配置被缓存了。
它是如何工作的...
当这些对象被注册后,我们可以在Block
、Helper
和Model
文件夹中创建类。这些对象中的类名需要遵循以下命名约定;否则,它们将无法找到:
<Vendor Namespace>_<Modulename>_<Objectype>_<Classname>
以app/code/<codepool>
开头的每个文件夹名都用于类名中,由下划线分隔。最后一部分是文件名。Model
、Block
和Helper
文件夹中的每个文件夹或文件都以大写字母开头。
我们的帮助类位于app/code/local/Packt/Helloworld/Helper
,因此类名为Packt_Helloworld_Helper_Data
。
要加载一个模型,我们必须使用Mage::getModel()
函数。在第一个参数中,我们必须指定类名,这与实际类名不同。要指定Packt_Helloworld_Model_Sample
类,我们必须使用Mage::getModel('helloworld/sample')
来获取该模型的实例。
我们已将我们的模型注册为helloworld
。Model
文件夹中模型的名称是sample
。当你查看之前讨论的getModel()
函数时,你会看到helloworld
和sample
这两个词在该函数的参数中。
获取辅助和块使用相同的语法。对于辅助,我们必须使用Mage::helper()
函数。对于块,这种语法用于布局 XML 文件或当我们使用核心函数处理块时。
添加新页面
现在,我们将对我们的模块做一些可见的操作。我们将向我们的 Magento 商店添加一个页面,我们可以用它来执行多个目的。
准备工作
我们必须在config.xml
文件中添加一些配置,我们还将创建一个控制器文件。
如何操作...
在以下步骤中,我们将通过在Packt_Helloworld
模块中添加额外的配置来向 Magento 安装中添加一个额外的页面:
-
打开你的 IDE 并导航到
module
文件夹。 -
在模块的
config.xml
文件的config
标签下添加以下配置:<frontend> <routers> <helloworld> <use>standard</use> <args> <module>Packt_Helloworld</module> <frontName>helloworld</frontName> </args> </helloworld> </routers> </frontend>
-
要创建控制器文件,我们将在
folder app/code/local/Packt/Helloworld/controllers/
中创建一个IndexController.php
文件。 -
在这个控制器文件中添加以下内容。这将创建
IndexController
类中的两个操作:<?php class Packt_Helloworld_IndexController extends Mage_Core_Controller_Front_Action { public function indexAction() { } public function helloAction() { echo 'Action hello in Helloworld IndexController'; } }
-
清除缓存并测试以下位置的控制器:
-
http://magento-dev.local/helloworld/index/hello
,对于helloAction
函数 -
http://magento-dev.local/helloworld
,对于indexAction
函数
helloAction()
函数将显示我们在echo
语句中设置的字符串。indexAction()
函数是一个空白页面,因为我们在这个操作中没有添加更多的代码。 -
它是如何工作的...
Magento 控制器的语法如下所示:
<modulename or frontname>/<controllerName>/<actionName>
在我们的例子中,模块名称和前端名称是helloworld
,控制器名称是index
,操作名称是hello
,因此我们得到/helloworld/index/hello
。
当类名以单词Controller
结尾时,Magento 会识别控制器文件。这就是为什么类名是IndexController
的原因。
同样的规则适用于控制器操作。函数的名称需要以单词Action
结尾。Action
之前的部分是操作的名称。名为helloAction()
的函数会导致 URL 中的hello
部分。
小贴士
在编写控制器和操作的名称时,确保你跟踪类和函数名称的首字母大写。如果你遗漏了一些大写字母,你的代码将无法工作。
更多内容...
当我们测试控制器操作时,我们会看到一个空白页面。这并不错误,因为控制器操作中没有添加任何逻辑。
如果我们想看到商店的前端,我们必须使用以下代码:
$this->loadLayout();
$this->renderLayout();
这将启动布局系统,该系统将加载布局指令。通常不推荐在 POST 操作中加载前端,因为我们想要处理 POST 并继续到下一页。
添加布局文件
在这个菜谱中,我们将使用自定义布局 XML 文件来定制我们之前创建的页面的前端。
准备工作
为了添加布局文件,我们必须在模块的config.xml
文件中添加一些配置,以及一个布局 XML 文件。
如何操作...
在以下步骤中,我们将学习如何向 Magento 模块添加布局文件。
-
将以下配置添加到您的
config.xml
文件中,以初始化布局 XML 文件。将其添加为<frontend>
标签的子标签:<layout> <updates> <helloworld> <file>helloworld.xml</file> </helloworld> </updates> </layout>
-
在您的主题布局文件夹中的
app/design/frontend/<package>/<theme>/layout/helloworld.xml
目录中创建helloworld.xml
文件。 -
在
helloworld.xml
文件中,添加以下内容以测试文件是否已加载:<?xml version="1.0" encoding="UTF-8"?> <layout> <default> <remove name="header" /> </default> </layout>
-
在前端重新加载页面。之前的配置将从每个前端页面中删除标题。
-
从
<default>
标签中删除内容并在布局 XML 文件中创建helloworld_index_hello
处理。您可以通过将以下 XML 代码作为<layout>
标签的子标签添加来实现这一点:<helloworld_index_hello> </helloworld_index_hello>
-
对于这个页面,我们将配置
2columns-right
布局。为此,在布局 XML 文件中将以下配置作为helloworld_index_hello
处理的子标签添加:<helloworld_index_hello> <reference name="root"> <action method="setTemplate"> <template>page/2columns-right.phtml</template> </action> </reference> </helloworld_index_hello>
-
在控制器的
helloAction()
函数中添加以下代码以启动布局系统:$this->loadLayout(); $this->renderLayout();
-
清除 Magento 缓存并导航到
http://magento-dev.local/helloworld/index/hello
页面。您将看到一个向右的列。
它是如何工作的...
如果您只在 app/design/frontend/<package>/<theme>/layout
文件夹中放置一个 layout.xml
文件,它将不会被 Magento 加载。为此,我们必须在我们的模块的 config.xml
文件中进行配置,就像在这个教程中所做的那样。
布局 XML 文件(helloworld.xml
)与其他所有布局文件一样工作,如第二章所述,主题化。
更多...
添加自定义布局 XML 文件为扩展您的安装提供了很多可能性。将 XML 文件复制到您的主题并更改所需的设置是最简单的方法。然而,如果您愿意,可以添加一个自定义布局文件并操作该文件中的块。
这种方法的目的是避免双重编码。关于模块化主题化这种方式的良好参考可以在 www.classyllama.com/development/magento-development/the-better-way-to-modify-magento-layout
找到。
添加翻译文件
在 Magento 中,您可以在多种语言中运行商店,这样您的模块就可以翻译成配置的语言。在本教程中,我们将向我们的模块添加一个自定义的翻译 CSV 文件,以便在需要时放置自定义字符串。
准备工作
对于这个教程,我们必须在我们的模块的 config.xml
文件中添加一些配置。此外,我们还需要在 locale
文件夹中创建一个翻译 CSV 文件。
如何做...
在以下步骤中,我们将向模块添加配置,以便我们可以将界面翻译成多种语言:
-
将以下配置作为
helloworld
模块的config.xml
文件中<frontend>
标签的子标签添加。这将初始化一个额外的translate
文件到安装中:<translate> <modules> <Packt_Helloworld> <files> <default>Packt_Helloworld.csv</default> </files> </Packt_Helloworld> </modules> </translate>
-
我们刚刚配置了我们的模块以使用
Packt_Helloworld.csv
文件。在app/locale/en_US
文件夹中创建此文件。 -
在控制器中创建一个测试翻译。在
IndexController
的indexAction()
函数中添加以下行:echo $this->__('Test translation packt');
-
前往适当的页面(
http://magento-dev.local/helloworld
)。你会看到Test translation packt被打印出来,如下截图所示: -
在
Packt_Helloworld.csv
文件中添加以下内容:"Test translation packt","Packt translation to test"
-
清除缓存并重新加载页面。你会看到输出已更改为Packt 翻译测试,如下截图所示:
它是如何工作的...
现在,我们将讨论 Magento 中翻译函数的行为。
当调用__('translate string')
函数时,Magento 将在以下资源中搜索翻译字符串:
-
数据库表中的
core_translate translate.csv
文件,位于app/design/frontend/<package>/<theme>/locale
文件夹中 -
翻译位于
app/locale/<language>
的文件
如果当前语言没有找到匹配的字符串,Magento 将返回translate
函数的第一个参数中存在的字符串。
如果在资源中找到一个字符串,Magento 不会进一步搜索该字符串。这意味着在core_translate
表和translate.csv
文件中的字符串将从数据库中加载。
translate
函数通常通过使用$this->__()
在当前对象上调用,但这始终指向该对象的helper
函数。如果$this->__()
不起作用(通常当你在一个不扩展抽象块、helper 或模型的类中时),你必须直接从模块的helper
类中调用translate
函数,如下面的代码所示:
Mage::helper('<module name>')->__('…')
添加新产品块
模块现在已准备好进行实际工作。在先前的菜谱中,我们用最常见的特点配置了模块。在这个菜谱中,我们将向之前创建的页面添加一个新产品块。
准备工作
要创建一个自定义块,我们必须在Packt_Helloworld
模块的Block
文件夹中创建一个Block
类,一个布局指令以将块添加到我们的页面,以及一个在主题中的phtml
模板来样式化块的 HTML 输出。
如何操作...
以下步骤描述了如何将带有新产品块的块添加到前端:
-
在
app/code/local/Packt/Helloworld/Block
中创建block
类。类的名称是Newproducts
,因此我们必须在此文件夹中创建一个Newproducts.php
文件。 -
在文件中添加以下内容。这将创建一个扩展
Mage_Core_Block_Template
类的类。如果指定,此类将输出一个模板:<?php class Packt_Helloworld_Block_Newproducts extends Mage_Core_Block_Template { }
-
将模板添加到您的主题
template
文件夹中。这位于app/design/frontend/<package>/<theme>/template/helloworld
文件夹中。在此文件夹中创建newproducts.phtml
文件。 -
在这个文件中添加一些 HTML 内容,例如
<h2>New Products</h2>
。 -
通过在
helloworld_index_hello
处理程序中添加以下 XML 代码来在helloworld.xml
布局文件中创建一个块:<reference name="content"> <block type="helloworld/newproducts" name="block_newproducts" template="helloworld/newproducts.phtml" /> </reference>
-
清除缓存并转到
http://magento-dev.local/helloworld/index/hello
页面。你将在网站的内容中看到新产品标题。 -
在
block
类中创建getProducts()
函数。这个函数将返回商店中的五个最新产品。getProducts()
函数的代码如下:public function getProducts() { $products = Mage::getModel('catalog/product')->getCollection() ->addAttributeToSelect('*') ->setOrder('created_at') ->setPageSize(5); return $products; }
在这个函数中,我们将对产品集合执行查询。我们按日期排序并限制结果为
5
,以便我们得到最新的五个产品。 -
在模板中调用
getProducts()
函数,并通过循环产品来打印它们。模板代码如下:<h2>New Products</h2> <ul> <?php foreach ($this->getProducts() as $_product): ?> <li><?php echo $_product->getName() ?></li> <?php endforeach; ?> </ul>
它是如何工作的...
在这个菜谱中,我们所做的是对标准 Magento 的基本扩展。我们创建了一个自定义块。这个块被放置在模块创建的自定义页面上。
在这个块中,有一个返回五个最新产品的函数,并且会执行查询来获取这些产品。这不是通过 SQL 完成的,而是通过使用 Magento 集合。
使用这个的目的在于获得一个简单的接口来返回正确的实体。由于产品不是存储在一个数据库表中,这可以节省你编写一个非常复杂的 SQL 查询。
重写核心类
在某些情况下,你可能想要更改关于 Magento 标准行为的某些内容。当你看到一些你想要更改的核心类中的代码时,你必须遵循这个菜谱。
由于不建议在核心文件中进行更改,我们可以将类的路径重写为自定义的一个,它是原始类的一个父类。
准备工作
在这个菜谱中,我们将把核心产品模型重写为模块中的一个自定义类。
如何做...
在以下步骤中,我们将更改产品getName()
函数的输出:
-
getName()
函数在产品详情页上被调用。导航到这个页面并打开catalog/product/view.phtml
模板。 -
当你在模板中搜索
getName()
时,你会看到这是在$_product
变量上完成的。为了知道这个变量的类,你可以通过echo get_class($_product)
来调试它。 -
这个函数的输出返回
Mage_Catalog_Model_Product
类。为了重写它,我们必须在我们的module
文件夹中创建一个扩展原始类的空类。在app/code/local/Packt/Helloworld/Model/Catalog/
文件夹中创建一个Product.php
文件。 -
在那个文件中粘贴以下代码:
<?php class Packt_Helloworld_Model_Catalog_Product extends Mage_Catalog_Model_Product { }
小贴士
当重写一个类时,最佳实践是遵循你模块中原有类的文件夹结构。在
Packt_Helloworld
模块的model
文件夹中,我们从Catalog
文件夹开始(指的是Catalog
模块)。在这个文件夹中,你可以看到与原始模块相同的文件结构。 -
当你清除缓存并重新加载前端时,你会看到
get_class
函数的输出没有改变。这是因为我们没有在config.xml
文件中添加重写配置。打开config.xml
文件,并将以下代码作为<models>
标签的子标签粘贴:<catalog> <rewrite> <product>Packt_Helloworld_Model_Catalog_Product</product> </rewrite> </catalog>
小贴士
当你想重写一个
block
类时,你必须将类似的配置作为<blocks>
标签的子标签粘贴。同样,对于helper
,将配置粘贴到<helpers>
标签下。 -
清除缓存并重新加载产品页面。你会看到
get_class
函数的输出已更改为我们刚刚创建的类。 -
当新类被加载时,只需覆盖此文件中要更改的函数即可。当我们想要更改
getName()
函数时,我们必须在此类中添加旧函数并更改一些行为。当你将以下代码粘贴到类中时,产品的名称将会改变:public function getName() { return 'Packt ' . $this->_getData('name'); }
-
重新加载产品页面,你会看到产品的名称以 Packt 开头。
它是如何工作的...
最佳实践是不要重写核心类,因为更改 Magento 的标准行为在某些情况下可能会破坏应用程序。一个更稳定的方法是与描述在 第八章 的 事件处理器和计划任务 中的事件处理器一起工作。
不幸的是,在某些情况下,无法使用事件进行操作。因此,你需要重写一个核心类。在这种情况下,你必须按照本食谱中描述的方式进行操作:
-
创建一个扩展原始类的空类
-
将函数粘贴到你想要更改的类中
-
在你的模块的
config.xml
文件中添加配置以重写核心类
当重写在 config.xml
类中添加时,当使用 Magento 函数调用类时,Magento 将会识别重写。对于一个模型,这是 Mage::getModel()
函数,其中第一个参数是模型的路径。
当直接在代码中调用类时,如 $product = new Mage_Catalog_Model_Product()
,重写将不会生效,因为 Mage_Catalog_Model_Product
类返回了一个实例。
第五章:数据库概念
在本章中,我们将涵盖:
-
在表中找到你的方向
-
在 Magento 中创建数据库连接
-
使用平面表
-
使用 EAV 表
-
配置主/从设置
-
修复数据库
简介
Magento 有一个非常大的数据库模型来存储各种信息。有许多 Magento 模块。每个模块在数据库中都有自己的表。使用命名约定可以在数据库中提供良好的概览。一些模块使用平面数据库模型(每个实体一个表),而其他模块使用EAV(实体属性值)数据库模型。本章中的食谱将涵盖你在与数据库一起工作时需要了解的最重要的事情。
在表中找到你的方向
当你查看表时,你会意识到表的数目非常高。在一个标准安装中,有超过 300 个数据库表。需要一个结构化的命名约定来在这个表的迷宫中找到你的方向。
准备工作
在这个食谱中,我们将对数据库进行一些查询,以了解表及其用途。
在这里,你首先需要使用phpMyAdmin与你的数据库客户端建立连接。
如何操作...
在接下来的步骤中,我们将学习一些方法,帮助你熟悉 Magento 中的数据库模型:
-
获取核心模块的列表。你可以在
app/code/core/Mage
文件夹中运行ls –l
命令来做到这一点。这将给出以下输出:drwxrwxr-x 7 www-data www-data Admin drwxrwxr-x 8 www-data www-data Adminhtml drwxrwxr-x 6 www-data www-data AdminNotification drwxrwxr-x 8 www-data www-data Api drwxrwxr-x 8 www-data www-data Api2 drwxrwxr-x 7 www-data www-data Authorizenet drwxrwxr-x 5 www-data www-data Backup drwxrwxr-x 9 www-data www-data Bundle drwxrwxr-x 8 www-data www-data Captcha drwxrwxr-x 9 www-data www-data Catalog drwxrwxr-x 5 www-data www-data CatalogIndex drwxrwxr-x 7 www-data www-data CatalogInventory drwxrwxr-x 6 www-data www-data CatalogRule drwxrwxr-x 8 www-data www-data CatalogSearch drwxrwxr-x 7 www-data www-data Centinel drwxrwxr-x 9 www-data www-data Checkout drwxrwxr-x 10 www-data www-data Cms drwxrwxr-x 7 www-data www-data Compiler drwxrwxr-x 7 www-data www-data Connect drwxrwxr-x 7 www-data www-data Contacts
-
在你的数据库上运行
SHOW TABLES
命令。这将在 phpMyAdmin 中给出以下结果: -
将表名的第一部分与模块列表进行比较。你会发现每个表的名字都是以模块的名字开头的。
-
打开 phpMyAdmin 并导航到数据库的设计器选项卡。这个页面将渲染一个包含所有数据库表的数据库模式,这些表都在 Magento 中。
-
你可以通过在左侧列中检查你想要看到的表来限制结果。取消选择所有表并选择核心表。这些表以
core_*
开头。这将给出核心表之间关系的概览,如下截图所示: -
在上一张截图,我们看到从
core_store
表开始有很多关系。让我们看看这个表的结构。你可以通过点击core_store
表的结构
选项卡中的关系视图链接来做到这一点。这将给出以下输出:
工作原理...
Magento 数据库就像任何带有表和它们之间关系的数据库一样。困难的是,有如此多的表使得简单的概览变得非常困难。
在工作过程中,你不需要知道数据库的整个结构。使用 phpMyAdmin 中的设计器选项,你可以显示你想要看到的表。
数据库中的大多数表都代表 Magento 中的实体。这些实体(模型)通过 Magento 框架与数据库连接。此框架使用集合在数据库上执行选择查询。这些集合将生成一个Zend_Db_Statement
对象,该对象生成 SQL 查询。
这些集合的目的在于它们返回 Magento 模型的实例。一个 SQL 或 Zend DB 查询将返回包含数据的数组。
在 Magento 中创建数据库连接
Magento 使用资源来连接到数据库。默认情况下,Magento 使用一个连接与数据库交互。在这个连接中,模块的config.xml
文件中声明了资源,以将模型与正确的表链接起来。
在这个菜谱中,我们将使用 Magento 连接来读取一些表,并学习如何配置第二个连接到另一个数据库,例如第三方系统。
准备工作
导航到我们在第四章“创建模块”中创建的Packt_Helloworld
模块的IndexController
。我们将测试indexController
中的某些连接。
如何操作...
在以下步骤中,我们将解释如何在您的脚本中处理多个数据库连接:
-
将以下代码粘贴到
indexAction
方法中:$resource = Mage::getSingleton('core/resource'); $connection = $resource->getConnection('core_read'); $results = $connection->query('SELECT * FROM core_store')->fetchAll(); Zend_Debug::dump($results);
此查询将返回
core_store
表中的值数组。 -
在前端重新加载
indexAction
方法。您将看到core_store
表中的所有值,如下所示:array(4) { [0] => array(7) { ["store_id"] => string(1) "0" ["code"] => string(5) "admin" ["website_id"] => string(1) "0" ["group_id"] => string(1) "0" ["name"] => string(5) "Admin" ["sort_order"] => string(1) "0" ["is_active"] => string(1) "1" } [1] => array(7) { ["store_id"] => string(1) "1" ["code"] => string(7) "default" ["website_id"] => string(1) "1" ["group_id"] => string(1) "1" ["name"] => string(7) "English" ["sort_order"] => string(1) "0" ["is_active"] => string(1) "1" } ...
-
创建一个新的连接。例如,我们将对第二个 Drupal 数据库进行一些查询。将以下代码粘贴到
indexAction
方法中,并使用您的凭据修改连接参数:$dbConfig = array( 'host' => 'localhost', 'dbname' => 'drupal', 'username' => 'drupal_web', 'password' => 'drupal_pwd', ); $_resource = Mage::getSingleton('core/resource'); //Create the connection $connection = $_resource->createConnection('drupalConnection', 'pdo_mysql', $dbConfig); $results = $connection->query('SELECT * FROM node')->fetchAll(); Zend_Debug::dump($results);
-
重新加载您的客户端,您将看到您之前查询的结果。前面的代码将使用 Magento 连接连接到外部数据库。
它是如何工作的...
Magento 中的数据库连接由Mage_Core_Model_Resource
模型管理。通过使用单例模式加载类,连接在每次进程中进行一次。
要获取模型的一个实例,您可以使用Mage::getModel()
函数,该函数返回对象的新实例。通过使用Mage::getSingleton()
函数,返回第一个参数中给出的对象实例。然而,一旦模型被声明,Magento 不会创建一个新的实例,而是返回对象的现有实例。
处理平面表
在与数据库交互时,Magento 有两种类型的实体:平面实体和EAV实体。
平面表与表示数据库表列的字段一起工作。下一章中描述的 EAV 表将使用属性。在这个菜谱中,我们将专注于平面表。
准备工作
在本食谱中,我们将对扁平表进行一些示例查询。打开 IndexController
从 Packt_Helloworld
模块,并在其中创建一个 flatAction()
方法。此控制器操作将用于触发示例查询。
如何操作...
在下一组说明中,我们将向您展示数据库中的扁平表是如何与 Magento 模型关联的:
-
查看评论表。您可以通过在 phpMyAdmin 表名中过滤它们来完成此操作。
-
从
review_detail
表中获取所有数据。您可以通过单击表来完成此操作。 -
通过在 Magento 代码中选择数据来打印相同的数据。为此,请将以下代码粘贴到您的
flatAction
方法中:$resource = Mage::getSingleton('core/resource'); $connection = $resource->getConnection('core_read'); $results = $connection->query('SELECT * FROM review_detail')->fetchAll(); Zend_Debug::dump($results);
在执行此代码时,会返回一个值数组。
-
我们可以通过处理 Magento 集合来获得相同的结果。您可以通过运行以下代码来完成此操作:
$reviews = Mage::getModel('review/review')->getCollection(); foreach ($reviews as $_review) { Zend_Debug::dump($_review->debug()); }
-
此代码将产生相同的输出,但集合的数据来自对象,而不是直接来自数据库。这样做的原因是我们可以直接在对象上调用函数。
小贴士
当与 Magento 数据库表一起工作时,建议使用集合而不是直接 SQL 查询。有关 Magento 集合的更多信息,请参阅第六章 使用 Magento 集合 的食谱,数据库和模块。
-
在屏幕上打印评论的 URL。我们可以通过在
review
实体上调用getReviewUrl()
函数来完成此操作。在每个循环中添加以下代码:echo $_review->getReviewUrl().'<br/>';
-
在重新加载页面时,您将看到所有评论的 URL 都被打印出来。URL 结构的逻辑是在
Mage_Review_Model_Review
类中完成的,它代表一个review
实体。
工作原理...
在 Magento 中处理扁平表并不困难。当使用 Magento 框架时,一个扁平实体由以下部分组成:
-
数据库表:数据库表用于存储实体的信息。此数据库表可以与 Magento 数据库中的不同表相关联,例如与产品相关联。
-
模型对象:模型对象是一个类,在从数据库表加载一行时返回一个实例。此类可以具有具有业务逻辑的方法,例如本食谱中的
getReviewUrl()
函数。 -
资源模型对象:资源模型是一个将模型与数据库表连接起来的类。此类将处理例如
save()
方法。 -
资源收集对象:资源收集对象是一个类,它使得可以处理实体的 Magento 收集。
处理 EAV 表
EAV 表是一个数据库模型,用于 Magento 中的一些实体。在本食谱中,我们将探讨 Magento 中 EAV 实现的细节。
准备工作
EAV 模式用于 Magento 中的一些实体,如产品实体。在这个菜谱中,我们将使用 SQL 查询进行一些更新产品的查询。打开你的数据库客户端,并准备好运行一些复杂的查询。
如何做到这一点...
以下步骤展示了如何创建一个查询来返回 Magento EAV 模型的数据:
-
Magento 的 EAV 实体在
eav_entity_type
表中声明。运行以下查询以查看哪些 EAV 实体可用:SELECT * FROM eav_entity_type;
记住
catalog_product
实体的 ID。 -
要获取与产品相关的属性,我们必须查看
eav_attribute
表。要查看哪些属性与产品相关,我们必须运行以下查询:SELECT * FROM eav_attribute WHERE entity_type_id = 10
确保实体类型 ID 为
10
的是catalog_product
。 -
现在我们有了实体和属性,所以下一步是找出值。在后台找到一个产品并记住其 ID。例如,ID 为
166
的HTC Touch Diamond
产品。 -
通过运行以下查询从
catalog_product_entity
表中选择产品:SELECT * FROM catalog_product_entity WHERE entity_id = 166;
-
最后一个查询只会返回该产品的实体信息。要获取属性信息,如
name
,我们必须查看value
表。对于name
属性,value
表是catalog_product_entity_varchar
。运行以下查询以获取产品的所有varchar
属性值:SELECT * FROM catalog_product_entity_varchar WHERE entity_id = 166
-
之前的查询给出了以下结果:
-
通过这个结果,你可以识别出产品的名称。如果你查看行的属性 ID 并将属性 ID 与
eav_attribute
表中的 ID 匹配,你会看到这指的是name
属性。 -
要更新产品的名称,对以下行进行更新查询:
UPDATE catalog_product_entity_varchar SET value = 'HTC Touch Diamond sql' WHERE value_id = 1131;
注意
value_id
值与之前结果中的值 ID 相匹配。 -
在后端重新加载产品,你会看到产品的名称已更新。
它是如何工作的...
Magento 中的 EAV 实体在eav_entity_type
表中声明,属性在eav_attribute
表中声明。
与每个实体一样,每个 EAV 实体都有自己的基础表。在这个基础表中,主字段作为此表中的列声明。所有其他字段作为eav_attribute
表中的属性声明。
每个属性都是特定类型的,如int
、varchar
、date
、time
、decimal
和text
。这些值存储的位置取决于实体类型,并在 Magento 的配置文件中声明。
以下截图显示了产品 EAV 表的结构:
还有更多...
并非所有 EAV 实体都通过实体使用多个表。例如,sales_flat_*
表都是 EAV 表,但正如其名称所暗示的,它们是扁平的。这意味着实体的所有属性都设置在扁平表的单独列中。
在目录配置中,你有能力为分类和产品表启用 Flat catalog 选项。当此配置被启用时,Magento 将将所有数据从 EAV 表同步到 catalog_flat_*
表,并从那里加载数据。
当 Flat catalog 选项被启用时,你会获得更好的性能,因为在一个单独的表上执行 select
查询比从不同的表中获取数据要快。优点是更好的性能,但另一方面,对于大量产品和分类,可能会有一些缺点。
主数据将存储在 EAV 表中。这意味着在扁平表中存在同步。这个过程作为 Magento 索引过程 实现。当有大量产品和分类时,这个过程可能运行非常长。另一件事是,数据库的大小可能会爆炸,因为数据被存储了两次。
配置主/从设置
数据库复制用于当你想要扩展你的基础设施以服务更多请求时。有许多方法可以扩展数据库以设置复制。具体选择因情况而异。
在这个菜谱中,我们将使用一个通用设置来为我们的 Magento 店创建一个主/从设置。这个设置将使用一个主数据库服务器,所有 write
查询都将保存在这里。从数据库服务器(们)将用于 read
查询。数据库是应用程序扩展性中最困难的部分。
准备中
为了完成主/从设置,我们将使用两个不同的 MySQL 服务器。获取连接信息,如主机名、IP 地址、用户名和密码。
如何操作...
在这里,我们将设置 MySQL 中的主/从复制,并配置 Magento 使用此设置。请查看以下步骤:
设置主数据库
-
使用 SSH 登录到服务器并打开
/etc/mysql/my.cnf
文件。 -
在文件中查找
bind-address
部分,并将其注释掉。通过注释这一行,你可以使不同服务器之间建立连接成为可能:#bind-address = 127.0.0.1
-
将以下代码粘贴到
[mysqld]
部分的my.cnf
文件中:server-id = 1 log_bin = /var/log/mysql/mysql-bin.log expire_logs_days = 10 max_binlog_size = 100M binlog_do_db = magento_dev
-
通过运行以下命令重启你的 MySQL 服务器:
sudo service mysql restart
-
通过运行以下命令打开 MySQL shell。这将使用 root 用户进行登录:
mysql -u root -p
-
当你登录后,你必须运行以下查询:
GRANT ALL PRIVILEGES ON `magento_dev` . * TO `user_slave`@`%` WITH GRANT OPTION IDENTIFIED BY `password_slave`; FLUSH PRIVILEGES;
-
运行以下命令以显示主数据库的状态:
USE magento_dev; FLUSH TABLES WITH READ LOCK; SHOW MASTER STATUS;
上一段代码中的最后一个命令将给出以下输出:
我们将在从设置中使用这些信息,所以请保留好。
-
使用以下命令解锁表:
UNLOCK TABLES; quit;
当你输入
quit
或exit
命令时,你将返回到服务器的 shell。主数据库的设置已完成。
设置从数据库
-
通过 SSH 登录到您的从服务器。这是一个不同于主服务器的不同服务器。
-
使用以下命令打开 MySQL 命令行:
mysql –u root –p
-
创建一个新的数据库
magento_dev
并将主数据库导入其中。 -
在
[mysqld]
部分下etc/mysql/my.cnf
文件中添加以下配置:[mysqld] server-id = 2 master-host = 192.168.56.3 master-user = user_slave master-password = password_slave master-connect-retry = 60 replicate-do-db = magento_dev
-
使用以下命令重新启动 MySQL 服务器:
sudo service mysql restart
-
在您的 MySQL 命令行中运行以下命令。请确保
MASTER_LOG_FILE
和MASTER_LOG_POST
的值与您在运行SHOW MASTER STATUS
命令时看到的值匹配:CHANGE MASTER TO MASTER_HOST=`192.168.56.3`, MASTER_USER=`user_slave`, MASTER_PASSWORD=`password_slave`, MASTER_LOG_FILE=`mysql-bin.000001`, MASTER_LOG_POS=88;
-
运行以下命令以启动从服务器:
START SLAVE;
-
通过运行以下命令来检查状态:
SHOW SLAVE STATUS \G
输出将如下所示:
配置 Magento
现在,当 Master/Slave 设置的 MySQL 配置完成时,是时候配置 Magento 以使用此设置了。我们必须在 Magento 的 app/etc/local.xml
文件中配置此设置。在这里,我们将配置一个 default_setup
连接和一个 default_read
连接。为此,在 <resources>
标签下 local.xml
文件中添加以下 XML:
<resources>
<db>
<table_prefix><![CDATA[]]></table_prefix>
</db>
<default_setup>
<connection>
<host><![CDATA[192.168.56.3]]></host>
<username><![CDATA[user_master]]></username>
<password><![CDATA[password_master]]></password>
<dbname><![CDATA[magento_dev]]></dbname>
<initStatements><![CDATA[SET NAMES utf8]]></initStatements>
<model><![CDATA[mysql4]]></model>
<type><![CDATA[pdo_mysql]]></type>
<pdoType><![CDATA[]]></pdoType>
<active>1</active>
</connection>
</default_setup>
<default_read>
<connection>
<host><![CDATA[192.168.56.3]]></host>
<username><![CDATA[user_slave]]></username>
<password><![CDATA[password_slave]]></password>
<dbname><![CDATA[magento_dev]]></dbname>
<active>1</active>
</connection>
</default_read>
</resources>
清除您的缓存,您的 Magento 将使用此设置。
它是如何工作的...
当主数据库中的数据发生变化时,它必须与从数据库保持同步。为了建立这种同步,使用二进制日志文件在它们之间进行通信。
您必须确认在主数据库上已配置二进制日志。如果没有,您必须启用此功能并重新启动您的 MySQL 服务器。如果没有启用,则没有日志文件,因此服务器之间没有通信。这也是为什么从数据库的 MASTER_LOG_FILE
必须与主数据库上的 MASTER_LOG_FILE
匹配的原因。
如果设置完成,并且 Magento 配置已位于 app/etc/local.xml
文件中,则一切完成。Magento 足够智能,能够将 write
请求发送到主服务器,将 read
请求发送到从服务器。
修复数据库
有时会发生您的 Magento 数据库损坏或损坏的情况。这可能是由于各种原因造成的,例如黑客攻击或服务器崩溃。当数据库损坏并且您必须修复它时,来自 Magento 的数据库修复工具非常有帮助。
在本食谱中,我们将使我们的数据库损坏,并使用修复工具来修复它。
准备工作
为了做好准备,从 Magento 网站下载数据库修复工具(www.magentocommerce.com
),并将 PHP 文件放置在您的服务器根目录下。
如何操作...
以下步骤将向您展示如何使您的数据库损坏,并使用数据库修复工具来修复它:
-
创建现有数据库的备份。
-
创建一个空数据库作为参考数据库。让我们称它为
magento_dev_repair
。 -
在
app/etc/local.xml
文件中配置 Magento 以使用此数据库。 -
清除缓存并运行 Magento。这将在此数据库中安装一个空的 Magento。
-
使您的原始数据库损坏。您可以通过运行以下查询来实现,这些查询删除了一个外键和一个表:
ALTER TABLE core_store DROP FOREIGN KEY FK_CORE_STORE_GROUP_ID_CORE_STORE_GROUP_GROUP_ID; DROP TABLE catalog_product_index_price
-
在浏览器中浏览到修复工具,并按照以下截图配置您的原始和参考数据库:
-
提交表单后,脚本将修复您的数据库。在下一页,您将看到对您的原始数据库所做的更改。
-
在
app/etc/local.xml
文件中切换数据库,以便 Magento 使用您原始的数据库。 -
清除缓存后,您的商店即可正常运行。
它是如何工作的...
在运行数据库修复工具时,脚本将比较原始数据库与参考数据库。如果比较完成,脚本将使您的原始数据库结构与参考数据库的结构相同。
数据库修复工具仅修复数据库的结构问题。如果您丢失了一些数据,这不是恢复数据的正确工具。
第六章。数据库和模块
在本章中,我们将涵盖:
-
注册资源模型
-
注册连接
-
安装和升级脚本
-
使用模型创建平面表
-
与 Magento 集合一起工作
简介
在上一章中,我们学习了如何使用 Magento 数据库,可用的连接以及如何与数据库交互。
在本章中,我们将使用上一章学到的知识执行一些实际任务。我们将扩展在 第四章 中创建的模块,并与数据库交互。
我们将创建与由模块安装的数据库表交互的 Magento 模型。
注册资源模型
我们首先要做的是注册 资源 模型。普通模型用于编写业务逻辑。资源模型用于与数据库交互。
准备工作
我们必须在模块的 config.xml
文件中添加额外的配置。打开 app/code/local/Packt/Helloworld/etc/config.xml
文件。
如何操作...
以下步骤解释了如何在现有的 Packt_Helloworld
模块中注册资源模型:
-
导航到注册模型的标签。这位于
config/global/models
标签中。 -
在其中添加以下配置以添加资源模型。你的
<global>
标签将如下所示:<global> <blocks> <helloworld> <class>Packt_Helloworld_Block</class> </helloworld> </blocks> <helpers> <helloworld> <class>Packt_Helloworld_Helper</class> </helloworld> </helpers> <models> <helloworld> <class>Packt_Helloworld_Model</class> </helloworld> <helloworld_resource> <class>Packt_Helloworld_Model_Resource</class> </helloworld_resource> </models> </global>
-
创建
app/code/local/Packt/Helloworld/Model/Resource
文件夹。 -
通过在
<models>
标签中添加以下 XML 代码将普通模型与资源模型链接起来。<models>
标签将如下所示:<models> <helloworld> <class>Packt_Helloworld_Model</class> <resourceModel>helloworld_resource</resourceModel> </helloworld> <helloworld_resource> <class>Packt_Helloworld_Model_Resource</class> </helloworld_resource> </models>
使用
<resourceModel>
标签建立helloworld
和helloworld_resource
模型之间的链接。 -
使用
wiz
命令行工具运行以下命令来测试你的配置:wiz devel-models | grep helloworld
你现在将得到以下输出:
之前的命令将显示所有注册的模型,并过滤输出以匹配
helloworld
单词的行。
它是如何工作的...
Magento 中的模型用于业务逻辑。例如,观察者模型通常包含由事件或计划任务触发的函数。
一个 Magento 对象也可以代表一个实体,如产品、客户和类别。代表实体的模型通常继承自 Mage_Core_Model_Abstract
类。这个类有与资源模型连接的逻辑。例如,save()
函数是在这个类中声明的。
当你在 Mage_Core_Model_Abstract
类的 save()
函数中查看时,你会看到调用了 getResource()
函数。这个 getResource()
函数将返回资源模型的一个实例。
资源模型用于将实体与数据库连接起来。在模型中编写特定的业务逻辑,并在与实体一起工作时调用模型。
如果你想要获取资源模型实例,你可以使用Mage::getResourceModel()
或Mage::getResourceSingleton()
方法。要获取类的实例,我们必须将 Magento 类名作为第一个参数传递。
注册连接
在这个菜谱中,我们将配置read
和write
适配器,以便在这个模块中使用。这些适配器用于将模型与数据库连接起来。
准备工作
我们将在Packt_Helloworld
模块的config.xml
文件中添加适配器配置。打开此文件,准备添加一些配置。
如何操作...
按照以下步骤创建Packt_Helloworld
模块的read
和write
连接:
-
导航到
<global>
标签,并向其中添加以下配置。这将注册read
适配器。<resources> <helloworld_read> <connection> <use>core_read</use> </connection> </helloworld_read> </resources>
-
配置
write
适配器,在 XML 代码的<resources>
标签中添加以下内容。你的资源标签将如下所示:<resources> <helloworld_write> <connection> <use>core_write</use> </connection> </helloworld_write> <helloworld_read> <connection> <use>core_read</use> </connection> </helloworld_read> </resources>
-
清除你的缓存,你就完成了。
它是如何工作的...
Magento 中的每个模型都与一个read
和write
适配器相关联。默认的read
适配器是core_read
。默认的write
适配器是core_write
。
通常,所有模型在数据库表位于 Magento 数据库中时,都使用core_read
和core_write
适配器。
更多内容...
你可以注册和配置一个使用另一个数据库连接的特定数据库模型。如果你导航到以下 URL,你可以找到一个很好的教程,它解释了这一点:
www.solvingmagento.com/accessing-an-external-database-from-your-magento-module/
安装和升级脚本
当你的模块使用自定义数据库表时,你需要对数据库进行某些更改,以便在预发布或生产服务器上部署你的模块。Magento 有一种方法可以在代码处于正确位置时自动触发和安装或更新脚本。
在这个菜谱中,我们将使用install
脚本扩展Packt_Helloworld
模块。这个install
脚本将为所有产品添加一个属性。
准备工作
对于这个菜谱,我们必须在模块文件夹和数据库中工作。打开你的数据库客户端,并转到Packt_Helloworld
模块中的代码。
如何操作...
以下步骤描述了创建和安装模块脚本的程序:
-
通过在
config.xml
文件的<resources>
标签中添加以下代码来初始化模块的设置程序:<helloworld_setup> <setup> <module>Packt_Helloworld</module> <class>Mage_Eav_Model_Entity_Setup</class> </setup> <connection> <use>core_setup</use> </connection> </helloworld_setup>
之前的代码将初始化一个名为
helloworld_setup
的设置程序。<module>
标签配置了与Packt_Helloworld
模块的关系。 -
创建安装脚本的文件夹。在这个例子中,文件夹名称是
app/code/local/Packt/Helloworld/sql/helloworld_setup
。 -
创建安装脚本。安装脚本的命名遵循
install-<version_number_config_xml>.php
的约定。在我们的例子中,这个脚本的名称是install-0.0.1.php
。 -
在文件中添加以下内容以测试安装过程:
die('test');
-
清除缓存并重新加载任何前端页面。您将看到一个带有测试字样的白色页面。
小贴士
安装或升级脚本是为了在安装期间运行一次。每个注册设置的版本号都存储在
core_resource
数据库表中。当您想再次运行脚本进行测试时,您可以删除条目或更改设置的版本号。 -
在安装脚本中添加以下内容。这将安装一个适用于所有产品的产品属性。
<?php $installer = $this; $installer->startSetup(); $installer->addAttribute('catalog_product', 'helloworld_label', array( 'group' => 'Helloworld', 'type' => 'varchar', 'label' => 'Helloworld label', 'input' => 'text', 'global' => Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE, 'visible' => true, 'required' => false, 'searchable' => false, 'filterable' => false, 'comparable' => false, 'visible_on_front' => true, 'unique' => false, 'apply_to' => 'simple,configurable,virtual,bundle,downloadable', 'is_configurable' => false )); $installer->endSetup();
之前的代码将创建
helloworld_label
产品属性。该属性将应用于所有产品。使用
group
选项,在查看后端产品时,属性将在Helloworld标签页中显示。 -
清除缓存并重新加载页面。在清除缓存后重新加载页面时,安装脚本将自动运行。
-
前往后端并打开一个产品以检查是否已添加产品属性。通常,您将看到一个包含属性的Helloworld标签页,类似于以下截图:
-
当您的安装脚本执行时,您可以在 Magento 的
core_resource
表中看到您的设置。在这个表中,存储了所有模块和版本号,因此 Magento 知道哪些安装或升级脚本需要执行。
它是如何工作的...
当您想更改数据库结构时,使用安装脚本是有用的。一些目的包括:
-
从开发/测试环境轻松部署到生产环境
-
修复数据库的能力
-
数据库变更概述
安装脚本中的$this
对象是在config.xml
文件中的设置注册中声明的类。在这种情况下,它是Mage_Eav_Model_Entity_Setup
类。这个类主要用于您想向实体添加 EAV 属性的情况,例如产品或类别,就像我们在本菜谱中所做的那样。大多数设置类都扩展了默认的设置类,即Mage_Core_Model_Resource_Setup
。
如果您想在安装脚本中做很多事情,您可以创建自己的设置类。这将扩展正常的设置类。
在这个类中声明的函数可以在安装文件中调用$this
对象。
使用模型创建扁平表
在这个菜谱中,我们将通过一个扁平数据库表扩展我们的模块。我们将创建一个升级脚本,包含创建表的指令。当表创建完成后,我们将通过添加所需的 Magento 模型、资源模型和集合来完成设置。完成整个设置后,我们已创建了一个具有所有 Magento ORM 功能的自定义 Magento 实体。
准备工作
对于这个菜谱,我们必须处理代码和数据库。在module
文件夹中打开您的 IDE,并获取对您的数据库客户端的访问权限。
如何操作...
以下步骤是创建一个数据库表并使用适当的 Magento 模型与之交互的指令:
-
在
app/code/local/Packt/Helloworld/etc/config.xml
文件中配置表名。 -
在
helloworld_resource
标签下添加以下代码,使标签看起来如下:<helloworld_resource> <class>Packt_Helloworld_Model_Resource</class> <entities> <subscription> <table>helloworld_subscription</table> </subscription> </entities> </helloworld_resource>
上述代码将声明一个实体,它引用
helloworld_subscription
表。 -
通过创建
app/code/local/Packt/Helloworld/sql/helloworld_setup/upgrade-0.0.1-0.0.2.php
文件来创建升级脚本。 -
在升级文件中添加以下代码。此代码是创建具有一些字段的表的命令。
<?php $installer = $this; $installer->startSetup(); $table = $installer->getConnection() ->newTable($installer->getTable('helloworld/subscription')) ->addColumn('subscription_id', Varien_Db_Ddl_Table::TYPE_INTEGER, null, array( 'identity' => true, 'unsigned' => true, 'nullable' => false, 'primary' => true, ), 'Subscription id') ->addColumn('created_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array( 'nullable' => false, ), 'Created at') ->addColumn('updated_at', Varien_Db_Ddl_Table::TYPE_TIMESTAMP, null, array( 'nullable' => false, ), 'Updated at') ->addColumn('firstname', Varien_Db_Ddl_Table::TYPE_TEXT, 64, array( 'nullable' => false, ), 'First name') ->addColumn('lastname', Varien_Db_Ddl_Table::TYPE_TEXT, 64, array( 'nullable' => false, ), 'Last name') ->addColumn('email', Varien_Db_Ddl_Table::TYPE_TEXT, 64, array( 'nullable' => false, ), 'Email address') ->addColumn('status', Varien_Db_Ddl_Table::TYPE_TEXT, 32, array( 'nullable' => false, 'default' => 'pending', ), 'Status') ->addColumn('message', Varien_Db_Ddl_Table::TYPE_TEXT, '64k', array( 'unsigned' => true, 'nullable' => false, ), 'Subscription notes') ->addIndex($installer->getIdxName('helloworld/subscription', array('email')), array('email')) ->setComment('Helloworld subscriptions'); $installer->getConnection()->createTable($table); $installer->endSetup();
-
清除缓存并重新加载前端。当你在数据库客户端刷新你的表时,你将看到
helloworld_subscription
表在列表中。小贴士
在创建表时,考虑命名约定。第一部分是模型注册的名称,后面跟着一个下划线。第二部分指的是实体模型。
确保模型的名称是单数。在这个例子中,它是
subscription
而不是subscriptions
。 -
检查该表是否已安装到数据库中。在 phpMyAdmin 中重新加载表并打开新表。结构将如下所示:
-
当使用升级脚本(从 0.0.1 升级到 0.0.2)安装表时,最后一步是创建一个与之前创建的表通信的 Magento 实体。为此,我们必须创建一个模型、一个资源模型和一个集合资源模型。第一步是创建以下文件:
-
app/code/local/Packt/Helloworld/Model/Subscription.php
(模型) -
app/code/local/Packt/Helloworld/Model/Resource/Subscription.php
(资源模型) -
app/code/local/Packt/Helloworld/Model/Resource/Subscription/Collection.php
(资源集合模型)
-
-
打开模型文件,并向其中添加以下内容。此内容将模型与适当的资源模型链接。
<?php class Packt_Helloworld_Model_Subscription extends Mage_Core_Model_Abstract { protected function _construct() { $this->_init('helloworld/subscription'); } }
-
打开资源模型文件,并向其中添加以下内容。接下来的内容将模型与数据库链接。在
_init
函数中,我们将模型与数据库表的主键链接。<?php class Packt_Helloworld_Model_Resource_Subscription extends Mage_Core_Model_Resource_Db_Abstract { protected function _construct() { $this->_init('helloworld/subscription', 'subscription_id'); } }
-
打开资源集合模型文件,并向其中添加以下内容。此文件使得在调用实体的
getCollection()
方法时,可以在模型上使用 Magento 集合。<?php class Packt_Helloworld_Model_Resource_Subscription_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract { protected function _construct() { $this->_init('helloworld/subscription'); } }
-
如果一切顺利,所有文件都已放置在正确的位置以开始测试。为了执行一些测试,在模块的
IndexController
中创建一个subscriptionAction()
方法。 -
通过访问
http://magento-dev.local/helloworld/index/subscription
URL 来导航到控制器中的新动作。你将看到一个空白页面。 -
在创建我们表中新订阅项的动作中添加以下内容:
public function subscriptionAction() { $subscription = Mage::getModel('helloworld/subscription'); $subscription->setFirstname('John'); $subscription->setLastname('Doe'); $subscription->setEmail('john.doe@example.com'); $subscription->setMessage('A short message to test'); $subscription->save(); echo 'success'; }
-
当你重新加载页面时,你会看到单词成功。这个单词的显示是所有动作已成功执行的标志。导航到你的数据库并执行以下查询:
SELECT * FROM helloworld_subscription;
这个查询将给出以下输出:
它是如何工作的...
当你使用数据库表中的实体之前的设置工作时,Magento ORM 会在实体和数据库之间建立联系。
在我们之前创建的 Magento 实体(Mage::getModel('helloworld/subscription')
)中,我们可以使用以下函数,这将导致对数据库的查询:
-
load($entityId)
-
save()
-
delete()
所有这些函数都是在Mage_Core_Model_Abstract
类中实现的。所有的 Magento 实体都将扩展这个抽象类以使用 ORM 框架。
使用 Magento 集合
在本章中,我们将探索 Magento 集合的可能性。一个 Magento 集合是一组实体,你可以添加过滤器来自定义你的结果。
在本章中,我们将探索使用 Magento 集合所能做的一切。
准备工作
前往helloworld
模块的indexController
并创建一个collectionAction
方法。在这个动作中,我们将执行一些测试来比较结果。
如何做...
下面的示例展示了在处理 Magento 集合时的可能性:
-
在
collectionAction
方法中添加以下代码并导航到页面。这段代码将返回 10 个产品。public function collectionAction () { $productCollection = Mage::getModel('catalog/product') ->getCollection() ->setPageSize(10,1); foreach ($productCollection as $product) { Zend_Debug::dump($product->debug()); } }
提示
在代码的末尾,有一个
foreach
循环。这个循环会在对象上调用debug()
函数。debug()
函数在所有扩展了Varien_Object
函数的对象上都是可用的。集合转储是一个非常大的数组,可能会导致 PHP 中的内存不足异常或在浏览器中产生非常大的响应。 -
当你查看输出时,你会看到以下数组:
array(11) { ["entity_id"] => string(2) "16" ["entity_type_id"] => string(2) "10" ["attribute_set_id"] => string(2) "38" ["type_id"] => string(6) "simple" ["sku"] => string(5) "n2610" ["created_at"] => string(19) "2007-08-23 13:03:05" ["updated_at"] => string(19) "2008-08-08 14:50:04" ["has_options"] => string(1) "0" ["required_options"] => string(1) "0" ["is_salable"] => string(1) "1" ["stock_item (Varien_Object)"] => array(1) { ["is_in_stock"] => string(1) "1" } }
-
这个对象中的值不包含属性值。为了选择它们,我们必须使用
addAttributeToSelect('<attribute_code>')
函数。添加以下代码来选择具有名称、价格和图像属性的第一个 10 个产品:public function collectionAction () { $productCollection = Mage::getModel('catalog/product') ->getCollection() ->addAttributeToSelect('name') ->addAttributeToSelect('price') ->addAttributeToSelect('image') ->setPageSize(10,1); foreach ($productCollection as $product) { Zend_Debug::dump($product->debug()); } }
这段代码将为每个产品输出一个数组,如下面的代码所示:
array(14) { ["entity_id"] => string(2) "16" ["entity_type_id"] => string(2) "10" ["attribute_set_id"] => string(2) "38" ["type_id"] => string(6) "simple" ["sku"] => string(5) "n2610" ["created_at"] => string(19) "2007-08-23 13:03:05" ["updated_at"] => string(19) "2008-08-08 14:50:04" ["has_options"] => string(1) "0" ["required_options"] => string(1) "0" ["name"] => string(16) "Nokia 2610 Phone" ["image"] => string(27) "/n/o/nokia-2610-phone-2.jpg" ["price"] => string(8) "149.9900" ["is_salable"] => string(1) "1" ["stock_item (Varien_Object)"] => array(1) { ["is_in_stock"] => string(1) "1" } }
-
我们现在将在产品集合上创建一个过滤器。下面的代码展示了如何过滤名为诺基亚 2610 手机的产品:
public function collectionAction () { $productCollection = Mage::getModel('catalog/product') ->getCollection() ->addAttributeToSelect('price') ->addAttributeToSelect('image') ->addAttributeToFilter('name', 'Nokia 2610 Phone'); foreach ($productCollection as $_product) { Zend_Debug::dump($_product->debug()); } }
注意
这个语句中的代码将在查询中创建一个
WHERE name = 'Nokia 2610 Phone'
语句,所以所有名为诺基亚 2610 手机的项目都将被返回。 -
使用
addAttributeToFilter
函数,我们可以做更多。下面的代码展示了如何创建一个WHERE product_id IN (159, 160, 161)
语句:public function collectionAction () { $productCollection = Mage::getModel('catalog/product') ->getCollection() ->addAttributeToSelect('price') ->addAttributeToSelect('image') ->addAttributeToFilter('entity_id', array( 'in' => array(159, 160, 161) )); foreach ($productCollection as $_product) { Zend_Debug::dump($_product->debug()); } }
-
我们接下来要使用的过滤器是
like
过滤器。添加以下代码来执行带有WHERE name LIKE '%PC%'
语句的查询:public function collectionAction () { $productCollection = Mage::getModel('catalog/product') ->getCollection() ->addAttributeToSelect('price') ->addAttributeToSelect('image') ->addAttributeToFilter('name', array( 'like' => '%PC%' )); foreach ($productCollection as $_product) { Zend_Debug::dump($_product->debug()); } }
-
当查询变得更加复杂时,有时知道将生成什么 SQL 查询来获取集合是很不错的。为了打印用于集合的 SQL 查询,我们可以使用以下代码行:
$productCollection->getSelect()->__toString()
-
当你添加以下代码时,你会看到这个集合的查询:
public function collectionAction () { $productCollection = Mage::getModel('catalog/product') ->getCollection() ->addAttributeToSelect('price') ->addAttributeToSelect('image') ->addAttributeToFilter('name', array( 'like' => '%PC%' )); $productCollection->load(); echo $productCollection->getSelect()->__toString(); }
此代码将输出以下 SQL 查询:
SELECT `e`.*, IF(at_name.value_id > 0, at_name.value, at_name_default.value) AS `name`, `price_index`.`price`, `price_index`.`tax_class_id`, `price_index`.`final_price`, IF(price_index.tier_price IS NOT NULL, Least(price_index.min_price, price_index.tier_price), price_index.min_price) AS `minimal_price`, `price_index`.`min_price`, `price_index`.`max_price`, `price_index`.`tier_price` FROM `catalog_product_entity` AS `e` INNER JOIN `catalog_product_entity_varchar` AS `at_name_default` ON ( `at_name_default`.`entity_id` = `e`.`entity_id` ) AND ( `at_name_default`.`attribute_id` = '96' ) AND `at_name_default`.`store_id` = 0 LEFT JOIN `catalog_product_entity_varchar` AS `at_name` ON ( `at_name`.`entity_id` = `e`.`entity_id` ) AND ( `at_name`.`attribute_id` = '96' ) AND ( `at_name`.`store_id` = 1 ) INNER JOIN `catalog_product_index_price` AS `price_index` ON price_index.entity_id = e.entity_id AND price_index.website_id = '1' AND price_index.customer_group_id = 0 WHERE ( IF(at_name.value_id > 0, at_name.value, at_name_default.value) LIKE '%PC%' )
小贴士
当使用
getSelect()->__toString()
函数时,请确保集合已加载。这就是为什么我们在打印 SQL 语句之前调用了$productCollection->load()
函数。当你在一个foreach()
循环中添加集合时,集合将自动加载。在 phpMyAdmin 中运行此查询,你会看到这个扁平响应可以用来创建产品集合。
-
通过前面的代码示例,我们只能从数据库中读取数据。通过使用
setDataToAll()
函数,你可以更新集合中所有实体的某些属性。使用以下代码来更新集合中的所有价格:public function collectionAction () { $productCollection = Mage::getModel('catalog/product') ->getCollection() ->addAttributeToSelect('price') ->addAttributeToSelect('image') ->addAttributeToFilter('name', array( 'like' => '%PC%' )); $productCollection->setDataToAll('price', 20); foreach ($productCollection as $_product) { Zend_Debug::dump($_product->debug()); } }
-
当你使用
setDataToAll()
函数时,除非你调用了save()
函数,否则数据库中不会有任何变化。在setDataToAll()
函数之后添加以下代码以保存集合:$productCollection->save();
如何工作...
当你想获取实体集合时,你可以通过调用以下两个方法来实现。这两个方法的返回值是一个集合对象。
Mage::getModel('model/entity')->getCollection();
Mage::getResourceModel('model/entity_collection');
Magento 集合对象始终扩展自Varien_Data_Collection
类。这个对象作为一个数组工作,因此你可以在集合中的项目之间迭代。
对于每个实体,在资源模型文件夹中都会创建一个集合类。在大多数情况下,这个类扩展了父类,在某些情况下,会为实体添加特定的方法。
产品实体是这方面的一个好例子。当你打开Mage_Catalog_Model_Resource_Product_Collection
类时,你会看到这个类不是空的。在这里,一些函数是专门为产品实体声明的。
当你调试集合的继承时,你会看到扁平实体和 EAV 实体之间存在差异,如下面的图所示:
你可以看到,扁平实体(Mage_Core_Model_Resource_Db_Collection_Abstract
)和 EAV 实体(Mage_Eav_Model_Entity_Collection_Abstract
)的类扩展了相同的父类。EAV 类添加了额外的函数并重新定义了一些现有的函数以与 EAV 系统协同工作。
这是集合查询为何在扁平实体和 EAV 实体之间不同的主要原因。
对于在字段上添加过滤器,EAV 的函数是addAttributeToFilter()
。对于扁平实体,函数是addFieldToFilter()
。addAttributeToFilter()
函数在Mage_Eav_Model_Entity_Collection_Abstract
类中声明,因此在Varien_Data_Collection_Db
类中不可用。
参见
如果你想了解 Magento 集合提供的所有可能选项,请查看 Magento 网站上的以下文章,其中包含了所有功能和选项的信息:
www.magentocommerce.com/wiki/1_-_installation_and_configuration/using_collections_in_magento
第七章. Magento 后端
在本章中,我们将涵盖以下主题:
-
注册后端控制器
-
扩展菜单
-
添加 ACL
-
扩展系统配置
-
从数据库表创建网格
-
添加客户属性
-
与源模型一起工作
简介
对于店主来说,后端是他们管理店铺中所有事物的接口。确保一切对心怀恶意访客的安全至关重要。标准 Magento 安装的后端可以通过多种方式扩展,因此每个人都可以通过自定义页面、配置、角色等方式对其进行扩展。
通过遵循 Magento 的配置模式,所有安全问题(匿名用户的访问、安全环境等)都由 Magento 的后端系统覆盖。本章中的食谱描述了您可以使用 Magento 扩展后端的最佳实践的所有方法。
注册后端控制器
我们首先将学习如何通过自定义控制器操作扩展后端。为此,我们必须创建一个受保护的控制器,以确保只有登录的后端用户才能看到此页面的内容。
当你想在你的后端添加一个额外的页面时,你需要一个后端控制器。这通常是在你使用自定义表单或概述时,你需要为你的模块使用的情况。
准备就绪
为了使测试管理员 URL 更容易,我们将从管理员 URL 中删除密钥(后端页面 URL 中的哈希)。您可以在 系统 | 配置 | 管理员 | 安全 中进行此配置。按照以下截图所示更改配置:
如何操作...
当你想在你的后端添加一个额外的页面时,你必须执行以下步骤:
-
打开
app/code/local/Packt/Helloworld/etc/config.xml
文件,并在global
标签下添加以下配置:<admin> <routers> <adminhtml> <args> <modules> <helloworld before="Mage_Adminhtml">Packt_Helloworld_Adminhtml </helloworld> </modules> </args> </adminhtml> </routers> </admin>
此配置将初始化
Adminhtml
模块,以便在Packt_Helloworld
模块的文件夹中查找控制器。 -
按照以下目录添加以下文件夹:
-
app/code/local/Packt/Helloworld/controllers/Adminhtml
-
app/code/local/Packt/Helloworld/controllers/Adminhtml/Helloworld
-
-
在最后一个文件夹中,创建一个名为
IndexController.php
的文件,内容如下:<?php class Packt_Helloworld_Adminhtml_Helloworld_IndexController extends Mage_Adminhtml_Controller_Action { public function indexAction() { } }
-
确保你从
Mage_Adminhtml_Controller_Action
类扩展,以确保所有安全情况都得到覆盖。 -
清除缓存,并通过访问 URL
http://magento-dev.local/index.php/admin/helloworld_index/
导航到控制器。这将给你一个空白页面。这是正常的,因为操作是空的。
-
在你的
indexAction
函数中添加以下代码,并重新加载页面。你会看到返回了一个空的后端页面:$this->loadLayout(); $this->renderLayout();
它是如何工作的...
前端控制器和后端控制器之间的区别在于父类。后端控制器扩展另一个类作为前端控制器。后端控制器始终扩展自Mage_Adminhtml_Controller_Action
类。这个类为控制器添加了安全性,以确保只有经过身份验证的用户才能访问控制器操作。
当我们在config.xml
文件中添加配置时,我们将使用我们的模块的controllers/Adminhtml
文件夹扩展Mage_Adminhtml
模块的controllers
文件夹。使用before="Mage_Adminhtml"
选项,Magento 将在模块的文件夹中查找控制器文件。稍后,它将在Mage_Adminhtml
模块中查找。
小贴士
当你为你的模块创建后端控制器时,确保你在controllers
文件夹中添加Adminhtml/Modulename
文件夹,以避免与现有的Mage_Adminhtml
控制器冲突。
还有更多...
在某些模块和教程中,你会找到以下配置来链接你的后端控制器:
<admin>
<routers>
<helloworld>
<use>admin</use>
<args>
<module>Packt_Helloworld</module>
<frontName>helloworld</frontName>
</args>
</helloworld>
</routers>
</admin>
不建议以这种方式进行配置,因为当你在同一模块中与前端和后端控制器一起工作时,这将会引发问题。
扩展菜单
在扩展后端时,确保用户可以轻松地导航到你的自定义页面是很重要的。你可以使用的唯一替代方案是使用你自己的项目扩展管理员菜单。使用 Magento 框架,可以在该菜单的每个级别添加菜单项。
准备工作
对于这个菜谱,我们唯一要做的任务是将正确的配置(位于adminhtml.xml
文件中)添加到Packt_Helloworld
模块的etc
文件夹中。
如何操作...
以下步骤描述了如何向管理员菜单添加额外的菜单项:
-
我们必须首先考虑的是,我们将在管理员菜单的哪个位置放置额外的菜单项。为此测试,我们将将其放在系统菜单下。记住系统 ID。我们必须使用它来进行菜单配置。
-
第二件事是在
adminhtml.xml
文件中添加配置。在config
标签下粘贴以下代码:<menu> <system> <children> <helloworld translate="title" module="helloworld"> <title>Helloworld</title> <sort_order>10</sort_order> <action>adminhtml/helloworld_index</action> </helloworld> </children> </system> </menu>
注意
在
<helloworld>
标签中,你可以看到translate
和module
属性。当这些属性被设置时,标题将使用helloworld
模块的帮助类进行翻译。 -
清除缓存并重新加载后端。当悬停在系统菜单上时,你会看到显示了一个HelloWorld链接,如下截图所示:
-
要更改菜单项的位置,我们必须在配置中添加以下
sort_order
标签:<sort_order>30</sort_order>
它是如何工作的...
Magento 的管理员菜单包含来自所有模块的所有菜单配置数据。标准菜单包含以下 10 个根项:
-
仪表板
-
销售
-
目录
-
移动
-
客户
-
促销
-
通讯录
-
报告
-
系统
当您想要将一个项作为根项的子项添加时,您必须使用这些标签在您的配置中,就像我们在本菜谱中所做的那样。这些菜单项的 ID 在 Magento core
模块的配置 XML 文件中声明。
您可以轻松地向菜单中添加额外的根项,但您必须确保菜单不要太长。当根菜单太长时,它将导致小屏幕分辨率的布局问题。
添加 ACL
在之前的菜谱中,我们创建了一个后台控制器操作,您可以导航到它。然而,当您想要配置自定义管理员角色时,您不能限制特定角色的访问权限。在这个菜谱中,我们将为我们的后台页面创建一个ACL(访问控制列表)并配置一个具有限制性访问权限的角色。
准备工作
每个管理员用户都有一个角色。这些角色包含访问权限,因此您可以限制某些用户角色的访问。在这个菜谱中,我们将为角色添加额外的权限,以便我们可以配置之前创建的页面的访问权限。
如何操作...
以下步骤显示了您如何限制特定用户的后台页面访问权限:
-
我们必须做的第一件事是检查哪些 ACL 可用。为了知道这一点,我们可以运行以下命令:
wiz admin-resources
-
或者,我们可以导航到后端的角色页面。这位于系统 | 权限 | 角色。点击添加新角色并打开资源标签。这将列出后端中所有可用的 ACL。
-
第二步是为它添加一个额外的 ACL(访问控制列表)。为此,我们可以在
adminhtml.xml
文件中的<config>
标签下添加以下配置:<acl> <resources> <all> <title>Allow Everything</title> </all> <admin> <children> <system> <children> <helloworld> <title>Hellworld index page</title> <sort_order>10</sort_order> </helloworld> </children> </system> </children> </admin> </resources> </acl>
-
清除您的缓存并重新加载后端的资源页面。当您搜索
Helloworld
时,您将看到之前创建的 ACL 旁边有一个可用的复选框,如图所示:到目前为止,我们的 ACL 正在工作。为了验证这一点,我们必须创建一个带有用户的角色并登录,以查看用户是否有访问页面的权限:
-
在系统 | 权限下的角色页面创建一个新的角色。命名为
Test Helloworld
并勾选Helloworld test和Manage Products ACL。 -
在系统 | 权限下的用户页面创建一个后台用户。填写表单并将用户添加到我们刚刚创建的角色中,如图所示:
-
以新用户身份登录,您将看到该用户只能访问我们在角色中配置的页面。
工作原理...
使用 ACL 系统,可以限制特定用户角色的后台页面。例如,产品经理只有管理产品、类别和促销规则的权利,而物流合作伙伴只有访问订单页面的权限。
小贴士
在 Magento 社区版中,无法限制对特定商店数据的访问。例如,物流合作伙伴只能看到 Store 1 的订单。限制基于控制器操作。
当你没有为页面创建 ACL 时,只有有权访问所有资源的角色才能访问该页面。在大多数情况下,这是管理员。对于其他角色,没有 ACL 无法访问页面。
扩展系统配置
当你想为你自己的模块保存一些配置参数时,你可以使用 Magento 配置表来保存你的配置。你可以在系统菜单下的配置中找到配置表单。在本菜谱中,我们将在系统配置中添加一个带有一些配置参数的配置页面。
准备工作
准备好扩展Packt_Helloworld
模块并添加一些额外的配置。同时,连接到你的数据库,因为我们将需要查看一些表。
如何操作...
以下步骤描述了在系统下的配置页面中创建额外配置参数的流程。
-
在
app/code/local/Packt/Helloworld/etc/
文件夹中创建以下文件:-
system.xml
-
adminhtml.xml
-
-
第二步是创建配置页面。我们将在新部分下的左侧列中创建一个新标签。为了创建名为
packt
的部分,我们必须在刚刚创建的system.xml
文件中添加以下代码:<tabs> <packt> <label>Packt</label> <sort_order>400</sort_order> </packt> </tabs>
-
要添加一个配置页面,我们必须在
system.xml
文件的global
标签下添加以下代码:<sections> <helloworld translate="label" module="helloworld"> <label>Helloworld</label> <tab>packt</tab> <sort_order>10</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> <groups> </groups> </helloworld> </sections>
-
当你清除缓存并重新加载页面时,菜单中会出现带有Helloworld标签的PACKT部分,如下截图所示:
-
当你点击链接时,你会看到一个 404 错误。这是因为我们还没有为这个配置部分创建 ACL。要添加 ACL,我们必须在第一步中创建的
adminhtml.xml
文件中添加以下代码:<?xml version="1.0" encoding="UTF-8"?> <config> <acl> <resources> <all> <title>Allow Everything</title> </all> <admin> <children> <system> <children> <config> <children> <helloworld translate="title" module="helloworld"> <title>Helloworld section</title> </helloworld> </children> </config> </children> </system> </children> </admin> </resources> </acl> </config>
-
我们必须确保 ACL 已添加。为了测试这一点,你必须清除你的缓存并转到系统|权限下的角色页面。在该页面上,点击管理员角色,打开角色资源选项卡,并将其更改为自定义。你将在列表中看到你的 ACL 条目,如下截图所示:
-
当我们在列表中看到Helloworld 部分时,这意味着 ACL 已添加。不要保存角色,因为这只是为了验证 ACL 的添加。为了确保所有 ACL 设置都在后端会话中,我们必须通过注销然后重新登录来创建一个新的后端会话。
小贴士
当创建 ACL 时,确保你的自定义 ACL 配置遵循你在放置页面的位置放置的 Magento XML 树。
-
当您再次登录时,导航到配置页面,您将看到一个空页面。这是因为字段尚未添加到配置中。
-
新的配置字段必须位于配置组中。要添加新组,我们必须在
system.xml
文件中添加一些配置。在config/sections/helloworld
标签下添加以下代码:<groups> <hellopage translate="label"> <label>Hello page settings</label> <sort_order>1</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> <fields> </fields> </hellopage> </groups>
-
在重新加载页面时,您将看不到任何内容,因为在
<fields>
标签中未定义任何字段。要添加字段(例如,header_title
),我们必须在<fields>
标签中添加以下代码:<header_title translate="label"> <label>Header title</label> <frontend_type>text</frontend_type> <sort_order>1</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </header_title>
-
清除缓存并重新加载页面后,您将在配置页面看到字段,如下面的截图所示:
-
在字段中输入值并保存配置。
-
要查看值保存的位置,我们必须查看
core_config_data
表。运行以下查询以查看字段的记录:SELECT * FROM core_config_data where path = 'helloworld/hellopage/header_title'
此查询返回字段的全部值,如下面的截图所示:
-
要读取配置数据,我们可以使用
Mage::getStoreConfig('<path>')
函数。在我们的例子中,路径是helloworld/hellopage/header_title
。您可以从core_config_data
表的路径列或从模块的system.xml
文件中确定路径。
工作原理...
Magento 配置保存在core_config_data
表中。此表包含您可以在后端系统 | 配置中设置的配置值。
每个配置设置都可以在三个级别上进行配置:
-
全局配置
-
网站配置
-
存储视图配置
当您与多个商店一起工作时,此设置使得为每个商店视图保存配置值成为可能。您可以通过屏幕左上角的下拉字段切换范围,如下所示:
每个配置值的类型都存储在范围列中。此范围在每级都有不同的代码:
-
默认(用于全局配置)
-
网站(用于网站配置)
-
商店(用于商店)
对于每个配置字段,您可以在您的system.xml
文件中配置范围。这是通过use_in_store
、use_in_website
和use_in_default
标签完成的。
配置参数的配置路径设置在模块的system.xml
文件中。在以下 XML 路径中,您可以确定值:
config/sections/$1/groups/$2/fields/$3
当您将此 XML 路径转换为配置路径时,它将如下所示:
$1/$2/$3
在此配方中,$1
对应于helloworld
,$2
对应于hellopage
,$3
对应于header_title
或is_enabled
。
从数据库表创建网格
在上一章中,我们创建了一个与数据库表链接的 Magento 实体。在本配方中,我们将创建一个后端界面,以便后端用户可以在后端查看此表中的数据。
我们将创建一个概述,它将使用标准的 Magento 后端网格小部件。这个小部件在后台广泛使用,用于在网格中显示信息,例如管理产品页面。
准备工作
对于这个菜谱,我们必须配置后端控制器、菜单项、ACL 以及正确的Block
文件来渲染网格输出。准备好扩展后端以使用自定义网格。
如何操作...
-
我们必须做的第一件事是为我们的网格创建一个后端控制器。我们将基于订阅实体创建一个网格,因此我们将创建
SubscriptionController
。在app/code/local/Packt/Helloworld/controllers/Adminhtml/Helloworld/
文件夹中创建一个SubscriptionController.php
文件。 -
在控制器中添加以下内容:
<?php class Packt_Helloworld_Adminhtml_Helloworld_SubscriptionController extends Mage_Adminhtml_Controller_Action { public function indexAction() { $this->loadLayout(); $this->renderLayout(); } }
-
为控制器创建一个菜单项。当你将以下代码添加到模块的
adminhtml.xml
文件中时,它将在客户项下创建一个菜单项:<menu> <customer> <children> <subscription translate="title" module="helloworld"> <title>Helloworld subscriptions</title> <sort_order>10</sort_order> <action>adminhtml/helloworld_subscription</action> </subscription> </children> </customer> </menu>
-
为控制器动作创建一个 ACL。这可以通过在
adminhtml.xml
文件中添加以下代码来完成,就像我们为菜单所做的那样。<acl> <resources> <all> <title>Allow Everything</title> </all> <admin> <children> <customer> <children> <subscription> <title>Hellworld subscriptions</title> <sort_order>10</sort_order> </subscription> </children> </customer> </children> </admin> </resources> </acl>
-
清除缓存并重新加载你的后端。现在菜单项在客户菜单下。当你导航到该页面时,你会看到一个空的后端页面。
现在我们有一个与菜单链接的后端页面。在接下来的步骤中,我们将在页面中添加一个网格。为此,我们必须创建两个块。第一个块是包装器(标题、类)。第二个块是网格,我们将定义列。
-
要创建包装块,创建
app/code/local/Packt/Helloworld/Block/Adminhtml
文件夹。在该文件夹中,创建一个包含以下内容的Subscription.php
文件:<?php class Packt_Helloworld_Block_Adminhtml_Subscription extends Mage_Adminhtml_Block_Widget_Grid_Container { public function __construct() { $this->_headerText = Mage::helper('helloworld')->__('Helloworld subscriptions'); $this->_blockGroup = 'helloworld'; $this->_controller = 'adminhtml_subscription'; parent::__construct(); } protected function _prepareLayout() { $this->_removeButton('add'); return parent::_prepareLayout(); } }
-
在
app/code/local/Packt/Helloworld/Block/Adminhtml/Subscription
文件夹中创建一个包含Grid.php
文件的文件夹。在该文件中,添加以下内容:<?php class Packt_Helloworld_Block_Adminhtml_Subscription_Grid extends Mage_Adminhtml_Block_Widget_Grid { public function __construct() { parent::__construct(); $this->setId('subscription_grid'); $this->setDefaultSort('subscription_id'); $this->setDefaultDir('DESC'); } protected function _prepareCollection() { $collection = Mage::getModel('helloworld/subscription')->getCollection(); $this->setCollection($collection); return parent::_prepareCollection(); } protected function _prepareColumns() { $this->addColumn('subscription_id', array ( 'index' => 'subscription_id', 'header' => Mage::helper('helloworld')->__('Subscription id'), 'type' => 'number', 'sortable' => true, 'width' => '100px', )); $this->addColumn('firstname', array ( 'index' => 'firstname', 'header' => Mage::helper('helloworld')->__('Firstname'), 'sortable' => false, )); $this->addColumn('lastname', array ( 'index' => 'lastname', 'header' => Mage::helper('helloworld')->__('Lastname'), 'sortable' => false, )); $this->addColumn('email', array ( 'index' => 'email', 'header' => Mage::helper('helloworld')->__('Email'), 'sortable' => false, )); return parent::_prepareColumns(); } public function getGridUrl() { return $this->getUrl('*/*/grid', array( '_current' => true, )); } }
-
要将块添加到页面,在之前创建的控制器文件中的
indexAction()
函数中添加以下代码:public function indexAction() { $this->loadLayout(); $this->_addContent($this->getLayout()->createBlock('helloworld/adminhtml_subscription')); $this->renderLayout(); }
-
重新加载页面,你会得到以下输出:
-
要更改页面标题,我们需要进入包装块类。这是
app/code/local/Packt/Helloworld/Block/Adminhtml/Subscription.php
文件。在__construct()
动作中的$this->_headerText
变量包含页面的标题。 -
现在,网格包含订阅 ID、名、姓和电子邮件。在
_prepareColumns()
中,我们将向网格添加更多列,以便显示数据库表的全部列。 -
created_at
字段的数据类型为datetime
。当我们向_prepareColumns()
函数中添加以下代码时,我们将看到带有日期过滤器的created_at
列:$this->addColumn('created_at', array ( 'index' => 'created_at', 'header' => Mage::helper('helloworld')->__('Created At'), 'type' => 'datetime', 'sortable' => true, 'width' => '150px', ));
-
我们将添加的最后一个列是状态列。在这个列中,我们将向值添加一些 HTML 标记。为此,我们需要在
_prepareColumns()
函数中添加以下代码:$this->addColumn('status', array ( 'index' => 'status', 'header' => Mage::helper('helloworld')->__('Status'), 'sortable' => true, 'frame_callback' => array($this, 'prepareStatusLayout'), 'width' => '150px', ));
-
frame_callback
列需要prepareStatusLayout()
函数。在grid
类中创建以下函数:public function prepareStatusLayout($value) { $class = ''; switch ($value) { case 'pending' : $class = 'grid-severity-notice'; break; case 'approved' : $class = 'grid-severity-major'; break; case 'declined' : $class = 'grid-severity-critical'; break; } return '<span class="'.$class.'"><span>'.$value.'</span></span>'; }
它是如何工作的...
后端网格是 Magento 中可用的后端小部件之一。其他广泛使用的小部件是表单或标签式左侧菜单。网格小部件是为了显示集合的内容而设计的,可以在列上进行排序和筛选。自动包含分页器,这可以防止在集合中有大量记录时出现内存不足异常。
网格的渲染是在Mage_Adminhtml_Block_Widget_Grid
类中完成的。我们的网格直接扩展了这个类,并覆盖了需要自定义输出以适应我们实体的函数。
在_prepareCollection()
函数中,我们初始化我们将要工作的集合。_prepareColumns()
函数用于定义网格的列。在_prepareColumns()
函数中,我们可以玩转列的定义。列定义是通过$this->addColumn()
函数完成的。在这个函数中,我们将初始化一个包含列参数的数组。建议为每个列使用以下参数:
-
header
(列标题) -
index
(数据库中的列) -
sortable
(当为真时,启用列的排序)
以下参数是可选的:
-
width
(为列定义宽度) -
frame_callback
(调用一个函数来渲染单元格的值) -
type
(定义过滤器小部件,如数字、日期和时间、选项) -
options
(当类型为options
时定义源模型)
添加客户属性
有时候,如果我们能像处理产品一样向客户添加属性,那就容易多了。这是可能的,但在后端没有添加属性的界面。我们必须使用一个模块来创建它,该模块将属性添加到customer
对象中。在这个菜谱中,我们将添加一个loyaltynumber
字段到客户。
准备就绪
要添加customer
属性,唯一任务是创建一个添加属性的升级脚本。然后,我们必须在form
表中链接属性。
如何做到这一点...
执行以下步骤以将loyaltynumber
属性添加到您的客户对象中:
-
第一步是创建升级脚本。在之前的章节中,我们在
app/code/local/Packt/Helloworld/sql/helloworld_setup
文件夹中创建了一个install
和upgrade
脚本。创建一个额外的安装脚本,命名为upgrade-002-003.php
。 -
要安装
customer
属性,在install
脚本中添加以下代码:<?php $installer = $this; $installer->startSetup(); //Create the attribute "loyaltynumber" for the customer entity $installer->addAttribute('customer', 'loyaltynumber', array( 'type' => 'varchar', 'input' => 'text', 'required' => false, 'label' => 'Loyaltynumber', 'visible' => true, 'adminhtml_only' => true, 'user_defined' => true, )); //Add the attribute to the backend forms //@todo $installer->endSetup();
这将为
customer
实体添加一个text
属性。 -
之前的代码是为了创建客户的属性。在这个步骤中,我们将属性添加到后端的客户表单中。我们可以通过在
@todo
注释下添加以下代码来完成:$loyaltyAttribute = Mage::getSingleton('eav/config')->getAttribute('customer', 'loyaltynumber'); $loyaltyAttribute->setData('used_in_forms', array('adminhtml_customer')); $loyaltyAttribute->save();
-
要执行升级脚本,将
config.xml
中的版本号从0.0.2
更新到0.0.3
。 -
要运行脚本,请清除缓存并重新加载页面。当您在后台导航到客户时,您将看到属性已按以下截图所示添加到表单中:
工作原理...
customer
属性的安装方式与通过代码添加product
属性相同。唯一的大区别是实体类型。将字段添加到客户的后台表单并不像添加产品那样简单。对于客户,要在表单中显示的字段存储在eav_form_attribute
表中。
在渲染表单时,Magento 将从该表中获取数据,并根据属性中的配置渲染字段。客户地址是与客户实体相关的另一个 EAV 实体。向客户地址添加属性的方式与添加客户实体相同。
使用源模型
Magento 与许多下拉字段一起工作,您可以在应用程序的表单中选择这些字段。此外,我们还可以在我们的自定义字段中使用下拉字段。
下拉或多选字段总是有可以在该字段中选择选项。为了渲染这些选项,Magento 使用一个返回选项的类。这样的类被称为源模型。
在这个菜谱中,我们将了解 Magento 使用的源模型以及如何为自定义配置字段创建自定义源模型。
准备工作
对于这个菜谱,我们将创建一个与自定义配置字段链接的源模型。这是在Packt_Helloworld
模块中完成的,我们将在本菜谱中扩展它。
如何操作...
以下步骤描述了如何为表单字段创建自己的源模型。
-
我们必须首先创建的是一个下拉类型的配置字段。这与正常配置的语法相同。在
Packt_Helloworld
模块的system.xml
文件中,在<fields>
标签下添加以下配置:<is_enabled translate="label"> <label>Enabled</label> <frontend_type>select</frontend_type> <sort_order>10</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </is_enabled>
-
清除缓存并转到配置页面。您将看到有一个没有选项的下拉字段。要创建选项,我们必须将源模型链接到它。以下代码向字段添加了是/否选项。在
is_enabled
标签下添加此代码:<source_model>adminhtml/system_config_source_yesno</source_model>
-
要创建我们自己的源模型,我们必须创建一个
Model
实例。创建Packt_Helloworld_Model_Source_Config_Relation
类。我们可以通过在app/code/local/Packt/Helloworld/Model/Source/Config
文件夹中创建一个Relation.php
文件来实现。将以下内容添加到该文件中:<?php class Packt_Helloworld_Model_Source_Config_Relation { public function toOptionArray() { return array( array( 'value' => null, 'label'=>Mage::helper('helloworld')->__('--Please Select--'), ), array( 'value' => 'bronze', 'label'=>Mage::helper('helloworld')->__('Bronze'), ), array( 'value' => 'silver', 'label'=>Mage::helper('helloworld')->__('Silver'), ), array( 'value' => 'gold', 'label'=>Mage::helper('helloworld')->__('Gold'), ), ); } }
-
要将之前创建的源模型链接到块,我们必须更改
system.xml
文件中的source_model
行。将行更改为以下内容:<source_model>helloworld/source_config_relation</source_model>
-
清除缓存后,您将看到字段的选项已根据源模型的输出更改,如图所示:
工作原理...
源模型是一个具有toOptionArray()
函数的模型实例。此函数返回一个包含源数组中所有项目的数组。此数组具有以下格式:
array(
array(
'value' => '0',
'label' => 'Label option 0',
),
array(
'value' => '1',
'label' => 'Label option 1',
)
)
value
键是下拉列表中<option>
的值。label
键是出现在下拉列表中的文本。
在这个配方中,我们为配置字段配置了一个源模型。我们还可以在以下情况下使用源模型:
-
后端的产品属性
-
后端中的客户属性
-
后端表单的附加信息
-
后端网格中的下拉筛选器
源模型的配置主要是在字段的配置中完成的。对于 EAV 表单,源模型的信息存储在数据库中的属性配置中。
当保存下拉或多选字段时,它总是保存在数据库的单个字段中。如果一个字段是下拉字段,则在该字段中存储一个值。当字段是多选字段时,所选值的逗号分隔列表将保存在该字段中。
第八章。事件处理程序和计划任务
在本章中,我们将涵盖以下主题:
-
理解 Magento 事件类型
-
创建您自己的事件
-
添加事件监听器
-
计划任务的介绍
-
创建新的计划任务
-
测试您的新计划任务
简介
在 Magento 的电子商务流程中,当访客从您的商店购买东西时,会发生许多事件。他或她将产品添加到购物车,选择支付方式,登录等。
Magento 通过 Mage::dispatchEvent()
函数分发这些事件,您有机会挂钩到事件以执行您的工作。这就像在 JavaScript 中挂钩到点击事件一样。
观察者设计模式用于实现事件处理系统。在安装过程中发生事件,配置将调用观察者类中的正确函数来执行。
Magento 的计划任务系统建立在相同的设计模式之上。计划任务在配置中进行设置。当计划任务运行时,Magento 将检查时间段并执行与配置匹配的任务。
理解 Magento 事件类型
与事件监听器一起工作比重写核心类更好。在分析一个过程时,考虑如何操作以及是否可以与 Magento 事件一起工作是很重要的。
在我们能够这样做之前,我们必须查看哪些事件可用,它们何时被触发,以及事件中发送了哪些参数。
准备工作
在本食谱中,我们将探讨 Magento 事件系统的工作原理。我们将使用 Magento 日志系统来调试核心类,以查看哪些事件可用以及它们何时被触发。
如何操作...
按照以下说明操作以查看事件系统的工作情况:
-
当调试某些内容时,建议您启用 Magento 日志。您可以通过在后台配置它来实现。导航到 系统 | 配置 | 高级 | 开发者。将 启用 参数配置如下所示:
小贴士
您还可以在命令行工具中运行 wiz 命令
wiz devel-logging yes
以启用日志。 -
Magento 事件通过
Mage::dispatchEvent()
函数分发。要调试此函数,我们必须编辑它。Mage::dispatchEvent()
函数将调用app/code/core/Mage/Core/Model/App.php
文件中的dispatchEvent()
函数。要编辑此文件,我们必须将其复制到app/code/local/Mage/Core/Model
文件夹中。如果该文件夹不存在,则创建它并将文件复制进去。 -
在该文件中,搜索
dispatchEvent()
函数。在该函数的第一行中,添加Mage::log($eventName);
以在日志文件中打印事件名称。该函数的开始部分将类似于以下代码片段:public function dispatchEvent($eventName, $args) { Mage::log($eventName); foreach ($this->_events as $area=>$events) { if (!isset($events[$eventName])) { $eventConfig = $this->getConfig()- >getEventConfig($area, $eventName); if (!$eventConfig) { $this->_events[$area][$eventName] = false; continue; } $observers = array(); foreach ($eventConfig->observers->children() as $obsName=>$obsConfig) { ...
-
清除缓存并重新加载前端页面。
-
看一下
var/log/system.log
文件。通过使用tail -f
命令,你可以实时看到文件的变化。在命令行中,转到你的 Magento 安装目录并运行以下命令:tail -f var/log/system.log
这将给出以下输出:
-
当重新加载新页面时,你会在 Magento 的日志文件中看到打印了许多事件。
-
现在已经足够进行调试了。是时候移除文件
app/code/local/Mage/Core/Model/App.php
。
它是如何工作的...
我们首先做的事情是启用 Magento 日志。这建议在开发环境中使用,因为所有的调试消息都打印在 var/log/system.log
文件中。
小贴士
PHP 警告和通知也会打印在这个文件中,因此在开发时查看它是有推荐的。
要打印调试消息,我们使用了 Mage::log()
函数。此函数的第一个参数是你想要调试的消息。如果参数是对象或数组,Magento 将在日志文件中打印变量的转储。
第二个参数是日志级别。当未指定时,使用错误级别 DEBUG
。此参数需要一个数值。这些值在 Zend_Log
类的 const
变量中设置,如下所示:
-
Zend_Log::EMERG
-
Zend_Log::ALERT
-
Zend_Log::CRIT
-
Zend_Log::ERR
-
Zend_Log::WARN
-
Zend_Log::NOTICE
-
Zend_Log::INFO
-
Zend_Log::DEBUG
使用第三个参数,你可以配置一个文件,该文件位于 var/log
文件夹中,需要打印日志消息。当此参数为空时,默认文件 system.log
被使用。
第四个也是最后一个参数是一个布尔值,你可以强制始终打印日志消息(即使 Magento 的日志记录设置为关闭)。
当使用 Mage::log()
函数时,你将打印调试数据,而不会改变 PHP 进程的输出。
对于每一个在 Magento 中的请求,都会触发许多事件。因此,在 Magento 流程中集成和执行自定义代码有许多方法。
与事件一起工作比重写 Magento 的核心类要好。当你重写核心类时,在升级 Magento 或安装第三方模块时,你的代码可能会变得不兼容。
当你与事件一起工作时,你将在 Magento 上创建一个扩展,而不是在重写核心类时创建修改。
因此,考虑你希望在 Magento 中执行自定义代码的方式是好的。
参见
Magento 事件的完整列表可以在以下位置找到:www.magentocommerce.com/wiki/5_-_modules_and_development/reference/magento_events
。
在处理自定义模块时,请确保这个列表不是完整的,因为它们可能有它们自己的事件。
创建自己的事件
当我们想要创建自己的事件时,我们必须使用自定义名称来触发它。在这个菜谱中,我们将学习事件是如何被触发的,以及我们可以使用我们将要传递的参数做什么。
准备工作
我们将创建一个事件,当访问者打开Packt_Helloworld
模块的helloAction()
函数时,该事件将被触发。在这个菜谱中,我们将基于在第四章、创建模块、第六章、数据库和模块和第七章、Magento 后端中创建的Packt_Helloworld
模块进行扩展。如果您愿意,您可以安装起始文件。
如何操作...
以下步骤描述了我们可以如何触发我们自己的事件。
-
打开
Packt_Helloworld
模块的IndexController
。在这个控制器中,有一个helloAction()
函数,我们将在这里工作以触发事件。 -
要触发一个事件,使用
Mage::dispatchEvent()
函数。当我们更改helloAction()
函数的代码为以下代码时,我们将触发一个名为helloworld_register_visit
的事件:public function helloAction() { $this->loadLayout(); Mage::dispatchEvent('helloworld_register_visit'); $this->renderLayout(); }
-
每次页面重新加载时,都会触发事件。因为没有添加监听器,所以不会发生任何事情。为了测试代码是否正常工作,您需要像在之前的菜谱中那样调试
Mage_Core_Model_App::dispatchEvent()
函数。您可以在文件app/code/core/Mage/Core/Model/App.php
中找到这个函数。 -
我们将要做的第二件事是为事件添加两个参数。例如,我们将向事件发送一个产品和分类。我们将使用第二个参数来发送参数。这个参数接受一个包含对象的键值数组。以下代码显示了如何向事件发送参数:
public function helloAction() { $this->loadLayout(); $parameters = array( 'product' => Mage::getModel('catalog/product')- >load(166), 'category' => Mage::getModel('catalog/category')- >load(10), ); Mage::dispatchEvent('helloworld_register_visit', $parameters); $this->renderLayout(); }
小贴士
确保产品分类 ID 存在于您的网店中。如果不存在,您可以使用您的网店中存在的另一个 ID。
它是如何工作的...
Mage::dispatchEvent()
函数在 Magento 中触发一个事件。当这个函数被调用时,Magento 将查找配置并执行匹配的事件观察者函数,在 Magento 世界中也称为观察者。
您可以在任何您想要的上下文中使用Mage::dispatchEvent()
函数,因此如果您需要,您需要决定将其放置在哪里。
添加事件观察者
当您想要挂钩到事件时,会使用事件监听器(观察者)。在之前的菜谱中,我们触发了一个事件。在这个菜谱中,我们将捕获事件并查看当事件在网站上发生时,我们如何执行自定义代码。
准备工作
对于这个菜谱,我们将添加两个事件观察者。第一个观察者将捕获我们在之前的菜谱中创建的事件。第二个事件监听器(观察者)将挂钩到产品的“添加到购物车”操作。
如何操作...
以下步骤描述了您可以使用事件观察者做什么:
-
要监听
helloworld_register_edit
事件,我们必须向Packt_Helloworld
模块的config.xml
文件添加配置。在<global>
标签下添加以下代码:<events> <helloworld_register_visit> <observers> <register_visit> <type>singleton</type> <class>helloworld/observer</class> <method>registerVisit</method> </register_visit> </observers> </helloworld_register_visit> </events>
-
我们刚刚配置了一个观察者,该观察者触发
helloworld/observer
类中的registerVisit()
函数。我们必须创建Packt_Helloworld_Model_Observer
类。创建文件app/code/local/Packt/Helloworld/Model/Observer.php
,内容如下:<?php class Packt_Helloworld_Model_Observer { public function registerVisit (Varien_Event_Observer $observer) { Mage::log('Registered'); } }
-
我们可以通过触发事件来测试事件观察者。事件在页面
http://magento-dev.local/helloworld/index/hello
上触发。导航到该文件并检查 Magento 日志文件(var/log/system.log
)中的调试消息。 -
观察者函数在 Magento 日志文件中打印调试消息。让我们看看我们与事件一起发送的参数。这些参数在函数中以
$observer
对象的形式发送。要获取产品和类别,将registerVisit()
操作更改为以下代码以调试产品和类别:public function registerVisit ($observer) { $product = $observer->getProduct(); $category = $observer->getCategory(); Mage::log($product->debug()); Mage::log($category->debug()); }
-
在下一部分,我们将挂钩到“添加到购物车”事件。当用户将产品添加到购物车时,我们必须检查数量是否为奇数。如果不是,我们必须显示一个错误消息,说明产品不能添加到购物车。
-
要做到这一点,我们必须为事件
checkout_cart_product_add_after
创建一个事件监听器(观察者)。我们通过在Packt_Helloworld
模块的config.xml
文件中添加以下代码来实现。在<events>
标签下粘贴以下代码:<checkout_cart_product_add_after> <observers> <check_cart_qty> <type>singleton</type> <class>helloworld/observer</class> <method>checkCartQty</method> </check_cart_qty> </observers> </checkout_cart_product_add_after>
-
这将调用观察者类中的
checkCartQty()
函数。当事件被触发时,以下代码将显示一个通知消息:public function checkCartQty ($observer) { Mage::getSingleton('checkout/session')- >addNotice('Product add event fired'); }
-
清除缓存并添加一个产品到购物车。当添加产品时,消息将出现在购物车页面,如下截图所示:
-
现在,我们必须添加以下代码来检查数量是奇数还是偶数。此代码从观察者获取数量,并添加对奇数或偶数的检查:
public function checkCartQty ($observer) { if ($observer->getProduct()->getQty() % 2 == 0) { //Even } else { //Odd } }
-
当数量为偶数时,我们必须显示一个通知。当数量为奇数时,我们必须取消“添加到购物车”操作并显示一条消息。我们可以通过抛出异常来实现。在事件观察者中,我们永远不能使用返回值。为了正确工作,检查函数将类似于以下代码:
public function checkCartQty (Varien_Event_Object $observer) { if ($observer->getProduct()->getQty() % 2 == 0) { //Even Mage::getSingleton('checkout/session')->addNotice('Even quantity added'); } else { //Odd Mage::throwException('Quantity is odd. It needs to be even'); } }
-
清除缓存并添加一个数量为偶数和奇数的产品到购物车。您将看到奇数数量将导致错误消息。
它是如何工作的...
事件监听器(观察者)总是在config.xml
文件中进行配置。当我们查看配置时,每个标签都有自己的用途:
<events>
<helloworld_register_visit>
<observers>
<register_visit>
<type>singleton</type>
<class>helloworld/observer</class>
<method>registerVisit</method>
</register_visit>
</observers>
</helloworld_register_visit>
</events>
<events>
标签是定义事件根标签。根据需要执行的事件范围,<events>
标签可以在<global>
、<frontend>
或<admin>
标签下进行配置。
在<events>
标签下,我们找到了<helloworld_register_visit>
标签。标签的名称是将被观察的事件的名称。观察者在子标签中定义。
在<helloworld_register_visit>
标签下,你可以看到一个<observers>
子标签。在这个标签中,所有观察者都被声明。
在这个配方中,我们有一个register_visit
观察者。在<observers>
标签下声明的观察者需要有一个唯一名称。当使用现有名称时,你将覆盖现有观察者的配置。
观察者标签(register_visit
)有以下子标签:
-
<type>
:这是调用类的设计模式,通常是singleton
或model
-
<class>
:这是类的 Magento 路径 -
<method>
:这是要执行的方法
前一个事件将在使用dispatchEvent()
函数触发事件时执行配置的类和函数。
对于每个观察者函数,都会传递一个$observer
对象。这个变量是Varien_Event_Observer
类的实例。这个类扩展了Varien_Object
类,并包含一些额外的与事件相关的函数。
事件观察者函数的返回参数将被忽略。当使用事件观察者时,你必须查看Mage::dispatchEvent()
函数放置的上下文。
有时,它被放置在一个try-catch
结构中,如本配方中的checkout_cart_product_add_after
事件。在其他情况下,你可以像model_save_before
事件那样更改传递的对象的值。
介绍 cron 作业
Cron 作业或计划任务是后台进程,使你的 Magento 网店保持运行。以下是一些 cron 作业的示例:
-
发送新闻通讯
-
重新计算下一天的目录促销规则
-
清理访客日志
-
发送产品库存和价格警报邮件
-
更新货币汇率
如果服务器上未配置 cron 作业,经过一段时间后,你将看到你的网店出现问题。
准备工作
在这个配方中,我们将学习如何在服务器上配置 cron 作业并验证它们是否正在运行。通过 SSH 连接到你的服务器,并准备好执行一些服务器配置。
如何做...
以下步骤描述了如何在你的服务器上配置 cron 作业:
-
实际上,我们必须每五分钟配置一次 cron 作业。为此,执行以下命令:
/bin/sh /var/www/packt/magento-dev/cron.sh
小贴士
为了避免权限问题,你必须以 Apache 用于服务 HTTP 请求的用户身份运行此命令。在我们的设置中,此用户是
www-data
。 -
当你执行前面的命令时,数据库中的 cron 作业表将更新为最近的 cron 作业。在
cron_schedule
表中,你可以看到接下来 30 分钟的队列。在你的数据库客户端中运行以下命令以查看表的内容:SELECT * from CRON_SCHEDULE;
此查询将给出以下输出:
-
在
scheduled_at
列中,您可以看到 cronjob 计划运行的时间。在scheduled_at
时间之后运行cron.sh
脚本后,我们必须再次运行查询。这将给出以下输出: -
要运行 cronjob,我们必须使用 Linux 服务器上的 crontab 文件。要配置它,我们必须切换到
www-data
用户。我们可以通过运行以下命令来完成此操作:sudo su www-data
-
接下来,我们必须配置 cronjob。我们可以通过运行
crontab -e
命令来完成此操作。这将打开一个文件,我们必须在其中放置以下截图所示的内容: -
保存文件后,cronjob 将每五分钟运行一次。
工作原理...
Magento 的 cron 脚本在 Magento 根目录下被称为cron.sh
文件。这将通过命令行界面(CLI)执行cron.php
文件。在这个cron.php
文件中,启动了 Magento 应用程序并初始化了 cron 进程。
当 cron 进程初始化时,Magento 将查看cron_schedule
表。所有具有过去scheduled_at
字段的预定 cronjob 都将被执行。当一个任务开始时,executed_at
字段将更新为当前时间戳。
当一个任务完成后,finished_at
字段将更新为当前时间戳。同时,状态也将更新。当状态为错误时,消息字段将更新为错误信息。
当进程完成后,Magento 将为接下来的 30 分钟创建一个队列。根据配置文件,Magento 知道每次需要安排哪个 cronjob。
创建一个新的 cronjob
cronjob 在 Magento 模块的config.xml
文件中定义。像这些文件中的每个配置一样,cronjob 配置很容易在我们的模块中扩展。在本教程中,我们将学习如何为 Magento 安装创建额外的 cronjob。
准备工作
执行 cronjob 的工作流程基本上与处理事件相同。我们必须在config.xml
文件中配置一个观察者函数,该函数将在 cronjob 执行时被调用。
在Packt_Helloworld
模块中,我们将创建一个 cronjob,该 cronjob 将在数据库中保存一些数据。
如何操作...
按照以下步骤进行,以查看配置额外 cronjob 所需的配置:
-
我们必须在
config.xml
文件中创建一个 crontab 配置。以下配置将完成此操作。将以下内容粘贴到文件app/code/local/Packt/Helloworld/etc/config.xml
中的<config>
标签下:<crontab> <jobs> <helloworld_check_subscriptions> <schedule> <cron_expr>* 10 * * *</cron_expr> </schedule> <run> <model>helloworld/observer::checkSubscriptions </model> </run> </helloworld_check_subscriptions> </jobs> </crontab>
-
清除缓存并验证配置是否正常工作,请在您的终端中运行以下命令:
wiz config-get crontab/jobs
此命令将列出配置中的所有 cronjob。
如果
helloworld_check_subscriptions
代码在列表中,这意味着配置正在正常工作。 -
当配置正确时,我们必须创建观察者函数。在
run
/model
标签中,我们将调用Packt_Helloworld_Model_Observer
类中的cronListener()
动作。如果该类不存在,则创建它,并在其中添加checkSubscriptions()
函数。该函数将类似于以下代码:public function checkSubscriptions() { $subscription = Mage::getModel('helloworld/subscription'); $subscription->setFirstname('Cron'); $subscription->setLastname('Job'); $subscription->setEmail('cron.job@example.com'); $subscription->setMessage('Created by cronjob'); $subscription->save(); }
当 cron 执行时,这将保存一个
subscription
实体到subscriptions
表中。
它是如何工作的...
当你查看 cronjob 配置时,你首先看到的 XML 标签是 cronjob 代码。这必须在所有模块中是唯一的。如果它存在,你将覆盖现有标准 cronjob 的设置。
一个好的做法是以你用来注册模块模型(helloworld
、catalog
等)的名称开始 cronjob 代码。此代码后面跟着你想要执行的操作。
小贴士
cronjob 代码始终是小写字母。空格被下划线替换。
在更深的层次上,你可以看到<schedule>
和<run>
标签。
在<schedule>
标签中,我们可以配置 cronjob 的间隔。此配置包含五个参数,代表以下配置:
-
分钟
-
小时
-
日期
-
月份
-
年份
类似于0 10 * * *
的配置将在每天上午 10:00 运行。这意味着每个工作日、每个月、每天,在 10 小时和 0 分钟。
<run>
标签仅初始化在 cron 需要执行时调用的观察者类和函数。无法向该函数发送参数。
测试你的新 cronjob
当你开发一个新的 cronjob 时,测试它并不那么容易。你不会等到 cron 执行,因为你将浪费很多时间。
在Magento Connect上有一个模块可以使 cronjob 的测试变得简单。此模块允许你从后台或命令行运行 cronjob。
它还向后台添加了一个视图,这是一个cron_schedule
表的图形表示。
准备工作
我们将在我们的 Magento 安装上安装来自 Magento Connect 的模块。有关该模块的更多信息,请参阅:www.magentocommerce.com/magento-connect/aoe-scheduler.html
。
如何操作...
以下步骤描述了模块测试 cronjob 的工作流程:
-
通过从 Magento Connect 获取扩展密钥来安装模块。确保使用以下截图所示的Magento Connect 2.0密钥:
-
在 Magento Connect Manager 中粘贴扩展密钥。导航到系统 | Magento Connect | Magento Connect Manager,粘贴扩展密钥,然后点击以下截图所示的安装按钮:
-
当你继续操作时,Magento Connect Manager 会将模块安装到安装目录。
小贴士
有可能 Magento Connect Manager 无法安装该模块。如果真的如此,你可以使用网站
freegento.com/ddl-magento-extension.php
下载模块的已准备好粘贴的格式。 -
当你返回到管理员界面时,你将看到在 系统 部分下添加了一个菜单项。当你导航到 系统 | 调度器 | 调度配置 时,你将看到所有可用 cron 作业的列表。
-
要运行一个 cron 作业,你必须在网格中选择一个。之后,在网格上方的下拉菜单中选择 立即运行,然后点击 提交。这将直接运行你的 cron 作业。
-
当你导航到 系统 | 调度器 | 时间线视图 时,你将在时间线上看到 cron 作业的信息,如下面的截图所示:
-
在 Magento 根目录的
shell
文件夹中,该模块通过命令行添加了一个 shell 脚本来测试 cron 作业。这是scheduler.php
文件,它有以下选项:-
action (
runNow
或scheduleNow
) -
code (cron 作业代码)
-
-
当你在 Magento 根目录下时,通过命令行运行我们的 cron 作业的命令如下:
php shell/scheduler.php -action runNow -code cronjob_code
-
之前的命令直接运行了 cron 作业。当你想要使用当前时间戳来安排 cron 作业(这样它将在下一次 cron 运行时执行)时,我们必须使用
scheduleNow
动作。使用此选项时,命令看起来如下:php shell/scheduler.php -action scheduleNow -code cronjob_code
它是如何工作的...
Aoe_Scheduler
模块向后台添加了一个界面,简化了测试 cron 作业的过程。它只是在 cron_schedule
表上的一个图形界面。
当你在模块中处理 cron 作业时,此模块推荐使用,可以节省大量时间。
该 shell 脚本被添加来通过命令行测试 cron 作业。确保当你通过 PHP CLI 运行 cron 作业或在浏览器中运行它时,使用的是其他 PHP 设置。Apache 请求使用 Apache php.ini 设置,而命令行 PHP 使用 PHP CLI 设置。
第九章。创建配送模块
在本章中,我们将涵盖以下主题:
-
初始化模块配置
-
编写适配器模型
-
扩展配送方法功能
-
在前端添加模块
简介
将订购的产品配送给客户是电子商务流程中的关键部分。在大多数情况下,店主与配送处理商有合同,每个人都有自己的业务规则。
在标准 Magento 中,以下配送处理程序得到支持:
-
DHL
-
FedEx
-
UPS
-
USPS
如果您的处理程序不在列表中,请查看 Magento Connect 上是否有可用的模块。如果没有,您可以配置标准配送方法,或者您可以创建自己的,我们将在本章中这样做。
初始化模块配置
在第四章“创建模块”中,我们学习了如何创建自定义模块。在本食谱中,我们将创建一个配送模块所需的文件,我们将使用本章的食谱扩展更多功能。
准备工作
使用 Magento 项目打开您的代码编辑器。同时,获取后端访问权限,我们将检查一些内容。
如何操作...
以下步骤描述了我们可以如何为配送模块创建配置:
-
创建以下文件夹:
-
app/code/local/Packt/
-
app/code/local/Packt/Shipme/
-
app/code/local/Packt/Shipme/etc/
-
app/code/local/Packt/Shipme/Model/
-
app/code/local/Packt/Shipme/Model/Carrier
-
-
在
app/etc/modules
文件夹中创建一个名为Packt_Shipme.xml
的模块文件,内容如下:<?xml version="1.0"?> <config> <modules> <Packt_Shipme> <active>true</active> <codePool>local</codePool> <depends> <Mage_Shipping /> </depends> </Packt_Shipme> </modules> </config>
-
在
app/code/local/Packt/Shipme/etc/
文件夹中创建一个名为config.xml
的文件,内容如下:<?xml version="1.0" encoding="UTF-8"?> <config> <modules> <Packt_Shipme> <version>0.0.1</version> </Packt_Shipme> </modules> <global> <models> <shipme> <class>Packt_Shipme_Model</class> </shipme> </models> </global> <default> <carriers> <shipme> <active>1</active> <model>shipme/carrier_shipme</model> <title>Shipme shipping</title> <express_enabled>1</express_enabled> <express_title>Express delivery</express_title> <express_price>4</express_price> <business_enabled>1</business_enabled> <business_title>Business delivery</business_title> <business_price>5</business_price> </shipme> </carriers> </default> </config>
-
清除缓存,并在后端导航到系统 | 配置 | 高级 | 禁用模块输出。观察
Packt_Shipme
模块是否在列表中。 -
到目前为止,模块已初始化并开始工作。现在,我们必须创建一个
system.xml
文件,我们将在此文件中放置配送模块的配置参数。创建文件app/code/local/Packt/Shipme/etc/system.xml
。 -
在此文件中,我们将为我们的配送模块创建配置参数。当您将以下代码粘贴到文件中时,您将在配送方法的配置中创建一个额外的组。在这个组中,我们可以为新配送方法设置设置:
<?xml version="1.0" encoding="UTF-8"?> <config> <sections> <carriers> <groups> <shipme translate="label" module="shipping"> <label>Shipme</label> <sort_order>15</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> <fields> <!-- Define configuration fields below --> <active translate="label"> <label>Enabled</label> <frontend_type>select</frontend_type> <source_model>adminhtml/ system_config_source_yesno</source_model> <sort_order>10</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </active> <title translate="label"> <label>Title</label> <frontend_type>text</frontend_type> <sort_order>20</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </title> </fields> </shipme> </groups> </carriers> </sections> </config>
-
清除缓存,并在后端导航到配送方法配置页面。为此,导航到系统 | 配置 | 销售 | 配送方法。您将看到添加了一个额外的组,如下面的截图所示:
-
您将看到有一个名为Shipme的新配送方法。我们将为此配置添加一些值。在模块的
<fields>
标签下添加以下代码:<active translate="label"> <label>Enabled</label> <frontend_type>select</frontend_type> <source_model>adminhtml/system_config_source_yesno</source_model> <sort_order>10</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </active> <title translate="label"> <label>Title</label> <frontend_type>text</frontend_type> <sort_order>20</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </title> <express_enabled translate="label"> <label>Enable express</label> <frontend_type>select</frontend_type> <source_model>adminhtml/system_config_source_yesno</source_model> <sort_order>30</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </express_enabled> <express_title translate="label"> <label>Title express</label> <frontend_type>text</frontend_type> <sort_order>40</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </express_title> <express_price translate="label"> <label>Price express</label> <frontend_type>text</frontend_type> <sort_order>50</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </express_price> <business_enabled translate="label"> <label>Enable business</label> <frontend_type>select</frontend_type> <source_model>adminhtml/system_config_source_yesno</source_model> <sort_order>60</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </business_enabled> <business_title translate="label"> <label>Title business</label> <frontend_type>text</frontend_type> <sort_order>70</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </business_title> <business_price translate="label"> <label>Price business</label> <frontend_type>text</frontend_type> <sort_order>80</sort_order> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>1</show_in_store> </business_price>
-
清除缓存并重新加载后端。你现在将看到以下截图所示的Shipme – 快递运输方法下的其他配置:
工作原理...
我们首先做的事情是创建初始化模块所需的必要文件。以下是需要初始化模块的文件:
-
app/etc/modules/Packt_Shipme.xml
-
app/code/local/Packt/Shipme/etc/config.xml
在第一个文件中,我们将使用<active>
标签激活模块。<codePool>
标签描述了该模块位于本地代码库中,代表app/code/local/
文件夹。
在此文件中,还有一个<depends>
标签。首先,这将检查Mage_Shipping
模块是否已安装。如果没有安装,Magento 将抛出异常。如果模块可用,依赖关系将在Mage_Shipping
模块之后加载此模块。这使得重写Mage_Shipping
模块中的一些值成为可能。
在第二个文件config.xml
中,我们配置了在这个模块中所需的所有内容。以下是需要配置的以下事项:
-
版本号(
0.0.1
) -
模型
-
配置值的默认值
我们最后做的事情是创建一个system.xml
文件,以便我们可以为运输模块创建自定义配置。
system.xml
文件中的配置向运输方法配置中添加了一些额外的值,这些值在系统 | 配置 | 销售 | 运输方法菜单下的后端中可用。
在这个模块中,我们创建了一个新的运输处理器,称为Shipme
。在这个处理器中,你可以配置两个运输选项:快递和商务。在system.xml
文件中,我们创建了配置选项可见性、名称和价格的字段。
参考信息
在这个配方中,我们使用了模块的system.xml
文件来创建配置值。你可以在扩展系统配置配方中找到有关信息,该配方来自第七章,Magento 后端。
在前端设置运输方法的标题
在上一个配方中初始化了一个新的运输模块。在上一个配方中我们所做的是为继续本配方中将要看到的业务部分做准备。我们将添加一个具有运输方法业务逻辑的模型。这个模型被称为适配器类,因为 Magento 要求为每个运输方法提供一个适配器类。这个类将扩展Mage_Shipping_Model_Carrier_Abstract
类。
这个类将用于以下事项:
-
使运输方法可用
-
计算运输费用
-
在前端设置运输方法的标题
如何操作...
执行以下步骤以创建用于运输方法的适配器类:
-
如果不存在,创建
app/code/local/Packt/Shipme/Model/Carrier
文件夹。 -
在这个文件夹中,创建一个名为
Shipme.php
的文件,并将以下内容添加到其中:<?php class Packt_Shipme_Model_Carrier_Shipme extends Mage_Shipping_Model_Carrier_Abstract implements Mage_Shipping_Model_Carrier_Interface { protected $_code = 'shipme'; public function collectRates (Mage_Shipping_Model_Rate_Request $request) { $result = Mage::getModel('shipping/rate_result'); //Check if express method is enabled if ($this->getConfigData('express_enabled')) { $method = Mage::getModel ('shipping/rate_result_method'); $method->setCarrier($this->_code); $method->setCarrierTitle ($this->getConfigData('title')); $method->setMethod('express'); $method->setMethodTitle ($this->getConfigData('express_title')); $method->setCost ($this->getConfigData('express_price')); $method->setPrice ($this->getConfigData('express_price')); $result->append($method); } //Check if business method is enabled if ($this->getConfigData('business_enabled')) { $method = Mage::getModel ('shipping/rate_result_method'); $method->setCarrier($this->_code); $method->setCarrierTitle ($this->getConfigData('title')); $method->setMethod('business'); $method->setMethodTitle ($this->getConfigData('business_title')); $method->setCost ($this->getConfigData('business_price')); $method->setPrice ($this->getConfigData('business_price')); $result->append($method); } return $result; } public function isActive() { $active = $this->getConfigData('active'); return $active==1 || $active=='true'; } public function getAllowedMethods() { return array('shipme'=>$this->getConfigData('name')); } }
-
保存文件并清除缓存;现在你的适配器模型已经创建。
它是如何工作的...
之前创建的类处理了所有需要的运输方法业务逻辑。因为这个适配器类是Mage_Shipping_Model_Carrier_Abstract
类的扩展,我们可以覆盖一些方法来自定义标准业务逻辑。
我们覆盖的第一个方法是isAvailable()
函数。在这个函数中,我们必须返回true
或false
来说明该模块是激活的。在我们的代码中,我们将根据系统配置字段active
激活模块。
第二个方法是collectRates()
函数。这个函数用于为每个运输方法设置正确的参数。对于每个运输方法,我们可以设置标题和价格。
该类实现了Mage_Shipping_Model_Carrier_Interface
接口。在这个接口中,声明了两个函数:isTrackingAvailable()
和getAllowedMethods()
函数。
我们在适配器类中创建了getAllowedMethods()
函数。isTrackingAvailable()
函数在父类Mage_Shipping_Model_Carrier_Abstract
中声明。
我们在Shipme运输方法下配置了两个选项。这些选项被称为Express delivery和Business delivery。我们将检查它们是否在配置中启用,并为每个选项设置配置的标题和价格。
最后要做的事情是返回正确的值。我们必须返回一个Mage_Shipping_Model_Rate_Result
类的实例。我们创建了一个空的实例,当方法可用时,我们将附加方法。
要添加一个方法,我们必须使用append($method)
函数。这个函数需要一个Mage_Shipping_Model_Rate_Result_Method
类的实例,我们在两个if
语句中创建了它。
扩展运输方法功能
当所有文件安装完成后,我们可以为运输方法添加更多功能。在这个菜谱中,我们将添加国家配置,并启用运输方法的跟踪代码。
如何操作...
执行以下步骤以跟踪代码和特定国家运输:
-
打开适配器文件
app/code/local/Packt/Shipme/Model/Carrier/Shipme.php
。 -
在此文件中添加以下函数以启用跟踪代码:
public function isTrackingAvailable() { return true; }
现在已为
Shipme
运输方法启用了跟踪代码。 -
要启用特定国家的运输,我们必须在模块的
system.xml
文件中添加一些配置字段。将以下代码作为<fields>
标签的子标签添加:<sallowspecific translate="label"> <label>Ship to Applicable Countries</label> <frontend_type>select</frontend_type> <sort_order>90</sort_order> <frontend_class>shipping-applicable-country</frontend_class> <source_model>adminhtml/system_config_source_shipping _allspecificcountries</source_model> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> </sallowspecific> <specificcountry translate="label"> <label>Ship to Specific Countries</label> <frontend_type>multiselect</frontend_type> <sort_order>100</sort_order> <source_model>adminhtml/system_config_source_country </source_model> <show_in_default>1</show_in_default> <show_in_website>1</show_in_website> <show_in_store>0</show_in_store> <can_be_empty>1</can_be_empty> </specificcountry>
-
清除缓存并打开运输方法的配置页面。你会看到有两个新的配置选项。当你将Ship to Applicable Countries改为Specific Countries时,你可以选择多个国家,如以下截图所示:
它是如何工作的...
我们首先做的事情是启用为Shipme
发货方式创建跟踪号码的可能性。我们覆盖了isTrackingAvailable()
函数,它返回标准的false
。通过返回true
,我们启用了跟踪号码。
我们做的第二件事是启用特定国家的发货。我们配置了两个具有标准命名约定的字段。当我们在后端启用配置时,我们只能在发货地址位于所选国家之一时使用发货方式。
在前端添加模块
在这个菜谱中,我们将测试发货方式在前端是否出现。我们在之前的菜谱中准备了代码和配置,以便实现这一点。
如何操作...
在这个菜谱中,我们将检查配置并使用新的发货方式下订单:
-
登录到管理面板。
-
通过访问系统 | 配置 | 销售 | 发货方式导航到发货方式页面。
-
检查Shipme – 快递方式的所有值是否正确。同时确保它已启用。
-
保存配置。
-
前往前端,将产品添加到购物车,并结账。
-
填写账单和发货步骤的正确数据。
-
在发货方式步骤中,将出现新的方法,如下截图所示:
-
从列表中选择一个方法并点击继续按钮。
-
在支付方式步骤中,选择支票汇款方法。
小贴士
如果你看不到支票汇款支付方式,你必须在其系统配置中启用它。
-
点击继续并下订单。你将看到成功页面。
-
在后端,前往销售 | 订单并点击最新的订单。
-
为了完成订单,我们必须为它创建一个发票以确认订单已付款。当你点击发票按钮时,你将被重定向到发票表单,你可以提交你的发票。
-
当发票保存时,你会看到订单状态已变为处理中。当状态为处理中时,你可以点击发货按钮来创建发货。你将看到以下屏幕:
-
当你点击添加跟踪号码按钮时,从下拉菜单中选择Shipme 发货选项,并添加一个跟踪号码,例如
1234567890
: -
当你点击提交发货按钮时,你的发货将被处理,订单状态将变为完成。
它是如何工作的...
在这个菜谱中,我们测试了本章中创建的发货方式。我们使用新的发货方式下订单,以检查一切是否按预期工作。
第十章。创建产品滑块小部件
在本章中,我们将涵盖以下主题:
-
创建一个空模块
-
注册帮助器和块
-
创建小部件配置文件
-
创建块和模板文件
-
创建自定义配置参数
-
完成主题设计
简介
Magento 小部件是前端中配置块的图形界面。对于每个小部件,都有一个可用的配置页面,您可以在其中设置该小部件所需的所有值。
配置完成后,您可以配置布局指令,以在多个前端位置显示小部件。
在这个菜谱中,我们将创建一个新的模块,在这个模块中我们将创建自己的小部件。我们将创建一个产品滑块,它将展示一个类别中的产品,我们可以在小部件中配置这些产品。
当我们为小部件完成了所需的配置后,我们将在前端完成其表示。我们将创建一个产品列表,我们将使用 jQuery 滑块脚本对其进行样式设计。
创建一个空模块
正如我们在上一章中做的那样,并在第四章中完全解释了,我们将创建创建空模块所需的所有文件,该模块将在后续章节中通过小部件配置进行扩展。
我们将从一个空的 Magento 模块开始,我们将在本菜谱中创建它。我们将创建所有必要的文件来初始化一个新模块,该模块可用于创建小部件。
准备工作
打开您的代码编辑器,准备创建一个名为Packt_Productslider
的新模块。
如何做到这一点...
当您执行以下步骤时,您将创建一个空的Packt_Productslider
模块:
-
为该模块创建以下文件夹:
-
app/code/local/
-
app/code/local/
-
app/code/local/Packt/Productslider/
-
app/code/local/Packt/Productslider/etc
-
-
在
app/etc
文件夹下的modules
文件夹中创建模块文件Packt_Productslider.xml
。 -
将以下代码粘贴到其中:
<?xml version="1.0"?> <config> <modules> <Packt_Productslider> <active>true</active> <codePool>local</codePool> <depends> <Mage_Widget /> </depends> </Packt_Productslider> </modules> </config>
-
创建模块的配置文件。这是位于
app/code/local/Packt/Productslider
文件夹下的etc
文件夹中的config.xml
文件。 -
将以下内容添加到文件中:
<?xml version="1.0" encoding="UTF-8"?> <config> <modules> <Packt_Productslider> <version>0.0.1</version> </Packt_Productslider> </modules> </config>
-
清除缓存并检查模块是否已安装。您可以通过导航到配置页面,系统 | 配置 | 高级 | 高级,检查模块是否在列表中。或者,您可以在命令行中运行命令
wiz module-list
。
它是如何工作的...
我们刚刚创建了一个名为Packt_Productslider
的新模块。这是一个自定义模块,因此我们在本地代码池中进行了配置。
实际上,这个模块什么也不做,但我们将扩展它在下个菜谱中。
注册帮助器和块
我们将创建的小部件模块将使用自定义块。为了在块和不同的配置中翻译字符串,我们需要配置一个帮助类。
准备工作
我们将初始化块和助手,并为我们的模块创建默认的助手,就像我们在第四章中做的那样,创建模块。
如何做...
以下步骤描述了如何为新模块配置助手和块。
-
创建以下文件夹:
-
app/code/local/Packt/Productslider/Block
-
app/code/local/Packt/Productslider/Helper
-
-
在模块的
config.xml
文件中,在<config>
标签下添加以下配置:<global> <blocks> <productslider> <class>Packt_Productslider_Block</class> </productslider> </blocks> <helpers> <productslider> <class>Packt_Productslider_Helper</class> </productslider> </helpers> </global>
-
在
app/code/local/Packt/Productslider/Helper
下创建Data.php
文件,并将以下内容粘贴到其中:<?php class Packt_Productslider_Helper_Data extends Mage_Core_Helper_Abstract { }
-
清除缓存,您的块和助手将被注册。
它是如何工作的...
在前面的代码中,我们使用名称 productslider
初始化了块和助手。使用此配置,可以使用如 productslider/block_name
的块名称。
助手也以 productslider
的名称注册。我们创建了一个默认助手,我们可以使用函数 Mage::helper('productslider')
来调用它。
创建部件配置文件
在这个菜谱中,我们将配置一个新的部件类型。我们必须创建一个新的配置文件,在其中我们将为部件类型初始化以下内容:
-
部件名称(在后台)
-
部件配置参数
-
部件块类型
-
部件模板(
.phtml
文件)
准备工作
要测试部件配置,我们可以导航到后端 CMS 下的 Widgets 页面来管理部件实例。
如何做...
执行以下步骤以创建 widget.xml
配置文件。
-
创建文件
app/code/local/Packt/Productslider/etc/widget.xml
。 -
将以下代码添加到该文件中:
<?xml version="1.0" encoding="UTF-8"?> <widgets> <category_product_slider type="catalog/product_list"> <name>Category product slider</name> <description>Displays the products for a given category in a slider widget</description> </category_product_slider> </widgets>
-
清除缓存并检查配置是否生效。
-
在后台,导航到 CMS | Widgets,点击 Add New Widget Instance 按钮,并确认部件在列表中,如图所示:
-
配置前端主题的部件,并转到下一页。
-
您现在在部件配置页面。在 Widget Options 选项卡中,我们将创建我们自己的部件参数。
-
要创建一个将在块中使用的标题字段,我们必须创建一个
parameters
标签,如下面的代码所示。将其粘贴到<category_product_slider>
标签下。<parameters> <title> <label>Title (frontend)</label> <type>text</type> <required>1</required> <visible>1</visible> </title> </parameters>
-
清除缓存,重新加载后台页面,并转到 Widget Options 选项卡。您将看到有一个标题字段可用,如图所示:
-
要显示一个类别的所有产品,我们必须创建一个可以从中选择类别的配置。我们将创建一个文本框,可以粘贴正确的类别 ID。要配置该字段,请在
<parameters>
标签下添加以下代码:<category_id> <label>Category ID</label> <type>text</type> <required>1</required> <sort_order>20</sort_order> <visible>1</visible> </category_id>
-
清除缓存并重新加载页面。您将看到配置表单中添加了第二个文本框。
它是如何工作的...
widget.xml
文件用于在 Magento 安装中定义小部件。在 Magento 中,所有小部件类型都在 <widgets>
配置标签下定义。
在此标签下,我们定义了一个名为 category_product_slider
的新小部件。在这个标签中,我们使用 type
属性定义了小部件将引用的 Block
类。在这种情况下,它是 catalog/product_list
。这是用于渲染类别产品列表或网格的块。
在 <category_product_slider>
标签下,我们使用了以下配置标签:
-
name
-
description
-
parameters
name
标签用于在创建小部件时在下拉菜单中使用的 widget 类型名称。
description
标签用于信息。它不会在前端或后端显示。它仅用于为小部件提供信息。
在 parameters
标签中,我们定义了小部件的配置参数。在我们的例子中,这些是 name
和 category_id
。
我们的字段只是文本字段,但我们可以利用其他输入字段,如下拉框和复选框。当与下拉框或多选框字段一起工作时,我们可以使用 <source_model>
标签定义一个源模型。
创建块和模板文件
在前面的章节中,我们学习了如何配置小部件。现在是时候展示小部件了。
我们将创建一个自定义块,在其中我们将编写一个查询,返回给定类别的产品,并在小部件配置页面上设置一个配置以使用两个模板文件。
准备工作
我们将进一步工作以创建我们自己的小部件实例。打开我们在前一个菜谱中创建的 widget.xml
文件。
如何操作...
以下步骤描述了如何为小部件实例配置一个自定义块和自定义模板。
-
首先,我们将创建一个扩展
Mage_Catalog_Block_Product_List
类的块类。我们这样做是因为那个类的行为正是我们小部件所需要的。 -
在
app/code/local/Packt/Productslider/Block/Catalog/Product
下创建List.php
文件。 -
向该文件添加以下内容以初始化我们将要配置的类和函数:
<?php class Packt_Productslider_Block_Catalog_Product_List extends Mage_Catalog_Block_Product_List implements Mage_Widget_Block_Interface { protected function _beforeToHtml() { parent::_beforeToHtml(); } protected function _prepareLayout() { parent::_prepareLayout(); } }
-
配置使用我们之前创建的类的块。我们通过更改
<category_product_slider>
标签中的type
属性来实现。将属性更改为productslider/catalog_product_list
。 -
当块类创建完成后,是时候为该块创建模板了。在这个小部件中,我们将使用两个模板。第一个模板包含产品的图片、标题和价格。第二个模板只显示图片和“添加到购物车”按钮。
-
在
app/design/frontend/base/default/template/productslider
文件夹下的category-product-slider
文件夹中创建模板。 -
创建文件夹并添加以下文件到该文件夹:
-
list.phtml
-
teaser.phtml
-
-
在
list.phtml
文件中,添加以下内容:<div class="block"> <div class="block-title"> <h2><?php echo $this->getTitle() ?></h2> </div> <div class="block-content"> <p>Test of the list.phtml template</p> </div> </div>
-
在
teaser.phtml
文件中,添加以下内容:<div class="block"> <div class="block-title"> <h2><?php echo $this->getTitle() ?></h2> </div> <div class="block-content"> <p>Teasers template</p> </div> </div>
-
配置小部件配置页面,以便您可以选择模板。我们可以通过将以下内容添加为
<parameters>
标签的子标签来完成:<template> <label>Frontend template</label> <type>select</type> <values> <list translate="label"> <value>productslider/category-product- slider/list.phtml</value> <label>List template</label> </list> <teaser translate="label"> <value>productslider/category-product- slider/teaser.phtml</value> <label>Teaser template</label> </teaser> </values> <sort_order>30</sort_order> <visible>1</visible> </template>
-
清除缓存并转到小部件配置页面。当您点击添加布局更新时,您可以选择小部件将显示的页面。最后一个下拉菜单是您可以为小部件使用的模板,如下面的截图所示:
小贴士
确保您选择正确的主题来配置小部件。我们将使用默认的/默认主题来配置小部件,所以请确保您的商店使用的是相同的主题。
-
完成表单以在主页上放置小部件实例。
-
当您清除缓存时,小部件将带有所选模板文件的内容出现在主页上。
-
当您启用开发者提示时,您将看到我们之前创建的自定义块被使用,如下面的截图所示:
小贴士
您可以通过在系统 | 配置 | 高级下的调试部分导航到开发者页面来在后台启用开发者提示。务必在网站或存储视图范围内进行配置。
您还可以运行
wiz devel-showhints yes
命令来启用提示。 -
我们最后要做的就是创建一个循环,显示产品的名称。将以下代码添加到
list.phtml
文件中:<?php $_productCollection = $this->getLoadedProductCollection() ?> <div class="block"> <div class="block-title"> <h2><?php echo $this->getTitle() ?></h2> </div> <div class="block-content"> <p><?php echo $this->__('Category ID: %s', $this->getCategoryId()) ?></p> <ul> <?php foreach ($_productCollection as $_product): ?> <li><?php echo $_product->getName() ?></li> <?php endforeach; ?> </ul> </div> </div>
-
配置小部件以使用正确的模板和有效的类别 ID。之后,重新加载前端,您将看到以下截图所示的内容:
小贴士
您可以在后端导航到类别时找到类别 ID。导航到目录 | 管理类别,选择任何类别,您将看到名称附近的 ID。
它是如何工作的...
我们为小部件创建了一个自定义的块类。通过配置小部件的type
属性,所有此类小部件实例都将使用类型为productslider/catalog_product_list
的块。
当我们查看这个类时,我们看到这个类将扩展Mage_Catalog_Block_Product_List
类,这是用于为类别渲染产品列表的块。我们使用这个类,这样我们就可以使用标准函数而不是编写自己的函数。
我们接下来要做的是使小部件可以选择两个模板。这是通过在配置小部件的<template>
参数上完成的。
模板在配置小部件页面的布局更新部分进行配置。此表单是模板文件中布局 XML 配置的图形实现。
当我们保存小部件时,Magento 将在数据库中为小部件创建一个布局更新。此布局更新存储在core_layout_update
表中。
当我们查看模板文件时,我们看到$this->getTitle()
函数用于填充块的标题标签。此函数将输出为块的title
参数设置的任何数据。
在配置中,我们创建了一个<title>
配置,当块创建时将在后台执行setData('title')
。
第二个配置参数是<category_id>
字段。当在这个块上设置分类 ID 时,$this->getLoadedProductCollection()
函数将返回给定分类 ID 的产品,这正是我们在这个情况下需要的。
创建自定义配置参数
小部件已创建。它在前端显示,并为给定的分类 ID 显示了正确的产品。
要配置分类 ID,我们必须从分类页面复制它并粘贴到文本框中。为了提高可用性,我们将在配置字段中创建一个自定义小部件,我们可以从弹出窗口中选择一个分类。
准备工作
为了做好准备,查看如何在后端为目录分类链接小部件配置分类。我们将为之前菜谱中创建的小部件配置相同的弹出窗口。
如何操作...
执行以下步骤,你可以创建一个自定义配置参数:
-
当我们查看目录分类链接小部件时,我们看到它们使用一个自定义小部件来选择分类——我们将在我们的模块中做同样的事情。
-
在
widget.xml
文件中,将<category_id>
配置参数替换为以下代码:<category_id> <label>Category</label> <type>label</type> <helper_block> <type>adminhtml/catalog_category_widget_chooser</type> <data> <button translate="open"> <open>Select Category...</open> </button> </data> </helper_block> <required>1</required> <sort_order>20</sort_order> <visible>1</visible> </category_id>
-
清除缓存并重新加载组件配置页面。当你点击配置参数按钮时,你会看到如下截图:
-
当你选择一个分类,保存小部件,清除缓存,并重新加载主页,你会看到小部件显示了错误的产品。
-
当你检查选择分类…按钮并导航到一些元素以上的隐藏字段时,你会看到其值类似于
category/<category_id>
。 -
小部件需要分类 ID,即斜杠后面的数字。我们现在有了用于生成 URL 的分类路径。为了解决这个问题,我们可以执行以下操作:
-
使用字符串函数从路径中获取 ID
-
确保在组件配置页面设置了正确的 ID
-
-
我们将采用第二种方法,因为它是最稳定的方法。
-
要修复这个问题,我们必须更新在组件配置页面显示的分类小部件选择器中的某些代码。我们将创建一个新的类,该类扩展了当前的小部件选择器。我们将把这个类添加到
app/code/local/Packt/Productslider/Block/Adminhtml/Category
目录下的Widget
文件夹中。 -
在这个文件夹中,创建一个名为
Chooser.php
的文件,内容如下:<?php class Packt_Productslider_Block_Adminhtml_Catalog_Category_Widget_Chooser extends Mage_Adminhtml_Block_Catalog_Category_Widget_Chooser { /** * Block construction * Defines tree template and init tree params */ public function prepareElementHtml(Varien_Data_Form_Element_Abstract $element) { $uniqId = Mage::helper('core')->uniqHash($element- >getId()); $sourceUrl = $this->getUrl('adminhtml/ productslider_catalog_category_widget/chooser', array('uniq_id' => $uniqId, 'use_massaction' => false)); $chooser = $this->getLayout()- >createBlock('widget/adminhtml_widget_chooser') ->setElement($element) ->setTranslationHelper($this->getTranslationHelper()) ->setConfig($this->getConfig()) ->setFieldsetId($this->getFieldsetId()) ->setSourceUrl($sourceUrl) ->setUniqId($uniqId); if ($element->getValue()) { $categoryId = $element->getValue(); if ($categoryId) { $label = Mage::getSingleton('catalog/ category')->load($categoryId)->getName(); $chooser->setLabel($label); } } $element->setData('after_element_html', $chooser->toHtml()); return $element; } public function getNodeClickListener() { if ($this->getData('node_click_listener')) { return $this->getData('node_click_listener'); } if ($this->getUseMassaction()) { $js = ' function (node, e) { if (node.ui.toggleCheck) { node.ui.toggleCheck(true); } } '; } else { $chooserJsObject = $this->getId(); $js = ' function (node, e) { '.$chooserJsObject.'.setElementValue (node.attributes.id); '.$chooserJsObject.'.setElementLabel(node.text); '.$chooserJsObject.'.close(); } '; } return $js; } }
-
在上一步中,我们创建了一个新的块类来处理所选分类的返回值。在这一步中,我们将配置配置页面使用新的块。在模块的
widget.xml
文件中,将parameters/category_id/helper_block/type
标签从adminhtml/catalog_category_widget_chooser
更改为productslider/adminhtml_catalog_category_widget_chooser
。widget.xml
文件现在将如下所示:<?xml version="1.0" encoding="UTF-8"?> <widgets> <category_product_slider type="productslider/catalog_product_list"> <name>Category product slider</name> <description>Displays the products for a given category in a slider widget</description> <parameters> <title> <label>Title (frontend)</label> <type>text</type> <required>1</required> <sort_order>10</sort_order> <visible>1</visible> </title> <category_id> <label>Category</label> <type>label</type> <helper_block> <type>productslider/adminhtml_catalog_category_ widget_chooser</type> <data> <button translate="open"> <open>Select Category...</open> </button> </data> </helper_block> <required>1</required> <sort_order>20</sort_order> <visible>1</visible> </category_id> <template> <label>Frontend template</label> <type>select</type> <values> <list translate="label"> <value>productslider/category-product- slider/list.phtml</value> <label>List template</label> </list> <teaser translate="label"> <value>productslider/category-product- slider/teaser.phtml</value> <label>Teaser template</label> </teaser> </values> <sort_order>30</sort_order> <visible>1</visible> </template> </parameters> </category_product_slider> </widgets>
-
我们需要一个自定义控制器操作来处理弹出窗口的 AJAX 调用。为了初始化模块的 admin 控制器,将以下代码添加到模块的
config.xml
文件中。将其粘贴为<config>
标签的子标签:<admin> <routers> <adminhtml> <args> <modules> <productslider before="Mage_Adminhtml"> Packt_Productslider_Adminhtml</productslider> </modules> </args> </adminhtml> </routers> </admin>
-
在
app/code/local/Packt/Productslider/Adminhtml/Productslider/Catalog
下的Category
文件夹中,创建一个WidgetController.php
文件,并在其中包含以下内容:<?php require_once('Mage/Adminhtml/controllers/Catalog/Category/WidgetController.php'); class Packt_Productslider_Adminhtml_Productslider_Catalog_Category_WidgetController extends Mage_Adminhtml_Catalog_Category_WidgetController { protected function _getCategoryTreeBlock() { return $this->getLayout()->createBlock('productslider/ adminhtml_catalog_category_widget_chooser', '', array( 'id' => $this->getRequest()->getParam('uniq_id'), 'use_massaction' => $this->getRequest() ->getParam('use_massaction', false) )); } }
-
清除缓存并加载配置页面。在弹出窗口中选择一个分类,并检查隐藏字段,就像我们在第 5 步中所做的那样。你会看到分类 ID 被设置为数字而不是路径。
-
保存小部件,清除缓存,并重新加载主页。你会看到显示的是配置分类的正确产品。
它是如何工作的...
在这个配方中,我们创建了一个自定义配置参数。我们这样做是为了更好的用户体验。
首先,我们配置了一个现有的配置小部件,在点击字段时显示一个分类弹出窗口。这并不困难,因为唯一的工作量是在widget.xml
文件中配置正确的设置。
但这个小部件并不是我们想要的。前端表示是好的,但在后台,返回了一个格式错误的分类 ID。
为了解决这个问题,我们创建了一个自定义配置字段,它扩展了之前的配置字段。我们只更改了必要的部分以格式化正确的输出。
因为小部件正在使用 AJAX 调用,我们不得不创建一个自定义控制器,它扩展了标准控制器以显示正确的块。在那个控制器中,我们包含了父控制器的相对路径,因为这个类不在 Magento 的自动加载器中。
还有更多...
我们在这个配方中配置了小部件参数,以展示如何为配置字段创建自定义 HTML 输出。
技术上,你可以用配置字段做最不可能的事情;然而,你需要知道的是,你必须在一个具有命名约定<input name="parameter[<parameter_name>]" />
的输入字段中设置配置值。
将<parameter_name>
标签替换为你的自定义参数名称,表单将像处理所有其他参数一样处理你的配置参数。
完成主题化
我们在前端制作的小部件不会邀请我们购买一些产品。它只是一个包含产品名称的分类列表。
在这个菜谱中,我们将完成小部件的主题设计。我们将创建一个 HTML 输出,显示给定产品的图片、名称和价格。
使用 jQuery 插件,我们将 HTML 输出转换为轮播图,以便我们可以滚动浏览产品。
准备工作
在互联网上搜索一个好的 jQuery 轮播插件。在这个菜谱中,我们将使用 caroufredsel.dev7studios.com/
。
如何操作...
以下步骤描述了完成小部件的最后一系列操作:
-
让我们生成一个适用于 jQuery 插件的良好 HTML。将以下代码添加到
list.phtml
模板中:<?php $_productCollection = $this->getLoadedProductCollection() ?> <div class="block-image-slider"> <div class="block-title"> <h2><?php echo $this->getTitle() ?></h2> </div> <div class="productslider-container"> <div id="productslider-<?php echo $this- >getCategoryId() ?>"> <?php foreach ($_productCollection as $_product): ?> <div class="slider-item"> <a href="<?php echo $_product->getProductUrl() ?>" title="<?php echo $this->stripTags($this-> getImageLabel($_product, 'small_image'), null, true) ?>" class="carousel-image"> <img src="<?php echo $this->helper('catalog/image') ->init($_product,'small_image')->resize(250); ?>" width="250" height="250" alt="<?php echo $this- >stripTags($this->getImageLabel($_product, 'small_image'), null, true) ?>" /></a> <div class="slider-item-content"> <p> <?php echo $_product->getName() ?> </p> <?php echo $this->getPriceHtml($_product) ?> </div> </div> <?php endforeach; ?> </div> <div class="productslider-controls"> <a id="btn-prev-<?php echo $this->getCategoryId() ?>" class="btn-prev" href="#"><</a> <a id="btn-next-<?php echo $this->getCategoryId() ?>" class="btn-next" href="#">></a> <div id="pager-<?php echo $this->getCategoryId() ?>" class="pager"></div> </div> </div> </div>
-
创建一个 CSS 文件以设置所需的样式。在
skin/frontend/base/default
下的css
文件夹中添加一个productslider.css
文件,内容如下:.productslider-container {width:700px;background-color:#DDD;} .productslider-container .slider-item {width:250px;padding:20px;margin:20px;background-color:#BBB;float:left;clear:none;} .productslider-container .productslider-controls {overflow:hidden;} .productslider-container .productslider-controls .btn-prev {float:left;} .productslider-container .productslider-controls .btn-next {float:right;} .productslider-container .productslider-controls .pager {text-align: center;} .productslider-container .productslider-controls .pager a {margin:0 10px;}
-
通过添加以下代码将 CSS 文件添加到 Magento 的头部。使用此代码,CSS 文件仅在页面上配置了小部件时才被添加。在
_prepareLayout()
函数中的app/code/local/Packt/Productslider/Block/Catalog/Product/List.php
:protected function _prepareLayout() { $this->getLayout()->getBlock('head')->addCss('css/productslider.css'); parent::_prepareLayout(); }
-
保存所有文件,清除缓存,并重新加载前端。你将看到一个样式化的输出,我们可以将其转换为滑块。
-
下一步是将 jQuery 轮播脚本添加到产品列表中。我们将使用以下脚本
caroufredsel.dev7studios.com/
。 -
下载源文件,解压,并将文件夹和内容粘贴到
skin/frontend/base/default/js
文件夹中。 -
通过更新
app/code/local/Packt/Productslider/Block/Catalog/Product/List.php
文件中的_prepareLayout()
函数来链接 CSS 和 JavaScript 文件。用以下代码替换函数:protected function _prepareLayout() { $this->getLayout()->getBlock('head')->addCss('css/productslider.css'); $this->getLayout()->getBlock('head')->addItem('skin_js', 'js/carouFredSel-6.2.1/jquery.carouFredSel-6.2.1- packed.js'); parent::_prepareLayout(); }
小贴士
确保你的主题中启用了 jQuery。你可以在 第二章 主题 中找到有关说明。
-
最后一步是为产品列表元素初始化脚本。将以下 JavaScript 代码添加到
list.phtml
模板的底部:<script type="text/javascript"> jQuery(document).ready(function(){ jQuery("#productslider-<?php echo $this-> getCategoryId() ?>").carouFredSel({ width: '100%', prev: '#btn-prev-<?php echo $this->getCategoryId() ?>', next: '#btn-next-<?php echo $this->getCategoryId() ?>', pagination: '#pager-<?php echo $this-> getCategoryId() ?>' }); }); </script>
-
清除缓存并重新加载前端。你已经完成了,输出结果将如以下截图所示:
它是如何工作的...
在初始步骤中,我们为模板创建了一个良好的 HTML 输出。此输出为循环中的每个产品生成缩放图像、标题和价格。
为了主题化 HTML 输出,我们使用了一个 CSS 文件,该文件仅在为给定页面配置了小部件时才被包含。当所有块初始化时,会调用 _prepareLayout()
函数。在这个函数中,我们添加一个 CSS 文件,稍后添加一个 JavaScript 文件到网站的头部。
为了显示带有动画的产品滑块,我们添加了一些 JavaScript 代码,将滑块添加到产品列表中。
第十一章。性能优化
在本章中,我们将涵盖以下主题:
-
探索网站的极限
-
优化数据库和 MySQL 配置
-
优化 Apache 网络服务器
-
调整 Magento 配置
-
配置 APC 和 Memcached
-
优化 PHP 配置
-
分析页面速度
简介
对于运动员来说,秒、毫秒或千分秒可能决定他们是否能赢得比赛。每一个提高性能的小方面都是赢得比赛正确方向上的一步。
对于网站来说,情况相同。您的 Magento 商店越快,效果越好。在性能方面,Magento 不是最有效的系统之一。尤其是当您处理大量产品和属性时,良好的设置可以提高您的网店性能。
网站的性能对您的访客有很大影响。以下是一些关于这个陈述的事实:
-
页面速度对搜索引擎如 Google 的页面排名有显著影响
-
在电子商务网站上,每一秒都意味着销售额总量的 1% 的损失
-
当您减少页面大小时,您可以从搜索引擎中获得更多访客。
如您所见,性能是提高您网站 SEO 的关键部分。当网站表现不佳时,客户会离开您的网站。
既然您已经完成了开发,是时候看看整个系统的性能了。在本章中,我们将使用许多工具来探索、检测和修复 Magento 网上商店的性能泄漏。
一些工具是作为 Magento 标准版提供的,但我们必须全面考虑。两个相同的 Magento 安装可能会有不同的页面速度结果,这可能是以下原因造成的:
-
硬件
-
网络
-
负载
-
客户端设备
探索网站的极限
您需要了解的第一件事是当前网站的局限性。我的网站有多少容量?是什么减慢了我的网站?我们必须对所有这些方面有一个全面的了解,以便得出哪种优化效果最显著的结论。
在这个菜谱中,我们将看到一些有助于创建性能视图的工具。以下是一些工具的列表:
-
ApacheBench
-
Siege
-
Magento Profiler
-
YSlow
-
PageSpeed
-
GTmetrix
-
WebPagetest
我们将对所有这些工具进行一些测试,以找出 Magento 商店的潜在问题。有了这些结果,我们可以找出哪些优化效果最显著。
一些优化非常简单,可以产生很大的效果。其他优化则比较困难,效果可能较小。决定权在您手中。
准备工作
首先,建议安装以下工具,以便对 Magento 安装进行一些性能测试:
-
ApacheBench (ab):此工具与 Apache 网络服务器自动安装。当您有权访问命令行界面时,您可以在 Linux 服务器上运行以下命令:
ab -h
-
Siege:我们将使用安装在同一个服务器上的 Siege 进行一些性能测试。为了查看它是否已安装,你可以运行以下命令:
siege -V
-
在这里,请确保
V
是大写字母。当 Siege 安装后,你将看到其版本号,如下面的截图所示: -
如果你使用的是基于 Debian 的 Linux 发行版,并且它尚未安装,你可以运行以下命令:
sudo apt-get install siege
-
第二种选择是下载安装文件,并按照以下步骤进行安装:
-
要下载存档,请运行以下命令:
wget ftp://ftp.joedog.org/pub/siege/siege-latest.tar.gz
-
要提取存档,请运行以下命令:
tar xzf siege-latest.tar.gz
-
将文件夹移动到首选位置并更改目录。
-
使用以下命令安装 Siege:
sudo ./configure
-
-
-
Magento 配置文件:这是 Magento 中的一个标准功能。你可以在系统配置中启用它。
-
YSlow:这是一个可以从附加组件商店下载和安装的 Firefox 插件。
因为GTMetrix和WebPagetest是在线工具,所以我们不需要安装它们。
如何做...
-
为了了解在并发用户负载下的响应时间,我们可以使用 ApacheBench。我们将使用以下命令进行测试,并将结果保存到 CSV 文件中:
ab -c 10 -n 50 -e apachebench.csv http://magento-dev.local/
-
在上一个命令中,我们使用以下参数进行了负载测试:
-
-c
:此参数表示并发用户数。在这个测试中,始终有 10 个请求同时运行。 -
-n
:此参数表示请求数量。在这种情况下是 50,所以我们将有 50 个结果在文件中。 -
-e
:此参数表示输出文件。输出将写入给定的 CSV 文件。小贴士
-g
选项与-e
选项含义相同,但-g
会生成一个.tsv(制表符分隔值)文件,也称为 gnuplot 文件。
-
-
当你运行命令时,一个带有结果的输出屏幕将被打印出来,如下面的截图所示:
此报告显示了测试的一般统计数据。具体结果保存在 CSV 文件中。
-
当你运行
ab -h
命令时,你会看到所有可用的选项。 -
使用 Siege 进行负载测试。
Siege 是另一个类似于 ApacheBench 的负载测试工具。Siege 与 ApacheBench 的区别在于,Siege 比 ApacheBench 有更多功能。它被设计用来进行多个并发用户的压力测试。它具有与 HTTP 身份验证、cookies、会话等一起工作的能力。当你编写一个好的脚本时,你可以模拟一个真实的压力情况。
-
对于使用 Siege 进行的负载测试,我们将使用一个文本文件,在其中我们将设置一些在 Siege 负载测试期间将使用的 URL。使用不同的 URL 进行测试更好,因为你可以测试你网站上更多的页面,这意味着你可以找到更多的陷阱。创建一个
siege_url.txt
文件,并将以下内容粘贴到其中:-
http://magento-dev.local/
-
http://magento-dev.local/skin/frontend/default/default/images/bkg_body.gif
-
http://magento-dev.local/skin/frontend/default/default/images/logo.gif
-
http://magento-dev.local/skin/frontend/default/default/images/ph_callout_left_top.gif
-
http://magento-dev.local/skin/frontend/default/default/images/ph_callout_left_rebel.jpg
-
http://magento-dev.local/skin/frontend/default/default/images/home_main_callout.jpg
-
http://magento-dev.local/index.php/electronics.html
-
http://magento-dev.local/index.php/electronics.html?cat=12
-
http://magento-dev.local/index.php/checkout/cart/add/product/46/
-
http://magento-dev.local/index.php/checkout/cart/
-
http://magento-dev.local/skin/frontend/default/default/images/i_shipping.gif
-
http://magento-dev.local/skin/frontend/default/default/images/i_discount.gif
-
http://magento-dev.local/skin/frontend/default/default/images/btn_trash.gif
-
http://magento-dev.local/skin/frontend/default/default/images/bkg_th.gif
-
http://magento-dev.local/skin/frontend/default/default/images/i_msg-success.gif
-
http://magento-dev.local/skin/frontend/default/default/images/btn_checkout.gif
-
http://magento-dev.local/index.php/checkout/onepage/
-
http://magento-dev.local/js/varien/accordion.js
-
http://magento-dev.local/skin/frontend/base/default/js/opcheckout.js
-
http://magento-dev.local/skin/frontend/default/default/images/opc-ajax-loader.gif
-
http://magento-dev.local/skin/frontend/default/default/images/cvv.gif
-
http://magento-dev.local/index.php/checkout/onepage/progress/?toStep=billing
-
http://magento-dev.local/index.php/electronics/cameras/accessories/universal-camera-case
-
http://magento-dev.local/index.php/tag/product/list/tagId/185/
-
http://magento-dev.local/index.php/tag/product/list/tagId/27/
-
http://magento-dev.local/index.php/customer/account/login/
-
-
您可以更改 URL,使其与您的 Magento 配置匹配。确保域名与您的配置匹配,并且 URL 存在。
-
运行以下命令以启动具有 50 个并发用户的负载测试:
siege -c50 -i -t 1M -d 3 -f siege_url.txt
-
此脚本运行的周期取决于网店的性能。命令的输出如下所示:
~$ siege -c50 -i -t 1M -d 3 -f siege_url.txt ** SIEGE 2.70 ** Preparing 50 concurrent users for battle. The server is now under siege... Lifting the server siege... done. Transactions: 2001 hits Availability: 100.00 % Elapsed time: 59.48 secs Data transferred: 0.49 MB Response time: 0.00 secs Transaction rate: 33.64 trans/sec Throughput: 0.01 MB/sec Concurrency: 0.03 Successful transactions: 38 Failed transactions: 0 Longest transaction: 0.04 Shortest transaction: 0.00
使用 Siege
-h
命令,您可以看到所有可用选项。 -
我们继续配置 Magento Profiler。要启用 Magento Profiler,请打开后台并导航到 系统 | 配置 | 高级 | 开发者 | 调试。配置 Profiler 字段,如以下屏幕截图所示:
-
要查看更多统计信息,请在
index.php
文件中取消以下行的注释:Varien_Profiler::enable();
-
当您重新加载前端时,您将看到以下屏幕截图所示的网格:
-
在前面的屏幕截图中,您可以查看每个执行函数的执行时间和内存使用情况。这可以帮助您找到性能泄漏。
-
接下来,我们将安装 YSlow 到 Firefox 中。
- YSlow 是一个与 Firebug 一起安装的 Firefox 插件
-
要打开 YSlow,可以通过按F12键或点击附加组件栏中的图标来打开 Firebug。
-
在 Firebug 启动后,打开 YSlow 标签页。
-
确认规则集下拉菜单设置为YSlow(V2)。
-
通过点击运行测试按钮开始测试。
-
YSlow 执行一些测试,然后显示如下截图所示的测试结果:
它是如何工作的...
我们从 ApacheBench 开始,这是一个与 Apache Web 服务器一起安装的工具。此工具旨在对 HTTP 请求进行基准测试。
我们使用多个并发用户进行了测试。当你用 10 个并发用户进行测试时,你将模拟测试期间持续的 10 个请求的负载。当一个请求完成时,会触发一个新的请求,所以总是有 10 个进程在运行。
当你将测试时间限制为 30 秒时,你可以看到在这段时间内你的 Apache 服务器完成了多少个成功的请求。这可以给你一个关于你的 Web 服务器容量的良好印象。
Siege 是一个与 ApacheBench 类似的工具。主要区别在于 Siege 比 ApacheBench 有更多的选项。在这个菜谱中,我们使用 URL 列表进行了负载测试。在这个列表中,你可以通过创建 POST 请求将产品添加到购物车中,以模拟网络商店上的人类流量。通常,产品页面是缓存的,但特定于会话的流程,如结账,不能缓存,这将在你的服务器上产生更多的负载。
小贴士
使用 ApacheBench 和 Siege,你可以在网站上创建负载。在远程网站上执行此操作时,你可能会因为来自同一主机的许多请求看起来像攻击而被防火墙阻止。
一个网页的性能取决于许多因素。YSlow 捆绑了最重要的因素,并使用这些因素进行测试。参数捆绑在规则集中。我们使用的规则集是包含大多数检查的 YSlow (V2)。你可以选择其他一些或创建自己的规则集。
当测试运行时,YSlow 将检查规则集中所有的因素。对于每个因素,它都会给出一个分数。所有分数的平均值乘以每个因素的权重。权重取决于每个因素对网站性能的影响。
在 YSlow 生成的报告中,针对每个特定部分提出了改进建议。选择哪种选项来优化你的网站取决于你。
还有更多...
当你的网站在公共 URL 上时,你可以使用一些在线工具来显示你网站性能的报告。这些工具将查看你的网站并生成报告。它们会提出一些建议,就像 YSlow 做的那样:
-
WebPagetest:这个工具可以在
www.webpagetest.org/
找到。 -
GTmetrix:这个工具可以在
gtmetrix.com/
找到。
优化数据库和 MySQL 配置
当扩展 Magento 网站时,您将有一个部分是件困难的工作,那就是 MySQL 数据库。您可以使用多个服务器来扩展您的商店,但数据库是网站所有数据的中央存储,因为它需要同步,所以并不容易扩展。在这个食谱中,我们将优化 Magento MySQL 数据库和 MySQL 服务器。
准备工作
登录 phpMyAdmin 并导航到显示数据库信息的下一页。
如何操作...
第一步是优化 Magento 数据库的表结构。请查看以下步骤:
-
当您在 phpMyAdmin 中时,点击 Magento 数据库,您将看到表概览。
-
在页面底部,点击全选按钮。
-
当您点击下拉列表时,您可以修复表并优化它,如下面的截图所示:
小贴士
确保您为 Magento 数据库中的所有表运行此操作。列表可能会被分隔并在多页中展示。
-
当您修复并优化了所有内容后,您的 MySQL 数据库已优化。您还可以使用与我们在第五章中配置主/从设置食谱中所做的相同方式,通过主/从设置来复制数据库。
第二步是优化 MySQL 服务器。按照以下步骤优化 MySQL 服务器:
-
一个好的服务器配置始于良好的硬件和正确的操作系统。为了运行 Magento,建议使用专用服务器或 VPS。当您使用共享环境时,RAM 和 CPU 负载将由其他用户共享。使用 VPS 或专用服务器,您有固定数量的 CPU 和固定数量的 RAM 可用。
-
当您的服务器配备了足够的 RAM 时,您可以关闭交换设备。有时,即使有足够的内存,交换选项也会自动使用。
-
在
my.cnf
文件中,在[mysqld]
部分下,使用skip-external-locking
参数来避免外部锁定。 -
以下命令用于在
MyISAM
表中设置key_buffer
的正确值。要查看配置,请在 MySQL 提示符下运行以下命令:mysql> SHOW VARIABLES LIKE '%key_buffer%';
-
我们将设置键缓冲区大小为 512 MB。我们可以在 MySQL 提示符下运行以下命令来完成此操作:
mysql> SET GLOBAL key_buffer_size = 536870912;
-
我们将在
my.cnf
文件中进行以下优化设置。这是 MySQL 服务器的主要配置文件,位于app/etc/mysql
文件夹中:-
打开文件,在
[mysqld]
部分下粘贴以下配置:[mysqld] key_buffer = 512M max_allowed_packet = 64M thread_stack = 192K thread_cache_size = 32 table_cache = 512 query_cache_type = 1 query_cache_size = 52428800 tmp_table_size = 128M expire_logs_days = 10 max_binlog_size = 100M sort_buffer_size = 4M read_buffer_size = 4M read_rnd_buffer_size = 2M myisam_sort_buffer_size = 64M wait_timeout = 300 max_connections = 400
-
保存文件并重新启动您的 MySQL 服务器。您可以通过运行以下命令来完成此操作:
sudo service mysql restart
-
它是如何工作的...
在优化 MySQL 服务器时,你必须了解你服务器的功能和预期的流量。有了这些参数,你可以计算出key_buffer
、query_cache
和table_cache
的良好值。还要考虑skip-external-locking
参数。使用此参数,你可以拒绝外部锁定。
使用以下命令,你可以查看 MySQL 服务器状态:
命令 | 更多信息 |
---|---|
mysql> SHOW STATUS; | 此命令显示 MySQL 服务器的当前状态。此命令在 MySQL 5.0 及以上版本中可用,并且是显示所有全局变量的标准命令。 |
mysql> SHOW VARIABLES; | 此命令显示所有 MySQL 变量。 |
mysql> SHOW INNODB STATUS; | 此命令让你了解当前的INNODB 状态。 |
mysql> SHOW GLOBAL STATUS; | 此命令显示数据库服务器对所有连接的负载值。 |
mysql> SHOW LOCAL STATUS; | 这与SHOW GLOBAL STATUS 命令相同,但值是在当前连接上计算的。 |
mysql> mysqladmin extended -i100 -r | 使用此命令查看 MySQL 服务器当前正在发生的情况。 |
数据库优化是调整你的 Magento 网店的关键方面之一。数据库优化占页面加载的 50%。
优化 Apache 网络服务器
当运行 Magento 网站时,Magento 推荐使用 Apache 网络服务器。市场上还有一些其他网络服务器,如 Nginx,但最初 Magento 是针对 Apache 进行优化的。
网络服务器的性能主要取决于服务器运行的硬件。在选择服务器时,网卡、RAM、硬盘、操作系统和 CPU 是重要的硬件考虑因素。
如何做...
-
首先要考虑的是网络服务器将运行的操作系统。强烈建议使用 Linux 发行版。在这本书的食谱中,我们使用了 Ubuntu 服务器(基于 Debian 的 Linux 发行版)。
小贴士
不要使用 Windows 服务器来运行 Magento。它可以工作,但效率较低,可能会导致文件权限、代码等问题。
-
将操作系统更新到最新稳定版本,因为更新软件更快更安全。使用 Apache2 而不是 Apache1 系列。Apache2 在提供静态内容时使用更少的 CPU 资源。
-
在网络服务器上仅安装所需的服务。当安装了很多服务时,你将会有一些后台任务会使用服务器的资源。
-
使用 XFS 和 ReiserFS 作为文件系统以获得更好的磁盘 I/O。
-
你必须配置网络服务器,使其永远不会需要交换。当你的网络服务器开始交换时,所有请求都将变慢。首先,比较服务器上的 RAM 容量与请求的平均内存负载和请求数量。你可以做的第二件事是配置
MaxClients
设置。此设置控制服务器交换时的子进程数量。 -
查看
HostnameLookups
设置并检查是否已配置为Off
值。 -
不建议使用
SymLinksIfOwnerMatch
设置。对于指定的目录,最好使用Options
+FollowSymLinks
+SymLinksIfOwnerMatch
。对于其他位置,使用Options
+FollowSymLinks
设置以防止系统调用lstat(2)
。lstat()
系统调用永远不会被缓存。 -
不要在
DirectoryIndex
设置中使用通配符语法。设置如下所示:DirectoryIndex index
-
最好只使用
index.php
,因为 Magento 使用index.php
作为默认索引。此设置如下所示:DirectoryIndex index.php
-
使用以下命令启用
deflate
和header
Apache 模块:sudo a2enmod deflate sudo a2enmod header
-
在 Magento 根目录中打开
.htaccess
文件并转到mod_deflate
配置标签。取消注释一些行,使块看起来像以下代码:<IfModule mod_deflate.c> ############################################ ## enable apache served files compression ## http://developer.yahoo.com/performance/rules.html#gzip # Insert filter on all content SetOutputFilter DEFLATE # Insert filter on selected content types only AddOutputFilterByType DEFLATE text/html text/plain text/xmltext/css text/javascript # Netscape 4.x has some problems... BrowserMatch ^Mozilla/4 gzip-only-text/html # Netscape 4.06-4.08 have some more problems BrowserMatch ^Mozilla/4\.0[678] no-gzip # MSIE masquerades as Netscape, but it is fine BrowserMatch \bMSIE !no-gzip !gzip-only-text/html # Don't compress images SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary # Make sure proxies don't deliver the wrong content Header append Vary User-Agent env=!dont-vary </IfModule>
小贴士
在更改后遇到内部服务器错误时,可能是 Apache
headers
模块未启用。运行sudo a2enmod header
命令并重新启动服务器以修复此问题。 -
查看您的 Apache 服务器的
KeepAlive
设置。当此设置为开启时,Apache 服务器可以通过相同的 TCP 连接处理多个请求。 -
为您的案例配置 MPM(多处理模块)。这些配置的值取决于您期望服务器上的资源和负载。
StartServers 50 MinSpareServers 15 MaxSpareServers 30 MaxClients 225 MaxRequestsPerChild 4000
-
当您再次运行一些负载测试时,可以将获得的结果与优化前的结果进行比较。通常,您会看到一些差异。
它是如何工作的...
Web 服务器的性能取决于许多因素。关键部分是应用程序、硬件、操作系统和网络。
-
应用程序:第一件事是确保您的应用程序能够高效地使用服务器的资源。如果您的应用程序对请求期望大量资源,您可以在这里进行优化。
-
硬件:第二项是服务器的硬件。您必须确保服务器的资源足够高,能够处理预期的负载和峰值。
-
操作系统:第三项是操作系统和 Web 服务器。使用带有 Apache 或 Nginx Web 服务器的 Linux 服务器来托管 Magento。始终使用软件的最新版本,因为它通常更快且更安全。
-
网络:最后一项是网络。Web 服务器通过网络将响应发送到客户端。当网络速度慢时,请求的下载时间会很长。将您的 Web 服务器托管在具有良好网络连接的地方,并将其托管在与您的网店目标受众相同的地区。例如,为意大利网站托管在意大利。
优化您所使用的系统配置。在这个菜谱中,我们对 Apache Web 服务器进行了一些优化,通过在 .htaccess
文件中定义自定义设置来实现。
最后一点是你的 Web 服务器的地理位置。最好将你的 Web 服务器放置在目标受众所在的地区。此外,限制 HTTP 请求的大小和数量。
调整 Magento 配置
Magento 提供了一些标准功能以提升性能,例如缓存和编译。在本教程中,我们将配置这些功能以优化设置的性能。
准备工作
登录管理面板并导航到系统 | 配置页面。
如何操作...
当你执行以下步骤时,你的 Magento 安装将运行得更快:
-
卸载或禁用你未使用的扩展。
-
启用 Magento 拥有的所有缓存系统。它们位于系统 | 缓存管理。
小贴士
当所有缓存都启用时,确保你在开发代码时清除它们。
-
编译你的 Magento 安装。你可以在系统 | 工具 | 编译页面点击运行编译过程来完成。当编译启用时,所有 Magento 类将在一个目录中构建,这将使它们加载更快。编译可以将速度提高 25%到 50%。
-
禁用未使用的模块。你可以通过导航到系统 | 配置 | 高级 | 禁用模块输出来禁用前端输出。当你不使用
Mage_Poll
模块时,你可以在那里禁用它。 -
禁用一些开发者功能,因为在实际网站上你不需要它们。前往系统 | 配置 | 高级 | 开发者并按照以下截图进行配置:
-
前往目录 | 属性 | 管理属性。当你点击一个属性时,你可以看到前端属性部分。仅设置你希望在前端使用的配置参数为是。
-
前往系统 | 配置 | 目录并启用扁平目录产品和平坦目录分类设置,如图所示。当启用时,EAV 系统的一部分将被转换为扁平表:
注意
你可以在第五章的使用 EAV 表食谱中找到有关扁平目录设置的更多信息。
工作原理...
标准的 Magento 配置并不是最佳性能配置。你启用的每个选项都会对开发灵活性和与平台和模块的兼容性产生影响。
当缓存启用时,你将获得更好的性能。然而,你必须记住,在开发代码时,你需要经常清除缓存,以便看到你的更改效果。
开发功能在编写代码时很有用,因为它包含有趣的信息。然而,当您的网站上线时,您不需要这些功能,因为它会减慢您的网站速度。建议您禁用此功能。
编译是一个过程,其中 Magento 将将 app/code
文件夹中的所有类复制到 includes
文件夹。当所有类都位于一个文件夹中时,加载一个类比在所有不同的模块文件夹中查找一个类要快。
编译的优势是您的网站将更快,但缺点是您必须在代码的每次更改后运行编译过程。当您使用不遵循 Magento 标准的模块时,启用编译可能会导致错误。
最后一步是启用扁平目录产品和扁平目录分类。当您启用这些功能时,Magento 将使用扁平表来加载产品和分类,而不是在多个 EAV 表中查找。
使用此方法的缺点是您必须从 EAV 表同步数据到扁平表。当处理大量产品和分类时,同步数据的索引过程会持续很长时间。
配置 APC 和 Memcached
Magento 的缓存系统基于 HTML 输出的缓存部分和一些配置文件。在本菜谱中,我们将配置 PHP 级别的缓存,您可以使用 APC 和 Memcached 来实现。APC 将缓存 PHP 文件,而 Memcached 将缓存对象。
准备工作
当为我们的 Magento 店铺配置 APC 和 Memcached 时,我们必须确保服务器上已安装 APC。
要安装 APC,请按照以下说明操作:
-
在基于 Debian 的 Linux 发行版上,您可以使用以下命令安装 APC:
sudo apt-get install php-apc
-
按照安装程序操作,APC 将会在您的 PHP 配置中可用。
-
要安装 Memcached,您可以运行以下命令:
sudo apt-get install php5-memcached
-
要测试扩展是否正确安装,您可以使用
phpinfo()
命令。此命令以图形方式显示所有可用的 PHP 设置。 -
要查看
phpinfo
,创建一个包含以下内容的phpinfo.php
文件:<?php phpinfo();
当您在 APC 和 Memcached 上搜索时,您将看到一个包含所有设置的块。
如何操作...
在以下步骤中,我们将讨论 Magento 中的 APC 和 Memcached 配置:
-
确保服务器上已安装 APC 和 Memcached。这在本菜谱的上一节中已有描述。
-
打开
app/code/local.xml
文件,并将一个<cache>
块作为<global>
标签的子标签添加。您的local.xml
文件如下所示:<?xml version="1.0"?> <config> <global> <install> <date><![CDATA[Mon, 22 Jul 2013 18:12:54 +0000]]></date> </install> <crypt> <key><![CDATA[cac0174a5563885b926aa73723aeb870]]></key> </crypt> <disable_local_modules>false</disable_local_modules> <resources> <db> <table_prefix><![CDATA[]]></table_prefix> </db> <default_setup> <connection> <host><![CDATA[localhost]]></host> <username><![CDATA[root]]></username> <password><![CDATA[root]]></password> <dbname><![CDATA[magento_dev]]></dbname> <initStatements><![CDATA [SET NAMES utf8]]></initStatements> <model><![CDATA[mysql4]]></model> <type><![CDATA[pdo_mysql]]></type> <pdoType><![CDATA[]]></pdoType> <active>1</active> </connection> </default_setup> </resources> <session_save><![CDATA[files]]></session_save> <cache> <backend>apc</backend> </cache> </global> <admin> <routers> <adminhtml> <args> <frontName><![CDATA[admin]]></frontName> </args> </adminhtml> </routers> </admin> </config>
-
清除所有 Magento 缓存并重新启动您的 Apache 服务器。当您使用 ApacheBench 进行负载测试时,您将看到性能有显著提升:
ab -c 5 -n 100 http://magento-dev.local
-
之前的配置启用了 Magento 的 APC 配置。通过下一个配置,我们启用 Memcached。我们通过将
<cache>
标签作为<global>
标签的子标签来实现这一点。使用 Memcached,local.xml
文件将如下所示:<?xml version="1.0"?> <config> <global> <install> <date><![CDATA[Mon, 22 Jul 2013 18:12:54 +0000]]></date> </install> <crypt> <key><![CDATA[cac0174a5563885b926aa73723aeb870]]></key> </crypt> <disable_local_modules>false</disable_local_modules> <resources> <db> <table_prefix><![CDATA[]]></table_prefix> </db> <default_setup> <connection> <host><![CDATA[localhost]]></host> <username><![CDATA[root]]></username> <password><![CDATA[root]]></password> <dbname><![CDATA[magento_dev]]></dbname> <initStatements><![CDATA [SET NAMES utf8]]></initStatements> <model><![CDATA[mysql4]]></model> <type><![CDATA[pdo_mysql]]></type> <pdoType><![CDATA[]]></pdoType> <active>1</active> </connection> </default_setup> </resources> <session_save><![CDATA[files]]></session_save> <cache> <backend>memcached</backend> <slow_backend>database</slow_backend> <memcached> <servers> <server> <host><![CDATA[127.0.0.1]]></host> <port><![CDATA[11211]]></port> <persistent><![CDATA[1]]></persistent> <weight><![CDATA[2]]></weight> <timeout><![CDATA[10]]></timeout> <retry_interval><![CDATA[10]]></retry_interval> <status><![CDATA[1]]></status> </server> </servers> <compression><![CDATA[0]]></compression> <cache_dir><![CDATA[]]></cache_dir> <hashed_directory_level><![CDATA[]]> </hashed_directory_level> <hashed_directory_umask><![CDATA[]]> </hashed_directory_umask> <file_name_prefix><![CDATA[]]></file_name_prefix> </memcached> </cache> </global> <cache> <backend>apc</backend> </cache> <admin> <routers> <adminhtml> <args> <frontName><![CDATA[admin]]></frontName> </args> </adminhtml> </routers> </admin> </config>
-
最后一件事情是将
var/cache
文件夹存储在内存中。我们可以通过使用 TMPFS 挂载文件夹来实现这一点:mount tmpfs /var/www/packt/magento-dev/var/cache -t tmpfs -o size=64m
它是如何工作的...
APC(替代 PHP 缓存)是一个开源的缓存框架,它优化 PHP 代码,编译它,并在共享内存中缓存它。执行预编译的代码比加载所有脚本要快。APC 的一个常见问题是,你必须清除 APC 缓存才能看到任何代码更改,因为旧代码的一些部分被缓存了。
Memcached 是 PHP 中的另一个缓存系统。Memcached 将缓存以后将在应用程序中使用的对象。例如,数据库对象、会话和 API 调用。Memcached 在服务器上运行,即在这个例子中,与 web 服务器相同的服务器。通过设置 Magento 配置,我们连接到运行在端口 11211 的本地 Memcached 服务器。
在 Magento 中,Memcached 提高了性能。在前端,有一些效果,但最大的效果是在后端,因为 Magento 本身缓存的东西非常少。
最后一件事情是将 Magento 的 cache
文件夹挂载到内存中。当有足够的内存可用时,将缓存文件存储在 RAM 中会更快。当它们位于 RAM 中时,加载缓存文件比从磁盘读取它要快。
优化 PHP 配置
当你不使用 APC 等加速器时,考虑使用 PHP 加速器;我们将在本菜谱中看到如何做到这一点。当请求 PHP 脚本时,脚本将被读取,然后它将被编译成二进制,这被称为操作码。这个编译后的代码将由 PHP 引擎执行。
操作码缓存只是一个简单的缓存机制,它保存编译后的代码,这样代码就不必每次脚本运行时都进行编译。在这个菜谱中,我们将优化 php.ini
设置以获得最佳性能。
准备工作
市场上有很多 PHP 加速器,如 APC、eAccelerator、XCache、Zend Accelerator 和 Zend Platform。在以下 URL 中,你可以找到有关这些加速器的更多信息:
-
APC:这个 PHP 加速器可以在
pecl.php.net/package/APC
找到 -
eAccelerator:这个 PHP 加速器可以在
eaccelerator.net/
找到 -
XCache:这个 PHP 加速器可以在
xcache.lighttpd.net/
找到
如何做到这一点...
以下步骤描述了一些你可以采取的措施来改进 PHP 配置:
-
使用一个高效的进程管理器,如 php-fpm,它在 FastCGI 上运行速度惊人。
-
使用
realpath_cache_size
配置设置来配置 PHP 中真实路径缓存的大小。在 PHP 打开和关闭大量文件的系统上,此值需要增加。您可以为 Magento 使用以下设置:realpath_cache_size=1M realpath_cache_ttl=86400
-
下表中设置的设置可以改善 PHP 设置的性能:
设置 描述 推荐值 max_execution_time
此设置设置进程可以存活的最大时间(以秒为单位)。 120 max_input_time
此属性设置脚本等待输入数据的时间(以秒为单位)。 240 memory_limit
使用此设置,您可以设置进程可以消耗的内存量。 对于 Magento,建议使用 216 MB。 output_buffering
使用此设置,您可以设置在向客户端发送响应之前缓冲的字节数。 4096 -
最后,当您的网站处于活动状态时,禁用一些错误报告级别。可以通过以下设置来完成:
error_reporting = E_COMPILE_ERROR|E_ERROR|E_ CORE_ERROR
它是如何工作的...
php.ini
配置文件中的值主要取决于您正在运行的应用程序以及该应用程序将对您的系统产生的负载。如果您的应用程序有一些将运行很长时间的过程(当您有大量产品时,重新索引过程就是这样),则需要增加max_execution_time
和max_input_time
的值。对于memory_limit
参数也是如此,对于 Magento,建议使用 216 MB。
在生产系统上禁用错误报告对于警告和通知是推荐的,但关键错误需要报告,因为解决错误时需要这些信息。
分析页面速度
YSlow 是一个分析网页速度的工具。它是一个可以安装到 Firebug 的插件。在运行报告时,网站将在一些参数上进行测试。将显示一个包含您网站总体得分的报告。
测试基于一组规则。您有 YSlow (V2) 和 YSlow (V1) 规则集。我们将使用 YSlow(V2) 规则集进行本食谱,它包含 22 条规则。如果您遵守以下 22 条规则,完整的页面加载(包括图像和静态内容)可以提高 25 到 50%:
-
最小化 HTTP 请求
-
使用内容分发网络
-
避免空的
src
或href
-
添加过期头或缓存控制头
-
为组件使用 Gzip
-
将样式表放在顶部
-
将脚本放在底部
-
避免使用 CSS 表达式
-
使 JavaScript 和 CSS 外部化
-
减少 DNS 查找
-
压缩 JavaScript 和 CSS
-
避免重定向
-
删除重复的脚本
-
配置 ETags
-
使 AJAX 可缓存
-
使用 GET 进行 AJAX 请求
-
减少 DOM 元素的数量
-
无 404 错误
-
减少 cookie 大小
-
使用无 cookie 域名进行组件
-
避免过滤器
-
不要在 HTML 中缩放图像
-
使
favicon.ico
文件小并可缓存
如何做到...
执行以下步骤以查看我们可以使用 YSlow 结果做什么:
-
在当前网页上运行 YSlow 测试。在标准的 Magento 网上商店中,测试通常返回一个总体 C 级分数。
-
第一件事是减少 HTTP 请求。你可以通过启用 CSS 和 JS 合并来解决这个问题。为了获得最佳分数,建议使用精灵图,这样你只有一个背景图片。
-
为了优化背景图片,你可以使用像 Yahoo 的 Smush.it 这样的图片优化工具。当你上传你的主题的
images
文件夹并运行它时,你可以下载如以下截图所示的优化版本: -
你可以下载优化后的图片,并在
skin
文件夹中替换它们。 -
测试中的下一个缺点是添加过期头。我们可以通过修改
.htaccess
文件中的某些规则来解决这个问题。 -
以下代码展示了如何向网站的静态内容添加
expire
头:<IfModule mod_expires.c> ############################################ ## Add default Expires header ## http://developer.yahoo.com/performance/rules.html#expires ExpiresDefault "access plus 1 year" ExpiresDefault ExpiresActive On ExpiresByType image/gif ExpiresByType image/jpg ExpiresByType image/jpeg ExpiresByType image/png ExpiresByType image/x-icon ExpiresByType text/css ExpiresByType application/x-javascript </IfModule> ############################################
-
以下代码将压缩一些文件类型:
<IfModule mod_deflate.c> ############################################ ## enable apache served files compression ## http://developer.yahoo.com/performance/rules.html#gzip # Insert filter on all content SetOutputFilter DEFLATE # Insert filter on selected content types only AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript # Netscape 4.x has some problems... BrowserMatch ^Mozilla/4 gzip-only-text/html # Netscape 4.06-4.08 have some more problems BrowserMatch ^Mozilla/4\.0[678] no-gzip # MSIE masquerades as Netscape, but it is fine BrowserMatch \bMSIE !no-gzip !gzip-only-text/html # Don't compress images SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary # Make sure proxies don't deliver the wrong content Header append Vary User-Agent env=!dont-vary </IfModule>
-
最后一步是使用 CDN 网络托管你的静态图片。你可以使用现有的 CDN 提供商来托管你网站的静态文件,但你也可以创建一个子域名
static.magento-dev.local
,它也指向 Magento 根目录,并配置 Magento 使用此子域名来托管静态内容。你可以在后台通过导航到 系统 | 配置 | 常规 | Web 来完成此操作,如下面的截图所示:
它是如何工作的...
YSlow 的所有 22 条规则的文档都可以在他们的网站上找到。如果你想了解更多关于某个规则的信息,请访问包含所有信息的以下页面:
developer.yahoo.com/performance/rules.html
第十二章。调试和单元测试
在本章中,我们将涵盖:
-
开始使用 Xdebug
-
使用 FirePHP 调试
-
安装 PHPUnit
-
创建一个 Magento 测试用例
简介
以高效的方式调试网站是 PHP 开发者最重要的工作之一。如今,一个网站远不止一些简单的 HTML 页面。在 Magento 网上商店中,你有很多复杂的业务逻辑,这些逻辑用于电子商务交易流程中。
PHP 中的调试不像其他编程语言那样直接。有许多方法可以配置 PHP 调试器(Xdebug)与一个好的代码编辑器,如 NetBeans。通过一些额外的工具,如 FirePHP 和 Zend Wildfire 插件,调试变得更加容易。
调试的另一个部分是自动化测试。在处理对象和函数时,有许多方法可以编写一些可以在一组配置的函数上运行的测试。报告将显示有关通过和失败的测试的信息。
开始使用 Xdebug
调试 PHP 应用程序最常见的方式是使用一些函数,如 echo
、die()
、var_dump()
和 print_r()
。在 Magento 中,你可以使用 Mage::log()
语句将一些日志打印到文件,但这并不是真正的调试器。
使用真正的调试器,你可以中断脚本并查看变量及其值。你还可以更改值、进一步执行、跳过语句等。
在 PHP 中,你可以配置 Xdebug 来调试你的 PHP 脚本或应用程序。在本食谱中,我们将了解如何在开发环境中安装 Xdebug,以及我们如何将其与 IDE 集成。
准备工作
在本食谱中,我们将使用 NetBeans IDE 启动一个 Xdebug 会话。打开 NetBeans 并将 Magento 项目设置为 主项目。确保在项目的 属性 设置中正确配置了所有 URL。
如何做...
以下步骤展示了你如何在开发服务器上安装 Xdebug:
-
我们将使用 PHP 的
pear
库来安装 Xdebug。确保它已安装。如果没有,请运行以下命令:sudo apt-get install php5-dev sudo apt-get install php-pear
-
下一步是安装
xdebug
库。你可以使用以下命令来完成:sudo pecl install xdebug
-
此命令将给出以下输出:
-
如截图所示,我们必须在
php.ini
文件中定位xdebug.so
文件。要找出xdebug.so
文件的路径,请运行以下命令:find / -name 'xdebug.so'
-
当你有了路径后,在
php.ini
文件中添加以下行。使用以下命令打开文件:sudo nano /etc/php5/apache2/php.ini
-
同样,对
cli/php.ini
文件也做相同的操作:sudo nano /etc/php5/cli/php.ini
-
在末尾粘贴以下代码行:
zend_extension="/usr/lib/php5/20100525/xdebug.so"
确保路径与服务器上
xdebug.so
文件的路径匹配。 -
使用以下命令重新启动 Apache 服务器:
sudo service apache2 restart
-
要测试 Xdebug 是否正确安装,你可以在浏览器中使用
phpinfo()
查看它,或者你可以运行以下命令来检查phpinfo()
页面:php -i | grep xdebug
-
之前的命令将给出以下输出:
xdebug xdebug support => enabled xdebug.auto_trace => Off => Off xdebug.cli_color => 0 => 0 xdebug.collect_assignments => Off => Off xdebug.collect_includes => On => On xdebug.collect_params => 0 => 0 xdebug.collect_return => Off => Off xdebug.collect_vars => Off => Off xdebug.coverage_enable => On => On xdebug.default_enable => On => On xdebug.dump.COOKIE => no value => no value xdebug.dump.ENV => no value => no value xdebug.dump.FILES => no value => no value xdebug.dump.GET => no value => no value xdebug.dump.POST => no value => no value xdebug.dump.REQUEST => no value => no value xdebug.dump.SERVER => no value => no value xdebug.dump.SESSION => no value => no value xdebug.dump_globals => On => On xdebug.dump_once => On => On xdebug.dump_undefined => Off => Off xdebug.extended_info => On => On xdebug.file_link_format => no value => no value xdebug.idekey => no value => no value xdebug.max_nesting_level => 100 => 100 xdebug.overload_var_dump => On => On xdebug.profiler_aggregate => Off => Off xdebug.profiler_append => Off => Off xdebug.profiler_enable => Off => Off xdebug.profiler_enable_trigger => Off => Off xdebug.profiler_output_dir => /tmp => /tmp xdebug.profiler_output_name => cachegrind.out.%p => cachegrind.out.%p xdebug.remote_autostart => Off => Off xdebug.remote_connect_back => Off => Off xdebug.remote_cookie_expire_time => 3600 => 3600 xdebug.remote_enable => Off => Off xdebug.remote_handler => dbgp => dbgp xdebug.remote_host => localhost => localhost xdebug.remote_log => no value => no value xdebug.remote_mode => req => req xdebug.remote_port => 9000 => 9000 xdebug.scream => Off => Off xdebug.show_exception_trace => Off => Off xdebug.show_local_vars => Off => Off xdebug.show_mem_delta => Off => Off xdebug.trace_enable_trigger => Off => Off xdebug.trace_format => 0 => 0 xdebug.trace_options => 0 => 0 xdebug.trace_output_dir => /tmp => /tmp xdebug.trace_output_name => trace.%c => trace.%c xdebug.var_display_max_children => 128 => 128 xdebug.var_display_max_data => 512 => 512 xdebug.var_display_max_depth => 3 => 3
-
下一步是配置 NetBeans 的 Xdebug 集成。为了使 NetBeans 与 Xdebug 一起工作,我们必须在
php.ini
文件末尾添加以下配置:xdebug.remote_enable=1 xdebug.remote_handler=dbgp xdebug.remote_mode=req xdebug.remote_host=localhost xdebug.remote_port=9000 xdebug.idekey="netbeans-xdebug"
-
重新启动您的 Apache 服务器,并查看
phpinfo()
页面以确认 Xdebug 设置是否已应用。 -
下一步是检查您的 NetBeans 调试设置。导航到 工具 | 选项 并按照以下截图进行配置:
-
下一步是检查项目 URL 是否具有正确的值。打开 项目属性 并转到 运行配置。确保 项目 URL 字段具有以下截图所示的正确值:
-
我们现在已准备好开始第一个调试会话。要开始它,我们必须点击靠近运行按钮的调试按钮。您也可以使用快捷键 Ctrl + F5。
-
当开始调试会话时,浏览器将添加一个新标签页,其 URL 形式如下:
http://magento-dev.local/?XDEBUG_SESSION_START=netbeans-xdebug
-
网页无法加载,因为调试器正在中断进程。要继续,我们必须使用 NetBeans 中的调试器控件。
-
当您在
index.php
文件的第 87 行添加断点Mage::run($mageRunCode, $mageRunType)
并继续使用调试器时,您将看到以下截图所示的变量值: -
当您继续使用断点时,您会看到页面将被加载。
-
调试会话将持续到您在 NetBeans 中点击停止按钮。当您浏览网站上的其他页面时,只要会话处于活动状态,调试器将继续运行。
-
要停止调试会话,请在 NetBeans 中点击停止按钮。一个浏览器页面将打开,显示会话已停止的消息。
它是如何工作的...
Xdebug 需要安装在您想要调试的服务器上。在这个菜谱中,使用的是本地调试服务器。
我们使用 PEAR 安装了 Xdebug。PEAR 是 PHP 插件的软件仓库。使用 PEAR,我们下载并安装了 xdebug
库。
当 Xdebug 在服务器上安装时,我们配置了 php.ini
文件以使用 xdebug
库。我们添加了一些设置以使 Xdebug 配置与 NetBeans 兼容。
小贴士
当在远程服务器上使用 Xdebug 时,请确保您可以通过端口 9000 连接到服务器。这通常在服务器和您的本地 PC 的防火墙中被禁用。
当服务器配置正确后,我们在 NetBeans 中检查了配置并开始调试会话。当会话开始时,我们能够像调试器一样调试 Magento 应用程序。
调试器启用了以下高级调试功能:
-
设置断点
-
逐句执行代码语句
-
跳过代码部分
-
浏览和更改变量名
使用 FirePHP 进行调试
FirePHP 是您可以在 Firefox 中安装的插件。它集成在 Firebug 控制台中,使其作为调试工具易于使用。在服务器上,FirePHP 也需要安装。我们可以通过 PEAR 仓库轻松完成此操作,就像我们在前面的食谱中做 Xdebug 一样。
要使用 FirePHP 进行调试,我们需要另一个 PHP 库。这是在 Zend Framework 中的 Zend Wildfire 插件。由于 Magento 是基于 Zend Framework 构建的,因此 Wildfire 插件是标准安装的。
准备中
FirePHP 是一个在 Firebug 中运行的插件。为了使它们正常工作,我们需要使用以下链接将 Firebug 和 FirePHP 作为 Firefox 插件安装:
addons.mozilla.org/us/firefox/addon/firebug/
addons.mozilla.org/us/firefox/addon/firephp/
如何操作...
以下步骤描述了如何在 Magento 项目中使用 FirePHP:
-
确保所有 Firefox 插件都已安装。
-
在您的 IDE(如 NetBeans)中打开您的 Magento 项目。
-
打开
index.php
文件,并在文件末尾添加以下代码:// ------------------------------------------------------ // Zend Wildfire log function // ------------------------------------------------------ /** * Logs variables to the Firebug Console * via HTTP response headers and the FirePHP Firefox Extension. * * @param mixed $var The variable to log. * @param string $label OPTIONAL Label to prepend to the log event. * @param string $style OPTIONAL Style of the log event. * @param array $options OPTIONAL Options to change how messages are processed and sent * @return boolean Returns TRUE if the variable was added to the response headers or buffered. * @throws Zend_Wildfire_Exception */ function logFirePHP($var, $label = 'Magento vars', $style = 'INFO', $options = array()) { if (Mage::getIsDeveloperMode()) { $httpRequest = new Zend_Controller_Request_Http(); $httpResponse = new Zend_Controller_Response_Http(); $channel = Zend_Wildfire_Channel_HttpHeaders::getInstance(); $channel->setRequest($httpRequest); $channel->setResponse($httpResponse); ob_start(); Zend_Wildfire_Plugin_FirePhp::send($var, $label, $style, $options); $channel->flush(); $httpResponse->sendHeaders(); } else { return null; } } // ------------------------------------------------------ // End Zend Wildfire function // ------------------------------------------------------
-
保存文件
index.php
。 -
启用 Magento Profiler 和 Magento 开发者模式。
-
我们已准备好 Magento 安装以与 FirePHP 一起使用。要通过它记录某些内容,我们可以使用
logFirePHP()
函数在日志中打印某些内容。打开文件app/design/frontend/base/default/template/catalog/product/view.phtml
。 -
在文件末尾添加以下代码:
<?php logFirePHP($_product->debug()) ?>
-
当您打开产品详情页面和 Firebug 控制台时,您应该得到一个类似于以下截图的输出:
它是如何工作的...
FirePHP 是一个在 Firebug 控制台中显示日志消息的日志工具。我们通过在 index.php
文件中添加的 logFirePHP()
函数来创建日志消息。
当使用 logFirePHP()
函数时,将在 HTTP 请求中添加一个标题。FirePHP 将捕获这些标题并在 Firebug 控制台中打印它们。
使用 logFirePHP()
函数,我们可以在第一个参数中设置日志消息。这可以是任何变量,例如字符串、数组、对象或其他内容。
第二个参数是将在 Firebug 控制台中显示的日志消息的名称。
最后一个参数是日志消息的类型。您可以使用以下选项显示消息:
-
LOG
-
INFO
-
WARN
-
ERROR
-
EXCEPTION
-
TRACE
-
TABLE
-
DUMP
-
GROUP_START
-
GROUP_END
安装 PHPUnit
当我们想在 PHP 中开始进行 单元测试 时,我们需要一个名为 PHPUnit 的工具。当 PHPUnit 安装完成后,我们可以在命令行中使用 phpunit
命令开始加载测试。
准备中
在安装 PHPUnit 之前,请确保您的服务器上已安装 PEAR。如果没有完成,您可以通过运行以下命令来完成此操作:
sudo apt-get install php-pear
您也可以使用文件来安装它。您可以通过导航到以下 URL 获取有关此过程的更多信息:
pear.php.net/manual/en/installation.php
如何做...
按照以下步骤在你的开发服务器上安装 PHPUnit:
-
要安装 PHPUnit,我们必须使用一个自定义的 PEAR 通道。在我们可以通过 PEAR 安装 PHPUnit 之前,我们需要注册 PHPUnit 的 PEAR 通道。使用以下命令,我们将添加额外的通道到 PEAR 环境中:
sudo pear channel-discover pear.phpunit.de
-
PHPUnit 通道依赖于一些其他通道。我们需要发现通道
components.ez.no
:sudo pear channel-discover components.ez.no
-
我们必须发现的最后一个通道是Symfony Components的 PEAR 通道。使用以下命令,我们将发现通道
pear.symfony-project.com
:sudo pear channel-discover pear.symfony-project.com sudo pear channel-discover pear.symfony.com
-
当我们发现了所有必需的通道后,是时候安装
PHPUnit
库了。我们可以通过运行以下命令来完成:sudo pear install phpunit/PHPUnit
-
此命令的结果将以以下行结束:
install ok: channel://pear.phpunit.de/File_Iterator-1.3.4 install ok: channel://pear.phpunit.de/Text_Template-1.1.4 install ok: channel://pear.phpunit.de/PHP_Timer-1.0.5 install ok: channel://pear.phpunit.de/PHP_TokenStream-1.2.1 install ok: channel://pear.phpunit.de/PHP_CodeCoverage-1.2.13 install ok: channel://pear.phpunit.de/PHPUnit_MockObject-1.2.3 install ok: channel://pear.symfony.com/Yaml-2.3.6 install ok: channel://pear.phpunit.de/PHPUnit-3.7.28
-
当运行此命令时,安装程序将下载并准备所有必需的包,以便你可以使用它们。
-
最后要做的事情是安装 PHP 的 CLI 组件。这很可能已经安装了;如果没有安装,我们必须运行以下命令:
sudo apt-get install php5-cli
-
最后要做的事情是测试我们安装的每一项是否正常工作。为此,我们可以运行命令
phpunit --version
:$ phpunit –version PHPUnit 3.7.28 by Sebastian Bergmann.
它是如何工作的...
作为 xUnit 的一部分,PHPUnit 是 PHP 单元测试中最受欢迎的框架。xUnit 框架包含许多编程语言的单元测试框架,例如 Java 的 JUnit。
要安装 PHPUnit 库,我们使用了 PEAR 安装程序(PHP 的扩展)来下载和安装额外的插件。要添加更多插件,我们可以像安装 PHPUnit 时那样添加额外的通道到 PEAR。
还有更多...
当你想在不使用 PEAR 的情况下安装PHPUnit
库时,你可以按照以下步骤进行:
-
首先要做的事情是下载你可以在以下 URL 找到的库文件
pear.phpunit.de/get/
。 -
在
php.ini
配置中定义的include_path
函数中提取文件。 -
下一步是配置 PHPUnit 脚本。首先将
phpunit.php
文件移动到phpunit
。 -
打开文件,将字符串
@php_bin@
替换为你的 PHP bin 路径。通常,这是/usr/bin/php
。 -
将文件复制到你的 Linux
PATH
中的位置。确保脚本可执行。 -
最后要做的事情是在
PHPUnit/Util/PHP.php
文件中更改一些内容。打开此文件,并将@php_bin@
字符串替换为你的 PHP bin 路径。通常,这是/usr/bin/php
。 -
现在,你可以运行
phpunit
命令。
创建一个 Magento 测试用例
对于本章的最后一个小菜谱,我们将使用 PHPUnit 编写一个自动化测试。PHPUnit 是我们上一章安装的单元测试框架。
单元测试是测试驱动开发(TDD)的关键部分。使用 TDD,我们将首先编写测试用例,然后编写返回我们在测试用例中定义的预期值的代码。
基于 Zend 框架并使用 TDD 构建的 Magento,可以编写用于自定义或现有模块的 PHPUnit 单元测试。在这个食谱中,我们将看到执行自定义单元测试所需的所有步骤。
准备工作
在这个食谱中,我们将为我们在第四章、创建模块、第六章、数据库和模块、第七章、Magento 后端和第八章、事件处理器和 Cron 作业中创建和扩展的Packt_Helloworld
模块创建一个单元测试。
如果您没有完整的代码,您可以从 Packt 出版社网站www.packtpub.com
下载。
如何操作...
按照以下步骤为 Magento 创建单元测试:
-
在您的 Magento 根目录中创建一个名为
unit-tests
的文件夹。 -
在
unit-tests
文件夹中创建一个名为autoload.php
的文件。 -
打开
autoload.php
文件,并在其中添加以下内容:<?php ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . dirname(__FILE__) . '/../app' . PATH_SEPARATOR . dirname(__FILE__)); //Set custom memory limit ini_set('memory_limit', '512M'); //Include Magento libraries require_once 'Mage.php'; //Start the Magento application Mage::app('default'); //Avoid issues "Headers already send" session_start();
-
在
unit-tests
文件夹中创建一个名为phpunit.xml
的文件,内容如下:<?xml version="1.0" encoding="UTF-8" ?> <phpunit backupGlobals="false" backupStaticAttributes="false" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" syntaxCheck="true" bootstrap="./autoload.php"> <testsuite name="Magento Unit Test"> <directory>./</directory> </testsuite> <filter> <whitelist> <directory>../app/code/local</directory> </whitelist> </filter> </phpunit>
-
下一步是创建一个如以下截图所示的文件夹树:
小贴士
除了手动创建所有文件夹外,您还可以在终端中运行以下 Linux 命令:
mkdir -p app/code/local/Packt/Helloworld/Model/
。 -
在
app/code/local/Packt/Helloworld/Model/
文件夹中,创建一个名为SubscriptionTest.php
的新 PHP 文件。 -
在
SubscriptionTest.php
文件中,添加以下内容:<?php class SubscriptionTest extends PHPUnit_Framework_TestCase { protected $_subscriptionInstance; public function setUp() { echo 'Start unit test for method: ' . $this->getName(); Mage::app('default'); $this->_subscriptionInstance = Mage::getModel('helloworld/subscription'); } protected function tearDown() { } public function testGetAllSubscriptions() { $subscriptions = $this->_subscriptionInstance->getCollection(); //Check if $subscriptions is instance of the collection class $this->assertInstanceOf('Packt_Helloworld_Model_Resource_Subscription_Collection', $subscriptions); } }
-
要开始单元测试,打开终端并使用以下命令导航到您的 Magento 安装的
unit-tests
目录:cd /var/www/magento-dev/unit-tests
-
在
unit-tests
文件夹中,运行phpunit
命令。这将给出以下截图所示的输出: -
当您看到消息
OK (1 test, 1 assertion)
时,测试已经通过。
它是如何工作的...
我们通过创建单元测试所需的全部文件夹来开始这个食谱。我们创建了以下文件:
-
autoload.php
-
phpunit.xml
-
SubscriptionTest.php
phpunit
命令的引导配置在phpunit.xml
文件中。在这个文件中,在命令的开始处,我们可以配置一些重要的参数。我们已配置了加载文件autoload.php
和一些全局变量和错误报告变量的其他值。
在phpunit.xml
文件中,我们首先配置了autoload.php
文件以便运行它。在这个文件中,我们设置了包含路径为 Magento 应用程序的路径、一些 PHP 设置以及Mage.php
文件的包含。
单元测试本身是在 SubscriptionTest.php
文件中编写的。在这个文件中,我们创建了一个继承自 PHPUnit_Framework_Testcase
类的类。这个父类包含了单元测试的所有逻辑和通用函数。
在 setup()
函数中,我们可以编写一些代码来初始化测试。在这个例子中,我们创建了一个订阅集合的实例。
单元测试位于 testGetAllSubscriptions()
方法中。phpunit
命令将会运行该类中所有的 test*
函数。在这个函数中,我们使用了 assertInstanceOf()
函数来检查类的类型是否与第一个参数中设置的值匹配。
还有更多...
在这个例子中,我们使用了断言函数 assertInstanceOf()
,它会检查变量的实例类型。PHPUnit 中还有许多其他的断言函数可供使用。例如,一个比较数字和检查空值的函数。
断言方法的完整列表可以在以下网址找到:
phpunit.de/manual/3.7/en/writing-tests-for-phpunit.html#writing-tests-for-phpunit.assertions