Estas en: Home > esquema

Entradas etiquetadas con esquema

Cron Job WordPress

WordPress 3.x para desarrolladores: Temas y plantillas, theme-options.php

0

Nuestro tema va tomando poco a poco forma. Hoy vamos a crear el archivo theme-options.php que contendrá toda la lógica de las opciones de administración.

 

THEME-OPTIONS.PHP

Creamos el archivo theme-options.php y añadimos el siguiente código:

[codesyntax lang=»php»]

<?php

/**
 * Properly enqueue styles and scripts for our theme options page.
 *
 * This function is attached to the admin_enqueue_scripts action hook.
 */
function newtheme_admin_enqueue_scripts( $hook_suffix ) {
	wp_enqueue_style( 'newtheme-theme-options', get_template_directory_uri() . '/inc/theme-options.css', false, '2011-04-28' );
	wp_enqueue_script( 'newtheme-theme-options', get_template_directory_uri() . '/inc/theme-options.js', array( 'farbtastic' ), '2011-06-10' );
	wp_enqueue_style( 'farbtastic' );
}
add_action( 'admin_print_styles-appearance_page_theme_options', 'newtheme_admin_enqueue_scripts' );

[/codesyntax]

Esta función añade un archivo css y un archivo javascript a la cabecera al apartado apariencia de la administración de WordPress.

[codesyntax lang=»php»]

<?php

/**
 * Register the form setting for our newtheme_options array.
 *
 * This function is attached to the admin_init action hook.
 *
 * This call to register_setting() registers a validation callback, newtheme_theme_options_validate(),
 * which is used when the option is saved, to ensure that our option values are complete, properly
 * formatted, and safe.
 */
function newtheme_theme_options_init() {

	register_setting(
		'newtheme_options',       // Options group, see settings_fields() call in newtheme_theme_options_render_page()
		'newtheme_theme_options', // Database option, see newtheme_get_theme_options()
		'newtheme_theme_options_validate' // The sanitization callback, see newtheme_theme_options_validate()
	);

	// Register our settings field group
	add_settings_section(
		'general', // Unique identifier for the settings section
		'', // Section title (we don't want one)
		'__return_false', // Section callback (we don't want anything)
		'theme_options' // Menu slug, used to uniquely identify the page; see newtheme_theme_options_add_page()
	);

	// Register our individual settings fields
	add_settings_field(
		'color_scheme',  // Unique identifier for the field for this section
		__( 'Color Scheme', 'newtheme' ), // Setting field label
		'newtheme_settings_field_color_scheme', // Function that renders the settings field
		'theme_options', // Menu slug, used to uniquely identify the page; see newtheme_theme_options_add_page()
		'general' // Settings section. Same as the first argument in the add_settings_section() above
	);

	add_settings_field( 'link_color', __( 'Link Color',     'newtheme' ), 'newtheme_settings_field_link_color', 'theme_options', 'general' );
	add_settings_field( 'layout',     __( 'Default Layout', 'newtheme' ), 'newtheme_settings_field_layout',     'theme_options', 'general' );
}
add_action( 'admin_init', 'newtheme_theme_options_init' );

[/codesyntax]

Esta función registra la configuración por defecto del tema y añade varios apartados para la administración.

[codesyntax lang=»php»]

<?php

/**
 * Change the capability required to save the 'newtheme_options' options group.
 *
 * @see newtheme_theme_options_init() First parameter to register_setting() is the name of the options group.
 * @see newtheme_theme_options_add_page() The edit_theme_options capability is used for viewing the page.
 *
 * By default, the options groups for all registered settings require the manage_options capability.
 * This filter is required to change our theme options page to edit_theme_options instead.
 * By default, only administrators have either of these capabilities, but the desire here is
 * to allow for finer-grained control for roles and users.
 *
 * @param string $capability The capability used for the page, which is manage_options by default.
 * @return string The capability to actually use.
 */
function newtheme_option_page_capability( $capability ) {
	return 'edit_theme_options';
}
add_filter( 'option_page_capability_newtheme_options', 'newtheme_option_page_capability' );

[/codesyntax]

Esta función permite cambiar las opciones del tema. Es obligatorio para controlar los diversos usuarios y roles que tengan acceso.

[codesyntax lang=»php»]

<?php

/**
 * Add our theme options page to the admin menu, including some help documentation.
 *
 * This function is attached to the admin_menu action hook.
 */
function newtheme_theme_options_add_page() {
	$theme_page = add_theme_page(
		__( 'Theme Options', 'newtheme' ),   // Name of page
		__( 'Theme Options', 'newtheme' ),   // Label in menu
		'edit_theme_options',                    // Capability required
		'theme_options',                         // Menu slug, used to uniquely identify the page
		'newtheme_theme_options_render_page' // Function that renders the options page
	);

	if ( ! $theme_page )
		return;

	add_action( "load-$theme_page", 'newtheme_theme_options_help' );
}
add_action( 'admin_menu', 'newtheme_theme_options_add_page' );

