sfPropelAlternativeSchemaPlugin - 0.9.0

Alternative propel schema syntax for customizable schemas

You are currently browsing
the website for symfony 1

Visit the Symfony2 website


« Back to the Plugins Home

Signin


Forgot your password?
Create an account

Tools

Stats

advanced search
Information Readme Releases Changelog Contribute
Show source | Show as Markdown

sfPropelAlternativeSchemaPlugin - Extension to the Propel schema syntax

Overview

This plugin extends the symfony model generator, based on Propel, to allow a schema to override another one. It also provides a new YAML syntax for defining database schemas, more explicit and more readable. This new syntax is completely backward compatible with symfony's current schema.yml syntax, so installing this plugin will not break your applications.

Installation

To install the plugin for a symfony project, the usual process is to use the symfony command line:

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

Alternatively, if you don't have PEAR installed, you can download the latest package attached to this plugin's wiki page and extract it under your project's plugins/ directory.

Clear the cache to enable the autoloading to find the new classes:

$ php symfony cc

That's it, you are ready to write schemas with the new syntax and override existing schemas.

New schema syntax

As an alternative to the current schema.yml syntax (which still works), this plugin proposes a new way to define a database schema.

Consider the following schema, using the current syntax:

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:       [user_id](title,)
    _uniques:
      my_other_index: [created_at]

  ab_group_i18n:
    motto:            longvarchar

With the new syntax, you can write it as follows:

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, foreignTable: ab_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:       [user_id](title,)
    uniques:
      my_other_index: [created_at]

  AbGroupI18n:
    columns:
      motto:          longvarchar

The main difference is that you declare classes, not tables, using the table phpName as a key. If you don't define a tableName, the plugin will determine one automatically based on the phpName using sfInflector::underscore().

This syntax is also more explicit, since you must create entries for classes and columns. But it gets rid of the ugly _attributes hack of the current syntax.

The connection parameter is optional. If it is not set, it will take propel as a default value.

Last but not least, all the 'magic' of the current syntax is still there (auto definition of primary keys, foreign keys, i18n tables, etc.).

Once you have defined such a schema, rebuild the model as usual:

$ php symfony propel-build-model

Customizing an existing schema

Schemas using this new syntax can be customized by other schemas. This is great for plugin schemas, for instance, to allow users to override some of the plugin's schema settings (such as the connection, the table names, etc).

When dealing with a schema with the alternative syntax, the symfony builder will look for custom YAML files for this schema, following this rule:

Original schema name                     | Custom schema name

*----------------------------------------|------------------------
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

Custom schemas will be looked for in the application's and plugins' config/ directories, so a plugin can override another plugin's schema.

Custom schemas must respect the same alternative syntax. The plugin will simply merge the two schemas, as follows:

# Original schema
#################
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 }

# Custom schema
###############
connection: myConnection
classes:
  Group:
    tableName:        ab_group
    package:          foo.bar.lib.model
    columns:
      id:
      name:           varchar(50)

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

  Article:
    columns:
      updated_at:

# Resulting schema
##################
connection: myConnection
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:

  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 }

TODO

  • Manage behaviors directly in the schema

Changelog

2007-10-05 | 0.9.0 Beta

  • francois: initial release