Estas en: Home > registro

Entradas etiquetadas con registro

Cron Job WordPress

WordPress 3.x para desarrolladores: Temas y plantillas, functions.php

0

Como ya llevamos varias plantillas creadas con algunas llamadas a funciones del archivo functions.php, vamos a crearlo y añadir esas funciones, después continuaremos añadiendo más plantillas.

 

FUNCTIONS.PHP

Creamos el archivo functions.php y añadimos el siguiente código:

  1. <?php
  2.  
  3. /**
  4.  * Set the content width based on the theme's design and stylesheet.
  5.  */
  6. if ( ! isset( $content_width ) )
  7. $content_width = 584;

Este código creara una variable que nos indicará el ancho que tendrá el contenido.

  1. <?php
  2.  
  3. /**
  4.  * Tell WordPress to run newtheme_setup() when the 'after_setup_theme' hook is run.
  5.  */
  6. add_action( 'after_setup_theme', 'newtheme_setup' );
  7.  
  8. if ( ! function_exists( 'newtheme_setup' ) ):
  9.  
  10. function newtheme_setup() {
  11.  
  12. }
  13.  
  14. endif;

 

Le decimos a WordPress que ejecute newtheme_setup cuando el hook after_setup_theme esté funcionando. Creamos un condicional para comprobar que la función newtheme_setup no ha sido aun creada, y creamos la función.

Dentro de newtheme_setup añadiremos las siguientes lineas de código:

  1. <?php
  2.  
  3. load_theme_textdomain( 'newtheme', get_template_directory() . '/languages' );

Esta línea cargará el archivo de idioma correspondiente.

  1. <?php
  2.  
  3. add_editor_style();

Le indicamos a WordPress que cargue el editor de estilos. Para ello WordPress buscará el archivo editor-style.css dentro de la carpeta del tema, por tanto copiaremos los archivos editor-style.css y editor-style-rtl.css del tema Twenty Eleven a nuestro tema.

  1. <?php
  2.  
  3. require( get_template_directory() . '/inc/theme-options.php' );
  4. require( get_template_directory() . '/inc/widgets.php' );

Estas dos lineas cargan los archivos de las opciones del tema que se mostrarán en la administración y los widgets de los que disponga el tema.

  1. <?php
  2.  
  3. add_theme_support( 'automatic-feed-links' );

Le indicamos a WordPress que los links de este tema se añadirán a un archivo rss que se creará automáticamente. Más sobre otros argumentos de esta función: http://codex.wordpress.org/Function_Reference/add_theme_support

  1. <?php
  2.  
  3. register_nav_menu( 'primary', __( 'Primary Menu', 'newtheme' ) );

Registramos la barra de menú de la cabecera.

  1. <?php
  2. add_theme_support( 'post-formats', array( 'aside', 'link', 'gallery', 'status', 'quote', 'image' ) );

Añadimos soporte para varios tipos de formato de posts.

  1. <?php
  2.  
  3. $theme_options = newtheme_get_theme_options();
  4. if ( 'dark' == $theme_options['color_scheme'] )
  5. $default_background_color = '1d1d1d';
  6. else
  7. $default_background_color = 'f1f1f1';
  8.  
  9. // Add support for custom backgrounds.
  10. add_theme_support( 'custom-background', array(
  11. // Let WordPress know what our default background color is.
  12. // This is dependent on our current color scheme.
  13. 'default-color' => $default_background_color,
  14. ) );

Primero recuperamos las opciones del tema a través de la función newtheme_get_theme_options(), que se encuentra en el archivo inc/theme_options.php, el cual crearemos más delante.

Comprobamos que tipo de esquema de estilos se ha elegido y guardamos en la variable $default_background_color el valor correspondiente.

Le indicamos a WordPress el color de nuestro fondo de página.

  1. <?php
  2.  
  3. add_theme_support( 'post-thumbnails' );

Le indicamos a WordPress que este tema utilizará imágenes destacadas o thumbnails.

  1. <?php
  2.  
  3. $custom_header_support = array(
  4. // The default header text color.
  5. 'default-text-color' => '000',
  6. // The height and width of our custom header.
  7. 'width' => apply_filters( 'newtheme_header_image_width', 1000 ),
  8. 'height' => apply_filters( 'newtheme_header_image_height', 288 ),
  9. // Support flexible heights.
  10. 'flex-height' => true,
  11. // Random image rotation by default.
  12. 'random-default' => true,
  13. // Callback for styling the header.
  14. 'wp-head-callback' => 'newtheme_header_style',
  15. // Callback for styling the header preview in the admin.
  16. 'admin-head-callback' => 'newtheme_admin_header_style',
  17. // Callback used to display the header preview in the admin.
  18. 'admin-preview-callback' => 'newtheme_admin_header_image',
  19. );
  20.  
  21. add_theme_support( 'custom-header', $custom_header_support );

Creamos un array con los encabezados personalizados y se lo indicamos a WordPress.

  1. <?php
  2.  
  3. if ( ! function_exists( 'get_custom_header' ) ) {
  4. // This is all for compatibility with versions of WordPress prior to 3.4.
  5. define( 'HEADER_TEXTCOLOR', $custom_header_support['default-text-color'] );
  6. define( 'HEADER_IMAGE', '' );
  7. define( 'HEADER_IMAGE_WIDTH', $custom_header_support['width'] );
  8. define( 'HEADER_IMAGE_HEIGHT', $custom_header_support['height'] );
  9. add_custom_image_header( $custom_header_support['wp-head-callback'], $custom_header_support['admin-head-callback'], $custom_header_support['admin-preview-callback'] );
  10. add_custom_background();
  11. }

En el caso de que se utilizase este tema con una versión de WordPress inferior a la 3.4 creamos varias constantes, y añadimos las cabeceras personalizadas y el fondo a WordPress.

  1. <?php
  2.  
  3. set_post_thumbnail_size( $custom_header_support['width'], $custom_header_support['height'], true );

Configuramos la imagen destacada definiendo un ancho y alto.

  1. <?php
  2.  
  3. add_image_size( 'large-feature', $custom_header_support['width'], $custom_header_support['height'], true );
  4. // Used for featured posts if a large-feature doesn't exist.
  5. add_image_size( 'small-feature', 500, 300 );

Añadimos la imagen a la cabecera definiendo su ancho y alto, además añadimos una imagen más pequeña para los post en el caso de que no existe imagen para la cabecera.

  1. <?php
  2.  
  3. register_default_headers( array(
  4. 'wheel' => array(
  5. 'url' => '%s/images/headers/wheel.jpg',
  6. 'thumbnail_url' => '%s/images/headers/wheel-thumbnail.jpg',
  7. /* translators: header image description */
  8. 'description' => __( 'Wheel', 'newtheme' )
  9. ),
  10. 'shore' => array(
  11. 'url' => '%s/images/headers/shore.jpg',
  12. 'thumbnail_url' => '%s/images/headers/shore-thumbnail.jpg',
  13. /* translators: header image description */
  14. 'description' => __( 'Shore', 'newtheme' )
  15. ),
  16. 'trolley' => array(
  17. 'url' => '%s/images/headers/trolley.jpg',
  18. 'thumbnail_url' => '%s/images/headers/trolley-thumbnail.jpg',
  19. /* translators: header image description */
  20. 'description' => __( 'Trolley', 'newtheme' )
  21. ),
  22. 'pine-cone' => array(
  23. 'url' => '%s/images/headers/pine-cone.jpg',
  24. 'thumbnail_url' => '%s/images/headers/pine-cone-thumbnail.jpg',
  25. /* translators: header image description */
  26. 'description' => __( 'Pine Cone', 'newtheme' )
  27. ),
  28. 'chessboard' => array(
  29. 'url' => '%s/images/headers/chessboard.jpg',
  30. 'thumbnail_url' => '%s/images/headers/chessboard-thumbnail.jpg',
  31. /* translators: header image description */
  32. 'description' => __( 'Chessboard', 'newtheme' )
  33. ),
  34. 'lanterns' => array(
  35. 'url' => '%s/images/headers/lanterns.jpg',
  36. 'thumbnail_url' => '%s/images/headers/lanterns-thumbnail.jpg',
  37. /* translators: header image description */
  38. 'description' => __( 'Lanterns', 'newtheme' )
  39. ),
  40. 'willow' => array(
  41. 'url' => '%s/images/headers/willow.jpg',
  42. 'thumbnail_url' => '%s/images/headers/willow-thumbnail.jpg',
  43. /* translators: header image description */
  44. 'description' => __( 'Willow', 'newtheme' )
  45. ),
  46. 'hanoi' => array(
  47. 'url' => '%s/images/headers/hanoi.jpg',
  48. 'thumbnail_url' => '%s/images/headers/hanoi-thumbnail.jpg',
  49. /* translators: header image description */
  50. 'description' => __( 'Hanoi Plant', 'newtheme' )
  51. )
  52. ) );

