在symfony 1.2中, 使用form来生成表单, form由widget组成, 每个widget对应一个数据库字段, 每个widget可以由任意个表单项组成, 但最终结果为一个值.
从另一个方面来讲, widget又分为widget和validator两部分, widget用来生成显示的表单项, validator负责对表单数据进行验证和对值的验证和处理.
__ sfWidgetFromGoogleMap.class.php __
<?php
// widget 需要继承 sfwidgetform
class sfWidgetFormGoogleMap extends sfWidgetForm
{
// configure方法用来设置widget中用到的选项(options)
protected function configure($options = array(), $attributes = array())
{
// value 的默认值, 在value未设置时生效
$this->addOption('default_value');
// google maps 的默认大小, 在size未设置时生效
$this->addOption('default_size');
// google map 的默认缩放级别, google maps支持0-19
$this->addOption('default_zoom');
// google maps 的大小, 是一个又width和height两个元素组成的数组
$this->addOption('size');
// google map 的缩放级别
$this->addOption('zoom');
// google map api key, 在google maps api官方网站上申请
$this->addOption('api_key');
/**
* 下面是给option设默认值
*/
$this->setOption('default_value', '34,134');
$this->setOption('default_zoom', '10');
$this->setOption('default_size', array('width' => '240px', 'height' => '150px'));
}
// getJavascripts实现了一个hook, 如果该方法被定义, 返回的是该widget需要加载的外部脚本
// @return array
public function getJavascripts()
{
return array('http://maps.google.com/maps?file=api&v=2&key='.$this->getOption('api_key'));
}
// getStylesheets实现了一个hook, 如果该方法被定义, 返回的是该widget需要加载的外部css样式表
// @return array
/*public function getStylesheets()
{
return array('/dbFormExtraPlugin/farbtastic/farbtastic.css' => 'screen');
}*/
// render()方法用来生成显示widget的html
// @param $name string 该参数用来定义widget生成表单项的name
// @param $value mixed widget的默认值, 可能是数组或者字符串, 需要进行判断, 如果
// 新建或者编辑, 传入的是从数据库来的字符串, 如果是表单错误
// 则传入的是刚刚提交的值, 可能是字符串也可能是数组
// @param $attributes array
// @param $errors ??
public function render($name, $value = null, $attributes = array(), $errors = array())
{
// load JQuery
use_helper("jQuery");
// set size
// 如果size被定义则使用size否则使用默认值
if ($this->hasOption('size') && $this->getOption('size')) {
$size = $this->getOption('size');
} else {
$size = $this->getOption('default_size');
}
// set zoom
// 如果zoom被定义则使用zoom否则使用默认值
if ($this->getOption('zoom')) {
$zoom = $this->getOption('zoom');
} else {
$zoom = $this->getOption('default_zoom');
}
// set value
// 如果value被定义则使用value否则使用默认值
if (empty($value)) {
$value = $this->getOption('default_value');
}
if (is_array($value)) {
//$value = array_merge($this->getOption('default_value'), $value);
} else {
$values = explode(',', $value);
$value = array('x' => $values[0], 'y' => $values[1]);
}
// build input
// 生成提供显示的html
$widget = new sfWidgetFormInput();
$html = '<div class="latlng_box" id="latlng_box">';
// 这里调用到了另一个widget的render方法, 因为坐标是又两个input type=text组成的, 所以这里我们调用两次sfWidgetFormInput的render
// 这里有一点需要注意, 如果一个widget由多个表单项组成, 则需要将它们的name构造成一个数组.
$html .= $widget->render($name.'[x]', $value['x']);
$html .= ',';
$html .= $widget->render($name.'[y]', $value['y']);
// build map
// 以下是调用google maps使用的代码
$html .= <<<EOT
<div id="map" style="width:{$size['width']};height:{$size['height']}"></div>
<script type="text/javascript">
//<![CDATA[
// 实例化一个map对象
var map = new GMap2(document.getElementById("map"));
// 实例化一个标记(icon)对象
var icon = new GIcon();
/**
* 下面是对icon的初始化操作
*/
icon.image = "http://labs.google.com/ridefinder/images/mm_20_red.png";
icon.shadow = "http://labs.google.com/ridefinder/images/mm_20_shadow.png";
icon.iconSize = new GSize(12, 20);
icon.shadowSize = new GSize(22, 20);
icon.iconAnchor = new GPoint(6, 20);
icon.infoWindowAnchor = new GPoint(5, 1);
// 这里可以给地图增加控制条
//map.addControl(new GSmallMapControl());
//map.addControl(new GMapTypeControl());
// 给google maps设定默认值
setLatlng();
// 这里使用jquery对widget的change事件进行绑定, 如果widget的值被修改则重新设定icon和地图中心
$('#shop_latlng_x').bind('change', function(){setLatlng()});
$('#shop_latlng_y').bind('change', function(){setLatlng()});
function setLatlng() {
// 根据widget的值实例化一个坐标对象
var point = new GLatLng(document.getElementById('shop_latlng_x').value, document.getElementById('shop_latlng_y').value);
// 清楚地图上的icon
map.clearOverlays();
// 重置地图中心
map.setCenter(point, $zoom);
// 给坐标添加icon
map.addOverlay(new GMarker(point, icon));
}
//]]>
</script>
EOT;
$html .= "</div>";
// 返回生成的html代码
return $html;
}
}
__ sfValidatorGoogleMap.class.php __
<?php
// validator继承自sfValidatorBase
class sfValidatorGoogleMap extends sfValidatorBase
{
// configure()用来初始化validator
protected function configure($options = array(), $messages = array())
{
// 添加一个可返回的名为'format_error',格式为'latlng format error %custom%'的验证错误, 其中%中的值可以被替换
$this->addMessage('format_error', 'latlng format error %custom%');
}
// doClean用来对提交上来的widget值进行验证和转换
protected function doClean($value)
{
$reg = '/[\+-]?\d{0,2}(\.\d*)?/i';
if (!preg_match($reg, $value['x'])) {
// 这里对经度的格式进行了验证, 如果格式错误则抛出一个验证错误
// 抛出验证异常将会终止表单继续提交, 返回提交页面
// 第二个参数是message名, 是我们在configure中定义的, 第三个参数将会是错误消息变成"latlng format error, lat format error"
throw new sfValidatorError($this, 'format_error', array('custom' => ', lat format error'));
}
$reg = '/[\+-]?\d{0,3}(\.\d*)?/i';
if (!preg_match($reg, $value['y'])) {
// 这里对纬度的格式进行了验证, 如果格式错误则抛出一个验证错误
throw new sfValidatorError($this, 'format_error', array('custom' => 'lng format error'));
}
if (abs($value['x']) > 90) {
// 这里对经度的范围进行了验证, 经度必须在-90和90之间
throw new sfValidatorError($this, 'format_error', array('custom' => 'lat format error, must > -90 and < 90));
}
if (abs($value['y']) > 180) {
// 这里对纬度的范围进行了验证, 纬度必须在-180和180之间
throw new sfValidatorError($this, 'format_error', array('custom' => 'lng format error, must > -180 and < 180'));
}
// 返回值为一个字符串, 最终将被保存到数据库
return $value['x'].','.$value['y'];
}
}
下面是这个widget的使用方法
__ ShopForm.class.php __
<?php
class ShopForm extends BaseShopForm
{
public function configure()
{
// 这里指定latlng为一个坐标,并且赋予了初始值, SettingPeer::getSetting()方法调用了系统设置, 不过并不是symfony内置的.
$this->setWidget('latlng', new sfWidgetFormGoogleMap(array('api_key' => SettingPeer::getSetting('googleMapApiKey'), 'zoom' => SettingPeer::getSetting('googleMap', 16))));
// 这里指定latlng使用坐标验证规则, validator可以有初始参数
$this->setValidator('latlng', new sfValidatorGoogleMap());
}
}
widget和validator可以放在一个lib下, 在加载form时将被自动调用, 最终效果图如下: