SummArg | Cursos y recursos para webmasters

Clase 16: Custom Post Types: Portfolio y plantilla single-cpt.php.

En esta clase y en las dos siguientes vamos a intentar tocar los aspectos mas relevantes a los Custom Post Types, con los que daremos vida a un portfolio que puede serles de utilidad para exhibir toda clase de productos, películas, libros, etc. Los Custom Post Types son la máxima expresión de contenido personalizado dentro de WordPress, podemos hacer que se comporten como posts o páginas, que usen categorías, taxonomías, etiquetas, todas o ninguna. Tienen su propio lugar en nuestro escritorio de la plataforma y nos dan una amplia flexibilidad a la hora de crear contenidos. También, vale aclarar, nos permite separar estos contenidos de las páginas y las entradas comunes. Pueden comenzar a leer sobre este tema en el codex de WordPress en este link. Nosotros iremos directo al grano y para ello lo primero que vamos a hacer es definir el tipo de contenido “Portfolio” desde el functions.php.

add_action( 'init', 'register_cpt_portfolio' );
function register_cpt_portfolio() {
    $labels = array(
        'name' => _x( 'Portfolio', 'portfolio' ),
        'singular_name' => _x( 'Portfolio', 'portfolio' ),
        'add_new' => _x( 'Agregar', 'portfolio' ),
        'add_new_item' => _x( 'Agregar trabajo', 'portfolio' ),
        'edit_item' => _x( 'Editar trabajo', 'portfolio' ),
        'new_item' => _x( 'Nuevo trabajo', 'portfolio' ),
        'view_item' => _x( 'Ver trabajo', 'portfolio' ),
        'search_items' => _x( 'Buscar en Porfolio', 'portfolio' ),
        'not_found' => _x( 'No se encontraron trabajos', 'portfolio' ),
        'not_found_in_trash' => _x( 'No se encontraron en la papelera', 'portfolio' ),
        'parent_item_colon' => _x( 'Parent Portfolio:', 'portfolio' ),
        'menu_name' => _x( 'Portfolio', 'portfolio' ),
    );
    $args = array(
        'labels' => $labels,
        'hierarchical' => false,
        'description' => 'Trabajos que realizamos recientemente',
        'supports' => array( 'title', 'editor', 'thumbnail', 'custom-fields' ),
        'public' => true,
        'show_ui' => true,
        'show_in_menu' => true,
        'show_in_nav_menus' => true,
        'publicly_queryable' => true,
        'exclude_from_search' => false,
        'has_archive' => true,
        'query_var' => true,
        'can_export' => true,
        'rewrite' => true,
        'capability_type' => 'post'
    );
    register_post_type( 'portfolio', $args );
}

Mediante este código registramos el post type asignándole todos los valores necesarios para que se conviertan en un tipo de entradas que acepta archivo, que se muestra en los menúes, que se puede exportar, que soporta los campos de título, el editor, miniaturas y custom fields, etc.  Existe un generador de código muy bueno que elaboró la gente de themergency.com en este link. Al completar el formulario en varios pasos obtenemos el código resultante de nuestras especificaciones. Vamos a querer asignarle categorías a nuestras entradas, y queremos definir las mismas de modo independiente al resto de las entradas comunes, así que para ello seguimos colocando código en nuestro functions.php:

function register_portfoliotaxonomies() {
	$labels = array(
		'name' 					=> _x( 'tipos', 'taxonomy general name' ),
		'singular_name' 		=> _x( 'tipo', 'taxonomy singular name' ),
		'add_new' 				=> _x( 'Agregar tipo', 'tipo'),
		'add_new_item' 			=> __( 'Agregar tipo' ),
		'edit_item' 			=> __( 'Editar tipo' ),
		'new_item' 				=> __( 'Nuevo tipo' ),
		'view_item' 			=> __( 'Ver tipo' ),
		'search_items' 			=> __( 'Buscar tipos' ),
		'not_found' 			=> __( 'No encontrado' ),
		'not_found_in_trash' 	=> __( 'No encotrado' ),
	);
	$pages = array('portfolio');
	$args = array(
		'labels' 			=> $labels,
		'singular_label' 	=> __('tipo'),
		'public' 			=> true,
		'show_ui' 			=> true,
		'hierarchical' 		=> true,
		'show_tagcloud' 	=> false,
		'show_in_nav_menus' => true,
		'_builtin' 			=> false,
		'rewrite' 			=> array('slug' => 'porfoliotax','with_front' => FALSE ),
	 );
	register_taxonomy('portfoliotaxonomies', $pages, $args);
}
add_action('init', 'register_portfoliotaxonomies');