Este es el paquete de imágenes que el tema Twenty Eleven trae por defecto, nosotros haremos lo mismo. Si no lo habéis hecho ya, copiad la carpeta images del tema Twenty Eleven a nuestro tema.

El marcador %s indica que se debe sustituir por la uri del tema.

Como esta función tiene llamadas a otras funciones dentro del mismo archivo functions.php, vamos a crearlas.

En primer lugar crearemos la función newtheme_header_style():

  1. <?php
  2.  
  3. if ( ! function_exists( 'newtheme_header_style' ) ) :
  4. /**
  5.  * Styles the header image and text displayed on the blog
  6.  */
  7. function newtheme_header_style() {
  8. $text_color = get_header_textcolor();
  9.  
  10. // If no custom options for text are set, let's bail.
  11. if ( $text_color == HEADER_TEXTCOLOR )
  12. return;
  13.  
  14. // If we get this far, we have custom styles. Let's do this.
  15. ?>
  16. <style type="text/css">
  17. <?php
  18. // Has the text been hidden?
  19. if ( 'blank' == $text_color ) :
  20. ?>
  21. #site-title,
  22. #site-description {
  23. position: absolute !important;
  24. clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
  25. clip: rect(1px, 1px, 1px, 1px);
  26. }
  27. <?php
  28. // If the user has set a custom color for the text use that
  29. else :
  30. ?>
  31. #site-title a,
  32. #site-description {
  33. color: #<?php echo $text_color; ?> !important;
  34. }
  35. <?php endif; ?>
  36. </style>
  37. <?php
  38. }
  39. endif; // newtheme_header_style

Simplemente en esta función se generan los estilos para el título y la descripción que se ubican en la cabecera de la página.

Creamos el método newtheme_admin_header_style():

  1. <?php
  2.  
  3. if ( ! function_exists( 'newtheme_admin_header_style' ) ) :
  4. /**
  5.  * Styles the header image displayed on the Appearance > Header admin panel.
  6.  *
  7.  * Referenced via add_theme_support('custom-header') in newtheme_setup().
  8.  */
  9. function newtheme_admin_header_style() {
  10. ?>
  11. <style type="text/css">
  12. .appearance_page_custom-header #headimg {
  13. border: none;
  14. }
  15. #headimg h1,
  16. #desc {
  17. font-family: "Helvetica Neue", Arial, Helvetica, "Nimbus Sans L", sans-serif;
  18. }
  19. #headimg h1 {
  20. margin: 0;
  21. }
  22. #headimg h1 a {
  23. font-size: 32px;
  24. line-height: 36px;
  25. text-decoration: none;
  26. }
  27. #desc {
  28. font-size: 14px;
  29. line-height: 23px;
  30. padding: 0 0 3em;
  31. }
  32. <?php
  33. // If the user has set a custom color for the text use that
  34. if ( get_header_textcolor() != HEADER_TEXTCOLOR ) :
  35. ?>
  36. #site-title a,
  37. #site-description {
  38. color: #<?php echo get_header_textcolor(); ?>;
  39. }
  40. <?php endif; ?>
  41. #headimg img {
  42. max-width: 1000px;
  43. height: auto;
  44. width: 100%;
  45. }
  46. </style>
  47. <?php
  48. }
  49. endif; // newhtme_admin_header_style

Esta función permite dar estilos a la imagen de la cabecera dentro de su apartado de opciones en la administración de WordPress, donde se podrá configurar cómo se ve la imagen.

Creamos la función newtheme_admin_header_image():

  1. <?php
  2.  
  3. if ( ! function_exists( 'newtheme_admin_header_image' ) ) :
  4. /**
  5.  * Custom header image markup displayed on the Appearance > Header admin panel.
  6.  *
  7.  * Referenced via add_theme_support('custom-header') in newtheme_setup().
  8.  */
  9. function newtheme_admin_header_image() { ?>
  10. <div id="headimg">
  11. <?php
  12. $color = get_header_textcolor();
  13. $image = get_header_image();
  14. if ( $color && $color != 'blank' )
  15. $style = ' style="color:#' . $color . '"';
  16. else
  17. $style = ' style="display:none"';
  18. ?>
  19. <h1><a id="name"<?php echo $style; ?> onclick="return false;" href="<?php echo esc_url( home_url( '/' ) ); ?>"><?php bloginfo( 'name' ); ?></a></h1>
  20. <div id="desc"<?php echo $style; ?>><?php bloginfo( 'description' ); ?></div>
  21. <?php if ( $image ) : ?>
  22. <img src="<?php echo esc_url( $image ); ?>" alt="" />
  23. <?php endif; ?>
  24. </div>
  25. <?php }
  26. endif; // newtheme_admin_header_image

Esta función crea el código html que mostrará la imagen, el título y la descripción en su respectiva zona de opciones de la administración de WordPress, para que el usuario pueda ver como quedaría.

  1. <?php
  2.  
  3. /**
  4.  * Sets the post excerpt length to 40 words.
  5.  *
  6.  * To override this length in a child theme, remove the filter and add your own
  7.  * function tied to the excerpt_length filter hook.
  8.  */
  9. function newtheme_excerpt_length( $length ) {
  10. return 40;
  11. }
  12. add_filter( 'excerpt_length', 'newtheme_excerpt_length' );

Esta función establece la longitud del extracto de un post a 40 palabras.

  1. <?php
  2.  
  3. /**
  4.  * Returns a "Continue Reading" link for excerpts
  5.  */
  6. function newtheme_continue_reading_link() {
  7. return ' <a href="'. esc_url( get_permalink() ) . '">' . __( 'Continue reading <span class="meta-nav">&rarr;</span>', 'newtheme' ) . '</a>';
  8. }

Esta función devuelve el link con el texto «Seguir leyendo…» en los extractos de los posts.

  1. <?php
  2.  
  3. /**
  4.  * Replaces "[...]" (appended to automatically generated excerpts) with an ellipsis and newtheme_continue_reading_link().
  5.  *
  6.  * To override this in a child theme, remove the filter and add your own
  7.  * function tied to the excerpt_more filter hook.
  8.  */
  9. function newtheme_auto_excerpt_more( $more ) {
  10. return ' &hellip;' . newtheme_continue_reading_link();
  11. }
  12. add_filter( 'excerpt_more', 'newtheme_auto_excerpt_more' );

Esta función remplaza el texto «[…]» por «…» utilizando «&hellip;» para ello, y el link de newtheme_continue_reading_link()

  1. <?php
  2.  
  3. /**
  4.  * Adds a pretty "Continue Reading" link to custom post excerpts.
  5.  *
  6.  * To override this link in a child theme, remove the filter and add your own
  7.  * function tied to the get_the_excerpt filter hook.
  8.  */
  9. function newtheme_custom_excerpt_more( $output ) {
  10. if ( has_excerpt() && ! is_attachment() ) {
  11. $output .= newtheme_continue_reading_link();
  12. }
  13. return $output;
  14. }
  15. add_filter( 'get_the_excerpt', 'newtheme_custom_excerpt_more' );

Añade la salida de newtheme_continue_reading_link() a los extractos personalizados de los posts.

  1. <?php
  2.  
  3. /**
  4.  * Get our wp_nav_menu() fallback, wp_page_menu(), to show a home link.
  5.  */
  6. function newtheme_page_menu_args( $args ) {
  7. $args['show_home'] = true;
  8. return $args;
  9. }
  10. add_filter( 'wp_page_menu_args', 'newtheme_page_menu_args' );

