msgbartop
Desarrollador web y android, con todo lo que ello implica
msgbarbottom

16 May 12 Selects anidados utilizando el atributo DATA de HTML5

Vamos a hacer uso del atributo DATA de HTML5 para crear dos selects anidados. En algunas ocasiones necesitamos saber que tipo de dato hemos seleccionado en el primer desplegable en lugar del valor en sí. Podría darse el caso de un primer select con razas de animales y el segundo (que cambiará según lo que hemos seleccionado en el primero) con comida para diferentes tipos de animal.

Creo que es más claro con un ejemplo:

1
2
3
4
5
6
<select id="animales">
  <option value="1" data-type="perro">Caniche</option>
  <option value="2" data-type="gato">Siamés</option>
  <option value="3" data-type="gato">Persa</option>
  <option value="3" data-type="perro">Pastor alemán</option>
</select>

En el segundo despegable mostraremos los diferentes tipos de comida según si hemos seleccionado un perro o un gato. En este caso nos da igual la raza, solo queremos saber que tipo de animal es.

Como se puede ver, he añadido el atributo data-type a cada option del select con el tipo de animal que es. Este valor personalizado lo podemos obtener fácilmente con jQuery tal que así:

1
2
3
4
5
6
7
8
$(function(){
    $('animales').change(function(){
       var seleccionado = $(this).find('option:selected');
       // 'type' es lo que va a continuación del guión en data-type
       var animal = seleccionado.data('type');
       ...
    });
});

También podemos acceder a este valor con Javascript básico:

1
2
3
var animales = document.getElementById('animales');
var seleccionado = animales.options[animales.selectedIndex];
var animal = seleccionado.getAttribute('data-type');

Ahora ya podremos generar el segundo desplegable con los diferentes tipos de comida según el tipo de animal que hemos seleccionado.

Más información sobre los atributos DATA de HTML5: HTML5 Custom Data Attributes (data-*)

Etiquetas: , , , , , ,

07 May 12 Desplegable con una relación entre entities en un formulario de Symfony2

En las entidades de Symfony2 es sencillo relacionar un campo con otra tabla con una relación ManyToOne y que este campo solo pueda tomar un valor que esté disponible en la segunda entity. Si creamos un formulario para poder introducir nuevos registros en la base de datos, necesitamos un campo del tipo select para seleccionar uno de los datos de la segunda tabla y relacionarlos.

Deberemos modificar el formulario y añadirle un par de opciones más. En este ejemplo, vamos a relacionar un usuario con su país.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
namespace Acme\MiBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
 
class PerfilType extends AbstractType {
 
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('pais', 'entity',
            array(
                'class' => 'AcmeMiBundle:Pais'
                'label' => 'Pais',
            )
        );
    }
}

Que no se nos olvide añadir el método __toString() a la entity Pais:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace Acme\MiBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
class Pais
{
 
    [...]
 
    public function __toString()
    {
        return $this->getNombre();
    }
}

Y así de sencillo, ahora en nuestra plantilla aparecerá automáticamente el campo de tipo select a la hora de seleccionar el país del usuario.

Etiquetas: , , , ,

29 Mar 12 Error con la función indexOf() al utilizarla con arrays en Internet Explorer

Trabajando con arrays en javascript, una función bastante interesante con la que contamos es con indexOf() para obtener la posición de la primera ocurrencia del valor especificado en un array.

Casualmente, esta función no está disponible para arrays en Internet Explorer 8 y anteriores pero si que está disponible para utilizar con strings.

Con este sencillo “hack” podremos contar con esta función cuando manejemos arrays.

1
2
3
4
5
6
7
8
9
10
if(!Array.indexOf){
    Array.prototype.indexOf = function(obj){
        for(var i=0; i<this.length; i++){
            if(this[i]==obj){
                return i;
            }
        }
        return -1;
    }
}

Etiquetas: , , , , ,

18 Mar 12 Error con Symfony2: You cannot refresh a user from the EntityUserProvider that does not contain an identifier

He creado dos bundles, uno llamado UserBundle y otro ProfileBundle. El bundle UserBundle es totalmente independiente y reutilizable en cualquier proyecto (contando con funcionalidades tales como registro, login, logout, recordar contraseña, confirmar usuario mediante email, etc.) con la única pega de que solamente se guardan el email y la contraseña del usuario. Hay proyectos que solo requieren estos datos y no merece la pena “ensuciarlo” con más.

