你现在的位置:首页 > PHP网站建设知识库 > PrestaShop > 正文

PrestaShop模块开发教程

介绍

很多人对于Prestashop模块的开发,多少有些迷茫,这主要是因为它缺少文档。但是别怕--这个系列的目的就是向开发PHP的人讲述如何开发Prestashop模块。它将会讲述怎样开发一个属于我们自己的功能模块, 这个模块体现了我们通常见到的模块所具有的功能。它还为我们提供一个可以用于自己代码的基础模版。在后续的系列中,我们会看到如何创建一个支付模块,如何扩展Administration界面。 在开始阶段,我们还是专注于模块类的开发, 因为它主要负责在你的页面显示东东, 这是Prestashop开发的基本。

\

开始之前

这个系列的文章是假定你有一个php 5.x.x的基本知识和它的面向对象编成方面的概念。(译者注, 其实不用担心,我之前也没有学习过PHP) 强烈建议你搭建一个Prestashop开发环境,这样你就可以看到Prestashop如何做到页面的配置。 就这篇文章的目的来说,我们要开发是一个基于当前发布版本(写这篇文章的时候是1.2.0.6)的示例。(译者注:我测试的版本是1.2.0.8)。但是我们的代码会写得比较通用一点,保证它与Prestashop的所有版本兼容,即使是1.1版之前的。如果你熟悉Prestashop使用的第三方工具和类库,会对理解这些文章有很大帮助。我特别建议你熟悉下smarty template引擎的基本知识。要注意到,Prestashop的核心就是处理这些模板的初始化,所以特别要了解得的就是模板变量的赋值,模板的创建(和展示),模板的修饰符(modifier)和函数(函数)。

PrestaShop的架构

理解Prestashop modules是怎么运作,根本上就是看网站页面是怎么显示的。Prestashop的框架大致上的为MVC,一个"Modle", 一个"View",和一个"Controller"。虽然它不是完全遵循,但这对我们理解程序是怎么运作的有好处。

Models

Prestashop中的Model是通过扩展基类ObjectModel来实现的。ObjectModel类定义了关于的DB表的通用操作。在提供创建、读取、更新、删除操作的同时,它还实现了数据校验、多语言处理等。通过扩展这个基础功能类,创建各自的model来管理店铺数据。Prestashop的model都放在 /classes 目录下,用来处理DB的各类数据(比如:分类 Category, 产品 Product,订单 Order,配置 Configuration 等等)。关于Model的约定是,每个类class放在一个php文件里,文件名和它其中的类名一致。

Views

我们这个购物车网站的数据展示用的是smarty template引擎。使用view可以达到业务逻辑和页面展示的分离。在严格的MVC环境中,view是不进行任何数据的处理,仅仅是显示从controller传过来的数据。大多数情况下Prestashop是这样的,除了有一些Ajax/Javascript进行数据的处理。Prestashop的view文件通常放在 /themes 目录下,和对应的template文件一起,可供管理页面挑选。许多module把自己的template放在自己的目录结构下。

Controller

Controller是一个或一些页面的主要业务逻辑所在。它负责与Model进行交互,获取数据,对数据进行业务逻辑的处理,然后输出结果到一个或多个页面。在Pretashop里,它们置于安装目录的根目录,对应就是网站的主要页面。

OK,那module好处在哪呢?

讲过了Prestashop的功能核心,已经足够我们开发出一个购物车了,就添加功能来说的确是很方便。比如,你想基于上面的框架修改店面首页,你无须直接修改controller页面。否则就代码维护来讲,这很麻烦(当有一个新版本发布时,你要亲手修改之前修改过的所有controller页面,到处跟踪调试),并且这需要对系统有相当的了解。

对一个没有技术背景的人来说,创建一个易扩展、可维护的系统的购物车系统,需要把各项功能采用插入--“挂载(hooked)”--方式添加到上面说的框架中去,通过controller代码中添加的各种挂载点(hook)。换个角度讲就是用和可安装的插件(Prestashop里称为模块(module))。这样,这些模块就具备了显示其它的或者被你修改了的内容,具备了添加新功能(业务逻辑)到controller中,甚至搜集信息的功能。

