<?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>Blog cakephp en español por Hospedaxes &#187; tree behavior</title>
	<atom:link href="http://cakephp.hospedaxes.com/tag/tree-behavior/feed" rel="self" type="application/rss+xml" />
	<link>http://cakephp.hospedaxes.com</link>
	<description>Blog sobre desarrollo web con cakephp en español por Hospedaxes</description>
	<lastBuildDate>Mon, 26 Apr 2010 07:11:23 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Tree Behavior o cómo crear una estructura jerárquica</title>
		<link>http://cakephp.hospedaxes.com/tree-behavior-o-como-crear-una-estructura-jerarquica</link>
		<comments>http://cakephp.hospedaxes.com/tree-behavior-o-como-crear-una-estructura-jerarquica#comments</comments>
		<pubDate>Wed, 14 Oct 2009 08:42:33 +0000</pubDate>
		<dc:creator>nuria</dc:creator>
				<category><![CDATA[Noticias]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[árbol]]></category>
		<category><![CDATA[cakephp 1.2]]></category>
		<category><![CDATA[organización jerárquica]]></category>
		<category><![CDATA[tree behavior]]></category>

		<guid isPermaLink="false">http://cakephp.hospedaxes.com/?p=237</guid>
		<description><![CDATA[En esta ocasión hablaremos de cómo crear una estructura jerárquica utilizando el Tree Behavior de CakePHP. Este comportamiento facilita muchísimo las cosas a la hora de manipular árboles jerárquicos de datos.
Utilizaremos también la librería de Javascript Ext JS para poder manipular el árbol gráficamente de manera sencilla, utilizando drag and drop.
Lo primero que tendremos que [...]]]></description>
			<content:encoded><![CDATA[<p>En esta ocasión hablaremos de cómo crear una estructura jerárquica utilizando el <a title="CakePHP: Tree Behavior" href="http://api.cakephp.org/class/tree-behavior">Tree Behavior</a> de CakePHP. Este comportamiento facilita muchísimo las cosas a la hora de manipular árboles jerárquicos de datos.</p>
<p>Utilizaremos también la librería de Javascript <a title="Ext JS" href="http://www.extjs.com/">Ext JS</a> para poder manipular el árbol gráficamente de manera sencilla, utilizando <a title="Wikipedia: Arrastrar y soltar" href="http://es.wikipedia.org/wiki/Arrastrar_y_soltar"><em>drag and drop</em></a>.</p>
<p>Lo primero que tendremos que hacer será añadir en la tabla de la base de datos de la entidad. Utilizando los nombres por defecto de Cake, serían los siguientes campos:</p>
<ul>
<li><em>parent_id</em>: hace referencia al padre del elemento.</li>
<li><em>lft</em>: almacena el identificador del elemento de la izquierda en el mismo nivel.</li>
<li><em>rght</em>: almacena el identificador del elemento de la derecha en el mismo nivel.</li>
</ul>
<p>Incluiremos también los campos <em>id</em> y <em>nombre</em>, en una entidad denominada, por ejemplo, Categoria.</p>
<p>El modelo lo haríamos de la siguiente manera:</p>
<p>Este comportamiento tiene funciones muy útiles para la manipulación del árbol. Las más utilizadas quizás sean las siguientes:</p>
<ul>
<li><strong><em>children</em></strong>: devuelve los hijos de un nodo concreto, pudiendo seleccionar si deseamos obtener sólo los hijos directos o todos los hijos en el árbol.</li>
<li><strong><em>generatetreelist</em></strong>: función muy útil para obtener los elementos a introducir en un select de HTML.</li>
<li><strong>getpath</strong>: devuelve todo el path hasta el nodo especificado en un array.</li>
<li><strong>movedown</strong> y <strong>moveup</strong>: sirven para bajar o subir un nivel de un nodo del árbol.</li>
<li><strong>removefromtree</strong>: elimina un nodo del árbol sin perder la estructura, es decir, modificando el padre de sus hijos.</li>
<li><strong>reorder</strong>: reordena un nodo (arrastrando también a sus hijos) en función de los parámetros especificados.</li>
</ul>
<p>Para realizar la manipulación visual del árbol de categorías crearemos las siguientes funciones en el controlador <em>app/controllers/categorias_controller.php</em> sería:</p>
<pre class="prettyprint"><code>&lt;?php

class CategoriasController extends AppController{

var $helpers = array( 'Javascript');

// Función para organizar las categorías
function organizar() {}

function getnodes() {
   Configure::write('debug', 0);
   // obtener el identificador del padre que se envío por POST vía Ajax
   $parent = intval($this-&gt;params['form']['node']);
   // encontrar los hijos directos del nodo anterior
   $nodes = $this-&gt;Categoria-&gt;children($parent, true, null, 'Categoria.lft ASC');

   $this-&gt;set(compact('nodes'));

   $this-&gt;render('getnodes', 'ajax');
}

function reorder()
{
   Configure::write('debug', 0);

   // delta es la diferencia en la posición (1 = nodo siguiente, -1 = nod anterior)
   $node = intval($this-&gt;params['form']['node']);
   $delta = intval($this-&gt;params['form']['delta']);

   if ($delta &gt; 0) {
      $this-&gt;Categoria-&gt;movedown($node, abs($delta));
   } elseif ($delta &lt; 0) {
      $this-&gt;Categoria-&gt;moveup($node, abs($delta));
   }

   exit('1');
}

function reparent()
{
   Configure::write('debug', 0);

   $node = intval($this-&gt;params['form']['node']);
   $parent = intval($this-&gt;params['form']['parent']);
   $position = intval($this-&gt;params['form']['position']);

   // guardamos el nuevo padre de la categoría
   $this-&gt;Categoria-&gt;id = $node;
   $this-&gt;Categoria-&gt;saveField('parent_id', $parent);

   // Si position == 0, nos movemos al inicio.
   // En otro caso, calculamos la distancia que nos moveremos ($delta).
   if ($position == 0) {
      $this-&gt;Categoria-&gt;moveup($node, true);
   } else {
      $count = $this-&gt;Categoria-&gt;childcount($parent, true);
      $delta = $count - $position - 1;
      if ($delta &gt; 0) {
         $this-&gt;Categoria-&gt;moveup($node, $delta);
      }
   }

   exit('1');
}
}
?&gt;</code></pre>
<p>Sólo nos quedaría hacer las vistas. En este caso, necesitamos crear 2 vistas: organizar.ctp y getnodes.ctp.</p>
<pre class="prettyprint"><code>// app/views/categorias/organizar.ctp

&lt;?php echo $html-&gt;css('/js/ext-2.0.1/resources/css/ext-custom.css'); ?&gt;
&lt;?php echo $javascript-&gt;link('/js/ext-2.0.1/ext-custom.js'); ?&gt;

&lt;script type="text/javascript"&gt;
Ext.BLANK_IMAGE_URL = '&lt;?php echo $html-&gt;url('/js/ext-2.0.1/resources/images/default/s.gif') ?&gt;';

Ext.onReady(function(){

   var getnodesUrl = '&lt;?php echo $html-&gt;url('/categorias/getnodes') ?&gt;';
   var reorderUrl = '&lt;?php echo $html-&gt;url('/categorias/reorder') ?&gt;';
   var reparentUrl = '&lt;?php echo $html-&gt;url('/categorias/reparent') ?&gt;';

   var Tree = Ext.tree;

   var tree = new Tree.TreePanel({
      el:'tree-div',
      autoScroll:true,
      animate:true,
      enableDD:true,
      containerScroll: true,
      rootVisible: true,
      loader: new Ext.tree.TreeLoader({
         dataUrl:getnodesUrl
      })
    });

   var root = new Tree.AsyncTreeNode({
      text:'Categorías',
      draggable:false,
      id:'root'
   });

   tree.setRootNode(root);
   tree.setHeight('auto');

   var oldPosition = null;
   var oldNextSibling = null;

   tree.on('startdrag', function(tree, node, event){
      oldPosition = node.parentNode.indexOf(node);
      oldNextSibling = node.nextSibling;
   });

   tree.on('movenode', function(tree, node, oldParent, newParent, position){

   if (oldParent == newParent){
      var url = reorderUrl;
      var params = {'node':node.id, 'delta':(position-oldPosition)};
   } else {
      var url = reparentUrl;
      var params = {'node':node.id, 'parent':newParent.id, 'position':position};
   }

tree.disable();

Ext.Ajax.request({
   url:url,
   params:params,
   success:function(response, request) {
      // if the first char of our response is zero, then we fail the operation,
      // otherwise we re-enable the tree
      if (response.responseText.charAt(0) != 1){
         request.failure();
      } else {
         tree.enable();
      }
   },
   failure:function() {
      // we move the node back to where it was beforehand and
      // we suspendEvents() so that we don't get stuck in a possible infinite loop
      tree.suspendEvents();
      oldParent.appendChild(node);
      if (oldNextSibling){
         oldParent.insertBefore(node, oldNextSibling);
      }
      tree.resumeEvents();
      tree.enable();
      alert("Oh no! Your changes could not be saved!");
   }
});
});

tree.render();
root.expand();
});
&lt;/script&gt;

&lt;div id="tree-div"&gt;&lt;/div&gt;</code></pre>
<p>Y la última vista:</p>
<pre class="prettyprint"><code>&lt;?php
// app/views/categorias/getnodes.ctp
$data = array();
foreach ($nodes as $node)
{</code>   <code>$data[] = array(</code>      <code>"text" =&gt; $node['Categoria']['nombre'],</code>      <code>"id" =&gt; $node['Categoria']['id']</code>   <code>);
}
echo $javascript-&gt;object($data);
?&gt;</code></pre>
<p>Ya sólo nos quedaría descargarnos la librería <a title="Ext JS" href="http://www.extjs.com/" target="_blank">ExtJS</a> y copiarla en la carpeta /app/webroot/js. En el <a title="Demostración de funcionamiento de Ext JS" href="http://cakephp.hospedaxes.com/pruebas/tree_behavior">ejemplo</a>, se ha utilizado la versión 2.0.1.</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 289px; width: 1px; height: 1px;">
<pre style="display: block;">Categoria</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://cakephp.hospedaxes.com/tree-behavior-o-como-crear-una-estructura-jerarquica/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