El bundle ProfileBundle subsana esta limitación y otorga al usuario un perfil con su nombre, apellidos, etc. El registro se hace desde el UserBundle y, una vez logueado en la aplicación, completas tu perfil de usuario mediante el ProfileBundle.

Al terminar de completar tu perfil y pulsar el botón guardar, el usuario logueado se recarga con los nuevos datos introducidos. Al hacer esto, me aparecía el siguiente error:

You cannot refresh a user from the EntityUserProvider that does not contain an identifier. The user object has to be serialized with its own identifier mapped by Doctrine.

Y en otras ocasiones también se mostraba este otro error:

Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken::serialize() must return a string or NULL

La solución es sencilla y solo hay que fijarse en el mensaje de error. Si el usuario se va a recargar una vez ya está logueado, la entidad Usuario debe implementar la clase Serializable y definir sus métodos serialize() y unserialize().

En diferentes blogs que hablan sobre Symfony2, en los métodos serialize y unserialize hacen uso del método getUsername() lo cual puede ser un error. Lo muestro con un ejemplo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class User implements UserInterface, \Serializable
{
 
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /**
     * @var string $email
     *
     * @ORM\Column(name="email", type="string", length=255, unique=true)
     * @Assert\Email()
     */
    private $email;
 
    [...]
 
    function getUsername()
    {
        return $this->getEmail();
    }
 
    public function serialize()
    {
       return serialize($this->getUsername());
    }
 
    public function unserialize($data)
    {
        $this->email = unserialize($data);
    }
}

En el ejemplo anterior vemos que la propiedad $email no es la clave primaria y, aunque hayamos marcado que es única, nos seguirá apareciendo el error anterior ya que Doctrine necesita que el refresh del usuario se haga mediante su identificador único (en algún caso podría llegar a ser el email, pero no en este ejemplo).

Solamente tendremos que cambiar el código anterior por este:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class User implements UserInterface, \Serializable
{
 
    /**
     * @var integer $id
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;
 
    /**
     * @var string $email
     *
     * @ORM\Column(name="email", type="string", length=255, unique=true)
     * @Assert\Email()
     */
    private $email;
 
    [...]
 
    function getUsername()
    {
        return $this->getEmail();
    }
 
    public function serialize()
    {
       return serialize($this->getId());
    }
 
    public function unserialize($data)
    {
        $this->id = unserialize($data);
    }
}

El código anterior se puede optimizar y se podrían añadir más propiedades a los métodos serialize y unserialize (se podrían utilizar en conjunto las propiedades id, email, etc.).

Etiquetas: , , , , , , , ,

28 Feb 12 Symfony2: Añadir a un formulario un campo que no está definido en el modelo

He estado desarrollando mi propio bundle para el registro de usuarios en Symfony2 y me he encontrado con el problema de querer añadir campos al formulario de registro que no he definido en el modelo. Uno de estos campos es un checkbox para aceptar los términos de uso de la aplicación. No me interesa guardar este campo en la base de datos por lo que no está definido en el modelo.

Así es como lo he hecho:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class RegisterFormType extends AbstractType
{
 
    public function buildForm(FormBuilder $builder, array $options)
    {
 
        $builder
            ->add('email', 'email')
            ->add('password', 'repeated', array(
                'type' => 'password',
                'invalid_message' => 'Las dos contraseñas deben coincidir',
                'options' => array('label' => 'Contraseña'),
                'required' => false))
	    ->add("accept_tos",
                "checkbox", array(
                    "property_path" => false,
                )
            );
 
 
        $builder
            ->addValidator(new CallbackValidator(function(FormInterface $form){
 
                if (!$form["accept_tos"]->getData())
                {
                    $form->addError(new FormError('Debes aceptar los términos de uso'));
                }
            })
        );
    }
 
}

Como vemos, añadimos un campo checkbox con nombre “accept_tos” y le indicamos la opción “property_path” a false, por lo que no validará este campo con el modelo. Eso sí, debemos añadir un validador para este campo para asegurarnos de que el usuario acepta las condiciones de uso.

Fuente: http://www.richsage.co.uk/2011/07/20/adding-non-entity-fields-to-your-symfony2-forms/

Etiquetas: , , , , , ,

19 Feb 12 Leer/parsear archivo XML en Android mediante SAX