Esta función hace que en nuestro menú de navegación aparezca un link hacía la página principal.

  1. <?php
  2.  
  3. /**
  4.  * Register our sidebars and widgetized areas. Also register the default Epherma widget.
  5.  */
  6. function newtheme_widgets_init() {
  7.  
  8. register_widget( 'New_Theme_Ephemera_Widget' );
  9.  
  10. register_sidebar( array(
  11. 'name' => __( 'Main Sidebar', 'newtheme' ),
  12. 'id' => 'sidebar-1',
  13. 'before_widget' => '<aside id="%1$s" class="widget %2$s">',
  14. 'after_widget' => "</aside>",
  15. 'before_title' => '<h3 class="widget-title">',
  16. 'after_title' => '</h3>',
  17. ) );
  18.  
  19. register_sidebar( array(
  20. 'name' => __( 'Showcase Sidebar', 'newtheme' ),
  21. 'id' => 'sidebar-2',
  22. 'description' => __( 'The sidebar for the optional Showcase Template', 'newtheme' ),
  23. 'before_widget' => '<aside id="%1$s" class="widget %2$s">',
  24. 'after_widget' => "</aside>",
  25. 'before_title' => '<h3 class="widget-title">',
  26. 'after_title' => '</h3>',
  27. ) );
  28.  
  29. register_sidebar( array(
  30. 'name' => __( 'Footer Area One', 'newtheme' ),
  31. 'id' => 'sidebar-3',
  32. 'description' => __( 'An optional widget area for your site footer', 'newtheme' ),
  33. 'before_widget' => '<aside id="%1$s" class="widget %2$s">',
  34. 'after_widget' => "</aside>",
  35. 'before_title' => '<h3 class="widget-title">',
  36. 'after_title' => '</h3>',
  37. ) );
  38.  
  39. register_sidebar( array(
  40. 'name' => __( 'Footer Area Two', 'newtheme' ),
  41. 'id' => 'sidebar-4',
  42. 'description' => __( 'An optional widget area for your site footer', 'newtheme' ),
  43. 'before_widget' => '<aside id="%1$s" class="widget %2$s">',
  44. 'after_widget' => "</aside>",
  45. 'before_title' => '<h3 class="widget-title">',
  46. 'after_title' => '</h3>',
  47. ) );
  48.  
  49. register_sidebar( array(
  50. 'name' => __( 'Footer Area Three', 'newtheme' ),
  51. 'id' => 'sidebar-5',
  52. 'description' => __( 'An optional widget area for your site footer', 'newtheme' ),
  53. 'before_widget' => '<aside id="%1$s" class="widget %2$s">',
  54. 'after_widget' => "</aside>",
  55. 'before_title' => '<h3 class="widget-title">',
  56. 'after_title' => '</h3>',
  57. ) );
  58. }
  59. add_action( 'widgets_init', 'newtheme_widgets_init' );

Esta función registra nuestro widget que crearemos en el archivo inc/widgets.php, y las cinco sidebars que añade el tema a diferentes partes de la página.

  1. <?php
  2.  
  3. if ( ! function_exists( 'newtheme_content_nav' ) ) :
  4. /**
  5.  * Display navigation to next/previous pages when applicable
  6.  */
  7. function newtheme_content_nav( $nav_id ) {
  8. global $wp_query;
  9.  
  10. if ( $wp_query->max_num_pages > 1 ) : ?>
  11. <nav id="<?php echo $nav_id; ?>">
  12. <h3 class="assistive-text"><?php _e( 'Post navigation', 'newtheme' ); ?></h3>
  13. <div class="nav-previous"><?php next_posts_link( __( '<span class="meta-nav">&larr;</span> Older posts', 'newtheme' ) ); ?></div>
  14. <div class="nav-next"><?php previous_posts_link( __( 'Newer posts <span class="meta-nav">&rarr;</span>', 'newtheme' ) ); ?></div>
  15. </nav><!-- #nav-above -->
  16. <?php endif;
  17. }
  18. endif; // newtheme_content_nav

Muestra los links para mostrar la siguiente o anterior página de posts cuando sea aplicable.

  1. <?php
  2.  
  3. /**
  4.  * Return the URL for the first link found in the post content.
  5.  *
  6.  * @return string|bool URL or false when no link is present.
  7.  */
  8. function newtheme_url_grabber() {
  9. if ( ! preg_match( '/<a\s[^>]*?href=[\'"](.+?)[\'"]/is', get_the_content(), $matches ) )
  10. return false;
  11.  
  12. return esc_url_raw( $matches[1] );
  13. }

Devuelve la url del primer link encontrado en el contenido del post.

  1. <?php
  2.  
  3. /**
  4.  * Count the number of footer sidebars to enable dynamic classes for the footer
  5.  */
  6. function newtheme_footer_sidebar_class() {
  7. $count = 0;
  8.  
  9. if ( is_active_sidebar( 'sidebar-3' ) )
  10. $count++;
  11.  
  12. if ( is_active_sidebar( 'sidebar-4' ) )
  13. $count++;
  14.  
  15. if ( is_active_sidebar( 'sidebar-5' ) )
  16. $count++;
  17.  
  18. $class = '';
  19.  
  20. switch ( $count ) {
  21. case '1':
  22. $class = 'one';
  23. break;
  24. case '2':
  25. $class = 'two';
  26. break;
  27. case '3':
  28. $class = 'three';
  29. break;
  30. }
  31.  
  32. if ( $class )
  33. echo 'class="' . $class . '"';
  34. }

Cuenta el número de sidebars habilitados para el pie de página y devuelve la clase que corresponda.

  1. <?php
  2.  
  3. if ( ! function_exists( 'newtheme_comment' ) ) :
  4. /**
  5.  * Template for comments and pingbacks.
  6.  *
  7.  * To override this walker in a child theme without modifying the comments template
  8.  * simply create your own newtheme_comment(), and that function will be used instead.
  9.  *
  10.  * Used as a callback by wp_list_comments() for displaying the comments.
  11.  *
  12.  */
  13. function newtheme_comment( $comment, $args, $depth ) {
  14. $GLOBALS['comment'] = $comment;
  15. switch ( $comment->comment_type ) :
  16. case 'pingback' :
  17. case 'trackback' :
  18. ?>
  19. <li class="post pingback">
  20. <p><?php _e( 'Pingback:', 'newtheme' ); ?> <?php comment_author_link(); ?><?php edit_comment_link( __( 'Edit', 'newtheme' ), '<span class="edit-link">', '</span>' ); ?></p>
  21. <?php
  22. break;
  23. default :
  24. ?>
  25. <li <?php comment_class(); ?> id="li-comment-<?php comment_ID(); ?>">
  26. <article id="comment-<?php comment_ID(); ?>" class="comment">
  27. <footer class="comment-meta">
  28. <div class="comment-author vcard">
  29. <?php
  30. $avatar_size = 68;
  31. if ( '0' != $comment->comment_parent )
  32. $avatar_size = 39;
  33.  
  34. echo get_avatar( $comment, $avatar_size );
  35.  
  36. /* translators: 1: comment author, 2: date and time */
  37. printf( __( '%1$s on %2$s <span class="says">said:</span>', 'newtheme' ),
  38. sprintf( '<span class="fn">%s</span>', get_comment_author_link() ),
  39. sprintf( '<a href="%1$s"><time pubdate datetime="%2$s">%3$s</time></a>',
  40. esc_url( get_comment_link( $comment->comment_ID ) ),
  41. get_comment_time( 'c' ),
  42. /* translators: 1: date, 2: time */
  43. sprintf( __( '%1$s at %2$s', 'newtheme' ), get_comment_date(), get_comment_time() )
  44. )
  45. );
  46. ?>
  47.  
  48. <?php edit_comment_link( __( 'Edit', 'newtheme' ), '<span class="edit-link">', '</span>' ); ?>
  49. </div><!-- .comment-author .vcard -->
  50.  
  51. <?php if ( $comment->comment_approved == '0' ) : ?>
  52. <em class="comment-awaiting-moderation"><?php _e( 'Your comment is awaiting moderation.', 'newtheme' ); ?></em>
  53. <br />
  54. <?php endif; ?>
  55.  
  56. </footer>
  57.  
  58. <div class="comment-content"><?php comment_text(); ?></div>
  59.  
  60. <div class="reply">
  61. <?php comment_reply_link( array_merge( $args, array( 'reply_text' => __( 'Reply <span>&darr;</span>', 'newtheme' ), 'depth' => $depth, 'max_depth' => $args['max_depth'] ) ) ); ?>
  62. </div><!-- .reply -->
  63. </article><!-- #comment-## -->
  64.  
  65. <?php
  66. break;
  67. endswitch;
  68. }
  69. endif; // ends check for newtheme_comment()

Esta función genera el código html necesario para mostrar cada comentario de un post.

  1. <?php
  2.  
  3. if ( ! function_exists( 'newtheme_posted_on' ) ) :
  4. /**
  5.  * Prints HTML with meta information for the current post-date/time and author.
  6.  * Create your own newtheme_posted_on to override in a child theme
  7.  */
  8. function newtheme_posted_on() {
  9. printf( __( '<span class="sep">Posted on </span><a href="%1$s" title="%2$s" rel="bookmark"><time class="entry-date" datetime="%3$s" pubdate>%4$s</time></a><span class="by-author"> <span class="sep"> by </span> <span class="author vcard"><a class="url fn n" href="%5$s" title="%6$s" rel="author">%7$s</a></span></span>', 'newtheme' ),
  10. esc_url( get_permalink() ),
  11. esc_attr( get_the_time() ),
  12. esc_attr( get_the_date( 'c' ) ),
  13. esc_html( get_the_date() ),
  14. esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ),
  15. esc_attr( sprintf( __( 'View all posts by %s', 'newtheme' ), get_the_author() ) ),
  16. get_the_author()
  17. );
  18. }
  19. endif;