[/codesyntax]

Añade la página de opciones de nuestro tema al menú de administración, incluyendo algo de documentación a la ayuda.

[codesyntax lang=»php»]

<?php

function newtheme_theme_options_help() {

	$help = '<p>' . __( 'Some themes provide customization options that are grouped together on a Theme Options screen. If you change themes, options may change or disappear, as they are theme-specific. Your current theme, Twenty Eleven, provides the following Theme Options:', 'newtheme' ) . '</p>' .
			'<ol>' .
				'<li>' . __( '<strong>Color Scheme</strong>: You can choose a color palette of "Light" (light background with dark text) or "Dark" (dark background with light text) for your site.', 'newtheme' ) . '</li>' .
				'<li>' . __( '<strong>Link Color</strong>: You can choose the color used for text links on your site. You can enter the HTML color or hex code, or you can choose visually by clicking the "Select a Color" button to pick from a color wheel.', 'newtheme' ) . '</li>' .
				'<li>' . __( '<strong>Default Layout</strong>: You can choose if you want your site&#8217;s default layout to have a sidebar on the left, the right, or not at all.', 'newtheme' ) . '</li>' .
			'</ol>' .
			'<p>' . __( 'Remember to click "Save Changes" to save any changes you have made to the theme options.', 'newtheme' ) . '</p>';

	$sidebar = '<p><strong>' . __( 'For more information:', 'newtheme' ) . '</strong></p>' .
		'<p>' . __( '<a href="http://codex.wordpress.org/Appearance_Theme_Options_Screen" target="_blank">Documentation on Theme Options</a>', 'newtheme' ) . '</p>' .
		'<p>' . __( '<a href="http://wordpress.org/support/" target="_blank">Support Forums</a>', 'newtheme' ) . '</p>';

	$screen = get_current_screen();

	if ( method_exists( $screen, 'add_help_tab' ) ) {
		// WordPress 3.3
		$screen->add_help_tab( array(
			'title' => __( 'Overview', 'newtheme' ),
			'id' => 'theme-options-help',
			'content' => $help,
			)
		);

		$screen->set_help_sidebar( $sidebar );
	} else {
		// WordPress 3.2
		add_contextual_help( $screen, $help . $sidebar );
	}
}

[/codesyntax]

Esta es la función que añade la ayuda que comentaba en la función anterior. Aquí además podemos ver como se añade un condicional para añadir retrocompatibilidad con versiones anteriores a la 3.3 y la 3.2.

[codesyntax lang=»php»]

<?php

/**
 * Returns an array of color schemes registered for New Theme.
 */
function newtheme_color_schemes() {
	$color_scheme_options = array(
		'light' => array(
			'value' => 'light',
			'label' => __( 'Light', 'newtheme' ),
			'thumbnail' => get_template_directory_uri() . '/inc/images/light.png',
			'default_link_color' => '#1b8be0',
		),
		'dark' => array(
			'value' => 'dark',
			'label' => __( 'Dark', 'newtheme' ),
			'thumbnail' => get_template_directory_uri() . '/inc/images/dark.png',
			'default_link_color' => '#e4741f',
		),
	);

	return apply_filters( 'newtheme_color_schemes', $color_scheme_options );
}

[/codesyntax]

Esta función devuelve un array con los esquemas de color para cada versión del tema.

[codesyntax lang=»php»]

<?php

/**
 * Returns an array of layout options registered for New Theme.
 */
function newtheme_layouts() {
	$layout_options = array(
		'content-sidebar' => array(
			'value' => 'content-sidebar',
			'label' => __( 'Content on left', 'newtheme' ),
			'thumbnail' => get_template_directory_uri() . '/inc/images/content-sidebar.png',
		),
		'sidebar-content' => array(
			'value' => 'sidebar-content',
			'label' => __( 'Content on right', 'newtheme' ),
			'thumbnail' => get_template_directory_uri() . '/inc/images/sidebar-content.png',
		),
		'content' => array(
			'value' => 'content',
			'label' => __( 'One-column, no sidebar', 'newtheme' ),
			'thumbnail' => get_template_directory_uri() . '/inc/images/content.png',
		),
	);

	return apply_filters( 'newtheme_layouts', $layout_options );
}

[/codesyntax]

Devuelve un array con las opciones para cada tipo de estructura del diseño.

[codesyntax lang=»php»]

<?php

/**
 * Returns the default options for New Theme.
 */
function newtheme_get_default_theme_options() {
	$default_theme_options = array(
		'color_scheme' => 'light',
		'link_color'   => newtheme_get_default_link_color( 'light' ),
		'theme_layout' => 'content-sidebar',
	);

	if ( is_rtl() )
 		$default_theme_options['theme_layout'] = 'sidebar-content';

	return apply_filters( 'newtheme_default_theme_options', $default_theme_options );
}

