jsDoctrineSchemaOverriderPlugin

Why I Wrote This Plugin

In order to understand what this plugin is for, it’s helpful to have a little background. There are two ways to handle your schema in symfony:

Method 1: Write your config/doctrine/schema.yml by hand (for this post we’ll assume you’re using Doctrine) and run symfony doctrine:build-sql to generate your database tables. This is what most people do.

Method 2: Create your database tables by hand and run symfony doctrine:build-schema to generate your schema. This is what I do.

Why do I generate my schema from my database instead of generating my database from my schema? There’s one big reason. Every time you run symfony doctrine:insert-sql, your entire database—meaning not just the database structure, but the data as well—gets wiped out. The database structure is put right back in place but if you want to keep your data intact, you’re on your own. Doing a mysqldump only works for the most trivial changes. Because the symfony developers are well aware that you’re going to want your data to stay intact, they let you use what are called fixtures—sets of data that you can easily load into your database—but it seems terribly inefficient to have to manually shuffle around your fixture data each time you make a change to your database structure. Plus, what if you like to periodically load your production data into your development database so you’re working with a realistic dataset? As far as I know, fixtures don’t accommodate that. Those are the main reasons why I use build-schema instead of build-sql. (From now on, I will call these two methods the build-from-database method and the build-from-schema method.)

Neither the build-from-database method or the build-from-schema method is perfect. (I just find build-from-database “less bad.”) I’ve come across the following problems with build-from-database:

  • If any of my entities inherit from other entities (read about that here), the relationship gets wiped out every time I build the schema
  • If I have SQL views in my database, each view shows up as a model, which doesn’t always make sense
  • If I have, say, a WordPress install as part of my website, all my WordPress tables show up as models (I know I could work around this by putting the WP install in a separate database. The point is that every table in your database will get translated into a model, regardless of whether you want them to or not)

These are the main problems I’ve had with the build-from-database method. The most frustrating part is that I would have to manually correct my schema every single time I ran build-schema! It gets old fast, as I’m sure you know if you do build-from-database instead of build-from-schema.

How jsDoctrineSchemaOverriderPlugin Fixes The Problem

The brilliant part of symfony’s model design is the idea where there’s a base class and an inheriting class for each model. For example, if you have a table in your database called animal, symfony creates an Animal class as well as a BaseAnimal class. Then, when you re-run symfony doctrine:build-model, BaseAnimal is wiped out and rewritten but Animal—the class you’re allowed to customize—is left undisturbed. If you’re familiar with this idea, you’ll understand how jsDoctrineSchemaOverriderPlugin works.

The meat of jsDoctrineSchemaOverriderPlugin is in two classes: BaseSchema and Schema. Like symfony’s models, BaseSchema is auto-generated and Schema is there for you to override BaseSchema. BaseSchema is automatically built by jsDoctrineSchemaOverriderPlugin as a direct reflection of your database structure and Schema is where you can edit that structure. And get this: you don’t have to re-do all your work each time you rebuild the schema!

Installation

$ symfony plugin:install http://jasonswett.net/jsDoctrineSchemaOverriderPlugin-0.1.0.tgz

Examples

Removing an Entity

Let’s first take a trivial example: you have an view called my_view that you don’t want a model for. Edit your plugins/jsDoctrineSchemaOverriderPlugin/lib/Schema.class.php by adding a line to unset this view:

<?php
class Schema extends BaseSchema
{
  public function configure()
  {
    unset($this->entities['MyView']);
  }
}

Now, instead of running symfony doctrine:build-schema, you’ll do something a little different. Run:

$ symfony schema-overrider:build-schema

You’ll want to always run schema-overrider:build-schema instead of doctrine:build-schema. This ensures that the overrides you make in Schema.class.php are reflected in your schema.yml.

That’s the simplest case: completely wiping out an entity. What if we want to edit an entity’s attributes?

Overriding Attributes

Let’s say we have two classes, Animal and Horse, with Horse inheriting from Animal. If you just run symfony doctrine:build-schema, symfony will have no idea just by looking at your database schema that Horse inherits from Animal. You can make sure symfony knows about this inheritance by adding the following to plugins/jsDoctrineSchemaOverriderPlugin/lib/Schema.class.php:

<?php
class Schema extends BaseSchema
{
  public function configure()
  {
    $this->entities['Horse'] = array(
      'connection' => 'doctrine',
      'tableName' => 'horse',
      'inheritance' => array(
        'extends' => 'Animal',
        'type' => 'concrete',
      ),  
    );
  }
}

Just like in the first example, we’ll run this command:

$ symfony schema-overrider:build-schema

Now Horse will always inherit from Animal and you won’t have to keep copying and pasting every time you run build-schema.

Please Help!

If you have any feedback of any kind about this plugin, please feel free to leave me a comment and let me know. I don’t know if I’m going about this the right way and I don’t know if my documentation is clear enough for other people to be able to use my plugin. All I know is that it works for me and it saves me a ton of work. Any feedback you have would be greatly appreciated!

3 Responses to “jsDoctrineSchemaOverriderPlugin”

  1. lawrence says:

    My comment might be slightly off-topic, but I’m curious if you have insight into what causes the occassional “Missing class name” error in Doctrine? I followed the #2 method that you list, creating the SQL schema first, and then I wanted to create the yaml from that. I ran into a problem that I described here:

    http://www.symfonyexperts.com/question/show/id/156

    I would be tempted to give your plugin a try if I thought it would resolve this problem, but I’ve wasted a whole day chasing false leads, so now I’m trying to narrow my efforts down to those with the best chance of success.

  2. jason says:

    If you want to post your own question on the symfony forum or Stack Overflow and then link me to it, I can try to help you sort it out.

  3. jason says:

    You may want to look at
    lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Import/Builder.php line 995 and
    lib/vendor/symfony/lib/plugins/sfDoctrinePlugin/lib/vendor/doctrine/Doctrine/Import.php line 385 for some clues.