Esta función devuelve los metadatos de un post.

  1. <?php
  2.  
  3. /**
  4.  * Adds two classes to the array of body classes.
  5.  * The first is if the site has only had one author with published posts.
  6.  * The second is if a singular post being displayed
  7.  */
  8. function newtheme_body_classes( $classes ) {
  9.  
  10. if ( function_exists( 'is_multi_author' ) && ! is_multi_author() )
  11. $classes[] = 'single-author';
  12.  
  13. if ( is_singular() && ! is_home() && ! is_page_template( 'showcase.php' ) && ! is_page_template( 'sidebar-page.php' ) )
  14. $classes[] = 'singular';
  15.  
  16. return $classes;
  17. }
  18. add_filter( 'body_class', 'newtheme_body_classes' );

Esta función devuelve las clases de estilos que corresponden para la etiqueta body dependiendo del tipo de página que se muestre al usuario.

Logo Cassandra

Cassandra 1.x y PHP para desarrolladores SQL: PHPCassa (I)

0

¡Por fin llego el día! ¡Hoy toca programar!

Para ello vamos a utilizar las clases de abstracción de la base de datos PHPCassa que nos ahorrará bastante tiempo para trabajar con Cassandra. Puedes descargarlo desde https://github.com/thobbs/phpcassa.

Ubicamos PHPCassa en una carpeta dentro de nuestro servidor web y creamos dos archivos. Yo los he llamado test.php y cassandra.php. El segundo será una clase que nos ahorrará algunas lineas de código con las tareas habituales. El primero lo utilizaré para testear la clase y sus respectivos métodos.

Bien empecemos.

 

PRIMEROS PASOS

En primer lugar necesitamos incluir un archivo de PHPCassa en cassandra.php:

  1. <?php
  2.  
  3. // CLASES NECESARIAS PARA CONECTAR CON CASSANDRA
  4. require_once('phpcassa/lib/autoload.php');

He inmediatamente llamamos a los namespace mínimos para trabajar con PHPCassa:

  1. <?php
  2.  
  3. use phpcassa\Connection\ConnectionPool;
  4. use phpcassa\ColumnFamily;
  5. use phpcassa\SystemManager;
  6. use phpcassa\Schema\StrategyClass;

 

Justo debajo creamos la clase:

  1. <?php
  2.  
  3. class cassandra {
  4.  
  5. }

 

CONECTANDO

Para realizar la conexión a la BD, PHPCassa nos pide tan solo un dato obligatorio: el nombre del keyspace con el que vamos a trabajar. También nos permite añadir las ips de los nodos con los que queramos trabajar, aunque por defecto su valor es localhost. Por tanto podemos crear un constructor como el siguiente:

  1. <?php
  2.  
  3. function __construct($keyspace, $nodos = array('localhost')){
  4. if (!empty($keyspace)){
  5. $this->conexion = new ConnectionPool($keyspace, $nodos);
  6. }else{
  7. print "El keyspace está vacío";
  8. }
  9. }

Este constructor nos permite indicarle a la clase el keyspace con el que queremos trabajar y la ip o nombre de servidor donde se encuentre nuestra instancia de Cassandra. Por defecto el puerto al que se va a conectar es el 9160. Si hubieses configurado Cassandra para escuchar en otro puerto deberás especificarlo: localhost:9160.

Este código nos crea una variable llamada conexion que será una instancia del objeto ConnectionPool, necesario para todas las consultas a la BD

 

GUARDANDO REGISTROS

Ahora procedemos a crear el método que guardará los registros. Para ello primero crearemos un objeto ColumnFamily al que le pasaremos el nombre de la column family en la que queremos trabajar:

  1. <?php
  2.  
  3. public function guardar($name_columnFamily, $key, $data = array()){
  4. try {
  5. $column_family = new ColumnFamily($this->conexion, $name_columnFamily);
  6. $column_family->insert($key, $data);
  7. return true;
  8. }catch(Exception $e){
  9. return false;
  10. }
  11. }

Este método nos devolverá true si se ha guardado correctamente el registro, o false en caso de error.
Como puedes ver, al objeto ColumnFamily se le pasa como primer parámetro el objeto ConnectionPool que creamos en el constructor, después se le pasa el nombre de la column family.
Una vez creado el objeto ColumnFamily ya podemos utilizar el método insert para guardar el registro pasándole como parámetros la key y un array con las columnas y sus respectivos valores.

 

RECUPERANDO REGISTROS

Recuperar registros de Cassandra es algo más complejo que guardarlos:

Primero debemos añadir un nuevo namespace que nos permitirá usar una clase que nos será muy útil. El listado de namespaces nos quedaría así:

  1. <?php
  2.  
  3. use phpcassa\Connection\ConnectionPool;
  4. use phpcassa\ColumnFamily;
  5. use phpcassa\ColumnSlice;
  6. use phpcassa\SystemManager;
  7. use phpcassa\Schema\StrategyClass;

A continuación creamos el método en nuestro archivo cassandra.php:

 

  1. <?php
  2.  
  3. public function obtener($name_columnFamily, $key, $column_names= NULL, $range_start = "", $range_end = "", $column_count = 100, $invertir_orden=false){
  4.  
  5. try{
  6. $column_slices = new ColumnSlice($range_start, $range_end, $column_count, $invertir_orden);
  7. $column_family = new ColumnFamily($this->conexion, $name_columnFamily);
  8. $result = $column_family->get($key, $column_slices, $column_names);
  9. }catch(Exception $e){
  10. return false;
  11. }
  12.  
  13. return $result;
  14.  
  15. }

 

Este método tiene algunos parámetros más que al guardar, pero tienen su razón de ser:

$columnFamily: Nombre de la column family donde buscar.
$key: Clave del registro donde buscar.
$columns: Columnas a buscar. No es obligatorio. Por defecto NULL.
$range_start: Key por la que Cassandra debe empezar a recuperar registros. No es obligatorio. Por defecto «».$range_end: Key por la que Cassandra dejará de recuperar registros. No es obligatorio. Por defecto «».
$column_count: Número de registros a obtener, Por defecto 100.
$invertir_orden: Invierte el orden en el que se recuperar los registros. De mayor a menor o viceversa, de la A-Z o viceversa, etc.

Como ves tienes bastantes opciones para recuperar registros. Este método devuelve un array con los registros.

 

PROBANDO LA CLASE

Vamos a probar el código a ver que tal funciona. Nos vamos al archivo test.php y escribimos el siguiente código:

  1. <?php
  2.  
  3. include_once "cassandra.php";
  4.  
  5. $cass = new cassandra('my_keyspace', array('192.168.1.10'));
  6.  
  7. $data = array(
  8. 'nombre' => 'pepito',
  9. 'ciudad' => 'Madrid',
  10. 'vehiculo' => 'coche',
  11. );
  12.  
  13. if ( $cass->guardar('my_column_family_standard', $key=1, $data)){
  14. print "El registro se ha guardado correctamente<br />";
  15. }else{
  16. print "Error al guardar el registro<br />";
  17. }
  18.  
  19. $result = $cass->obtener('my_column_family_standard', $key=1);
  20.  
  21. print_r($result);
  22. ?>

Explico un poco el código anterior:

  1. Incluimos la clase en el archivo.
  2. Creamos el objeto indicándole el keyspace con el que trabajar (my_keyspace), y dentro de un array, la ip donde se encuentra en nodo de Cassandra. Si tuvieses Cassandra instalado en localhost no sería necesario indicar el segundo parámetro.
  3. Creamos el array que contendrá los datos a guardar.
  4. Creamos un condicional que guardará los datos en Cassandra y nos indicará si se han guardado correctamente o ha habido algún error.
  5. Guardamos en una variable ($result) los registros que recuperamos a través del método obtener del objeto $cass. A este método le indicamos la column family a buscar y la key a obtener.
  6. Mostramos los registros.

Si todo ha salido correctamente verás un texto como el siguiente cuando ejecutes el script:

El registro se ha guardado correctamente
 Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche )

Si te aparecen más registros es posible que no vaciases las column family con la que trabajas. Simplemente ves a Cassandra Cluster Admin, entra en la column family y pulsa en Truncate Column Family. Así eliminarás todos los datos de la column family.

 

ACTUALIZAR UN REGISTRO

Ahora que ya funciona nuestra clase podemos ampliarla con nuevas características.
Aunque pienses que para actualizar un registro es necesario un nuevo método, en Cassandra no es necesario, simplemente utilizaremos el método guardar indicandole la key a modificar y el array con los datos a guardar.

