Development

sfPropelAlternativeSchemaPlugin/Es

You must first sign up to be able to contribute.

sfPropelAlternativeSchemaPlugin - Extensión a la sintaxis de esquemas de Propel

Artículo original: sfPropelAlternativeSchemaPlugin - Extension to the Propel schema syntax. Traducción realizada por Raúl Alonso - raul (arroba) raulalonso (punto) net

Descripción

Este plugin amplía el modelo generador de Symfony sobre la base de Propel, de forma que será posible redefinir las propiedades de un esquema con otro esquema. El uso de este plugin puede agregar columnas, cambiar los nombres de la tabla, o la conexión de base de datos sin modificar los actuales esquemas.

También proporciona una nueva sintaxis YAML para definir esquemas de bases de datos, más explícita y más legible que la actual. Por último, esta sintaxis alternativa de esquemas añade algunas nuevas características a los esquemas, como es la posibilidad de definir un comportamiento desde un esquema.

Instalación

Para instalar el plugin en un proyecto Symfony, el proceso usual es utilizar Symfony en la línea de comandos:

$ php symfony plugin-install http://plugins.symfony-project.com/sfPropelAlternativeSchemaPlugin

Por otra parte, si usted no tiene PEAR instalado, puede descargar el último paquete del plugin y copiarlo en el directorio plugins del proyecto.

Posteriormente, borrar la caché para que la carga automática de clases del proyecto pueda leer las nuevas clases:

$ php symfony cc

Eso es todo, usted está listo para reemplazar los actuales esquemas y escribir con la nueva sintaxis.

Personalizando un esquema existente

Una vez instalado el plugin, un esquema puede redefinirse con otro esquema. Este plugin es ideal, por ejemplo, para permitir a los usuarios personalizar algunas de las opciones sobre aquellos esquemas que vienen por defecto en otros plugins (como la conexión, la tabla de nombres, etc), o para permitir que un plugin amplíe a otro plugin.

Durante la construcción del modelo, el plugin buscará archivos YAML personalizados para cada esquema, partiendo de esta norma:

Nombre del esquema original Nombre del esquema personalizado
config/schema.yml schema.custom.yml
config/foobar_schema.yml foobar_schema.custom.yml
plugins/myPlugin/config/schema.yml myPlugin_schema.custom.yml
plugins/myPlugin/config/foo_schema.yml myPlugin_foo_schema.custom.yml

Los esquemas personalizados se buscarán en el directorio config/ de la aplicación y del plugin, de forma que un esquema pueda redefinir a otro, pudiendo haber más de una personalización por esquema.

El plugin combinará los dos esquemas de una manera inteligente, de la siguiente manera:

# Esquema original
#################
propel:
  article:
    _attributes:    { phpName: Article }
    title:          varchar(50)
    user_id:        { type: integer }
    created_at:

# Personalización del esquema
###############
propel:
  article:
    _attributes:    { phpName: Article, package: foo.bar.lib.model }
    stripped_title: varchar(50)

# Esquema resultante
##################
propel:
  article:
    _attributes:    { phpName: Article, package: foo.bar.lib.model }
    title:          varchar(50)
    user_id:        { type: integer }
    created_at:
    stripped_title: varchar(50)

Al combinarse dos tablas, el plugin examinará el phpName de la tabla como una clave, lo que posibilita al usuario que pueda modificar el nombre de una tabla en la base de datos, siempre y cuando mantenga el mismo phpName en el esquema. Aquí presentamos un ejemplo más complejo:

# Esquema original
#################
propel:
  cd_user:
    _attributes:    { phpName: User }
    first_name:     { type: varchar, size: 255, default: "Anonymous" }
    last_name:      varchar(50)
    age:            { type: integer, required: true, index: true }
    created_at:
  
  ij_article:
    _attributes:    { phpName: Article }
    title:          varchar(50)
    user_id:        { type: integer }
    created_at:
    _foreignKeys:
      -
        foreignTable: cd_user
        onDelete:     cascade
        references:
          - { local: user_id, foreign: id }

# Personalización del esquema
###############
myConnection:
  ab_group:
    _attributes:    { phpName: Group, package: foo.bar.lib.model }
    id:
    name:           varchar(50)
  
  ef_user:
    _attributes:    { phpName: User, isI18N: true, i18nTable: cd_user_i18n }
    ab_group_id:
  
  ij_article:
    updated_at:

# Esquema resultante
##################
myConnection:
  ef_user:
    _attributes:    { phpName: User, isI18N: true, i18nTable: cd_user_i18n }
    first_name:     { type: varchar, size: 255, default: "Anonymous" }
    last_name:      varchar(50)
    age:            { type: integer, required: true, index: true }
    created_at:
    ab_group_id:
  
  ij_article:
    _attributes:    { phpName: Article }
    title:          varchar(50)
    user_id:        { type: integer }
    created_at:
    updated_at:
    _foreignKeys:
      -
        foreignTable: cd_user
        onDelete:     cascade
        references:
          - { local: user_id, foreign: id }

  ab_group:
    _attributes:    { phpName: Group, package: foo.bar.lib.model }
    id:
    name:           varchar(50)