Con motivo del concurso #BigBangApps convocado por la web Ideas4All en que proponían desarrollar una aplicación utilizando su API, me puse a programar una aplicación para Android de la web. La API de Ideas4All devuelve los datos en XML (hubiese preferido en JSON pero de momento no lo permite) por lo que el “gran secreto” de la app es leer correctamente este tipo de archivos.

Hay varias formas de leer archivos XML, siendo una de ellas mediante SAX (Simple API for XML). En este post voy a explicar los pasos para leer/parsear correctamente un archivo XML disponible en Internet.

El fichero de ejemplo que queremos leer es el listado de categorías:

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="UTF-8"?>
<categories type="array">
  <category>
    <id type="integer">6</id>
    <name>Vida/Salud</name>
  </category>
  <category>
    <id type="integer">3</id>
    <name>Sostenibilidad</name>
  </category>
...
...
</categories>

Lo primero que necesitamos es una clase que utilizaremos para guardar los datos de cada categoría. El objetivo es devolver una lista de objetos del tipo categoría, donde se encontraran todas las categorías leídas del XML.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ParsedCategoryDataSet {
 
    private String id = null;
    private String name = null;
 
    public String getId() {
      return id;
    }
    public void setId(String extractedString) {
     this.id = extractedString;
    }    
 
    public String getName() {
     return name;
    }
    public void setName(String extractedString) {
     this.name = extractedString;
    }
 
    public String toString(){
         return "name = " + this.name;
    }	
}

Como podemos observar, solamente se trata de una clase con setters y getters para almacenar los datos de cada categoría.

Ahora pasamos a crear la clase con la que parsearemos el archivo XML mediante SAX en Android.
El modelo SAX lee secuencialmente el fichero XML y ejecuta varios métodos (que podemos controlar) por cada elemento leído. Estos métodos son:

- startDocument(): comienza a leer el XML.
- endDocument(): ha terminado de leer XML.
- startElement(): ha encontrado el comienzo de una etiqueta XML.
- endElement(): ha encontrado el cierre de una etiqueta XML.
- characters(): texto que ha encontrado entre el comienzo y el cierre de una etiqueta

Estos métodos se encuentran en la clase org.xml.sax.helpers.DefaultHandler, de la que heredaremos nuestro propio parseador de categorías:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import java.util.Vector;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
 
public class CategoryHandler extends DefaultHandler{
 
    public CategoryHandler() {
	super();
	this.myParsedCategoryDataSet = new Vector<ParsedCategoryDataSet>();
    }	
 
    // Variables de control para saber cuando estamos en el interior de cada etiqueta
    @SuppressWarnings("unused")
    private boolean in_category = false;
    private boolean in_id = false;
    private boolean in_name = false;
 
    // En esta variable guardamos el texto encontrado entre las etiquetas
    StringBuilder builder;
 
    // Aquí guardamos cada objeto categoria
    private ParsedCategoryDataSet DataSet;
 
    // Vector donde se guardaran todas las categorías encontradas
    private Vector<ParsedCategoryDataSet> myParsedCategoryDataSet; 
 
    public Vector<ParsedCategoryDataSet> getParsedCategoryDataSets() {
        return this.myParsedCategoryDataSet;
    }      
 
    public Vector<ParsedCategoryDataSet> getParsedData() {
         return this.myParsedCategoryDataSet;
    }
 
    @Override
    public void startDocument() throws SAXException {
         // Comenzamos a leer el fichero xml, creamos el vector donde se guardarán las categorías
         this.myParsedCategoryDataSet = new Vector<ParsedCategoryDataSet>();
    }
 
    @Override
    public void endDocument() throws SAXException {
         // Ha terminado de leer el fichero, en este paso no hacemos nada
    }     
 
    @Override
    public void startElement(String namespaceURI, String localName,
              String qName, Attributes atts) throws SAXException {
         if (localName.equals("category")) {
             // Ha encontrado la etiqueta principal de cada elemento "category"
             // Creamos un nuevo objeto categoría donde iremos guardando los datos
             this.in_category = true;
             DataSet = new ParsedCategoryDataSet();
         }else if (localName.equals("id")) {
             // Estamos dentro de la etiqueta "id", creamos el StringBuilder que utilizaremos
             // en el método characters para guardar el contenido
             this.in_id = true;
             builder = new StringBuilder();
         }else if (localName.equals("name")) {
             // Estamos dentro de la etiqueta "name", creamos el StringBuilder que utilizaremos
             // en el método characters para guardar el contenido
             this.in_name = true;
             builder = new StringBuilder();
         }
    } 
 
