Como escrever um schema do Propel com a sintaxe alternativa

Visão Geral

Com o symfony 1.1, você pode escolher descrever a sua estrutura do modelo relacional em uma nova sintaxe YAML. O symfony reconhece os arquivos schema.yml escritos tanto com a sintaxe descrita no Capítulo 8 do livro do symfony, como os escritos com a sintaxe descrita abaixo. A sintaxe alternativa é mais orientada à objeto e torna o processo da fusão de vários esquemas mais fácil de entender.

Exemplo Base

Considere o seguinte schema, usando a sintaxe atual:

    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]
        _behaviors:
          paranoid:     { column: deleted_at }
  
      ab_group_i18n:
        motto:            longvarchar

Sintaxe alternativa

Exemplo base, com a sintaxe alternativa

Aqui está como escrever exatamente a mesma estrutura como a listada acima, com a sintaxe alternativa:

    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]
        behaviors:
          paranoid:     { column: deleted_at }
  
      AbGroupI18n:
        columns:
          motto:          longvarchar

A principal diferença é que você declara classes, não tabelas, usando a tabela phpName como a chave.

Esta sintaxe alternativa também é mais explícita, uma vez que você deve criar entradas para classes e colunas. Mas torna-se livre do hack feio _attributes da sintaxe atual, desta forma, o schema.yml não tenta imitar uma sintaxe XML.

Por último mas não menos importante, todas as "mágicas" da sintaxe clássica continuam lá (auto definição das chaves primárias, chaves estrangeiras, tabelas i18n, etc.)

Configurações de conexão

Ao invés de serem definidas como _attributes da conexão, as configurações da conexão, juntamente com o nome da conexão, são todas chaves no nível-1:

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

Todas estas chaves são opcionais, incluindo a chave connection. Se ela não for informada, o symfony utilizará como valor default o propel.

Classes

Uma definição de classe lista o nome da tabela no banco de dados, as colunas, chaves estrangeiras, índices e behaviours em uma sintaxe natural chave/valores:

    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]
      behaviors:
        paranoid:     { column: deleted_at }

Note que você pode definir chaves estrangeiras tanto com o atributo usual foreignTable, que espera o nome da tabela, ou através do novo atributo foreignClass, que espera o nome da classe.

Schemas mistos

Você pode ter, em um projeto, schemas mistos com a sintaxe atual e a sintaxe alternativa.

O sistema de extensão de schema, descrito no Capítulo 17 do livro do symfony, funciona seja qual for a sintaxe do schema original e qualquer que seja a sintaxe personalizada do schema. Isto significa que você pode personalizar um schema já existente com a sintaxe clássica usando um esquema personalizado com a sintaxe alternativa, e vice-versa. O symfony irá fazer a conversão internamente tornando a fusão sempre possível.

Note que a fusão de schema é fácil de entender quando considerada a sintaxe alternativa para ambos os schemas original e personalizado. De fato, este é um formato interno utilizado pelo symfony para a fusão. A listagem seguinte mostra a fusão dos schemas:

    # Schema original, em plugins/myPlugin/config/schema.yml
    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 }
    # Schema personalizado, em myPlugin_schema.custom.yml
    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:
    # Schema resultante, realizada a fusão interna e utilizado para o modelo e a geração de sql
    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 }

Para maior clareza, recomenda-se o uso da sintaxe alternativa para os schemas, tanto quanto possível.