支付模块是一个特别的模块,但它为了提供用户更多选择,也是挂载到标准的controller里。至于管理面板的扩展完全是另一码事,它更像是开发controller,需要单独来谈。感谢Prestashop的模块框架,给我们在管理界面添加module带来相当大的便利性,总之,在大多除非特别复杂情况下,只是最好的实现方式。

综述

读了这篇文章,你应该对在页面创建基本的区块来装点你的店铺有个了解。在下一篇中,我们会看看基于这些概念怎么开发我们的第一模块,以扩展Prestashop。
 
第二篇 创建一个简单模块
介绍

在这个系列的第二篇里,我们来看看如何创建自己的第一个Prestashop模块,并且能够在后台操作这个模块。

模块类

与基类ObjectModel类似,所有的Prestashop模块都扩展自一个为它们定义了基本功能的通用类。Module类提供了管理界面和模块之间的接口,还提供国际化和挂载管理功能。

代码约定

在我们开始写代码前,有一个要点,那就是模块类、目录和文件名的命名。

在Prestashop里,模块都必须放在你的店铺安装根目录下的 /modules 目录下面。而且,模块目录和类文件读必须是模块类名的小写。注意,模块类名本身不用小写(按照一般约定采用大小写混写的方式“Camel case”, 译者注:比如ShoppingCart),其根本就是放置模块的陌路和文件要遵循这个约定。

一个“空”模块

在这个系列教程的这一部分,我们要创建一个“TutorialFirst”模块,它包含了创建一个最初级的模块的必要代码。第一步是创建模块目录,命名为“tutorialfirst”。然后是在这个目录下创建模块的主类文件。使用下面的代码:
<?php
class TutorialFirst extends Module
{
function __construct()
{
$this->name = 'tutorialfirst';

parent::__construct();

$this->tab = 'phpstudio.info Tutorials';
$this->version = '0.1.0';
$this->displayName = $this->l('First Tutorial Module');
$this->description = $this->l('Our very first module - it does absolutely nothing at all.');
}
}
// End of: tutorialfirst.php
?>

把文件保存为模块目录下的“tutorialfirst.php”,把它放到服务器上去。虽然这些代码看起来什么也不是,但是它的确是包含了某些功能,这要归功于它扩展的基类。一旦你把他上传到服务器上的开发测试站点上,你就可以在服务器的模块列表上看到一个模块分组“phpstudio.info Tutorials”,而我们的第一个模块就在这个分组下面。这是你可以安装并卸载它了。

看看我们在这段代码里做了什么吧,还有为什么这么做。
$this->name = 'tutorialfirst';
parent::__construct();

类构造器的头两行是设置模块的基本属性,以确保能够正确地继承到基类里的必要功能。首先是设置了模块的内部名称(译者注:相对系统开发而言)。要注意到它同样遵循了模块目录名和主类文件名的约定,还是类名的小写形式。只有这一步完成后我们才能放心的调用父类构造函数,让它帮我们设置必要的模块其它内部属性。

之后是定义需要用来在后台显示我们的模块的属性。
$this->tab = 'phpstudio.info Tutorials';
$this->version = '0.1.0';
$this->displayName = $this->l('First Tutorial Module');
$this->description = $this->l('Our very first module - it does absolutely nothing at all.');
$this->tab 成员变量定义了我们希望这个模块显示在后台模块列表中的哪个分类里。通常情况下我们应该选标准的分类(比如‘Advertisement’, ‘Products’, ‘Tools’, ‘Blocks’ 这些)。但怎么设置这个属性完全是你自己的事。在这个系列的黎子里,我选择把它们归在“phpstudio.info Tutorials”分类下。

$this->version 成员变量是让我们用来在模块列表里显示模块的当前版本,让用户能够可视化地分辨他们使用的模块的版本。当我们使用外部的一些外部模块时,这也对我们区分版本是否陈旧过时非常有用。