    @Override
    public void endElement(String namespaceURI, String localName, String qName)
              throws SAXException {
        if (localName.equals("category")) {
            // Hemos llegado al final de la etiqueta principal de cada elemento "category"
            // Añadimos al vector el elemento leído
            this.in_category = false;
            myParsedCategoryDataSet.add(DataSet);
        }else if (localName.equals("id")) {
            // Ha encontrado la etiqueta de cierre de "id"
            this.in_id = false;
        }else if (localName.equals("name")) {
            // Ha encontrado la etiqueta de cierre de "name"
            this.in_name = false;
        }
    } 
 
    @Override
    public void characters(char ch[], int start, int length) {
 
       // Si estamos dentro de la etiqueta "id"
       if(this.in_id){
       	   if (builder!=null) {
       	        for (int i=start; i<start+length; i++) {
                    // Añadimos al StringBuilder (definido al encontrar el comienzo de la etiqueta "id")
                    // lo que haya entre las etiquetas de inicio y fin
       	            builder.append(ch[i]);
       	        }
       	   }        	 
           // Lo asignamos al "id" del objeto categoría (DataSet)
       	   DataSet.setId(builder.toString()); 
       }
 
       // Si estamos dentro de la etiqueta "id"
       if(this.in_name){
           if (builder!=null) {
    	       for (int i=start; i<start+length; i++) {
                    // Añadimos al StringBuilder (definido al encontrar el comienzo de la etiqueta "name")
                    // lo que haya entre las etiquetas de inicio y fin
    	            builder.append(ch[i]);
    	        }
     	   }            	 
           // Lo asignamos al "name" del objeto categoría (DataSet)
           DataSet.setName(builder.toString()); 
       }   
 
   } 
}

El código está comentado y creo que se explica bastante bien. Me consta que algunos programadores leen el contenido que se encuentra entre las etiquetas de inicio y cierre, cuando llegan a esta última etiqueta en lugar de en el método characters. Yo me he encontrado con muchos problemas haciéndolo así ya que se corre el riesgo de no conseguir leer todos los caracteres que realmente hay. La propia documentación del método lo explica:

The Parser will call this method to report each chunk of character data. SAX parsers may return all contiguous character data in a single chunk, or they may split it into several chunks; however, all of the characters in any single event must come from the same external entity so that the Locator provides useful information.

Ya tenemos casi todo lo necesario para leer el fichero XML de la API. Solamente nos queda implementar el método que parseará el archivo utilizando nuestra clase.
Esto solo es el extracto del método que lee el XML, encontraréis el código completo de la activity aquí, donde también se hace uso de la combinación de ProgressDialog y Thread para mostrar un mensaje de “cargando” mientras se leen los datos (este tema da para otro post :) ).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
...
...
private Vector<ParsedCategoryDataSet> categories;
private String categories_url = "http://url_de_la_api/categories.xml";
...
...
 
public void loadCategories() {
 
    try {
 
        // Url del archivo XML
        URL url = new URL(categories_url);
 
        SAXParserFactory spf = SAXParserFactory.newInstance();
        SAXParser sp = spf.newSAXParser();
 
        XMLReader xr = sp.getXMLReader();
        // Utilizamos nuestro propio parseador (CategoryHandler)
        CategoryHandler myExampleHandler = new CategoryHandler();
        xr.setContentHandler(myExampleHandler);
 
        InputSource is = new InputSource(url.openStream());
        // Le indicamos la codificación para evitar errores
        is.setEncoding("UTF-8");
        xr.parse(is);
 
        // Asignamos al vector categories los datos parseados
        categories = myExampleHandler.getParsedData();
 
     } catch (Exception e) {
          // Ha ocurrido algún error
          Log.e("Ideas4All", "Error", e);
     }        
 
}

Y eso es todo, no olvidéis indicar en el AndroidManifest.xml solicitar permisos de acceso a Internet.

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

Podéis encontrar la aplicación completa en github:
Código fuente de la aplicación de Ideas4All para Android

Etiquetas: , , , , , , , ,

17 Feb 12 Seleccionar variable php completa (incluido el ‘dolar’) en Sublime Text 2

Un detalle de Sublime Text 2 bastante incómodo cuando estás programando en PHP, es que cuando hacemos doble click en una variable para seleccionarla, solamente selecciona el nombre de la variable excluyendo el símbolo dolar ($).

La solución es muy sencilla, solo debemos ir a:

Preferences / File Settings - Defaults

Allí encontraremos la línea:

"word_separators": "./\\()\"'-:,.;<>~!@#$%^&*|+=[]{}`~?",

Solamente debemos quitar el símbolo $ de la línea anterior, dejándolo así:

"word_separators": "./\\()\"'-:,.;<>~!@#%^&*|+=[]{}`~?",

Si ahora cerramos y volvemos a abrir el editor, ya se seleccionará la variable completa.

Truco encontrado aquí:
http://www.sublimetext.com/forum/viewtopic.php?f=3&t=2704

Etiquetas: , ,

13 Feb 12 Paso de datos/variables entre actividades en Android

Una de las cosas más habituales cuando desarrollamos una aplicación para Android, es poder “enviar” datos (variables, objetos, etc.) desde una Activity a otra. En términos generales, una Activity es una “pantalla” de nuestra aplicación.

Para realizar esta tarea, Android pone a nuestra disposición los llamados “Intent“. En general, utilizaremos un Intent cuando queramos movernos de una actividad a otra, permitiéndonos a su vez pasar datos desde la Activity en la que estamos hacia la nueva.

1
2
3
4
5
6
// Pasaremos de la actividad actual a OtraActivity
Intent intent = new Intent(this, OtraActivity.class);
intent.putExtra("variable_integer", objeto.getId());
intent.putExtra("variable_string", objeto.getNombre());
intent.putExtra("objeto_float", objeto.getPrecio());
startActivity(intent);

- En la segunda línea creamos un Intent, al que se le pasa como parámetros la actividad actual (this) y la actividad a la que queremos pasar (en este caso OtraActivity.class).

- En las tres líneas siguientes definimos tres variables de tres tipos diferentes. En el primer caso, estaremos creando una variable llamada “variable_integer” con el contenido de objeto.getId(). Esta variable se “envía” a OtraActivity y podremos recuperar su contenido allí para utilizarlo.

- En la última línea, iniciamos la nueva actividad pasando como parámetro el Intent que hemos creado.

El código que viene a continuación es el que se ejecutará en la actividad OtraActivity. Se muestran dos formas de recuperar las variables, se puede utilizar la que más convenga en cada ocasión.

1
2
3
4
5
6
7
8
9
10
11
12
// Estamos en OtraActivity
int recuperamos_variable_integer = getIntent().getIntExtra("variable_integer");
String recuperamos_variable_string = getIntent().getStringExtra("variable_string");
float recuperamos_variable_float = getIntent().getFloatExtra("objeto_float");
 
// O también de esta otra forma
 
// Estamos en OtraActivity
Bundle datos = this.getIntent().getExtras();
int recuperamos_variable_integer = datos.getInt("variable_integer");
String recuperamos_variable_string = datos.getString("variable_string");
float recuperamos_variable_float = datos.getFloat("objeto_float");

Es importante que sepamos de que tipo son las variables que enviamos en el Intent, ya que deberemos asignar esos valores al tipo de variable correcto.

Etiquetas: , , , , , , ,

09 Feb 12 Lanzador de Sublime Text 2 en Linux

Sublime Text 2 es un editor de texto que se está haciendo muy popular debido a su facilidad de uso y su entorno minimalista. Sublime Text 2 hace que solo te concentres en el código que estás escribiendo.

Si nos descargamos el programa desde su propia web, descubrimos que al descomprimirlo se trata de una carpeta donde se encuentra todo lo necesario para ejecutar el programa. Cada vez que queramos abrirlo, debemos entrar en esta carpeta y ejecutar el archivo de la aplicación (también podemos crear un acceso directo en el escritorio). En esta carpeta no encontraremos ni siquiera el icono de Sublime Text 2.

¿Cómo solucionarlo?

- Primero nos descargamos el icono del programa para que el lanzador quede “bonito” :)

- Creamos un enlace de la aplicación a la carpeta /usr/bin/:

sudo ln -s /home/jonseg/sublime-text-2/sublime_text /usr/bin/sublime_text

- Copiamos el icono que nos hemos descargado antes al directorio donde se encuentran los iconos del sistema:

sudo cp sublime.png /usr/share/icons/

- Creamos el archivo sublime.desktop con el siguiente contenido:

[Desktop Entry]
Version=2.0
Type=Application
Name=Sublime Text 2
Comment=Editor Sublime Text 2
TryExec=sublime_text
Exec=sublime_text %F
Icon=/usr/share/icons/sublime.png
MimeType=text/plain;