De este modo generamos las categorías para “Portfolio”. Pueden revisar una descripción de los parámetros de register_taxonomy en el codex. Procedemos a completar con algunas categorías antes de subir material. Necesitamos tener algunas entradas ingresadas para poder trabajar, así que vamos a crear un portfolio de una empresa de marketing y diseño. tipos

Ahora si podemos proceder a agregar algunas entradas en diferentes categorías. A medida que lo hagamos veremos que tenemos problemas para visualizar los contenidos (salvo que no estén usando URLs amigables), por lo que debemos recrear los permalinks. El modo mas sencillo es cambiando la configuración un momento a predeterminado y volviéndola a cambiar a la que hayamos elegido. permalinks

Metabox: Los custom fields de forma fácil

Vamos a echar mano a un recurso muy útil en esa nueva sección: los metaboxes. Estas cajas son una versión sofisticada para manejar los valores de custom fields, dejando al usuario un entorno mas amigable para completar los campos. Vamos a crear tres campos de texto y un campo con dos radio buttons. Los datos que recabemos con los tres primeros elementos serán usados en clases por venir, pero los radio los vamos a usar hoy mismo. metabo

Functions.php

/* Metabox */
$meta_box = array(
 'id' => 'metabox-portfolio',
 'title' => 'Elementos del Portfolio',
 'page' => 'portfolio',
 'context' => 'normal',
 'priority' => 'high',
 'fields' => array(
 array(
 'name' => 'Cliente',
 'desc' => 'Nombre del cliente',
 'id' => 'cliente',
 'type' => 'text',
 'std' => 'Un Cliente'
 ),
 array(
 'name' => 'Socios',
 'desc' => 'Nombre del/los socios',
 'id' => 'socios',
 'type' => 'text',
 'std' => ''
 ),
 array(
 'name' => 'Descripcion',
 'desc' => 'Descripcion corta para la portada',
 'id' => 'descripcion',
 'type' => 'text',
 'std' => ''
 ),
 array(
 'name' => 'Layout',
 'id' => 'layout',
 'type' => 'radio',
 'options' => array(
 array('name' => 'Layout1', 'value' => 'Layout1'),
 array('name' => 'Layout2', 'value' => 'Layout2')
 )
 )
 )
);
add_action('admin_menu', 'mytheme_add_box');
// Add meta box
function mytheme_add_box() {
 global $meta_box;
 add_meta_box($meta_box['id'], $meta_box['title'], 'mytheme_show_box', $meta_box['page'], $meta_box['context'], $meta_box['priority']);
}
// Callback function to show fields in meta box
function mytheme_show_box() {
 global $meta_box, $post;
 // Use nonce for verification
 echo '<input type="hidden" name="mytheme_meta_box_nonce" value="', wp_create_nonce(basename(__FILE__)), '" />';
 echo '<table class="form-table">';
foreach ($meta_box['fields'] as $field) {
 // get current post meta data
 $meta = get_post_meta($post->ID, $field['id'], true);
 echo '<tr>',
 '<th style="width:20%"><label for="', $field['id'], '">', $field['name'], '</label></th>',
 '<td>';
 switch ($field['type']) {
 case 'text':
 echo '<input type="text" name="', $field['id'], '" id="', $field['id'], '" value="', $meta ? $meta : $field['std'], '" size="30" style="width:97%" />',
 '<br />', $field['desc'];
 break;
 case 'textarea':
 echo '<textarea name="', $field['id'], '" id="', $field['id'], '" cols="60" rows="4" style="width:97%">', $meta ? $meta : $field['std'], '</textarea>',
 '<br />', $field['desc'];
 break;
 case 'select':
 echo '<select name="', $field['id'], '" id="', $field['id'], '">';
 foreach ($field['options'] as $option) {
 echo '<option', $meta == $option ? ' selected="selected"' : '', '>', $option, '</option>';
 }
 echo '</select>';
 break;
 case 'radio':
 foreach ($field['options'] as $option) {
 echo '<input type="radio" name="', $field['id'], '" value="', $option['value'], '"', $meta == $option['value'] ? ' checked="checked"' : '', ' />', $option['name'];
 }
 break;
 case 'checkbox':
 echo '<input type="checkbox" name="', $field['id'], '" id="', $field['id'], '"', $meta ? ' checked="checked"' : '', ' />';
 break;
 }
 echo '<td>',
 '</tr>';
 }
 echo '</table>';
}
add_action('save_post', 'mytheme_save_data');
// Save data from meta box
function mytheme_save_data($post_id) {
 global $meta_box;
 // verify nonce
 if (!wp_verify_nonce($_POST['mytheme_meta_box_nonce'], basename(__FILE__))) {
 return $post_id;
 }
// check autosave
 if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
 return $post_id;
 }