Justo debajo del código que ya tenemos en el archivo test.php escribimos lo siguiente:

  1. <?php
  2.  
  3. $data = array(
  4. 'ciudad' => 'Barcelona',
  5. );
  6.  
  7. if ( $cass->guardar('my_column_family_standard', $key=1, $data)){
  8. print "El registro se ha guardado correctamente<br />";
  9. }else{
  10. print "Error al guardar el registro<br />";
  11. }
  12.  
  13. $result = $cass->obtener('my_column_family_standard', $key=1);
  14.  
  15. print_r($result);

Como observarás el array no tiene todos los datos de la key que vamos a modificar, ya que no es necesario, tan solo pasaremos los datos que queremos actualizar. El resultado de este código es el siguiente:

El registro se ha guardado correctamente
 Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche )
 El registro se ha guardado correctamente
 Array ( [ciudad] => Barcelona [nombre] => pepito [vehiculo] => coche )

La columna ciudad a cambiado, el resto sigue igual, la razón está en que cuando le pasas a Cassandra una key que ya existe, lo que hace es actualizar las columnas que se correspondan con las keys del array. Si en ese array hubiera keys que no existen como columnas en el registro, Cassandra simplemente las crearía nuevas.

 

GUARDANDO UNA SUPER COLUMN

Aquí tampoco nos hace falta crear un nuevo método ya que nos sirve perfectamente el método guardar, pero deberemos hacerle algunas modificaciones.

En primer lugar, para trabajar con column families super es necesario añadir una llamada al namespace concreto. La lista de namespaces quedaría de la siguiente manera:

  1. <?php
  2.  
  3. use phpcassa\Connection\ConnectionPool;
  4. use phpcassa\ColumnFamily;
  5. use phpcassa\SuperColumnFamily;
  6. use phpcassa\ColumnSlice;
  7. use phpcassa\SystemManager;
  8. use phpcassa\Schema\StrategyClass;

Ahora debemos modificar el método guardar y añadirle el parámetro $is_super_column, que nos permitirá crear un objeto SuperColumnFamily o ColumnFamily según corresponda. El método nos quedaría así:

  1. <?php
  2.  
  3. public function guardar($name_columnFamily, $key, $data = array(), $is_super_column = false){
  4.  
  5. try {
  6. if ($is_super_column){
  7. $column_family = new SuperColumnFamily($this->conexion, $name_columnFamily);
  8. }
  9. else{
  10. $column_family = new ColumnFamily($this->conexion, $name_columnFamily);
  11. }
  12.  
  13. $column_family->insert($key, $data);
  14. return true;
  15. }catch(Exception $e){
  16. return false;
  17. }
  18. }

Como ambas clases comparten el método insert no nos hace falta incluir esa linea dentro del condicional.

A continuación modificamos el método obtener con el mismo parámetro y utilizando el mismo sistema para crear el objeto correspondiente:

  1. <?php
  2.  
  3. public function obtener($name_columnFamily, $key, $is_super_column=false, $column_names= NULL, $range_start = "", $range_end = "", $column_count = 100, $invertir_orden=false){
  4.  
  5. try{
  6. if ($is_super_column){
  7. $column_family = new SuperColumnFamily($this->conexion, $name_columnFamily);
  8. }
  9. else{
  10. $column_family = new ColumnFamily($this->conexion, $name_columnFamily);
  11. }
  12. $column_slices = new ColumnSlice($range_start, $range_end, $column_count, $invertir_orden);
  13. $result = $column_family->get($key, $column_slices, $column_names);
  14. }catch(Exception $e){
  15. return false;
  16. }
  17.  
  18. return $result;
  19.  
  20. }

Ahora podemos añadir el siguiente código a nuestro archivo test.php:

  1. <?php
  2.  
  3. $data = array('vecino_1' => array(
  4. 'nombre' => 'pepito',
  5. 'ciudad' => 'Madrid',
  6. 'vehiculo' => 'coche',
  7. ));
  8.  
  9. if ( $cass->guardar('my_column_family_super', $key=1, $data, $is_super_column=true)){
  10. print "El registro se ha guardado correctamente<br />";
  11. }else{
  12. print "Error al guardar el registro<br />";
  13. }
  14.  
  15. $result = $cass->obtener('my_column_family_super', $key=1, $is_super_column=true);
  16.  
  17. print_r($result);

El único cambio además del nombre de la column family donde vamos a guardar el registro, se encuentra en el array con los datos. Este tiene un elemento que es la key de la super column, y como valor, el array de las columns a guardar.

Si ejecutamos el código anterior nos devolverá lo siguiente:

El registro se ha guardado correctamente
 Array ( [vecino_1] => Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche ) )

[ci-box type=»warning»]Al probar el código, PHP me devolvió el siguiente aviso:
Strict Standards: Declaration of phpcassa\SuperColumnFamily::add() should be compatible with that of phpcassa\ColumnFamily::add() in /var/www/prueba_cassandra/phpcassa/lib/phpcassa/SuperColumnFamily.php on line 491. 

No te preocupes no afecta ni a los ejemplos ni a la clase que funcionan correctamente.[/ci-box]

ACTUALIZANDO UNA SUPER COLUMN

La actualización de una super column se limita a las columnas que contienen los valores del registro no a su key. Si modificáramos la key , lo que en realidad estaríamos haciendo sería crear una nueva super column. Añadimos el siguiente código a nuestro archivo test.php:

  1. <?php
  2.  
  3. $data = array('vecino_1' => array(
  4. 'nombre' => 'pepito',
  5. 'ciudad' => 'Barcelona',
  6. 'vehiculo' => 'coche',
  7. ));
  8.  
  9. if ( $cass->guardar('my_column_family_super', $key1, $data, $is_super_column=true)){
  10. print "El registro se ha guardado correctamente<br />";
  11. }else{
  12. print "Error al guardar el registro<br />";
  13. }
  14.  
  15. $result = $cass->obtener('my_column_family_super', $key=1, $is_super_column=true);
  16.  
  17. print_r($result);

Y el resultado es:

El registro se ha guardado correctamente
 Array ( [vecino_1] => Array ( [ciudad] => Barcelona [nombre] => pepito [vehiculo] => coche ) )

 

ELIMINAR REGISTROS

Sí, aquí toca crear un nuevo método para nuestra clase en cassandra.php, aunque tampoco nos vamos a matar programando:

 

  1. <?php
  2.  
  3. public function eliminar($name_columnFamily, $key, $is_super_column = false){
  4.  
  5. try{
  6. if ($is_super_column){
  7. $column_family = new SuperColumnFamily($this->conexion, $name_columnFamily);
  8. }
  9. else{
  10. $column_family = new ColumnFamily($this->conexion, $name_columnFamily);
  11. }
  12. $column_family->remove($key);
  13. return true;
  14. }catch(Exception $e){
  15. return false;
  16. }
  17. }

 

El método es bastante sencillo: se le pasan tres parámetros que ya conocemos.

Dentro del método se crea un objeto ColumnFamily y se utiliza el método remove para eliminar el registro pasándole la key correspondiente, y si fuese una super column, la key de este.
El método remove tiene un segundo parámetro que por defecto tiene un valor null, en él se pueden añadir como array los nombres de las columnas a eliminar. Como no es habitual ese proceso he creído adecuado no utilizarlo.

Una vez añadido esté método a nuestra clase modificamos el archivo test.php con el siguiente código:

  1. <?php
  2.  
  3. if ( $cass->eliminar('my_column_family_standard', $key=1)){
  4. print "El registro se ha eliminado correctamente<br />";
  5. }else{
  6. print "Error al eliminar el registro<br />";
  7. }
  8.  
  9. $result = $cass->obtener('my_column_family_standard', $key=1);
  10.  
  11. print_r($result);

Y el resultado es:

El registro se ha eliminado correctamente

 

De momento con esto es suficiente, dejo en tus manos estos métodos para que juegues con ellos todo lo que quieras y te familiarices con PHPCassa.

En el próximo post trataré las Counter Columns que nos servirán para contar registros, los Secondary Index que nos permitirán realizar búsquedas por columnas concretas, y algunas cosas más.

Logo Cassandra

Cassandra 1.x y PHP para desarrolladores SQL: Cassandra Cluster Admin, el phpMyAdmin de Cassandra

3

En el anterior post expliqué el funcionamiento de la consola de Cassandra y como trabajar con ella. En este post explicaré como hacer lo mismo de una forma más rápida y fácil.

La razón de que no haya empezado ha explicar como se utiliza Cassandra desde PHP, es que primero hay que conocer cómo funciona Cassandra, su modelo de datos (que ya hemos visto) y algunos comandos básicos para ir aprendiendo a guardar y recuperar datos. El próximo post ya trataré el funcionamiento de PHPCassa y empezaremos a programar.