[/codesyntax]

Devuelve las opciones por defecto del tema.

[codesyntax lang=»php»]

<?php

/**
 * Returns the default link color for New Theme, based on color scheme.
 *
 *
 * @param $string $color_scheme Color scheme. Defaults to the active color scheme.
 * @return $string Color.
*/
function newtheme_get_default_link_color( $color_scheme = null ) {
	if ( null === $color_scheme ) {
		$options = newtheme_get_theme_options();
		$color_scheme = $options['color_scheme'];
	}

	$color_schemes = newtheme_color_schemes();
	if ( ! isset( $color_schemes[ $color_scheme ] ) )
		return false;

	return $color_schemes[ $color_scheme ]['default_link_color'];
}

[/codesyntax]

Devuelve el color por defecto para los vínculos.

[codesyntax lang=»php»]

<?php

/**
 * Returns the options array for New Theme.
 */
function newtheme_get_theme_options() {
	return get_option( 'newtheme_theme_options', newtheme_get_default_theme_options() );
}

[/codesyntax]

Devuelve un array con todas las opciones del tema.

[codesyntax lang=»php»]

<?php

/**
 * Renders the Color Scheme setting field.
 */
function newtheme_settings_field_color_scheme() {
	$options = newtheme_get_theme_options();

	foreach ( newtheme_color_schemes() as $scheme ) {
	?>
	<div class="layout image-radio-option color-scheme">
	<label class="description">
		<input type="radio" name="newtheme_theme_options[color_scheme]" value="<?php echo esc_attr( $scheme['value'] ); ?>" <?php checked( $options['color_scheme'], $scheme['value'] ); ?> />
		<input type="hidden" id="default-color-<?php echo esc_attr( $scheme['value'] ); ?>" value="<?php echo esc_attr( $scheme['default_link_color'] ); ?>" />
		<span>
			<img src="<?php echo esc_url( $scheme['thumbnail'] ); ?>" width="136" height="122" alt="" />
			<?php echo $scheme['label']; ?>
		</span>
	</label>
	</div>
	<?php
	}
}

[/codesyntax]

Esta función genera el grupo de radio buttons que permitirá elegir entre un esquema de color u otro.

[codesyntax lang=»php»]

<?php

/**
 * Renders the Link Color setting field.
 */
function newtheme_settings_field_link_color() {
	$options = newtheme_get_theme_options();
	?>
	<input type="text" name="newtheme_theme_options[link_color]" id="link-color" value="<?php echo esc_attr( $options['link_color'] ); ?>" />
	<a href="#" class="pickcolor hide-if-no-js" id="link-color-example"></a>
	<input type="button" class="pickcolor button hide-if-no-js" value="<?php esc_attr_e( 'Select a Color', 'newtheme' ); ?>" />
	<div id="colorPickerDiv" style="z-index: 100; background:#eee; border:1px solid #ccc; position:absolute; display:none;"></div>
	<br />
	<span><?php printf( __( 'Default color: %s', 'newtheme' ), '<span id="default-color">' . newtheme_get_default_link_color( $options['color_scheme'] ) . '</span>' ); ?></span>
	<?php
}

[/codesyntax]

Esta función genera el formulario para poder elegir el color de los vínculos.

[codesyntax lang=»php»]

<?php

/**
 * Renders the Layout setting field.
 */
function newtheme_settings_field_layout() {
	$options = newtheme_get_theme_options();
	foreach ( newtheme_layouts() as $layout ) {
		?>
		<div class="layout image-radio-option theme-layout">
		<label class="description">
			<input type="radio" name="newtheme_theme_options[theme_layout]" value="<?php echo esc_attr( $layout['value'] ); ?>" <?php checked( $options['theme_layout'], $layout['value'] ); ?> />
			<span>
				<img src="<?php echo esc_url( $layout['thumbnail'] ); ?>" width="136" height="122" alt="" />
				<?php echo $layout['label']; ?>
			</span>
		</label>
		</div>
		<?php
	}
}

[/codesyntax]

Esta función genera el campo para elegir la estructura del blog.

[codesyntax lang=»php»]

<?php

/**
 * Returns the options array for New Theme.
 */
function newtheme_theme_options_render_page() {
	?>
	<div class="wrap">
		<?php screen_icon(); ?>
		<?php $theme_name = function_exists( 'wp_get_theme' ) ? wp_get_theme() : get_current_theme(); ?>
		<h2><?php printf( __( '%s Theme Options', 'newtheme' ), $theme_name ); ?></h2>
		<?php settings_errors(); ?>

		<form method="post" action="options.php">
			<?php
				settings_fields( 'newtheme_options' );
				do_settings_sections( 'theme_options' );
				submit_button();
			?>
		</form>
	</div>
	<?php
}

