viernes, 3 de junio de 2011

Crear campos personalizados y taxonomias

Una de las grandes novedades que incorpora WordPress 3.0 es la posibilidad de crear Entradas Personalizadas (Custom Post Types), es decir un nuevo tipo de entradas parecidas a las Entradas normales y a las Páginas, y que aparecerán también de manera separada en el menú de la izquierda del administrador de WordPress. ¿Qué ventajas nos ofrecen las Entradas Personalizadas? Una mayor facilidad de uso de nuestro tema, estructurando mejor los distintos tipos de entradas. Por ejemplo, si queremos insertar una lista de productos con una serie de características comunes, podemos crear una estradas personalizadas que se llamen Producto y que estén configuradas de manera específica para insertar dichos productos. En vez de decirle a nuestro cliente que cree una entrada normal y que rellene toda una serie de campos personalizados sólo utilizables para insertar un producto pero no para cualquier otra entrada, que no se equivoque con las categorías que son sólo para productos y no para el resto de entradas, etc.. ahora símplemente tendrá que hacer click en “Productos” y rellenar cada campo específico para ese tipo de entrada.

Pero como siempre, esto se verá mejor con un ejemplo. Vamos a crear una entrada personalizada que se llame “Productos”. Con el permiso de Friendware (su web la hice yo hace un par de años) vamos a usar algunos de sus juegos para PC. Cada producto va a tener un título, una descripción, un precio, un fabricante, una plataforma y una imagen.

Vamos a usar campos personalizados para el precio y el fabricante y taxonomías personalizadas para la Plataforma (en realidad Fabricante debería ser una taxonomía, pero lo pongo como campo personalizado para ver mejor cómo se crean varios).
En el administrador vamos a tener en el menú de la izquierda:

Si hacemos click sobre Añadir tendremos:

Y al hacer click sobre Editar tendremos una tabla con todos los productos:


Pero ahora viene lo mejor.
Si tenemos activados los enlaces permanentes podemos ir a una url que sea por ejemplo www.miweb.es/Productos/ donde se vean todos los productos introducidos con los estilos que hayamos definido:


Bien, ya sabemos lo que vamos a crear. Ahora vamos a ver paso a paso cómo crearlo.
No he visto ningún artículo completo que indique todos lo pasos de manera completa. Por ello me he basado en diferentes artículos y webs. Sus enlaces están al final del artículo.

Paso 1: Crear una Entrada Personalizada