Este post será bastante ligerito. Explicaré principalmente como hacer lo mismo que hicimos por consola pero de modo gráfico. CassAdmin, como yo le llamo, tiene más opciones de edición para los keyspaces y column families de los que vamos a ver (igual que ocurría con la consola), pero solo tocaremos las opciones más habituales.

 

INSTALACIÓN

Lo típico en estos casos. Descargar. Descomprimir y ejecutar. Descargamos los archivos desde el repositorio de git: https://github.com/sebgiroux/Cassandra-Cluster-Admin, o a través de git o como archivo comprimido. Como más te guste.

Proceso para descargar Cassandra Cluster Admin

Página de descargas del administrador

Ahora descomprimimos a una carpeta dentro de nuestro servidor web, ya que trabajamos con una aplicación programada en PHP.

Si tienes Cassandra instalado en otro equipo que no sea en local, una máquina virtual por ejemplo, tendrás que cambiar la ip a la que se conecta cassAdmin. Es bastante sencillo. Para ello debes modificar el archivo includes/config.inc.php y sustituir la ip de localhost (127.0.0.1)  por la ip del equipo en el que tengas instalado Cassandra:

Archivo config.inc.php

Como verás en la captura, yo utilizo la ip local 192.168.1.10 que pertenece a una máquina virtual. Si tuviera instalado Cassandra en  el mismo equipo la ip a la que debería apuntar sería 127.0.0.1. El puerto no lo modificamos.

Una vez instalado y configurado (si fuese el caso) probamos a acceder desde nuestro navegador. Debería mostrarte algo como esto:

Página principal de Cassandra Cluster Admin

CREAR KEYSPACES

Como has podido ver, en la página principal del administrador ya aparece un keyspace, llamado system, perteneciente a Cassandra, con lo que lo mantendremos como está.

Justo encima tenemos un botón para crear un nuevo keyspace (Create a new keyspace). Lo pulsamos.

Formulario para crear un keyspace

Solo nos pide tres parámetros a rellenar:


Keyspace name
: El nombre del keyspace a crear.
Replication factor: Es el número de servidores o instancias de Cassandra de los que se debe guardar un registro u obtener una respuesta al recuperar algún registro.
Strategy: Es la estrategia que seguirá Cassandra para guardar los datos. En el caso de tener varios servidores o centro de datos diferenciados entre sí, se aplicará una estrategia u otra para que los datos no se pierdan. Más info: http://answers.oreilly.com/topic/2408-replica-placement-strategies-when-using-cassandra/ 

En nuestro caso escribiremos los siguientes valores:

Formulario para crear un keyspace con datos

Pulsamos en Create keyspace y nos llevará a la página principal:

Página principal mostrando el keyspace que acabamos de crear

Pulsamos sobre el keyspace que acabamos de crear y nos llevará a la siguiente página:

Página de detalle del keyspace my_keyspace

Aquí podemos ver información relativa al keyspace y al anillo (o cluster) en el que se encuentra guardado. También nos permite editar el keyspace o eliminarlo, pero la opción más interesante es la de crear nuevas column families (Create a new column family).

 

CREAR UNA COLUMN FAMILY

Dentro del keyspace pulsamos sobre el botón Create a new column family, nos mostrará lo siguiente:

Formulario para crear una column family

 

Primero crearemos una column family standard con los siguientes datos:

Datos para crear la primera column family

Para crear una column family solo son necesarios tres parámetros:


Column Family Name
: El nombre de la column family que vamos a crear.
Column Type: El tipo de column family que vamos a crear (Standard o Super).
Comparator Type: El comparador principal de las columnas. Es decir, la codificación que tendrán los datos dentro de la column family.

Pulsamos en Create Column Family y en la página de nuestro keyspace se habrá añadido una nueva column family:

Detalle de la column family creada

 

AÑADIR REGISTROS A UNA COLUMN FAMILY STANDARD

Ahora toca guardar registros en la BD.
Como comenté en post anteriores Cassandra no necesita conocer los campos de las tablas (nuestras column families) ya que los creamos cada vez que añadimos un registro.

Primero en la página principal del keyspace haz click en el nombre de la column family. Te aparecerá lo siguiente:

 

Detalle de la página principal de la column family creada

En esta página te aparecerá, además de la información referente a la column family, los siguientes botones:


Browse Data
: Para mostrar un listado de los registros que contiene la column family.
Create Secondary Index: Para crear indices secundarios. Parecido a los indices de MySQL.
Get Key: Para buscar una clave o registro concreto.
Insert Row: Para insertar un nuevo registro.
Edit Column Family: Para editar los parámetros de la column family.
Truncate Column Family: Para eliminar los registros de la column family sin eliminar esta.
Drop Column Family: Para eliminar la column family y su contenido.

Pulsamos en Insert Row. Nos aparecerá lo siguiente:

Formulario para insertar registros

Siempre que añadamos un nuevo registro deberemos indicar una key diferente que deberá ser única, sino cassAdmin pensará que lo que quieres hacer es actualizar ese registro. Añadimos los siguientes datos al formulario:

Formulario para insertar registros con datos

 

Para añadir otra fila de cuadro de texto solo tienes que pulsar el botón Add…
Volvemos a la página principal de la column family y pulsamos en Browse Data. Aquí veremos un listado con los registros que hayamos añadido:

 

Listado de registros

 

CREAR UNA COLUMN FAMILY SUPER Y AÑADIR REGISTROS

El proceso para crear una column family Super es idéntico a las column family Standard, solo hay que cambiar un valor a la hora de crearla:

Creación de una column family Super

Entramos en la página principal de la nueva column family y pulsamos en Insert Row. Aquí veremos que el formulario a cambiado un poco, nos aparece un nuevo campo llamado Super Column Name y un nuevo botón encima del formulario, Add Super Column:

Formulario para insertar registros de una column family super

Las column family Super son como las muñecas matrioskas, esas figuras que si las abrías había otra igual dentro, y dentro de esa otra más, y otra, etc. Algo parecido sucede con estas column family. Disponemos de una key (nuestra primera matrioska) que contiene a las super columns (segunda matrioska), que a su vez contienen las columnas clave:valor.

Añadamos un registro para verlo:

Datos para crear una super column

Si nos vamos a Browse Data:

Listado de registros en una column family Super

Como ves, ahora las columns se agruparían dentro de la super column. Añadiré más registros para que lo veas mejor:

Listado de registros en una column family Super

¿Ves? Las keys agrupan a las super columns y estas a su vez a las columns.

 

Creo que esto es suficiente para que conozcas el funcionamiento de Cassandra Cluster Admin. Quedarían algunas cosillas, como editar registros, hacer búsquedas o eliminar registros, pero eso es bastante sencillo (a excepción de las Secondary Index y las Counter Columns que trataré más adelante).

Para cualquier duda o problema déjala en los comentarios.

Logo Cassandra

Cassandra y PHP para desarrolladores SQL: PHPCassa (I)

10

¡Por fin llego el día! ¡Hoy toca programar!

Para ello vamos a utilizar el cliente de alto nivel PHPCassa que nos ahorrará bastante tiempo para trabajar con Cassandra. Puedes descargarlo desde https://github.com/thobbs/phpcassa.

Ubicamos phpCassa en una carpeta dentro de nuestro servidor web y creamos dos archivos. Yo los he llamado test.php y cassandra.php, El segundo será una clase que nos ahorrará algunas lineas de código con las tareas habituales. El primero lo utilizaré para testear la clase y sus respectivos métodos.

Bien empecemos.

 

PRIMEROS PASOS

En primer lugar necesitamos incluir dos archivos de phpCassa en cassandra.php:

  1. <?php
  2.  
  3. // CLASES NECESARIAS PARA CONECTAR CON CASSANDRA
  4. require_once('phpcassa/connection.php');
  5. require_once('phpcassa/columnfamily.php');

El primero de ellos dispone de los métodos para conectar con Cassandra a través de Thirft; el segundo tiene los métodos para trabajar con las column families.

Justo debajo creamos la clase:

  1. <?php
  2.  
  3. class cassandra {
  4.  
  5. }

 

CONECTANDO

Para realizar la conexión a la BD, phpCassa nos pide tan solo un dato obligatorio: el nombre del keyspace con el que vamos a trabajar. También nos permite añadir las ips de los nodos con los que queramos trabajar, aunque por defecto su valor es localhost. Por tanto podemos crear un constructor como el siguiente:

  1. <?php
  2.  
  3. function __construct($keyspace, $nodos = array('localhost')){
  4. if (!empty($keyspace)){
  5. $this->conexion = new ConnectionPool($keyspace, $nodos);
  6. }else{
  7. print "El keyspace está vacío";
  8. }
  9. }

Este constructor nos permite indicarle a la clase el keyspace con el que queremos trabajar y la ip o nombre de servidor donde se encuentre nuestra instancia de Cassandra. Por defecto el puerto al que se va a conectar es el 9160. Si hubieses configurado Cassandra para escuchar en otro puerto deberás especificarlo: localhost:9160.