[/codesyntax]

Función que genera todo el formulario de opciones.

[codesyntax lang=»php»]

<?php

/**
 * Sanitize and validate form input. Accepts an array, return a sanitized array.
 *
 * @see newtheme_theme_options_init()
 * @todo set up Reset Options action
 */
function newtheme_theme_options_validate( $input ) {
	$output = $defaults = newtheme_get_default_theme_options();

	// Color scheme must be in our array of color scheme options
	if ( isset( $input['color_scheme'] ) && array_key_exists( $input['color_scheme'], newtheme_color_schemes() ) )
		$output['color_scheme'] = $input['color_scheme'];

	// Our defaults for the link color may have changed, based on the color scheme.
	$output['link_color'] = $defaults['link_color'] = newtheme_get_default_link_color( $output['color_scheme'] );

	// Link color must be 3 or 6 hexadecimal characters
	if ( isset( $input['link_color'] ) && preg_match( '/^#?([a-f0-9]{3}){1,2}$/i', $input['link_color'] ) )
		$output['link_color'] = '#' . strtolower( ltrim( $input['link_color'], '#' ) );

	// Theme layout must be in our array of theme layout options
	if ( isset( $input['theme_layout'] ) && array_key_exists( $input['theme_layout'], newtheme_layouts() ) )
		$output['theme_layout'] = $input['theme_layout'];

	return apply_filters( 'newtheme_theme_options_validate', $output, $input, $defaults );
}

[/codesyntax]

Esta función valida que los datos del formulario son correctos.

[codesyntax lang=»php»]

<?php

/**
 * Enqueue the styles for the current color scheme.
 */
function newtheme_enqueue_color_scheme() {
	$options = newtheme_get_theme_options();
	$color_scheme = $options['color_scheme'];

	if ( 'dark' == $color_scheme )
		wp_enqueue_style( 'dark', get_template_directory_uri() . '/colors/dark.css', array(), null );

	do_action( 'newtheme_enqueue_color_scheme', $color_scheme );
}
add_action( 'wp_enqueue_scripts', 'newtheme_enqueue_color_scheme' );

[/codesyntax]

Función que cambiará el estilo actual de la página dependiendo del color de esquema elegido.

[codesyntax lang=»php»]

<?php

/**
 * Add a style block to the theme for the current link color.
 *
 * This function is attached to the wp_head action hook.
 */
function newtheme_print_link_color_style() {
	$options = newtheme_get_theme_options();
	$link_color = $options['link_color'];

	$default_options = newtheme_get_default_theme_options();

	// Don't do anything if the current link color is the default.
	if ( $default_options['link_color'] == $link_color )
		return;
?>
	<style>
		/* Link color */
		a,
		#site-title a:focus,
		#site-title a:hover,
		#site-title a:active,
		.entry-title a:hover,
		.entry-title a:focus,
		.entry-title a:active,
		.widget_newtheme_ephemera .comments-link a:hover,
		section.recent-posts .other-recent-posts a[rel="bookmark"]:hover,
		section.recent-posts .other-recent-posts .comments-link a:hover,
		.format-image footer.entry-meta a:hover,
		#site-generator a:hover {
			color: <?php echo $link_color; ?>;
		}
		section.recent-posts .other-recent-posts .comments-link a:hover {
			border-color: <?php echo $link_color; ?>;
		}
		article.feature-image.small .entry-summary p a:hover,
		.entry-header .comments-link a:hover,
		.entry-header .comments-link a:focus,
		.entry-header .comments-link a:active,
		.feature-slider a.active {
			background-color: <?php echo $link_color; ?>;
		}
	</style>
<?php
}
add_action( 'wp_head', 'newtheme_print_link_color_style' );

[/codesyntax]

Función que añadirá el color a los vínculos a través de los estilos si este no es el mismo que el de defecto.

[codesyntax lang=»php»]

<?php

/**
 * Adds New Theme layout classes to the array of body classes.
 */
function newtheme_layout_classes( $existing_classes ) {
	$options = newtheme_get_theme_options();
	$current_layout = $options['theme_layout'];

	if ( in_array( $current_layout, array( 'content-sidebar', 'sidebar-content' ) ) )
		$classes = array( 'two-column' );
	else
		$classes = array( 'one-column' );

	if ( 'content-sidebar' == $current_layout )
		$classes[] = 'right-sidebar';
	elseif ( 'sidebar-content' == $current_layout )
		$classes[] = 'left-sidebar';
	else
		$classes[] = $current_layout;

	$classes = apply_filters( 'newtheme_layout_classes', $classes, $current_layout );

	return array_merge( $existing_classes, $classes );
}
add_filter( 'body_class', 'newtheme_layout_classes' );

[/codesyntax]

Esta función añade las clases correspondientes a la etiqueta body dependiendo de la estructura de blog elegida.

[codesyntax lang=»php»]

