blog webempresa

Cómo desarrollar un componente de Google Maps en Joomla 1.6

por | Jun 29, 2010 | Aprender Joomla!

Aprovechando la salida de Joomla 1.6 beta 4 nos hemos puesto manos a la obra y hemos empezado a indagar en las entrañas de Joomla 1.6. En principio os adelanto que no hay grandes cambios a la hora de desarrollar extensiones, y que el que sepa desarrollar extensiones para Joomla 1.5 no tendrá ningún problema con Joomla 1.6.

En este tutorial crearemos un extensión sencilla que recogerá de una BBDD información de lugares del mundo, y los mostrará pintados en un mapa de Google Maps. Nos hemos basado en el tutorial de Google Maps y PHP que publicó Google y lo hemos adaptado a Joomla 1.6 beta 4.

 

Los pasos que hemos seguido han sido los siguientes:

  1. Creamos  la tabla #__markers en la BBDD de Joomla 1.6.
  2. Introducimos datos de ejemplo en la tabla.
  3. Creamos  el punto de entrada del componente.
  4. Creamos  el controlador base.
  5. Creamos  la vista markers que contiene el div contenedor del mapa.
  6. Creamos  el modelo que obtiene datos de la tabla #__markers.
  7. Creamos  el task en el controlador base que devuelve la información de los lugares en formato XML.
  8. Creamos el fichero Javascript que se encargará de recoger la información en XML y pintar los marcadores en el mapa con el API de Google Maps.
  9. Testeamos que funcione todo correctamente.

Una vez vista la hoja de ruta del tutorial, vamos allá:

Creamos la tabla #__markers

CREATE TABLE `jos_markers` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`name` VARCHAR( 60 ) NOT NULL ,
`address` VARCHAR( 80 ) NOT NULL ,
`lat` FLOAT( 10, 6 ) NOT NULL ,
`lng` FLOAT( 10, 6 ) NOT NULL ,
`type` VARCHAR( 30 ) NOT NULL
) ENGINE = MYISAM ;

Deberás cambiar jos por el prefijo de tu BBDD.

Introducimos los datos de ejemplo

INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Pan Africa Market', '1521 1st Ave, Seattle, WA', '47.608941', '-122.340145', 'restaurant');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Buddha Thai & Bar', '2222 2nd Ave, Seattle, WA', '47.613591', '-122.344394', 'bar');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('The Melting Pot', '14 Mercer St, Seattle, WA', '47.624562', '-122.356442', 'restaurant');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Ipanema Grill', '1225 1st Ave, Seattle, WA', '47.606366', '-122.337656', 'restaurant');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Sake House', '2230 1st Ave, Seattle, WA', '47.612825', '-122.34567', 'bar');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Crab Pot', '1301 Alaskan Way, Seattle, WA', '47.605961', '-122.34036', 'restaurant');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Mama\'s Mexican Kitchen', '2234 2nd Ave, Seattle, WA', '47.613975', '-122.345467', 'bar');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Wingdome', '1416 E Olive Way, Seattle, WA', '47.617215', '-122.326584', 'bar');
INSERT INTO `jos_markers` (`name`, `address`, `lat`, `lng`, `type`) VALUES ('Piroshky Piroshky', '1908 Pike pl, Seattle, WA', '47.610127', '-122.342838', 'restaurant');

Creamos el punto de entrada del componente

El punto de entrada se ubica en el fichero components/com_maps/maps.php, la convención es la misma que en Joomla 1.5, por lo que no tendremos dudas.

<?php
defined('_JEXEC') or die();

jimport('joomla.application.component.controller');

$controller = JController::getInstance('Maps');
$controller->execute(JRequest::getCmd('task', 'display'));
$controller->redirect();
?>

Si os fijáis a la hora de crear el punto de entrada ya no hace falta estar haciendo “malabarismos” para instanciar el controlador base u otro específico dependiendo del parámetro controller en la petición. Ahora todo esto se soluciona con el método getInstance. Aún así, si te gusta hacer malabarismos, podrás usar el método antiguo.

Creamos el controlador base

Esta parte es igual a Joomla 1.5. Dejo el código a continuación:

<?php
defined('_JEXEC') or die;
jimport('joomla.application.component.controller');