Este código nos crea una variable llamada conexion que contendrá el objeto ConnectionPool, necesario para todas las consultas a la BD

 

GUARDANDO REGISTROS

Ahora procedemos a crear el método que guardará los registros. Para ello primero crearemos un objeto ColumnFamily al que le pasaremos el nombre de la column family en la que queremos trabajar:

  1. <?php
  2.  
  3. public function guardar($columnFamily, $key, $data = array()){
  4. try {
  5. $column_family = new ColumnFamily($this->conexion, $columnFamily);
  6. $column_family->insert($key, $data);
  7. return true;
  8. }catch(Exception $e){
  9. return false;
  10. }
  11. }

Este método nos devolverá true si se ha guardado correctamente el registro, o false en caso de error.
Como puedes ver, al objeto ColumnFamily se le pasa como primer parámetro el objeto ConnectionPool que creamos en el constructor, después se le pasa el nombre de la column family.
Una vez creado el objeto ColumnFamily ya podemos utilizar el método insert para guardar el registro pasándole como parámetros la key y un array con las columnas y sus respectivos valores.

 

RECUPERANDO REGISTROS

Recuperar registros de Cassandra es algo más complejo que guardarlos:

  1. <?php
  2.  
  3. public function obtener($columnFamily, $key, $super_column=NULL, $columns= NULL, $range_start = "", $column_count = 100, $invertir_orden=false){
  4.  
  5. try{
  6. $column_family = new ColumnFamily($this->conexion, $columnFamily);
  7. $result = $column_family->get($key, $columns, $range_start, "", $invertir_orden, $column_count, $super_column);
  8. }catch(Exception $e){
  9. return false;
  10. }
  11.  
  12. return $result;
  13.  
  14. }

Este método tiene algunos parámetros más que al guardar, pero tienen su razón de ser:

$columnFamily: Nombre de la column family donde buscar.
$key: Clave del registro donde buscar.
$super_column: Key o clave de la super column que se debe buscar. No es obligatorio. Por defecto NULL.
$columns: Columnas a buscar. No es obligatorio. Por defecto NULL.
$range_start: Key por la que Cassandra debe empezar a recuperar registros. No es obligatorio. Por defecto «».
$column_count: Número de registros a obtener, Por defecto 100.
$invertir_orden: Invierte el orden en el que se recuperar los registros. De mayor a menor o viceversa, de la A-Z o viceversa, etc.

Como ves tienes bastantes opciones para recuperar registros. Este método devuelve un array con los registros.

 

PROBANDO LA CLASE

Vamos a probar el código a ver que tal funciona. Nos vamos al archivo test.php y escribimos el siguiente código:

  1. <?php
  2.  
  3. include_once "cassandra.php";
  4.  
  5. $cass = new cassandra('my_keyspace', array('192.168.1.10'));
  6.  
  7. $data = array(
  8. 'nombre' => 'pepito',
  9. 'ciudad' => 'Madrid',
  10. 'vehiculo' => 'coche',
  11. );
  12.  
  13. if ( $cass->guardar('my_column_family_standard', 1, $data)){
  14. print "El registro se ha guardado correctamente<br />";
  15. }else{
  16. print "Error al guardar el registro<br />";
  17. }
  18.  
  19. $result = $cass->obtener('my_column_family_standard', 1);
  20.  
  21. print_r($result);
  22. ?>

Explico un poco el código anterior:

  1. Incluimos la clase en el archivo.
  2. Creamos el objeto indicándole el keyspace con el que trabajar (my_keyspace), y dentro de un array, la ip donde se encuentra en nodo de Cassandra. Si tuvieses Cassandra instalado en localhost no sería necesario indicar el segundo parámetro.
  3. Creamos el array que contendrá los datos a guardar.
  4. Creamos un condicional que guardará los datos en Cassandra y nos indicará si se han guardado correctamente o ha habido algún error.
  5. Guardamos en una variable ($result) los registros que recuperamos a través del método obtener del objeto $cass. A este método le indicamos la column family a buscar y la key a obtener.
  6. Mostramos los registros.

Si todo ha salido correctamente verás un texto como el siguiente cuando ejecutes el script:

El registro se ha guardado correctamente
Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche )

Si te aparecen más registros es posible que no vaciases las column family con la que trabajas. Simplemente ves a cassAdmin, entra en la column family y pulsa en Truncate Column Family. Así eliminarás todos los datos de la column family.

 

ACTUALIZAR UN REGISTRO

Ahora que ya funciona nuestra clase podemos ampliarla con nuevas características.
Aunque pienses que para actualizar un registro es necesario un nuevo método, en Cassandra no es necesario, simplemente utilizaremos el método guardar indicandole la key a modificar y el array con los datos a guardar.

Justo debajo del código que ya tenemos en el archivo test.php escribimos lo siguiente:

  1. <?php
  2.  
  3. $data = array(
  4. 'ciudad' => 'Barcelona',
  5. );
  6.  
  7. if ( $cass->guardar('my_column_family_standard', 1, $data)){
  8. print "El registro se ha guardado correctamente<br />";
  9. }else{
  10. print "Error al guardar el registro<br />";
  11. }
  12.  
  13. $result = $cass->obtener('my_column_family_standard', 1);
  14.  
  15. print_r($result);

Como observarás el array no tiene todos los datos de la key que vamos a modificar, ya que no es necesario, tan solo pasaremos los datos que queremos actualizar. El resultado de este código es el siguiente:

El registro se ha guardado correctamente
Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche )
El registro se ha guardado correctamente
Array ( [ciudad] => Barcelona [nombre] => pepito [vehiculo] => coche )

La columna ciudad a cambiado, el resto sigue igual, la razón está en que cuando le pasas a Cassandra una key que ya existe, lo que hace es actualizar las columnas que se correspondan con las keys del array. Si en ese array hubiera keys que no existen como columnas en el registro, Cassandra simplemente las crearía nuevas.

 

GUARDANDO UNA SUPER COLUMN

Aquí tampoco nos hace falta crear un nuevo método ya que nos sirve perfectamente el método guardar.

Añadimos el siguiente código a nuestro archivo test.php:

  1. <?php
  2.  
  3. $data = array('vecino_1' => array(
  4. 'nombre' => 'pepito',
  5. 'ciudad' => 'Madrid',
  6. 'vehiculo' => 'coche',
  7. ));
  8.  
  9. if ( $cass->guardar('my_column_family_super', 1, $data)){
  10. print "El registro se ha guardado correctamente<br />";
  11. }else{
  12. print "Error al guardar el registro<br />";
  13. }
  14.  
  15. $result = $cass->obtener('my_column_family_super', 1);
  16.  
  17. print_r($result);

El único cambio además del nombre de la column family donde vamos a guardar el registro, se encuentra en el array con los datos. Este tiene un elemento que es la key de la super column, y como valor, el array de las columns a guardar.

Si ejecutamos el código anterior nos devolverá lo siguiente:

El registro se ha guardado correctamente
Array ( [vecino_1] => Array ( [ciudad] => Madrid [nombre] => pepito [vehiculo] => coche ) )

 

ACTUALIZANDO UN SUPER COLUMN

La actualización de una super column se limita a las columnas que contienen los valores del registro no a su key. Si modificáramos la key , lo que en realidad estaríamos haciendo sería crear una nueva super column. Añadimos el siguiente código a nuestro archivo test.php:

  1. <?php
  2.  
  3. $data = array('vecino_1' => array(
  4. 'nombre' => 'pepito',
  5. 'ciudad' => 'Barcelona',
  6. 'vehiculo' => 'coche',
  7. ));
  8.  
  9. if ( $cass->guardar('my_column_family_super', 1, $data)){
  10. print "El registro se ha guardado correctamente<br />";
  11. }else{
  12. print "Error al guardar el registro<br />";
  13. }
  14.  
  15. $result = $cass->obtener('my_column_family_super', 1);
  16.  
  17. print_r($result);

Y el resultado es:

El registro se ha guardado correctamente
Array ( [vecino_1] => Array ( [ciudad] => Barcelona [nombre] => pepito [vehiculo] => coche ) )

 

ELIMINAR REGISTROS

Sí, aquí toca crear un nuevo método para nuestra clase en cassandra.php, aunque tampoco nos vamos a matar programando:

  1. <?php
  2.  
  3. public function eliminar($columnFamily, $key, $super_column = null){
  4.  
  5. try{
  6. $column_family = new ColumnFamily($this->conexion, $columnFamily);
  7. $column_family->remove($key, null, $super_column);
  8. return true;
  9. }catch(Exception $e){
  10. return false;
  11. }
  12. }