<?php

/**
 * Implements New Theme theme options into Theme Customizer
 *
 * @param $wp_customize Theme Customizer object
 * @return void
 */
function newtheme_customize_register( $wp_customize ) {
	$wp_customize->get_setting( 'blogname' )->transport = 'postMessage';
	$wp_customize->get_setting( 'blogdescription' )->transport = 'postMessage';

	$options  = newtheme_get_theme_options();
	$defaults = newtheme_get_default_theme_options();

	$wp_customize->add_setting( 'newtheme_theme_options[color_scheme]', array(
		'default'    => $defaults['color_scheme'],
		'type'       => 'option',
		'capability' => 'edit_theme_options',
	) );

	$schemes = newtheme_color_schemes();
	$choices = array();
	foreach ( $schemes as $scheme ) {
		$choices[ $scheme['value'] ] = $scheme['label'];
	}

	$wp_customize->add_control( 'newtheme_color_scheme', array(
		'label'    => __( 'Color Scheme', 'newtheme' ),
		'section'  => 'colors',
		'settings' => 'newtheme_theme_options[color_scheme]',
		'type'     => 'radio',
		'choices'  => $choices,
		'priority' => 5,
	) );

	// Link Color (added to Color Scheme section in Theme Customizer)
	$wp_customize->add_setting( 'newtheme_theme_options[link_color]', array(
		'default'           => newtheme_get_default_link_color( $options['color_scheme'] ),
		'type'              => 'option',
		'sanitize_callback' => 'sanitize_hex_color',
		'capability'        => 'edit_theme_options',
	) );

	$wp_customize->add_control( new WP_Customize_Color_Control( $wp_customize, 'link_color', array(
		'label'    => __( 'Link Color', 'newtheme' ),
		'section'  => 'colors',
		'settings' => 'newtheme_theme_options[link_color]',
	) ) );

	// Default Layout
	$wp_customize->add_section( 'newtheme_layout', array(
		'title'    => __( 'Layout', 'newtheme' ),
		'priority' => 50,
	) );

	$wp_customize->add_setting( 'newtheme_theme_options[theme_layout]', array(
		'type'              => 'option',
		'default'           => $defaults['theme_layout'],
		'sanitize_callback' => 'sanitize_key',
	) );

	$layouts = newtheme_layouts();
	$choices = array();
	foreach ( $layouts as $layout ) {
		$choices[$layout['value']] = $layout['label'];
	}

	$wp_customize->add_control( 'newtheme_theme_options[theme_layout]', array(
		'section'    => 'newtheme_layout',
		'type'       => 'radio',
		'choices'    => $choices,
	) );
}
add_action( 'customize_register', 'newtheme_customize_register' );

[/codesyntax]

Esta función añade todas las opciones al personalizador de temas.

[codesyntax lang=»php»]

<?php

/**
 * Bind JS handlers to make Theme Customizer preview reload changes asynchronously.
 * Used with blogname and blogdescription.
 */
function newtheme_customize_preview_js() {
	wp_enqueue_script( 'newtheme-customizer', get_template_directory_uri() . '/inc/theme-customizer.js', array( 'customize-preview' ), '20120523', true );
}
add_action( 'customize_preview_init', 'newtheme_customize_preview_js' );

[/codesyntax]

Esta función indica a WordPress que añada un archivo javascript para la previsualización de la página para poder ver los cambios del tema.

Symfony: El modelo (III)

0

CONEXIONES A LA BASE DE DATOS

[codesyntax lang=»text» title=»Conexión básica»]

> php symfony configure:database "mysql://login:password@localhost/blog"

[/codesyntax]

[codesyntax lang=»text» title=»Definiendo una conexión para un entorno concreto de la aplicación»]

> php symfony --env=prod configure:database "mysql://login:password@localhost/blog"

[/codesyntax]

[codesyntax lang=»text» title=»Definiendo una conexión para una aplicación concreta»]

> php symfony --app=frontend configure:database "mysql://login:password@localhost/blog"

[/codesyntax]

 

[codesyntax lang=»text» title=»Definiendo una conexión diferente de la principal para el proyecto»]

> php symfony --name=otraconexion configure:database "mysql://login:password@localhost/blog"

[/codesyntax]

 

Las opciones de conexión con la base de datos también se pueden establecer manualmente en el archivo databases.yml que se encuentra en el directorio config/.

 

[codesyntax lang=»text» title=»Opciones abreviadas de la conexión a la base de datos»]

all:
  propel:
    class:          sfPropelDatabase
    param:
      dsn:          mysql://login:password@localhost/blog

[/codesyntax]

[codesyntax lang=»text» title=»Ejemplo de conexiónes con la base de datos»]

prod:
  propel:
    param:
      hostspec:           mi_servidor_datos
      usuarioname:           mi_nombre_usuario
      password:           xxxxxxxxxx

