<?xml version="1.0" encoding="UTF-8"?><!-- generator="bbPress" -->

<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
>

<channel>
<title>Dudas de cakephp &#187; Tag: tree behavior - Recent Posts</title>
<link>http://cakephp.hospedaxes.com/dudas/</link>
<description>Just another bbPress community</description>
<language>en</language>
<pubDate>Thu, 09 Sep 2010 03:03:01 +0000</pubDate>

<item>
<title>nuria en "Tree Behaviour o cómo crear una estructura jerárquica"</title>
<link>http://cakephp.hospedaxes.com/dudas/topic/tree-behaviour-o-como-crear-una-estructura-jerarquica#post-22</link>
<pubDate>Mie, 14 Oct 2009 08:42:35 +0000</pubDate>
<dc:creator>nuria</dc:creator>
<guid isPermaLink="false">22@http://cakephp.hospedaxes.com/dudas/</guid>
<description>&#60;blockquote&#62;&#60;p&#62;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.&#60;/p&#62;
&#60;p&#62;Utilizaremos también la librería de Javascript Ext JS para poder manipular el árbol gráficamente de manera sencilla, utilizando &#60;em&#62;drag and drop&#60;/em&#62;.&#60;/p&#62;
&#60;p&#62;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:&#60;/p&#62;
&#60;ul&#62;
&#60;li&#62;parent_id: hace referencia al padre del elemento.&#60;/li&#62;
&#60;li&#62;lft: almacena el identificador del elemento de la izquierda en el mismo nivel.&#60;/li&#62;
&#60;li&#62;rght: almacena el identificador del elemento de la derecha en el mismo nivel.&#60;/li&#62;
&#60;/ul&#62;
&#60;p&#62;Incluiremos también los campos &#60;em&#62;id&#60;/em&#62; y &#60;em&#62;nombre&#60;/em&#62;, en una entidad denominada, por ejemplo, Categoria.&#60;/p&#62;
&#60;p&#62;El modelo lo haríamos de la siguiente manera:&#60;/p&#62;
&#60;p&#62;Este comportamiento tiene funciones muy útiles para la manipulación del árbol. Las más utilizadas quizás sean las siguientes:&#60;/p&#62;
&#60;ul&#62;
&#60;li&#62;children: devuelve los hijos de un nodo concreto, pudiendo seleccionar si deseamos obtener sólo los hijos directos o todos los hijos en el árbol.&#60;/li&#62;
&#60;li&#62;generatetreelist: función muy útil para obtener los elementos a introducir en un select de HTML.&#60;/li&#62;
&#60;li&#62;getpath: devuelve todo el path hasta el nodo especificado en un array.&#60;/li&#62;
&#60;li&#62;movedown y moveup: sirven para bajar o subir un nivel de un nodo del árbol.&#60;/li&#62;
&#60;li&#62;removefromtree: elimina un nodo del árbol sin perder la estructura, es decir, modificando el padre de sus hijos.&#60;/li&#62;
&#60;li&#62;reorder: reordena un nodo (arrastrando también a sus hijos) en función de los parámetros especificados.&#60;/li&#62;
&#60;/ul&#62;
&#60;p&#62;Para realizar la manipulación visual del árbol de categorías crearemos las siguientes funciones en el controlador &#60;em&#62;app/controllers/categorias_controller.php&#60;/em&#62; sería:&#60;/p&#62;
&#60;pre class=&#34;http://cakephp.hospedaxes.co/wp-admin/prettyprint&#34;&#62;&#60;code&#62;&#38;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-&#38;gt;params['form']['node']);
   // encontrar los hijos directos del nodo anterior
   $nodes = $this-&#38;gt;Categoria-&#38;gt;children($parent, true, null, 'Categoria.lft ASC');

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

   $this-&#38;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-&#38;gt;params['form']['node']);
   $delta = intval($this-&#38;gt;params['form']['delta']);

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

   exit('1');
}

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

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

   // guardamos el nuevo padre de la categoría
   $this-&#38;gt;Categoria-&#38;gt;id = $node;
   $this-&#38;gt;Categoria-&#38;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-&#38;gt;Categoria-&#38;gt;moveup($node, true);
   } else {
      $count = $this-&#38;gt;Categoria-&#38;gt;childcount($parent, true);
      $delta = $count - $position - 1;
      if ($delta &#38;gt; 0) {
         $this-&#38;gt;Categoria-&#38;gt;moveup($node, $delta);
      }
   }

   exit('1');
}
}
?&#38;gt;&#60;/code&#62;&#60;/pre&#62;
&#60;p&#62;Sólo nos quedaría hacer las vistas. En este caso, necesitamos crear 2 vistas: organizar.ctp y getnodes.ctp.&#60;/p&#62;
&#60;pre class=&#34;http://cakephp.hospedaxes.co/wp-admin/prettyprint&#34;&#62;// app/views/categorias/organizar.ctp

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