Nueva sintaxis del esquema

Como alternativa a la actual sintaxis de schema.yml (que aún funciona), este complemento se propone una nueva manera de definir un esquema de base de datos.

Considere el siguiente esquema, utilizando la actual sintaxis:

propel:
  _attributes:      { noXsd: false, defaultIdMethod: none, package: lib.model }
  ab_group:
    _attributes:    { phpName: Group, package: foo.bar.lib.model }
    id:
    name:           varchar(50)
    
  cd_user:
    _attributes:    { phpName: User, isI18N: true, i18nTable: cd_user_i18n }
    first_name:     { type: varchar, size: 255, default: "Anonymous" }
    last_name:      varchar(50)
    age:            { type: integer, required: true, index: true }
    ab_group_id:
    created_at:
  
  cd_user_i18n:
    description:    longvarchar
    
  ef_article:
    title:          { type: longvarchar, required: true, index: unique }
    stripped_title: { type: longvarchar, required: true, primaryKey: true, sequence: my_custom_sequence_name }
    user_id:
    my_group:       { type: integer, foreignTable: ab_group, foreignReference: id, onDelete: setnull }
    created_at:     timestamp
    updated_at:

  ij_article:
    _attributes:    { phpName: Article }
    title:          varchar(50)
    user_id:        { type: integer }
    _foreignKeys:
      -
        foreignTable: cd_user
        onDelete:     cascade
        references:
          - { local: user_id, foreign: id }
    created_at:
    _indexes:
      my_index:       [title, user_id]
    _uniques:
      my_other_index: [created_at]
  
  ab_group_i18n:
    motto:            longvarchar

Con la sintaxis alternativa que este plugin posibilita, y que se implementará en Symfony 1.1. usted puede escribir el mismo esquema de la siguiente manera:

connection:           propel
noXsd:                false
defaultIdMethod:      none
package:              lib.model

classes:
  Group:
    tableName:        ab_group
    package:          foo.bar.lib.model
    columns:
      id:
      name:           varchar(50)
  
  User:
    tableName:        cd_user
    isI18N:           true
    i18nTable:        cd_user_i18n
    columns:
      first_name:     { type: varchar, size: 255, default: "Anonymous" }
      last_name:      varchar(50)
      age:            { type: integer, required: true, index: true }
      ab_group_id:
      created_at:
  
  CdUserI18n:
    columns:
      description:    longvarchar
  
  EfArticle:
    columns:
      title:          { type: longvarchar, required: true, index: unique }
      stripped_title: { type: longvarchar, required: true, primaryKey: true, sequence: my_custom_sequence_name }
      user_id:
      my_group:       { type: integer, foreignClass: Group, foreignReference: id, onDelete: setnull }
      created_at:     timestamp
      updated_at:
  
  Article:
    tableName:        ij_article
    columns:
      title:          varchar(50)
      user_id:        { type: integer }
      created_at:
    foreignKeys:
      -
        foreignTable: cd_user
        onDelete:     cascade
        references:
          - { local: user_id, foreign: id }
    indexes:
      my_index:       [title, user_id]
    uniques:
      my_other_index: [created_at]
  
  AbGroupI18n:
    columns:
      motto:          longvarchar

La principal diferencia es que usted declara clases, y no tablas, utilizando el phpName de la tabla como una clave. En caso de no definir un nombre de tabla (tableName), el plugin determinará uno automáticamente basado en el phpName utilizando sfInflector::underscore().

Esta sintaxis alternativa es también más explícita, ya que usted debe crear entradas para las clases y las columnas, deshaciéndose de la fea definición de atributos (_attributes) de la actual sintaxis.

El parámetro de conexión es opcional. Si no se establece, se utilizará propel como valor por defecto.

Tenga en cuenta que puede definir claves foráneas, ya sea con el habitual atributo foreignTable que espera un nombre de tabla, o por medio del nuevo atributo foreignClass, que espera un nombre de clase.

Por último, pero no menos importante, todos los métodos mágicos de la sintaxis actual se mantienen (auto definición de claves primarias, claves foráneas, tablas i18n, etc.)

Una vez que tenga definido su esquema, ejecute la reconstrucción del modelo, como se hace habitualmente:

$ php symfony propel-build-model

El plugin reconocerá automáticamente la sintaxis alternativa. Tenga en cuenta que usted puede mantener en un proyecto esquemas que combinen la sintaxis actual con la alternativa.

Comportamientos (behaviors)

La sintaxis de esquemas alternativa le permite definir comportamientos directamente desde el propio esquema. Para que esto sea posible, hay que cambiar dos líneas en la sección 'Builder Settings' del archivo propel.ini del proyecto:

// In config/propel.ini
; builder settings
propel.builder.peer.class              = plugins.sfPropelAlternativeSchemaPlugin.lib.SfAlternativePeerBuilder
propel.builder.object.class            = plugins.sfPropelAlternativeSchemaPlugin.lib.SfAlternativeObjectBuilder

Ahora usted puede añadir una sección behaviors para cada clase que usted quiera definir en el esquema, del siguiente modo:

classes:
  Article:
    columns:
      title:          varchar(50)
    behaviors:
      paranoid:       { column: deleted_at }

Naturalmente, no olvide ejecutar una reconstrucción del modelo después de modificar su esquema.

También es posible definir comportamientos si se habilita el constructor personalizado en el archivo propel.ini. Simplemente, agregue un guión bajo delantero antes de la clave de comportamientos (behaviors) y defina los mismos comportamientos que ya añadió antes:

propel:
  ij_article:
    _attributes:    { phpName: Article }
    title:          varchar(50)
    _behaviors:
      paranoid:     { column: deleted_at }

Nota: Los comportamientos introducidos de este modo son registrados en el modelo y en las clases Peer, lo que parece resolver algunos problemas que se estaban produciendo con los comportamientos.

Esquemas mixtos

La personalización de los esquemas se procesa sea cual sea la sintaxis del esquema original y la sintaxis empleada en el esquema de la personalización. Esto significa que se puede personalizar un esquema existente que emplea sintaxis antigua usando un esquema de personalización escrito con la nueva sintaxis, y viceversa. El plugin hará la conversión internamente de forma que la combinación de ambos esquemas es siempre posible.

Considere que la fusión de los dos esquemas es más comprensible cuando ambos utilizan la sintaxis alternativa. De hecho, tal es el formato utilizado por el plugin para la combinación. La siguiente lista es el mismo ejemplo que el utilizado en la sección “Personalizando un esquema existente”, excepto porque utiliza la sintaxis alternativa… y comportamientos:

# Esquema original
#################
classes:
  User:
    tableName:        cd_user
    columns:
      first_name:     { type: varchar, size: 255, default: "Anonymous" }
      last_name:      varchar(50)
      age:            { type: integer, required: true, index: true }
      created_at:
  
  Article:
    tableName:        ij_article
    columns:
      title:          varchar(50)
      user_id:        { type: integer }
      created_at:
    foreignKeys:
      -
        foreignTable: cd_user
        onDelete:     cascade
        references:
          - { local: user_id, foreign: id }

# Personalización del esquema
###############
connection: myConnection
classes:
  Group:
    tableName:        ab_group
    package:          foo.bar.lib.model
    behaviors:        [paranoid]
    columns:
      id:
      name:           varchar(50)

  User:
    tableName:        ef_user
    isI18N:           true
    i18nTable:        cd_user_i18n
    columns:
      ab_group_id:
  
  Article:
    columns:
      updated_at:

# Esquema resultante
##################
connection: myConnection
classes:
  Group:
    tableName:        ab_group
    package:          foo.bar.lib.model
    behaviors:        [paranoid]
    columns:
      id:
      name:           varchar(50)
  
  User:
    tableName:        cd_user
    isI18N:           true
    i18nTable:        cd_user_i18n
    columns:
      first_name:     { type: varchar, size: 255, default: "Anonymous" }
      last_name:      varchar(50)
      age:            { type: integer, required: true, index: true }
      ab_group_id:
      created_at:
  
  Article:
    tableName:        ij_article
    columns:
      title:          varchar(50)
      user_id:        { type: integer }
      created_at:
      updated_at:
    foreignKeys:
      -
        foreignTable: cd_user
        onDelete:     cascade
        references:
          - { local: user_id, foreign: id }

Por claridad, recomendamos utillizar la sintaxis de esquemas alternativa siempre que sea posible.

Comprobando que el plugin está instalado

Los esquemas alternativos pueden parecer algunas veces esquemas YAML tradicionales. Si el plugin no está instalado, es posible que el intérprete del esquema tradicional trate de transformar un esquema con la sintaxis alternativa en un esquema XML, pero sobre la base de los esquemas tradicionales. Esto será, probablemente, causa de problemas.

Para evitar esto, debería asegurarse de que el plugin está instalado antes de intentar interpretar un esquema YAML de sintaxis alternativa. Para ello, un buen truco es aprovechar el hecho de que los archivos YAML se ejecutan como archivos PHP, antes de ser convertidos a arrays. De esta forma, se puede añadir la siguiente línea en la parte superior de cada esquema YAML escrito con la sintaxis alternativa:

<?php if(!is_callable(array('sfPropelDatabaseSchema', 'convertOldToNewYaml'))) throw new Exception('You must install the sfPropelAlternativeSchemaPlugin to use this schema') ?>