all:
  propel:
    class:                sfPropelDatabase
    param:
      phptype:            mysql     # fabricante de la base de datos
      hostspec:           localhost
      database:           blog
      usuarioname:           login
      password:           passwd
      port:               80
      encoding:           utf8      # Codificación utilizada para crear la tabla
      persistent:         true      # Utilizar conexiones persistentes

[/codesyntax]

Los valores permitidos para el parámetro phptype corresponden a los tipos de bases de datos soportados por PDO:

  • mysql
  • mssql
  • pgsql
  • sqlite
  • oracle

Si se utiliza una base de datos de tipo SQLite, el parámetro hostspec debe indicar la ruta al archivo de base de datos.

[codesyntax lang=»text» title=»Opciones de conexión a una base de datos SQLite»]

all:
  propel:
    class:          sfPropelDatabase
    param:
      phptype:      sqlite
      database:     %SF_DATA_DIR%/blog.db

[/codesyntax]

 

EXTENDER EL MODELO

Los métodos del modelo que se generan automáticamente están muy bien, pero no siempre son suficientes. Si se incluye lógica de negocio propia, es necesario extender el modelo añadiendo nuevos métodos o redefiniendo algunos de los existentes.

 

AÑADIR NUEVOS MÉTODOS

Los nuevos métodos se pueden añadir en las clases vacías del modelo que se generan en el directorio lib/model/. Se emplea $this para invocar a los métodos del objeto actual y self:: para invocar a los métodos estáticos de la clase actual. No se debe olvidar que las clases personalizadas heredan los métodos de las clases Base del directorio lib/model/om/.

[codesyntax lang=»php» title=»Personalizar el modelo, en lib/model/Articulo.php»]

<?

class Articulo extends BaseArticulo
{
  public function __toString()
  {
    return $this->getTitulo();  // getTitulo() se hereda de BaseArticulo
  }
}

[/codesyntax]

También se pueden extender las clases peer, como por ejemplo para obtener todos los artículos ordenados por fecha de creación:

[codesyntax lang=»php» title=»Personalizando el modelo, en lib/model/ArticuloPeer.php»]

<?

class ArticuloPeer extends BaseArticuloPeer
{
  public static function getTodosOrdenadosPorFecha()
  {
    $c = new Criteria();
    $c->addAscendingOrderByColumn(self::CREATED_AT);
    return self::doSelect($c);
  }
}

[/codesyntax]

 

[codesyntax lang=»php» title=»El uso de métodos personalizados del modelo es idéntico al de los métodos generados automáticamente»]

<?

foreach (ArticuloPeer::getTodosOrdenadosPorFecha() as $articulo)
{
  echo $articulo;   // Se llama al método mágico __toString()
}

[/codesyntax]

 

REDEFINIR MÉTODOS EXISTENTES

Si alguno de los métodos generados automáticamente en las clases Base no satisfacen las necesidades de la aplicación, se pueden redefinir en las clases personalizadas. Solamente es necesario mantener el mismo número de argumentos para cada método.

Por ejemplo, el método $articulo->getComentarios() devuelve un array de objetos Comentario, sin ningún tipo de ordenamiento. Si se necesitan los resultados ordenados por fecha de creación siendo el primero el comentario más reciente, se puede redefinir el método getComentarios(). Como el método getComentarios() original (guardado en lib/model/om/BaseArticulo.php) requiere como argumentos un objeto de tipo Criteria y una conexión, la nueva función debe contener esos mismos parámetros.

[codesyntax lang=»php» title=»Redefiniendo los métodos existentes en el modelo, en lib/model/Articulo.php»]

<?

public function getComentarios($criteria = null, $con = null)
{
  if (is_null($criteria))
  {
    $criteria = new Criteria();
  }
  else
  {
    // Los objetos se pasan por referencia en PHP5, por lo que se debe clonar
    // el objeto original para no modificarlo
    $criteria = clone $criteria;
  }
  $criteria->addDescendingOrderByColumn(ComentarioPeer::CREATED_AT);

  return parent::getComentarios($criteria, $con);
}

[/codesyntax]

El método personalizado acaba llamando a su método padre en la clase Base, lo que se considera una buena práctica. No obstante, es posible saltarse completamente la clase Base y devolver el resultado directamente.

 

USO DE COMPORTAMIENTOS EN EL MODELO

Algunas de las modificaciones que se realizan en el modelo son genéricas y por tanto se pueden reutilizar. Por ejemplo, los métodos que hacen que un objeto del modelo sea reordenable o un bloqueo de tipo optimistic en la base de datos para evitar conflictos cuando se guardan de forma concurrente los objetos se pueden considerar extensiones genéricas que se pueden añadir a muchas clases.

