<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>4's symfony blog &#187; widget</title>
	<atom:link href="http://www.foolbirds.com/t/widget/feed" rel="self" type="application/rss+xml" />
	<link>http://www.foolbirds.com</link>
	<description>all about symfony</description>
	<lastBuildDate>Tue, 17 Aug 2010 01:22:43 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.4</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>编写widget一例:支持Google Maps的坐标输入框</title>
		<link>http://www.foolbirds.com/symfony-latlng-widget-with-google-maps.html</link>
		<comments>http://www.foolbirds.com/symfony-latlng-widget-with-google-maps.html#comments</comments>
		<pubDate>Tue, 16 Dec 2008 06:23:59 +0000</pubDate>
		<dc:creator>maker</dc:creator>
				<category><![CDATA[symfony]]></category>
		<category><![CDATA[form]]></category>
		<category><![CDATA[validator]]></category>
		<category><![CDATA[widget]]></category>

		<guid isPermaLink="false">http://symfony.bobhero.net/?p=113</guid>
		<description><![CDATA[在symfony 1.2中, 使用form来生成表单, form由widget组成, 每个widget对应一个数据库字段, 每个widget可以由任意个表单项组成, 但最终结果为一个值.
从另一个方面来讲, widget又分为widget和validator两部分, widget用来生成显示的表单项, validator负责对表单数据进行验证和对值的验证和处理.
__ sfWidgetFromGoogleMap.class.php __
&#60;?php
// widget 需要继承 sfwidgetform
class sfWidgetFormGoogleMap extends sfWidgetForm
{
// configure方法用来设置widget中用到的选项(options)
protected function configure($options = array(), $attributes = array())
{
// value 的默认值, 在value未设置时生效
$this-&#62;addOption('default_value');
// google maps 的默认大小, 在size未设置时生效
$this-&#62;addOption('default_size');
// google map 的默认缩放级别, google maps支持0-19
$this-&#62;addOption('default_zoom');
// google maps 的大小, 是一个又width和height两个元素组成的数组
$this-&#62;addOption('size');
// google map 的缩放级别
$this-&#62;addOption('zoom');
// google map api key, 在google maps api官方网站上申请
$this-&#62;addOption('api_key');
/**
 * 下面是给option设默认值
 */
$this-&#62;setOption('default_value', [...]]]></description>
			<content:encoded><![CDATA[<p>在symfony 1.2中, 使用form来生成表单, form由widget组成, 每个widget对应一个数据库字段, 每个widget可以由任意个表单项组成, 但最终结果为一个值.</p>
<p>从另一个方面来讲, widget又分为widget和validator两部分, widget用来生成显示的表单项, validator负责对表单数据进行验证和对值的验证和处理.</p>
<p>__ sfWidgetFromGoogleMap.class.php __</p>
<pre class="php" name="code">&lt;?php
// widget 需要继承 sfwidgetform
class sfWidgetFormGoogleMap extends sfWidgetForm
{
// configure方法用来设置widget中用到的选项(options)
protected function configure($options = array(), $attributes = array())
{
// value 的默认值, 在value未设置时生效
$this-&gt;addOption('default_value');
// google maps 的默认大小, 在size未设置时生效
$this-&gt;addOption('default_size');
// google map 的默认缩放级别, google maps支持0-19
$this-&gt;addOption('default_zoom');
// google maps 的大小, 是一个又width和height两个元素组成的数组
$this-&gt;addOption('size');
// google map 的缩放级别
$this-&gt;addOption('zoom');
// google map api key, 在google maps api官方网站上申请
$this-&gt;addOption('api_key');
/**
 * 下面是给option设默认值
 */
$this-&gt;setOption('default_value', '34,134');
$this-&gt;setOption('default_zoom', '10');
$this-&gt;setOption('default_size', array('width' =&gt; '240px', 'height' =&gt; '150px'));
}

// getJavascripts实现了一个hook, 如果该方法被定义, 返回的是该widget需要加载的外部脚本
// @return array
public function getJavascripts()
{
return array('http://maps.google.com/maps?file=api&amp;amp;v=2&amp;amp;key='.$this-&gt;getOption('api_key'));
}

// getStylesheets实现了一个hook, 如果该方法被定义, 返回的是该widget需要加载的外部css样式表
// @return array
/*public function getStylesheets()
{
return array('/dbFormExtraPlugin/farbtastic/farbtastic.css' =&gt; '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-&gt;hasOption('size') &amp;&amp; $this-&gt;getOption('size')) {
$size = $this-&gt;getOption('size');
} else {
$size = $this-&gt;getOption('default_size');
}

// set zoom
// 如果zoom被定义则使用zoom否则使用默认值
if ($this-&gt;getOption('zoom')) {
$zoom = $this-&gt;getOption('zoom');
} else {
$zoom = $this-&gt;getOption('default_zoom');
}

// set value
// 如果value被定义则使用value否则使用默认值
if (empty($value)) {
$value = $this-&gt;getOption('default_value');
}
if (is_array($value)) {
//$value = array_merge($this-&gt;getOption('default_value'), $value);
} else {
$values = explode(',', $value);
$value  = array('x' =&gt; $values[0], 'y' =&gt; $values[1]);
}

// build input
// 生成提供显示的html
$widget = new sfWidgetFormInput();
$html = '&lt;div class="latlng_box" id="latlng_box"&gt;';
// 这里调用到了另一个widget的render方法, 因为坐标是又两个input type=text组成的, 所以这里我们调用两次sfWidgetFormInput的render
// 这里有一点需要注意, 如果一个widget由多个表单项组成, 则需要将它们的name构造成一个数组.
$html .= $widget-&gt;render($name.'[x]', $value['x']);
$html .= ',';
$html .= $widget-&gt;render($name.'[y]', $value['y']);

// build map
// 以下是调用google maps使用的代码
$html .= &lt;&lt;&lt;EOT
&lt;div id="map" style="width:{$size['width']};height:{$size['height']}"&gt;&lt;/div&gt;
&lt;script type="text/javascript"&gt;
//&lt;![CDATA[
// 实例化一个map对象
var map = new GMap2(document.getElementById("map"));
// 实例化一个标记(icon)对象
var icon = new GIcon();
/**
 * 下面是对icon的初始化操作
 */
icon.image = "<a href="http://labs.google.com/ridefinder/images/mm_20_red.png">http://labs.google.com/ridefinder/images/mm_20_red.png</a>";
icon.shadow = "<a href="http://labs.google.com/ridefinder/images/mm_20_shadow.png">http://labs.google.com/ridefinder/images/mm_20_shadow.png</a>";
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));
}

//]]&gt;
&lt;/script&gt;
EOT;
$html .= "&lt;/div&gt;";
// 返回生成的html代码
return $html;
}
}</pre>
<p>__ sfValidatorGoogleMap.class.php __</p>
<pre class="php" name="code">&lt;?php
// validator继承自sfValidatorBase
class sfValidatorGoogleMap extends sfValidatorBase
{
// configure()用来初始化validator
protected function configure($options = array(), $messages = array())
{
// 添加一个可返回的名为'format_error',格式为'latlng format error %custom%'的验证错误, 其中%中的值可以被替换
$this-&gt;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']) &gt; 90) {
// 这里对经度的范围进行了验证, 经度必须在-90和90之间
throw new sfValidatorError($this, 'format_error', array('custom' => 'lat format error, must > -90 and < 90));
}
if (abs($value['y']) &gt; 180) {
// 这里对纬度的范围进行了验证, 纬度必须在-180和180之间
throw new sfValidatorError($this, 'format_error', array('custom' => 'lng format error, must > -180 and < 180'));
}

// 返回值为一个字符串, 最终将被保存到数据库
return $value['x'].','.$value['y'];
}
}</pre>
<p>下面是这个widget的使用方法</p>
<p>__ ShopForm.class.php __</p>
<pre class="php" name="code">&lt;?php
class ShopForm extends BaseShopForm
{
public function configure()
{
// 这里指定latlng为一个坐标,并且赋予了初始值, SettingPeer::getSetting()方法调用了系统设置, 不过并不是symfony内置的.
$this-&gt;setWidget('latlng', new sfWidgetFormGoogleMap(array('api_key' =&gt; SettingPeer::getSetting('googleMapApiKey'), 'zoom' =&gt; SettingPeer::getSetting('googleMap', 16))));
// 这里指定latlng使用坐标验证规则, validator可以有初始参数
$this-&gt;setValidator('latlng', new sfValidatorGoogleMap());
}
}
</pre>
<p>widget和validator可以放在一个<strong>lib下, 在加载form时将被自动调用</strong>, 最终效果图如下:<br />
<img class="aligncenter size-full wp-image-117" title="googlemaps" src="http://www.foolbirds.com/wp-content/uploads/2008/12/googlemaps.gif" alt="googlemaps" width="596" height="226" /></pre>
<ul class="related_post"><li><a href="http://www.foolbirds.com/symfony-form-offsetunset.html" title="Symfony中自定义form中offsetUnset的应用">Symfony中自定义form中offsetUnset的应用</a></li><li><a href="http://www.foolbirds.com/use_symfony_filter_to_filteredurl.html" title="使用SYMFONY Filter 过滤URL">使用SYMFONY Filter 过滤URL</a></li><li><a href="http://www.foolbirds.com/%e4%bb%bfsymfony%e6%9c%ba%e5%88%b6%e5%ae%9e%e7%8e%b0%e4%b8%8d%e7%94%a8require%e6%88%96%e8%80%85include%e6%9d%a5%e5%ae%9e%e4%be%8b%e5%8c%96%e7%b1%bb.html" title="仿symfony机制实现不用require或者include来实例化类">仿symfony机制实现不用require或者include来实例化类</a></li><li><a href="http://www.foolbirds.com/use-datetime-in-php5-1-x-with-symfony.html" title="symfony1.4 DateTime对于PHP低版本的兼容问题">symfony1.4 DateTime对于PHP低版本的兼容问题</a></li><li><a href="http://www.foolbirds.com/%e5%a6%82%e4%bd%95%e5%9c%a8fixtures-yml%e5%86%99%e5%be%aa%e7%8e%af%e6%b7%bb%e5%8a%a0%e6%95%b0%e6%8d%ae.html" title="如何在fixtures.yml写循环添加数据">如何在fixtures.yml写循环添加数据</a></li><li><a href="http://www.foolbirds.com/symfony-1-4-database-utf8.html" title="symfony 1.4 数据库 utf8设置">symfony 1.4 数据库 utf8设置</a></li><li><a href="http://www.foolbirds.com/symfony-cheat-sheet.html" title="symfony cheat sheet">symfony cheat sheet</a></li><li><a href="http://www.foolbirds.com/cheat-sheets.html" title="Cheat Sheets!">Cheat Sheets!</a></li><li><a href="http://www.foolbirds.com/how-to-embed-forms-in-symfony-12-admin-generator.html" title="内嵌表单详解（How to Embed Forms in Symfony 1.2 Admin Generator 中文版）">内嵌表单详解（How to Embed Forms in Symfony 1.2 Admin Generator 中文版）</a></li><li><a href="http://www.foolbirds.com/how-to-use-swift-to-send-mail-in-symfon.html" title="如何使用swift发送邮件">如何使用swift发送邮件</a></li></ul>]]></content:encoded>
			<wfw:commentRss>http://www.foolbirds.com/symfony-latlng-widget-with-google-maps.html/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