Y lo copiamos al directorio /usr/share/applications/:

sudo cp sublime.desktop /usr/share/applications/

Y listo. Si pulsamos la tecla de Windows de nuestro teclado (en Unity), el resultado debe ser algo parecido a esto:

Etiquetas: , , , , ,

23 Nov 11 Atajos de teclado útiles en Netbeans para PHP

Netbeans es mi IDE de desarrollo favorito cuando estoy programando en PHP. Podemos mejorar nuestra productividad utilizando Netbeans haciendo uso de sus numerosos atajos de teclado que nos facilitará el trabajo. Aunque sé que hay bastantes más (y por supuesto para otros lenguajes), voy a intentar enumerar los más útiles que conozco. Si vosotros conocéis alguno más, no dudéis en escribir un comentario y lo añadiré al post.

  • [CTRL] + [SPACE] : Completa el código que estamos escribiendo. Por ejemplo, si queremos escribir LocateRegistry solamente escribimos Loc y presionamos el atajo para que nos sugiera la palabra completa. Si presionamos dos veces el atajo, muestra todas las opciones que comiencen con los caracteres escritos.
  • [ALT] + [INSERT] : Agrega constructores, métodos accesores, propiedaes, overrides, etc..
  • [ALT] + [ENTER] : Se usa sobre el texto subrayado para ver la sugerencia que nos hace el IDE cuando hay algún error de sintaxis o se necesita realizar algun import o surround
  • [CTRL] + [R]: Si lo hacemos sobre el identificador, renombra todas las ocurrencias.
  • Generador de documentación PHPDoc: Si posicionamos el cursor en la línea anterior donde empieza la declaración de la función, escribimos /** y pulsamos [ENTER], automáticamente se nos generará la documentación PHPDoc de esa función.
  • [ALT]+[SHIFT]+F : Tabula el código automáticamente.
  • [SHIFT]+[ESC] : Maximiza o minimiza la ventana donde tecleamos el codigo.
  • [CTRL]+B ó [CTRL]+(click) : Nos abre el archivo donde se encuentra la declaración de la variable/funcion/clase.
  • [CTRL]+[TAB] : Lista de los archivos que tenemos abierto para cambiar de uno a otro rápidamente.
  • [CTRL]+[SHIFT]+UP ó [CTRL]+[SHIFT]+DOWN : Copia la linea en la que estamos en la fila superior o inferior.
  • [ALT]+[SHIFT]+UP ó [ALT]+[SHIFT]+DOWN : Moveremos la línea en la que nos encontramos hacia arriba o hacia abajo.
  • [CTRL]+E : Borra la línea en la que estamos.
  • [CTRL]+[SHIFT]+C : Comenta la línea actual. Si hacemos una selección, comentará todo el código seleccionado.
  • [CTRL]+R : Renombramos una variable/función/clase y hace el cambio automáticamente en el resto del código (MUY útil).

Vamos a ver a continuación unos atajos para escribir código mucho más rápido con Netbeans. Tan solo tendremos que escribir la primera palabra que se indica y pulsar la tecla del tabulador.

  • fore + [TAB] : Genera un bucle foreach típico.
    foreach ($array as $value) {
     
    }
  • forek + [TAB] : Nos generará un bucle foreach, extrayendo también la clave del array.
    foreach ($array as $key => $value) {
     
    }
  • if + [TAB] : Autocompleta el código de un bloque if.
    if (true) {
     
    }
  • swi + [TAB] : Genera un bloque switch.
    switch ($categoriesLlistat) {
        case $value:
     
            break;
        default:
     
            break;
    }
  • while + [TAB] : Escribirá automáticamente un bucle while.
    while (true) {
     
    }
  • my_fa + [TAB] : Genera un bucle while, extrayendo los datos de una consulta mysql en forma de array.
    while ($row = mysql_fetch_array($query)) {
     
    }
  • my_fo + [TAB] : Genera un bucle while, extrayendo los datos de una consulta mysql en forma de objeto.
    while ($row = mysql_fetch_object($query)) {
     
    }
  • my_fr + [TAB] : Genera un bucle while, extrayendo los datos de una consulta mysql en forma de filas.
    while ($row = mysql_fetch_row($query)) {
     
    }

También se pueden generar atajos de código definidos por nosotros mismos. Ese es un tema que da para otro post :)

Etiquetas: , , , , , ,