Symfony encapsula estas extensiones en «comportamientos» (del inglés behaviors). Los comportamientos son clases externas que proporcionan métodos extras a las clases del modelo. Las clases del modelo están definidas de forma que se puedan enganchar estas clases externas y Symfony extiende las clases del modelo mediante sfMixer.

Para habilitar los comportamientos en las clases del modelo, se debe modificar una opción del archivo config/propel.ini:

propel.builder.AddBehaviors = true // El valor por defecto es false

Symfony no incluye por defecto ningún comportamiento, pero se pueden instalar mediante plugins. Una vez que el plugin se ha instalado, se puede asignar un comportamiento a una clase mediante una sola línea de código. Si por ejemplo se ha instalado el plugin sfPropelParanoidBehaviorPlugin en la aplicación, se puede extender la clase Articulo con este comportamiento añadiendo la siguiente línea de código al final del archivo Articulo.class.php:

[codesyntax lang=»php»]

<?

sfPropelBehavior::add('Articulo', array(
  'paranoid' => array('column' => 'deleted_at')
));

[/codesyntax]

 

Después de volver a generar el modelo, los objetos de tipo Articulo que se borren permanecerán en la base de datos, aunque será invisibles a las consultas que hacen uso de los métodos del ORM, a no ser que se deshabilite temporalmente el comportamiento mediante sfPropelParanoidBehavior::disable().

Desde la versión 1.1 de Symfony también es posible declarar los comportamientos directamente en el archivo schema.yml, incluyéndolos bajo la clave _behaviors.

La lista de plugins de Symfony disponible en el wiki incluye numerosos comportamientos http://trac.symfony-project.org/wiki/SymfonyPlugins#Behaviors. Cada comportamiento tiene su propia documentación y su propia guía de instalación.

 

SINTAXIS EXTENDIDA DEL ESQUEMA

Atributos

Se pueden definir atributos específicos para las conexiones y las tablas. Estas opciones se establecen bajo la clave _attributes.

[codesyntax lang=»text»]

propel:
  _attributes:   { noXsd: false, defaultIdMethod: none, package: lib.model }
  blog_articulo:
    _attributes: { phpName: Articulo }

[/codesyntax]

Si se quiere validar el esquema antes de que se genere el código asociado, se debe desactivar en la conexión el atributo noXSD. La conexión también permite que se le indique el atributo defaultIdMethod. Si no se indica, se utilizará el método nativo de generación de IDs –por ejemplo, autoincrement en MySQL o sequences en PostgreSQL. El otro valor permitido es none.

El atributo package es como un namespace; indica la ruta donde se guardan las clases generadas automáticamente. Su valor por defecto es lib/model/, pero se puede modificar para organizar el modelo en una estructura de subpaquetes. Si por ejemplo no se quieren mezclar en el mismo directorio las clases del núcleo de la aplicación con las clases de un sistema de estadísticas, se pueden definir dos esquemas diferentes con los paquetes lib.model.business y lib.model.stats.

Ya se ha visto el atributo de tabla phpName, que se utiliza para establecer el nombre de la clase generada automáticamente para manejar cada tabla de la base de datos.

Las tablas que guardan contenidos adaptados para diferentes idiomas (es decir, varias versiones del mismo contenido en una tabla relacionada, para conseguir la internacionalización) también pueden definir dos atributos adicionales.

[codesyntax lang=»text»]

propel:
  blog_articulo:
    _attributes: { isI18N: true, i18nTable: db_group_i18n }

[/codesyntax]

 

DETALLE DE LAS COLUMNAS

La sintaxis básica ofrece dos posibilidades: dejar que Symfony deduzca las características de una columna a partir de su nombre (indicando un valor vacío para esa columna) o definir el tipo de columna con uno de los tipos predefinidos.
[codesyntax lang=»text»]

propel:
  blog_articulo:
    id:                 # Symfony se encarga de esta columna
    titulo: varchar(50)  # Definir el tipo explícitamente

[/codesyntax]

Se pueden definir muchos más aspectos de cada columna. Si se definen, se utiliza un array asociativo para indicar las opciones de la columna.

[codesyntax lang=»text»]

propel:
  blog_articulo:
    id:       { type: integer, required: true, primaryKey: true, autoIncrement: true }
    name:     { type: varchar(50), default: foobar, index: true }
    group_id: { type: integer, foreignTable: db_group, foreignReference: id, onDelete: cascade }

[/codesyntax]