&#60;script type=&#34;http://cakephp.hospedaxes.co/wp-admin/text/javascript&#34;&#62;
Ext.BLANK_IMAGE_URL = '&#60;?php echo $html-&#62;url('/js/ext-2.0.1/resources/images/default/s.gif') ?&#62;';

Ext.onReady(function(){

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

   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(&#34;Oh no! Your changes could not be saved!&#34;);
   }
});
});

tree.render();
root.expand();
});
&#60;/script&#62;
&#60;div id=&#34;tree-div&#34;&#62;&#60;/div&#62;
&#60;/pre&#62;
&#60;p&#62;Y la última vista:&#60;/p&#62;
&#60;pre class=&#34;http://cakephp.hospedaxes.co/wp-admin/prettyprint&#34;&#62;&#60;code&#62;&#38;lt;?php
// app/views/categorias/getnodes.ctp
$data = array();
foreach ($nodes as $node)
{&#60;/code&#62;   &#60;code&#62;$data[] = array(&#60;/code&#62;      &#60;code&#62;&#34;text&#34; =&#38;gt; $node['Categoria']['nombre'],&#60;/code&#62;      &#60;code&#62;&#34;id&#34; =&#38;gt; $node['Categoria']['id']&#60;/code&#62;   &#60;code&#62;);
}
echo $javascript-&#38;gt;object($data);
?&#38;gt;&#60;/code&#62;&#60;/pre&#62;
&#60;p&#62;Ya sólo nos quedaría descargarnos la librería &#60;a title=&#34;http://cakephp.hospedaxes.co/wp-admin/Ext JS&#34; href=&#34;http://www.extjs.com/&#34; target=&#34;_blank&#34;&#62;ExtJS&#60;/a&#62; y copiarla en la carpeta /app/webroot/js. En el ejemplo, se ha utilizado la versión 2.0.1.&#60;/p&#62;
&#60;p&#62;Podemos ver una &#60;a title=&#34;http://cakephp.hospedaxes.co/wp-admin/Ejemplo de funcionamiento de Tree Behaviour&#34; href=&#34;http://cakephp.hospedaxes.com/pruebas/tree_behaviour&#34; target=&#34;_blank&#34;&#62;demostración de su funcionamiento&#60;/a&#62;.&#60;/p&#62;
&#60;div id=&#34;_mcePaste&#34; style=&#34;overflow: hidden; position: absolute; left: -10000px; top: 289px; width: 1px; height: 1px;&#34;&#62;
&#60;pre style=&#34;display: block;&#34;&#62;Categoria&#60;/pre&#62;
&#60;/div&#62;
&#60;/blockquote&#62;
&#60;p&#62;&#60;br/&#62;&#60;a href=&#34;http://cakephp.hospedaxes.com/tree-behaviour-o-como-crear-una-estructura-jerarquica&#34;&#62;Tree Behaviour o cómo crear una estructura jerárquica&#60;/a&#62;&#60;/p&#62;</description>
</item>

</channel>
</rss>