/*
* Base controller class for Maps.
*/
class MapsController extends JController
{
public function display()
{
$document = JFactory::getDocument();

$lName     = JRequest::getWord('layout', 'default');
$view = $this->getView("markers", "html");

$view->assignRef('document', $document);
$view->display();
}
public function getMarkersXML()
{
$document = JFactory::getDocument();
$document->setMimeEncoding('text/xml');
$model = $this->getModel('markers');
$data = $model->getData();
echo "<markers>";
foreach($data as $marker){
echo '<marker ';
echo 'name="' . $this->parseToXML($marker->name) . '" ';
echo 'address="' . $this->parseToXML($marker->address) . '" ';
echo 'lat="' . $marker->lat . '" ';
echo 'lng="' . $marker->lng . '" ';
echo 'type="' . $marker->type . '" ';
echo '/>';
}
echo "</markers>";

}
private function parseToXML($htmlStr)
{
$xmlStr=str_replace('<','&lt;',$htmlStr);
$xmlStr=str_replace('>','&gt;',$xmlStr);
$xmlStr=str_replace('"','&quot;',$xmlStr);
$xmlStr=str_replace("'",'&#39;',$xmlStr);
$xmlStr=str_replace("&",'&amp;',$xmlStr);
return $xmlStr;
}
}
?>

Creamos la vista markers que contiene el div contenedor del mapa

Para crear la vista markers hay que proceder del mismo modo del que se hace en Joomla 1.5. Creamos el directorio markers, dentro del directorio views del componente. Dentro del directorio markers creamos el fichero view.html.php y el directorio tmpl donde se encuentran los layouts de la vista. En este caso crearemos el layout default.php. Podéis ver el código a continuación.

Fichero view.html.php

<?php
defined('_JEXEC') or die;
jimport('joomla.application.component.view');

class MapsViewMarkers extends JView
{
public function display($tpl = null)
{
JHTML::_( 'behavior.mootools' );
$this->document->setTitle(JText::_('Lista de marcadores'));
$this->document->addScript("http://maps.google.com/maps?file=api&amp;v=2&amp;sensor=true&amp;key=ABQIAAAAJP_tKIqNZxUwzi9ODnS-yxSF-qWcih2JzrSPTv88Wguu5XhySxTpN5Uw2o0ObTphklDWOKgTsuUfRw");
$this->document->addScript("components/com_maps/assets/maps.js");
parent::display($tpl);
}
}
?>

Fichero default.php

<h2 class="componentheading">Lista de marcadores</h2>
<div id="map" style="width: 600px; height: 400px"></div>

Creamos el modelo que obtiene datos de la tabla #__markers

Creamos el modelo markers, para ello creamos el fichero markers.php en el directorio models. Podéis ver el código del modelo a continuación.

<?php
defined('_JEXEC') or die;
jimport('joomla.application.component.modelitem');

class MapsModelMarkers extends JModelItem
{
protected $_data = null;

public function getData(){

try {
$db = $this->getDbo();
$query = $db->getQuery(true);

$query->select('m.*');
$query->from('#__markers AS m');

$db->setQuery($query);

$data = $db->loadObjectList();

if ($error = $db->getErrorMsg()) {
throw new Exception($error);
}

if (empty($data)) {
throw new Exception(JText::_('COM_MAPS_ERROR_MARKER_NOT_FOUND'), 404);
}

$this->_data = $data;
}
catch (Exception $e)
{
$this->setError($e);
$this->_data = false;
}
return $this->_data;
}
}
?>

Creamos el task en el controlador base que devuelve la información de los lugares en formato XML

Implementamos el task getMarkersXML que usaremos para obtener un listado XML de los lugares introducidos en la base de datos. Usaremos la función privada parseToXML para evitar problemas con los caracteres que puedan chocar con el estándar XML

public function getMarkersXML()
{
$document = JFactory::getDocument();
$document->setMimeEncoding('text/xml');
$model = $this->getModel('markers');
$data = $model->getData();
echo "<markers>";
foreach($data as $marker){
echo '<marker ';
echo 'name="' . $this->parseToXML($marker->name) . '" ';
echo 'address="' . $this->parseToXML($marker->address) . '" ';
echo 'lat="' . $marker->lat . '" ';
echo 'lng="' . $marker->lng . '" ';
echo 'type="' . $marker->type . '" ';
echo '/>';
}
echo "</markers>";

}
private function parseToXML($htmlStr)
{
$xmlStr=str_replace('<','&lt;',$htmlStr);
$xmlStr=str_replace('>','&gt;',$xmlStr);
$xmlStr=str_replace('"','&quot;',$xmlStr);
$xmlStr=str_replace("'",'&#39;',$xmlStr);
$xmlStr=str_replace("&",'&amp;',$xmlStr);
return $xmlStr;
}