Los parámetros de las columnas son los siguientes:

  • type: Tipo de columna. Se puede elegir entre boolean, tinyint, smallint, integer, bigint, double, float, real, decimal, char, varchar(tamano), longvarchar, date, time, timestamp, bu_date, bu_timestamp, blob y clob
  • required: valor booleano. Si vale true la columna debe tener obligatoriamente un valor.
  • default: el valor por defecto.
  • primaryKey: valor booleano. Si vale true indica que es una clave primaria.
  • autoIncrement: valor booleano. Si se indica true para las columnas de tipo integer, su valor se auto-incrementará.
  • sequence: el nombre de la secuencia para las bases de datos que utilizan secuencias para las columnas autoIncrement (por ejemplo PostgreSQL y Oracle).
  • index: valor booleano. Si vale true, se construye un índice simple; si vale unique se construye un índice único para la columna.
  • foreignTable: el nombre de una tabla, se utiliza para crear una clave externa a otra tabla.
  • foreignReference: el nombre de la columna relacionada si las claves externas se definen mediante foreignTable.
  • onDelete: determina la acción que se ejecuta cuando se borra un registro en una tabla relacionada. Si su valor es setnull, la columna de la clave externa se establece a null. Si su valor es cascade, se borra el registro relacionado. Si el sistema gestor de bases de datos no soporta este comportamiento, el ORM lo emula. Esta opción solo tiene sentido para las columnas que definen una foreignTable y una foreignReference.
  • isCulture: valor booleano. Su valor es true para las columnas de tipo culture en las tablas de contenidos adaptados a otros idiomas.

 

CLAVES EXTERNAS

Además de los atributos de columna foreignTable y foreignReference, es posible añadir claves externas bajo la clave _foreignKeys: de cada tabla.

[codesyntax lang=»text»]

propel:
  blog_articulo:
    id:
    titulo:     varchar(50)
    usuario_id: { type: integer }
    _foreignKeys:
      -
        foreignTable: blog_usuario
        onDelete:     cascade
        references:
          - { local: usuario_id, foreign: id }

[/codesyntax]

La sintaxis alternativa es muy útil para las claves externas múltiples y para indicar un nombre a cada clave externa.

[codesyntax lang=»text»]

_foreignKeys:
  mi_clave_externa:
    foreignTable:  db_usuario
    onDelete:      cascade
    references:
      - { local: usuario_id, foreign: id }
      - { local: post_id, foreign: id }

[/codesyntax]

 

INDICES

Además del atributo de columna index, es posible añadir claves índices bajo la clave _indexes: de cada tabla. Si se quieren crean índices únicos, se debe utilizar la clave _uniques:. En las columnas que requieren un tamaño, por ejemplo por ser columnas de texto, el tamaño del índice se indica entre paréntesis, de la misma forma que se indica el tamaño de cualquier columna.

[codesyntax lang=»text»]

propel:
  blog_articulo:
    id:
    titulo:            varchar(50)
    created_at:
    _indexes:
      mi_indice:      [titulo(10), usuario_id]
    _uniques:
      mi_otro_indice: [created_at]

[/codesyntax]

 

COLUMNAS VACÍAS

Cuando Symfony se encuentra con una columna sin ningún valor, utiliza algo de magia para determinar su valor.

[codesyntax lang=»text»]

// Las columnas vacías llamadas "id" se consideran claves primarias
id:         { type: integer, required: true, primaryKey: true, autoIncrement: true }

// Las columnas vacías llamadas "XXX_id" se consideran claves externas
loquesea_id:  { type: integer, foreignTable: db_loquesea, foreignReference: id }

// Las columnas vacías llamadas created_at, updated at, created_on y updated_on
// se consideran fechas y automáticamente se les asigna el tipo "timestamp"
created_at: { type: timestamp }
updated_at: { type: timestamp }

[/codesyntax]

Para las claves externas, Symfony busca una tabla cuyo phpName sea igual al principio del nombre de la columna; si se encuentra, se utiliza ese nombre de tabla como foreignTable.

 

TABLAS I18N

Symfony permite internacionalizar los contenidos mediante tablas relacionadas. De esta forma, cuando se dispone de contenido que debe ser internacionalizado, se guarda en 2 tablas distintas: una contiene las columnas invariantes y otra las columnas que permiten la internacionalización.

Todo lo anterior se considera de forma implícita cuando en el archivo schema.yml se dispone de una tabla con el nombre cualquiernombre_i18n.

[codesyntax lang=»text»]

propel:
  db_group:
    id:
    created_at:

  db_group_i18n:
    name:        varchar(50)

[/codesyntax]

[codesyntax lang=»text»]

propel:
  db_group:
    _attributes: { isI18N: true, i18nTable: db_group_i18n }
    id:
    created_at:

  db_group_i18n:
    id:       { type: integer, required: true, primaryKey: true, foreignTable: db_group, foreignReference: id, onDelete: cascade }
    culture:  { isCulture: true, type: varchar(7), required: true, primaryKey: true }
    name:     varchar(50)

[/codesyntax]

 

COMPORTAMIENTOS

Los comportamientos son plugins que modifican el modelo de datos para añadir nuevas funcionalidades a las clases de Propel.

[codesyntax lang=»text»]

propel:
  blog_articulo:
    titulo:         varchar(50)
    _behaviors:
      paranoid:     { column: deleted_at }

[/codesyntax]

 

Ir arriba