最后的两个成员变量是用来在模块列表里显示我们的模块的,让它具有相当的自述性。要注意到他们使用的是Module类的 $this->l() 函数,并不是直接赋一个静态的字符串。

l() (来自基类) 成员函数 实现了多语言支持,使得店主能够自己完成翻译。我们希望显示的文本的英文版本直接传给了这函数,只有这样,店主才能通过后台的Tools->Translations页面将它翻译成自己选择的语言。这样包装模块中的静态文本可使得它支持翻译功能,并且我们模块里的任何非语言独立的静态文本都需要采用这种处理方式。

综述

读完这篇文章,你应该可以创建一个能够显示在Prestashop后台并被操作的新模块了,甚至显示的是店主自己的语言。在下一讲中,我们将会看到如何扩展我们的第一个模块,并支持模块的后台配置,以及基于后台配置的前台页面显示
 
第三篇 保存模块配置
介绍

在这个系列的第三部分,我们来看看怎样把模块的配置保存到Prestashop数据库里去,看看用户如何通过操作这些数据改变模块的行为。我们还会初步接触到,通过改变模块的配置来生成不同的输出。
译者注:如果你安装过模块,你会看到模块里有些配置项,这里所说的配置就是那个

管理用户设置

我们把接下来的这个模块命名为“TutorialSecond”。所以还得创建一个新的模块目录和类文件,分别是“tutorialsecond” 和 “tutorialsecond.php”。类文件(tutorialsecond.php) 需要包含下面的代码:
<?php
class Tutorialsecond extends Module
{
private $_html = '';

function __construct()
{
$this->name = 'tutorialsecond';
parent::__construct();

$this->tab = 'phpstudio.info Tutorials';
$this->version = '0.1.0';
$this->displayName = $this->l('Second Tutorial Module');
$this->description = $this->l('Our second module - A "Hello world" redux');
}

public function getContent()
{

}

private function _displayForm()
{

}

}
// End of: tutorialsecond.php
?>

你也许会注意到我们实现了两个新的成员函数::getContent() 和 ::_displayForm()。如果你把文件安装到服务器上,你会在模块列表里看到模块“Second Tutorial Module”有个新的选项。这个新选项是一个“>> Configure”链接,显示在模块项中,虽然你点它只是在后台返回一个近乎空白的页面。成员函数::getContent()的作用就是为模块提供后台接口(页面)。

除了这两个方法,我们还增加了私有成员变量$_html private,待会我们用它来构建必要的后台输出页面。

保存和读取配置数据

Prestashop有一个“Configuration”类,它提供了几个操作配置数据的函数,但常用的是两个主要函数:

Configuration::updateValue($key, $values, $html = false);
Configuration::get($key, $id_lang = NULL);

Configuration::updateValue() 函数用来存储配置项到数据库里(在多语言环境中,$values为数组), Configuration::get()函数用来读取配置,以某个选择的语言或店铺默认语言。目前,我们先忽略那些有默认值的参数,但会在下篇关于表单验证的文章中的Configuration::updateValue()函数再次用到 $html 参数。

实现配置页面

在我们的源文件里,我们创建了一个私有成员函数_displayForm()。尽管这个方法完全是可选的,我们可以把所有的页面代码放在getContent()成员函数中,但我们还是推荐把它分离出来,便于代码维护。我们要在这个方法里创建一个表单,用类接受用户输入。
private function _displayForm()
{
$this->_html .= '
<form action="'.$_SERVER['REQUEST_URI'].'" method="post">
<label>'.$this->l('Message to the world').'</label>
<div class="margin-form">
<input type="text" name="our_message" />
</div>
<input type="submit" name="submit" value="'.$this->l('Update').'" class="button" />
</form>';
}

你可以看到,::_displayForm()函数只是简单地把标准的html表单代码添加到 $_html 成员变量中,表单的target是$_SERVER['REQUEST_URI']。当Post提交表单时,Presstashop的后台框架会为我们将把这个请求自动定向到类成员函数::getContent。

下一步是在::getContent()函数添加代码,以显示表单和处理提交。
public function getContent()
{
if (Tools::isSubmit('submit'))
{
Configuration::updateValue($this->name.'_message', Tools::getValue('our_message'));
}

$this->_displayForm();

return $this->_html;
}