La mejor manera que he encontrado para crear una Entrada Personalizada es utilizando la clase creada por Matt Wiebe. La razón es que además de crear una función que nos permite crear Entradas Personalizadas con gran facilidad, añade a la función que habitualmente crea entradas personalizadas (register_post_type) una serie de funcionalidades extra:
1. URLs personalizadas para cada Entrada Personalizada. (ejemplos http://miweb.com/productos/, http://miweb.com/productos/page/2/, http://miweb.com/productos/feed/ )
2. Archivos personalizados para cada Entrada Personalizada, por ejemplo producto/index.php o producto.php en el directorio de nuestro tema.
3. Páginas individuales personalizadas: WP busca el fichero single-producto.php (en su defecto por single.php). Con esta función podemos usar producto/single.php para una mejor organización..
4. Añade clases a body_class() y a post_class().
5. Auto genera las etiquetas apropiadas para el Administrador si no suministras una.
El punto 1 me parece muy importante, ya que WordPress 3.0, al menos en la beta 2, no crea por defecto una url donde podamos ver todas las entradas de una misma entrada personalizada (por ejemplo, todos los productos).
El código, que habría que llevar al archivo functions.php, lo podemos descargar desde la web de Matt Wiebe. Una vez tengamos ese código pegado en el archivo functions.php de nuestro tema, escribiremos a continuación para crear la Entrada Personalizada “Producto”:
1
sd_register_post_type( 'Producto' );
Aquí está el código completo. Hay que tener en cuenta que Matt Wiebe está continuamente mejorando esta función añadiendo nuevas características, por lo que estad pendientes de sus actualizaciones.
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/*
 * Copyright 2010 Matt Wiebe.
 *
 * This code is licensed under the GPL v2.0
 * http://www.opensource.org/licenses/gpl-2.0.php
 *
 * If you do something cool with it, let me know! http://somadesign.ca/contact/
 *
 * Version 1.3
 *
 * === Changelog ===
 *
 * 1.0
 *  - Initial release
 * 1.1
 *  - Added feed support in URL rewrites
 * 1.2
 *  - Removed redundant post_class code.
 *  - Removed redundant single post_type template code
 *  - Introduced directory support for template files
 * 1.3
 *  - Use the newer, more robust labels array to set defaults
 *  - Add possible support for adding post_type to nav_menus. Commented out by default since the 'show_in_nav_menus' $arg should provide for that, although it doesn't seem to work right now.
 *
 */
 
/**
 * SD_Register_Post_Type class
 *
 * @author Matt Wiebe
 * @link http://somadesign.ca
 *
 * @param string $post_type The post type to register
 * @param array $args The arguments to pass into @link register_post_type(). Some defaults provided to ensure the UI is available.
 * @param string $custom_plural The plural name to be used in rewriting (http://yourdomain.com/custom_plural/ ). If left off, an "s" will be appended to your post type, which will break some words. (person, box, ox. Oh, English.)
 **/
 
if ( ! class_exists('SD_Register_Post_Type') ) {
 
 class SD_Register_Post_Type {
 
  private $post_type;
  private $post_slug;
  private $args;
  private $post_type_object;
 
  private $defaults = array(
   'show_ui' => true,
   'public' => true,
   'supports' => array('title', 'editor', 'thumbnail')
  );
 
  private function set_defaults() {
   $plural = ucwords( $this->post_slug );
   $post_type = ucwords( $this->post_type );
 
   $this->defaults['labels'] = array(
    'name' => $plural,
    'singular_name' => $post_type,
    'add_new_item' => 'Add New ' . $post_type,
    'edit_item' => 'Edit ' . $post_type,
    'new_item' => 'New ' . $post_type,
    'view_item' => 'View ' . $post_type,
    'search_items' => 'Search ' . $plural,
    'not_found' => 'No ' . $plural . ' found',
    'not_found_in_trash' => 'No ' . $plural . ' found in Trash'
   );
  }
 
  public function __construct( $post_type = null, $args = array(), $custom_plural = false ) {
   if ( ! $post_type ) {
    return;
   }
 
   // meat n potatoes
   $this->post_type = $post_type;
   $this->post_slug = ( $custom_plural ) ? $custom_plural : $post_type . 's';
 
   // a few extra defaults. Mostly for labels. Overridden if proper $args present.
   $this->set_defaults();
   // sort out those $args
   $this->args = wp_parse_args($args, $this->defaults);
 
   // magic man
   $this->add_actions();
   $this->add_filters();
 
  }
 
  public function add_actions() {
   add_action( 'init', array($this, 'register_post_type') );
   add_action( 'template_redirect', array($this, 'context_fixer') );
  }
 
  public function add_filters() {
   add_filter( 'generate_rewrite_rules', array($this, 'add_rewrite_rules') );
   add_filter( 'template_include', array($this, 'template_include') );
   add_filter( 'body_class', array($this, 'body_classes') );
  }
 
  public function context_fixer() {
   if ( get_query_var( 'post_type' ) == $this->post_type ) {
    global $wp_query;
    $wp_query->is_home = false;
   }
  }
 
  public function add_rewrite_rules( $wp_rewrite ) {
   $new_rules = array();
   $new_rules[$this->post_slug . '/page/?([0-9]{1,})/?$'] = 'index.php?post_type=' . $this->post_type . '&paged=' . $wp_rewrite->preg_index(1);
   $new_rules[$this->post_slug . '/(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?post_type=' . $this->post_type . '&feed=' . $wp_rewrite->preg_index(1);
   $new_rules[$this->post_slug . '/?$'] = 'index.php?post_type=' . $this->post_type;
 
   $wp_rewrite->rules = array_merge($new_rules, $wp_rewrite->rules);
   return $wp_rewrite;
  }
 
  public function register_post_type() {
   register_post_type( $this->post_type, $this->args );
  }
 
  public function template_include( $template ) {
   if ( get_query_var('post_type') == $this->post_type ) {
 
    if ( is_single() ) {
     if ( $single = locate_template( array( $this->post_type.'/single.php') ) )
      return $single;
    }
    else { // loop
     return locate_template( array(
      $this->post_type . '/index.php',
      $this->post_type . '.php',
      'index.php'
     ));
    }
 
   }
   return $template;
  }
 
  public function body_classes( $c ) {
   if ( get_query_var('post_type') === $this->post_type ) {
    $c[] = $this->post_type;
    $c[] = 'type-' . $this->post_type;
   }
   return $c;
  }
 
 
 } // end SD_Register_Post_Type class
 
 /**
  * A helper function for the SD_Register_Post_Type class. Because typing "new" is hard.
  *
  * @author Matt Wiebe
  * @link http://somadesign.ca
  *
  * @uses SD_Register_Post_Type class
  * @param string $post_type The post type to register
  * @param array $args The arguments to pass into @link register_post_type(). Some defaults provided to ensure the UI is available.
  * @param string $custom_plural The plural name to be used in rewriting (http://yourdomain.com/custom_plural/ ). If left off, an "s" will be appended to your post type, which will break some words. (person, box, ox. Oh, English.)
  **/
 
 if ( ! function_exists( 'sd_register_post_type' ) && class_exists( 'SD_Register_Post_Type' ) ) {
  function sd_register_post_type( $post_type = null, $args=array(), $custom_plural = false ) {
   $custom_post = new SD_Register_Post_Type( $post_type, $args, $custom_plural );
  }
 }
}
 
sd_register_post_type( 'Producto' );
Del código de Matt Wiebe hay que destacar esta parte:
1
2
3
4
5
private $defaults = array(
 'show_ui' => true,
 'public' => true,
 'supports' => array('title', 'editor', 'thumbnail')
);
La línea que pone ‘supports’ crea una cadena con los campos que soporta la Entrada Personalizada, en este caso el Título (title), el Contenido (editor) y la Imagen Destacada (thumbnail). Podemos ver todos los componentes posibles y más información sobre register_post_type en la web de wordpress. Como vemos, podríamos haber añadido ‘author’, ‘excerpt’ o ‘custom-fields’ por ejemplo.
Ahora podemos crear en el directorio de nuestro tema un archivo producto.php que recoja todos los productos introducidos y un archivo single-producto.php que muestre cada producto individual.
Archivo producto.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php get_header(); ?>
  <div id="container">
    <div id="content">
      <?php while ( have_posts() ) : the_post(); ?>
          <?php
             $custom = get_post_custom($post->ID);
             $precio = $custom["precio"][0];
             $fabricante = $custom["fabricante"][0];
           ?>
           <div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
            <h2 class="entry-title"><a href="<?php the_permalink(); ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></h2>
             <div class="entry-meta">
                   <div class="entry-content">
                    <?php the_post_thumbnail(); ?>
                    <?php the_content(); ?>
                     <p class="caracteristicas">Precio: <?=$precio?> - Fabricante: <?=$fabricante?> - <?php echo get_the_term_list($post->ID, 'plataforma', 'Plataforma: ', ', ', ''); ?></p>
                   </div>
             </div>
         </div>
      <?php endwhile; ?>
  <div><!-- #content -->
</div><!-- #container -->
<?php get_sidebar(); ?>
<?php get_footer(); ?>
Ojo: En la línea p class=”caracteristicas” hemos incluido la taxonomía “Plataforma” que aún no hemos creado (la creamos más abajo).
Archivo single-producto.php:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?php get_header(); ?>
   <div id="container">
     <div id="content">
       <?php the_post(); ?>
       <?php
         $custom = get_post_custom($post->ID);
         $precio = $custom["precio"][0];
         $fabricante = $custom["fabricante"][0];
      ?>
        <div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
         <h1 class="entry-title"><?php the_title(); ?> - <?=$precio?> - <?=$fabricante?></h1>
          <div class="entry-meta">
        <div class="entry-content">
      <?php the_post_thumbnail(); ?>
  <?php the_content(); ?>
    </div>
         </div>
      </div>
  </div><!-- #content -->
</div><!-- #container -->
<?php get_sidebar(); ?>
<?php get_footer(); ?>
Una vez tengamos todo este código listo podremos introducir nuestros Productos y podremos verlos todos en la url www.nuestraweb.com/Productos gracias a producto.php y podremos ver cada producto en concreto en la url www.nuestraweb.com/Productos/nombre-del-producto gracias a single-producto.php (hay que tener activados los enlaces permanentes. Si sale “página no encontrada” volved a grabar los enlaces permanentes ya que a veces necesitan actualizarse).

Paso 2- Crear los Campos Personalizados

A continuación vamos a crear los Campos Personalizados Precio y Fabricante para nuestras Entradas Personalizadas. Lo haremos siguiendo este artículo de Nettuts+. Ahora podremos darle a nuestros campos personalizados nombre propios claros para saber lo que es cada uno (en este caso Precio y Fabricante). Para ello añadimos a functions.php este código:
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
add_action("admin_init", "admin_init");
add_action('save_post', 'guardar_precio');
add_action('save_post', 'guardar_fabricante');
 
function admin_init(){
  add_meta_box("prodInfo-meta", "Opciones Producto", "meta_options", "Producto", "normal", "low");
}
function meta_options(){
  global $post;
  $custom = get_post_custom($post->ID);
  $precio = $custom["precio"][0];
  $fabricante = $custom["fabricante"][0];
?>
  <label>Precio:</label><input name="precio" value="<?php echo $precio; ?>" /><br /><br />
  <label>Fabricante:</label><input name="fabricante" value="<?php echo $fabricante; ?>" />
<?php
}
 
function guardar_precio(){
  global $post;
  update_post_meta($post->ID, "precio", $_POST["precio"]);
}
function guardar_fabricante(){
  global $post;
  update_post_meta($post->ID, "fabricante", $_POST["fabricante"]);
}
La primera linea indica que cuando se inicialice el panel de administración se llame a la función admin_init(). Esta función dice a WordPress que añada un área llamada “Opciones Producto” para cualquier Entrada Personalizada llamada Producto y que use la función meta_options() para mostrar los campos del formulario. Puedes leer más sobre add_meta_box aquí. meta_options() obtendrá entonces cualquier valor preexistente y lo mostrará en el formulario. Las dos lineas de acción que siguen a la primera línea hacen que cuando se grabe una entrada se llame a las funciones guardar_precio() y guardar_fabricante() que utilizan update_post_meta() para añadir o actualizar los campos personalizados “Precio” y “Fabricante”.

Paso 3 – Crear Taxonomías personalizadas asociadas

Una vez tengamos nuestras Entradas Personalizadas es posible que queramos tener una serie de Taxonomías que nos ayuden a clasificar los distintos productos según determinadas categorías. Podemos ver más información sobre las taxonomías en el artículo Taxonomías en WordPress.
Vamos a crear, como dijimos, una Taxonomía personalizada llamada “Plataforma”, donde podremos poner si el juego es para PC CD-ROM, Playstation 3, etc… Para ello escribimos en functions.php:
1
register_taxonomy("plataforma", array("Producto"), array("hierarchical" => true, "label" => "Plataforma", "singular_label" => "Plataforma", "rewrite" => true));
Puedes encontrar más información sobre la función register_taxonomy() en la web de WordPress. Aquí creamos una nueva Taxonomía llamada “Plataforma” y la asociamos a la entrada personalizada “Producto”. Para poder ver luego la taxonomía escribiríamos (como hemos visto más arriba en producto.php):
1
<?php echo get_the_term_list($post->ID, 'plataforma', 'Plataforma: ', ', ', ''); ?>

Paso 4 – Crear la tabla de productos en el panel de administración

Vimos al principio de este artículo que al hacer click sobre Editar en Producto salía una tabla con el nombre de cada producto, su descripción, precio, fabricante y plataforma. Esto no se crea por defecto. Para conseguirlo, tal y como vemos en el mismo artículo de nettuts+, hay que añadir en functions.php:
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
add_filter("manage_edit-Producto_columns", "prod_edit_columns");
add_action("manage_posts_custom_column",  "prod_custom_columns");
 
function prod_edit_columns($columns){
   $columns = array(
      "cb" => "<input type=\"checkbox\" />",
      "title" => "Nombre del Producto",
      "descripcion" => "Descripción",
      "precio" => "Precio",
      "plataforma" => "Plataforma",
      "fabricante" => "Fabricante",
);
 
return $columns;
}
 
function prod_custom_columns($column){
    global $post;
    switch ($column)
       {
 case "descripcion":
    the_excerpt();
    break;
 case "precio":
    $custom = get_post_custom();
    echo $custom["precio"][0];
    break;
        case "fabricante":
    $custom = get_post_custom();
    echo $custom["fabricante"][0];
    break;
 case "plataforma":
    echo get_the_term_list($post->ID, 'plataforma', '', ', ','');
    break;
 }
}
En prod_edit_columns() tenemos un array donde los índices (keys) de cada variable se usan para referenciar cierta información de las entradas que definimos en la segunda función, prod_custom_columns() y donde los valores son las cabeceras de las columnas. En prod_edit_columns() hay 6 columnas pero sólo mostramos 4 en prod_custom_columns() ya que ‘cb’ y ‘title’ son parte de los índices por defecto de WordPress para los que ya tiene asociaciones creadas.
Bueno, ¡y eso sería todo! Con este código adecuadamente incluído podemos tener las pantallas que veíamos al principio del artículo. Pero como véis hay mucho campo para ampliar las posibilidades que se nos ofrecen con las Entradas Personalizadas. Aquí hemos hecho una lista de Productos, pero podría haber sido las habitaciones de un hotel, una lista de canciones de distintos autores, una lista de libros o cualquier cosa que se nos ocurra.

Otras lecturas recomendadas

Smarter Custom Post Types
Rock-Solid WordPress 3.0 Themes using Custom Post Types
Custom post types in WordPress
Showing custom post types on your home/blog page
Custom Post Types in WordPress 3.0
Extending Custom Post Types in WordPress 3.0
Introducing WordPress 3 Custom Taxonomies

No hay comentarios:

Publicar un comentario