Creamos el fichero Javascript que se encargará de recoger la información en XML y pintar los marcadores en el mapa con el API de Google Maps

El fichero javascript maps.js es donde se encuentra el código que recoge esta información en XML y la pinta en el mapa usando el API de Google Maps. Usa iconos personalizados y genera ventanas de información para cada marcador. Podéis ver el código a continuación:

function load() {
if (GBrowserIsCompatible()) {
var map = new GMap2(document.getElementById("map"));
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
map.setCenter(new GLatLng(47.614495, -122.341861), 13);

GDownloadUrl("index.php?option=com_maps&task=getMarkersXML&format=raw", function(data) {
var xml = GXml.parse(data);
var markers = xml.documentElement.getElementsByTagName("marker");
for (var i = 0; i < markers.length; i++) {
var name = markers[i].getAttribute("name");
var address = markers[i].getAttribute("address");
var type = markers[i].getAttribute("type");
var point = new GLatLng(parseFloat(markers[i].getAttribute("lat")),
parseFloat(markers[i].getAttribute("lng")));
var marker = createMarker(point, name, address, type);
map.addOverlay(marker);
}
});
}
}

function createMarker(point, name, address, type) {
var marker = new GMarker(point, customIcons[type]);
var html = "<b>" + name + "</b> <br/>" + address;
GEvent.addListener(marker, 'click', function() {
marker.openInfoWindowHtml(html);
});
return marker;
}

var customIcons = [];

document.addEvent( 'domready' ,  function() {

var iconBlue = new GIcon();
iconBlue.image = 'http://labs.google.com/ridefinder/images/mm_20_blue.png';
iconBlue.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
iconBlue.iconSize = new GSize(12, 20);
iconBlue.shadowSize = new GSize(22, 20);
iconBlue.iconAnchor = new GPoint(6, 20);
iconBlue.infoWindowAnchor = new GPoint(5, 1);

var iconRed = new GIcon();
iconRed.image = 'http://labs.google.com/ridefinder/images/mm_20_red.png';
iconRed.shadow = 'http://labs.google.com/ridefinder/images/mm_20_shadow.png';
iconRed.iconSize = new GSize(12, 20);
iconRed.shadowSize = new GSize(22, 20);
iconRed.iconAnchor = new GPoint(6, 20);
iconRed.infoWindowAnchor = new GPoint(5, 1);

customIcons["restaurant"] = iconBlue;
customIcons["bar"] = iconRed;
load();

});

Probamos que funcione todo correctamente

Una vez seguidos estos pasos el componente debe funcionar correctamente. Como veis, la programación en Joomla 1.6 es muy similar a la de Joomla 1.5. Se han introducido nuevos elementos, en este pequeño tutorial hemos visto el objeto JQuery, no confundir con jQuery, el framework javascript. El objeto JQuery nos permitirá construir consultas SQL de una forma más sencilla. En Joomla 1.5 teníamos que construir las consultas manualmente y a veces esto se hacía complicado con consultas complejas, sobre todo a la hora de añadir las condiciones where, o los joins. También hemos visto que el punto de entrada se ha simplificado bastante, ahora se ha creado el método getInstance que simplifica el proceso y abstrae al programador de crear el punto de entrada, que aunque siempre acabábamos copiando y pegando, esta forma es más elegante que la anterior. Nos hemos dejado muchas cosas, como por ejemplo, ver el nuevo sistema de generación de formularios del API de Joomla 1.6, usando el objeto JModelForm, que nos permitirá crear los validadores usando definiciones en archivos XML. Conforme se vaya estabilizando el desarrollo de Joomla 1.6 iremos viendo más características sobre desarrollo web, que nos permitirán mantener nuestros desarrollos a la vanguardia de la tecnología Joomla.

Descargar los ficheros del tutorial (com_maps.zip)

¿Te ha resultado útil este artículo?

Promo abril