// check permissions
 if ('page' == $_POST['post_type']) {
 if (!current_user_can('edit_page', $post_id)) {
 return $post_id;
 }
 } elseif (!current_user_can('edit_post', $post_id)) {
 return $post_id;
 }
 foreach ($meta_box['fields'] as $field) {
 $old = get_post_meta($post_id, $field['id'], true);
 $new = $_POST[$field['id']];
 if ($new && $new != $old) {
 update_post_meta($post_id, $field['id'], $new);
 } elseif ('' == $new && $old) {
 delete_post_meta($post_id, $field['id'], $old);
 }
 }
}

Esos radio buttons los vamos a usar para crear dos estilos diferentes de archivo single para nuestro custom post type.

La plantilla single-cpt.php

Arranquemos a generar la plantilla para visualizar un elemento. Recordemos lo referente a jerarquías de plantillas (template hierarchy) y según el mapa de plantillas necesitamos crear un archivo single-portfolio.php.
Vamos a crear tres plantillas en una usando condicionales en base a ese campo “Layout”. La primera de ellas contendrá un cuadro con los datos del metabox arriba de todo, y en la barra lateral lista entradas de la misma categoría.

layout1

Veamos la primera parte de nuestro código en single-portfolio.php. La estructura de colocar al inicio un get_header() seguido de un div que haga de contenedor es igual en todas. Lo que nosotros necesitamos es inmediatamente obtener el valor del custom field layout por lo que primero llamamos a la variable global $post y luego procedemos a usar get_post_meta.

Construimos un contenedor con los valores de cliente, socios y descripción, a la que le colocamos una miniatura a la izquierda (para obtener la miniatura pequeña por defecto de WordPress usamos ‘thumb’). Seguidamente, y siempre dentro del bucle, colocamos las funciones para llamar al título y al contenido.

<?php get_header(); ?>
<div class="wrapperPortfolio">
<?php global $post; $layout = get_post_meta(get_the_id(), 'layout', TRUE);
if ($layout == 'Layout1') {
 if ( have_posts() ) : while ( have_posts() ) : the_post();
 $cliente = get_post_meta(get_the_id(), 'cliente', TRUE);
 $socios = get_post_meta(get_the_id(), 'socios', TRUE);
 $descripcion = get_post_meta(get_the_id(), 'descripcion', TRUE);
 ?>
	<div class="portfolioUno">
	<h2><a href="<?php the_permalink() ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></h2>
		<div class="portfolioDetalle">
			<?php the_post_thumbnail('thumb'); ?>
			<?php echo '<p><div class="detalleTitulo">Cliente</div> '.$cliente;
			echo '<br /><div class="detalleTitulo">Socios</div> '.$socios;
			echo '<br /><div class="detalleTitulo">Decripcion</div> '.$descripcion.'</p>'?>
		</div>
	<?php the_content(); ?>
	</div><!-- end of portfolioUno -->
<?php endwhile; endif; ?>

En la segunda parte de nuestro Layout1 queremos encargarnos de colocar en la barra lateral otras entradas de la misma categoría. Esto ya resulta fácil para nosotros, ¿no es cierto? De paso dejo establecidos los condicionales para los otros Layouts, por el momento no se verá nada mas que un texto.

<div class="sidebarPortfolio">
	<div class="itemPortfolio">
		<h3>Otros trabajos relacionados</h3>
	</div>
	<?php
	$categories = get_the_category($post->ID);
	$args= array(
		'cat'      => $categories,
		'posts_per_page' => 4,
		'post_type' => 'Portfolio'
	);
	query_posts($args);
	if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
		<div class="itemPortfolio">
			<?php the_post_thumbnail('portfolio'); ?>
			<h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
		</div>
	<?php endwhile; endif; ?>
</div>
<?php } else if ($layout == 'Layout2') { ?>
Otro Layout
<?php } else { ?>
Y otro layout mas....
<?php } ?>
</div><!-- end of wrapper-->
<?php get_footer(); ?>

Necesitamos definir el tamaño de la imagen destacada, colocamos el siguiente valor en nuestro functions.php:

add_image_size( 'portfolio', 230,130, true );

Y hasta aquí nuestro CSS.

.wrapperPortfolio {
width: 940px;
float:left;
margin: 20px 0 20px 20px;
}
.portfolioUno {
width: 670px;
float:left;
}
.sidebarPortfolio {
width: 230px;
margin: 20px 0 0 20px;
float:left;
}
.itemPortfolio {
width: 230px;
border:1px solid #7ec5ff;
float:left;
display:block;
margin: 0 0 20px 0;
background-color:#7ec5ff;
}
.itemPortfolio img {
margin:0;
padding:0;
}
.itemPortfolio h3 {
font-size:16px;
line-height:22px;
font-weight:bold;
text-align:center;
margin: 5px 0 0 0;
color:white;
}
.itemPortfolio h3 a {
color:white;
text-decoration:none;
}
.portfolioDetalle {
width: 650px;
padding:10px;
margin: 0 0 20px 0;
background-color: #f4f4f4;
-webkit-border-radius: 5px;
border-radius: 5px;
display:block;
float:left;
}
.portfolioDetalle img {
float:left;
margin: 0 10px 0 0;
}
.detalleTitulo {
width: 85px;
color: #7ec5ff;
font-weight:bold;
float:left;
text-align:right;
margin: 0 5px 0 0;
}