函数::getContent()首先使用Prestashop工具类“Tools”的方法检测是否在处理action为POST的提交,又或者是通过其他方式调用到的,比如在模块列表里点击“Configure”链接,这时的参数就是我们赋给表单的“update”按钮的名称。

如果这个函数被直接从后台调用(这种情况下Tools::isSubmit(’submit’)返回的是false),就会直接调用前面创建的表单绘制函数,并把结果赋给$this->_html变量以在后台显示。

如果这个函数是通过表单POST请求调用的,我们就可以把表单上输入的配置参数存到数据库。这里再一次用到了Tools类来获取表单变量,方法是Tools::getValue("our_message"),其中"our_message"是表单上的input元素的名字。

你会看到我把模块名称加在了配置项的前头 — 这是使用它作为这些配置的命名空间来确保它们在整个店铺中是唯一的。

在存完数据之后,表单还是会显示的,如果需要的话,可以多输入几次。

在页面显示模块内容

现在我们有了一个可以获取用户输入并存储DB的方法,显然下面是要拿它来做点什么,比如在页面上显示出来。在第一步里,我们谈到了“hooks”与模块添加功能有关的。要用到这点,我们需要告诉Prestashop我们的模块需要挂载到前台页面,那就是使用下面的函数(基类Module里定义的):
$this->registerHook($hook_name);

参数 $hook_name 是指Prestashop内核允许模块添加内容或者数据处理的各个点。目前,我们用一个比较常用的 — “leftColumn’,它让我们在所有页面左侧添加内容。要实现挂载,我们需要在自己的类里象下面这样覆盖一个方法:
public function install()
{
parent::install();

if (!$this->registerHook('leftColumn'))
return false;
}

它告诉Prestashop,在绘制所有页面的左侧时执行我们模块的挂载功能。下一步是添加一个方法处理挂载回调。函数名的约定是在挂载点名的前面加上“hook”:
public function hookLeftColumn()
{
return '<div class="block"><h4>'. Configuration::get($this->name.'_message') . '</h4></div>';
}

在我们的简单示例中,我们仅仅是用html标签把配置参数包了下,让它能正确显示在页面上。

如果之前装过了这个模块,需要把它卸载再安装,确保挂载正确地注册到Prestashop内核里去了。现在你可以在模块的配置页面输入值了,比如“Hello World”,它会作为店铺左侧页面的一个内容块的标题显示出来。第二个例子的完整代码在这里:
<?php
class Tutorialsecond extends Module
{
private $_html = '';

function __construct()
{
$this->name = 'tutorialsecond';
parent::__construct();

$this->tab = 'phpstudio.info Tutorials';
$this->version = '0.1.0';
$this->displayName = $this->l('Second Tutorial Module');
$this->description = $this->l('Our second module - A "Hello world" redux');
}

public function install()
{
parent::install();

if (!$this->registerHook('leftColumn'))
return false;
}

public function getContent()
{
if (Tools::isSubmit('submit'))
{
Configuration::updateValue($this->name.'_message', Tools::getValue('our_message'));
}

$this->_displayForm();

return $this->_html;
}

private function _displayForm()
{
$this->_html .= '
<form action="'.$_SERVER['REQUEST_URI'].'" method="post">
<label>'.$this->l('Message to the world').'</label>
<div class="margin-form">
<input type="text" name="our_message" />
</div>
<input type="submit" name="submit" value="'.$this->l('Update').'" class="button" />
</form>';
}

public function hookLeftColumn()
{
return '<div class="block"><h4>'. Configuration::get($this->name.'_message') . '</h4></div>';
}

}
// End of: tutorialsecond.php
?>

综述

在这篇文章里,我们扩展了第一个模块,加入了一个配置表单,使用挂载把配置显示到页面的左侧。下一讲中,我们会看到配置功能的扩展,使得它更适于一个真正模块的实现。内容包括校验用户输入,基于当前配置预设表单输入项,在后台进行模块的错误/警告输出。