El método es bastante sencillo: se le pasan tres parámetros que ya conocemos.

Dentro del método se crea un objeto ColumnFamily y se utiliza el método remove para eliminar el registro pasándole la key correspondiente, y si fuese una super column, la key de este.
Observarás que el segundo parámetro del método remove tiene un valor null, en él se pueden añadir como array los nombres de las columnas a eliminar. Como no es habitual ese proceso he creído adecuado dejarlo como null.

Una vez añadido esté método a nuestra clase modificamos el archivo test.php con el siguiente código:

  1. <?php
  2.  
  3. if ( $cass->eliminar('my_column_family_standard', 1)){
  4. print "El registro se ha eliminado correctamente<br />";
  5. }else{
  6. print "Error al eliminar el registro<br />";
  7. }
  8.  
  9. $result = $cass->obtener('my_column_family_standard', 1);
  10.  
  11. print_r($result);

Y el resultado es:

El registro se ha eliminado correctamente

 

De momento con esto es suficiente, dejo en tus manos estos métodos para que juegues con ellos todo lo que quieras y te familiarices con phpCassa.

En el próximo post trataré las Counter Columns que nos servirán para contar registros, los Secondary Index que nos permitirán realizar búsquedas por columnas concretas, y algunas cosas más.

Logo Cassandra

Cassandra y PHP para desarrolladores SQL: Cassandra Cluster Admin, el phpMyAdmin de Cassandra

4

En el anterior post expliqué el funcionamiento de la consola de Cassandra y como trabajar con ella. En este post explicaré como hacer lo mismo de una forma más rápida y fácil.

La razón de que no haya empezado ha explicar como se utiliza Cassandra desde PHP, es que primero hay que conocer cómo funciona Cassandra, su modelo de datos (que ya hemos visto) y algunos comandos básicos para ir aprendiendo a guardar y recuperar datos. El próximo post ya trataré el funcionamiento de phpCassa y empezaremos a programar.

Este post será bastante ligerito. Explicaré principalmente como hacer lo mismo que hicimos por consola pero de modo gráfico. CassAdmin, como yo le llamo, tiene más opciones de edición para los keyspaces y column families de los que vamos a ver (igual que ocurría con la consola), pero solo tocaremos las opciones más habituales.

 

INSTALACIÓN

Lo típico en estos casos. Descargar. Descomprimir y ejecutar. Descargamos los archivos desde el repositorio de git: https://github.com/sebgiroux/Cassandra-Cluster-Admin, o a través de git o como archivo comprimido. Como más te guste.

Repositorio de Cassandra Cluster Admin

Página de descargas del administrador

Ahora descomprimimos a una carpeta dentro de nuestro servidor web, ya que trabajamos con una aplicación programada en PHP.

Si tienes Cassandra instalado en otro equipo que no sea en local, una máquina virtual por ejemplo, tendrás que cambiar la ip a la que se conecta cassAdmin. Es bastante sencillo. Para ello debes modificar el archivo includes/config.inc.php y sustituir la ip de localhost (127.0.0.1)  por la ip del equipo en el que tengas instalado Cassandra:

Archivo config.inc.php

Como verás en la captura, yo utilizo la ip local 192.168.1.10 que pertenece a una máquina virtual. Si tuviera instalado Cassandra en  el mismo equipo la ip a la que debería apuntar sería 127.0.0.1. El puerto no lo modificamos.

Una vez instalado y configurado (si fuese el caso) probamos a acceder desde nuestro navegador. Debería mostrarte algo como esto:

Página principal de Cassandra Cluster Admin

CREAR KEYSPACES

Como has podido ver, en la página principal del administrador ya aparece un keyspace, llamado system, perteneciente a Cassandra, con lo que lo mantendremos como está.

Justo encima tenemos un botón para crear un nuevo keyspace (Create a new keyspace). Lo pulsamos.

Formulario para crear un keyspace

Solo nos pide tres parámetros a rellenar:


Keyspace name
: El nombre del keyspace a crear.
Replication factor: Es el número de servidores o instancias de Cassandra de los que se debe guardar un registro u obtener una respuesta al recuperar algún registro.
Strategy: Es la estrategia que seguirá Cassandra para guardar los datos. En el caso de tener varios servidores o centro de datos diferenciados entre sí, se aplicará una estrategia u otra para que los datos no se pierdan. Más info: http://answers.oreilly.com/topic/2408-replica-placement-strategies-when-using-cassandra/ 

En nuestro caso escribiremos los siguientes valores:

Formulario para crear un keyspace con datos

Pulsamos en Create keyspace y nos llevará a la página principal:

Página principal mostrando el keyspace que acabamos de crear

Pulsamos sobre el keyspace que acabamos de crear y nos llevará a la siguiente página:

Página de detalle del keyspace my_keyspace

Aquí podemos ver información relativa al keyspace y al anillo (o cluster) en el que se encuentra guardado. También nos permite editar el keyspace o eliminarlo, pero la opción más interesante es la de crear nuevas column families (Create a new column family).

 

CREAR UNA COLUMN FAMILY

Dentro del keyspace pulsamos sobre el botón Create a new column family, nos mostrará lo siguiente:

Formulario para crear una column family

 

Primero crearemos una column family standard con los siguientes datos:

Datos para crear la primera column family

Para crear una column family solo son necesarios tres parámetros:


Column Family Name
: El nombre de la column family que vamos a crear.
Column Type: El tipo de column family que vamos a crear (Standard o Super).
Comparator Type: El comparador principal de las columnas. Es decir, la codificación que tendrán los datos dentro de la column family.

Pulsamos en Create Column Family y en la página de nuestro keyspace se habrá añadido una nueva column family:

Detalle de la column family creada

 

AÑADIR REGISTROS A UNA COLUMN FAMILY STANDARD

Ahora toca guardar registros en la BD.
Como comenté en post anteriores Cassandra no necesita conocer los campos de las tablas (nuestras column families) ya que los creamos cada vez que añadimos un registro.

Primero en la página principal del keyspace haz click en el nombre de la column family. Te aparecerá lo siguiente:

 

Detalle de la página principal de la column family creada

En esta página te aparecerá, además de la información referente a la column family, los siguientes botones:


Browse Data
: Para mostrar un listado de los registros que contiene la column family.
Create Secondary Index: Para crear indices secundarios. Parecido a los indices de MySQL.
Get Key: Para buscar una clave o registro concreto.
Insert Row: Para insertar un nuevo registro.
Edit Column Family: Para editar los parámetros de la column family.
Truncate Column Family: Para eliminar los registros de la column family sin eliminar esta.
Drop Column Family: Para eliminar la column family y su contenido.

Pulsamos en Insert Row. Nos aparecerá lo siguiente:

Formulario para insertar registros

Siempre que añadamos un nuevo registro deberemos indicar una key diferente que deberá ser única, sino cassAdmin pensará que lo que quieres hacer es actualizar ese registro. Añadimos los siguientes datos al formulario:

Formulario para insertar registros con datos

 

Para añadir otra fila de cuadro de texto solo tienes que pulsar el botón Add…
Volvemos a la página principal de la column family y pulsamos en Browse Data. Aquí veremos un listado con los registros que hayamos añadido:

 

Listado de registros

 

CREAR UNA COLUMN FAMILY SUPER Y AÑADIR REGISTROS

El proceso para crear una column family Super es idéntico a las column family Standard, solo hay que cambiar un valor a la hora de crearla:

Creación de una column family Super

Entramos en la página principal de la nueva column family y pulsamos en Insert Row. Aquí veremos que el formulario a cambiado un poco, nos aparece un nuevo campo llamado Super Column Name y un nuevo botón encima del formulario, Add Super Column:

Formulario para insertar registros de una column family super

Las column family Super son como las muñecas matrioskas, esas figuras que si las abrías había otra igual dentro, y dentro de esa otra más, y otra, etc. Algo parecido sucede con estas column family. Disponemos de una key (nuestra primera matrioska) que contiene a las super columns (segunda matrioska), que a su vez contienen las columnas clave:valor.

Añadamos un registro para verlo:

Datos para crear una super column

Si nos vamos a Browse Data:

Listado de registros en una column family Super

Como ves, ahora las columns se agruparían dentro de la super column. Añadiré más registros para que lo veas mejor:

Listado de registros en una column family Super

¿Ves? Las keys agrupan a las super columns y estas a su vez a las columns.

 

Creo que esto es suficiente para que conozcas el funcionamiento de Cassandra Cluster Admin. Quedarían algunas cosillas, como editar registros, hacer búsquedas o eliminar registros, pero eso es bastante sencillo (a excepción de las Secondary Index y las Counter Columns que trataré más adelante).

Para cualquier duda o problema déjala en los comentarios.

Ir arriba