En “Layout 2” vamos a disponer de los elementos de un modo ligeramente diferente, construyendo en nuestra barra lateral el área en donde mostraremos los datos del metabox y debajo nuestra imagen destacada. Añadimos al functions.php el nuevo tamaño que usaremos para the_post_thumbnail();. Insertamos el siguiente valor debajo del último add_image_size();

add_image_size( 'layout2', 340, 300, true);

Así quedará nuestro “Layout 2”:

layout2

Ahora procedemos a completar el single-php.php reemplazando ese texto que colocamos previamente “Otro Layout”.

if ( have_posts() ) : while ( have_posts() ) : the_post();
 $cliente = get_post_meta(get_the_id(), 'cliente', TRUE);
 $socios = get_post_meta(get_the_id(), 'socios', TRUE);
 $descripcion = get_post_meta(get_the_id(), 'descripcion', TRUE);
 ?>
 <div class="portfolioDos">
 <h2><a href="<?php the_permalink() ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></h2>
 <?php the_content(); ?>
 </div><!-- end of portfolioDos -->
 <div class="sidebarPortfolioDos">
 <div class="portfolioDosDetalle">
 <?php echo '<p><div class="detalleTitulo">Cliente</div> '.$cliente;
 echo '<br /><div class="detalleTitulo">Socios</div> '.$socios;
 echo '<br /><div class="detalleTitulo">Decripcion</div> '.$descripcion.'</p>'?>
 </div>
 <?php the_post_thumbnail('layout2'); ?>
 </div>
<?php endwhile; endif; ?>

Este segundo layout es mas sencillo pues tiene un solo bucle. Veamos el CSS:

.portfolioDos {
width: 560px;
float:left;
}
.portfolioDosDetalle {
width:320px;
padding:10px;
margin: 0 0 20px 0px;
background-color: #f4f4f4;
-webkit-border-radius: 5px;
border-radius: 5px;
display:block;
float:right;
}
.sidebarPortfolioDos {
width: 340px;
margin: 20px 0 0 10px;
padding: 0 0 0 10px;
border-left: 1px solid #f4f4f4;
float:left;
}

Para el tercer Layout, puesto que ya comprendimos la idea, vamos a insertar parte de la plantilla single.php con la salvedad de que tenemos que eliminar un contenedor y vamos a eliminar el area de información de la entrada.

layout3

if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
 <div class="dos-tercios listado">
 <h2><a href="<?php the_permalink() ?>" title="<?php the_title(); ?>"><?php the_title(); ?></a></h2>
 <?php the_content(); ?>
 </div><!-- end of dos-tercios -->
<?php endwhile; endif;
get_sidebar();

Con esto terminamos por hoy, esperamos que hayan comprendido la idea de la plantilla single-cpt.php y que puedan explotarla al máximo a la hora de diseñar sus sitios.

Demo online Layout 1 | Demo online Layout 2 | Demo online Layout 3

Descargar la clase de hoy: themeTaller 16 (172)

Nota importante: Para generar los metabox nosotros utilizamos un framework que encontramos hace un tiempo en la web y no recordamos quién es su autor, por eso se los dejamos para que lo utilicen aquí: meta-box (102)

Si alguno reconoce el código y al autor, por favor envíenos un mensaje para mencionarlo como corresponde.

Dejar un comentario

  1. […] Clase 16:  Custom Post Types: Portfolio y plantilla single-cpt.php. […]

  2. […] Clase 16:  Custom Post Types: Portfolio y plantilla single-cpt.php. […]

  3. Cristhian Bórquez

    Muy buen tutorial me gusto mucho, tengo una pregunta si deseo utilizar tu metodo de Custom Post Types como podria transformarlo para que fuese plugin intente pero no puedo vincular los datos de single-portfolio.php

    agredecere mucho tu ayuda.

  4. hola, resulta que estoy intentando crear un custom post type, y ya se me creo la seccion, pero a la hora de visitar un post creado, me sale error 404.

  5. Hola, buen tutorial , podría despejarme una duda? es posible crear un theme de wordpress usando parte de código jquery mobile? no digo todo pero por ejemplo vincular los listviews y data collapsible?, eso funcionaría o luego no se podrían hacer los menús?

Dejar un comentario