It would be sweet if we had a nicer syntax for hook_schema.
Imo, if we do it right, then shorter = easier to read and write.
D8:
Whether we move this to an event, is a different question, and independent of this proposal.
EDIT (donquixote):
I originally had two ideas about this, but find one a lot more convincing. As so far I am the only one active on this issue, I take the freedom to remove the other proposal.
I recently did some experiments in D7, and this is the (working) result: http://drupal.org/project/injapi
The current proposal is mostly taken from there.
Method chaining / "fluid" syntax for hook_schema().
The hook_schema() would receive an argument that would be the starting point for the method chaining game.
(I say "$api" for "InjectedAPI", following a pattern I use in my contrib projects. Feel free to suggest otherwise.)
The draft is taken directly from injapi module:
http://drupalcode.org/project/injapi.git/blob/refs/heads/7.x-1.x:/exampl...
/**
* Implements hook_schema()
*/
function hook_schema($schema) {
// That's the table.
$table = $schema->table('comment');
// Primary key.
// "field_id" automatically gives us auto-increment, and registers this field as the primary key.
$table->field_id('cid');
// Foreign keys
// field_idForeign can take optional parameters to automatically create relations.
$table->field_idForeign('pid');
$table->field_idForeign('nid', 'node', 'nid', 'comment_node');
$table->field_idForeign('uid', 'users', 'uid', 'comment_author');
// Data fields
// Most field types have extra parameters for stuff that is special to this type. E.g. varchar length.
// required() has a second parameter for the default value.
$table->field_varchar('subject', 64)->required(TRUE, '');
$table->field_varchar('hostname', 128)->required(TRUE, '');
$table->field_seconds('created')->required(TRUE, 0);
$table->field_seconds('changed')->required(TRUE, 0);
$table->field_unsigned('status', 'tiny')->required(TRUE, 1);
$table->field_varchar('thread', 255)->required();
$table->field_varchar('name', 60);
$table->field_varchar('mail', 64);
$table->field_varchar('homepage');
$table->field_varchar('language', 12)->required(TRUE, '');
// Field descriptions
// We keep these separate, to have the upper part more readable.
// This said, we could instead just put the ->description() on each field directly.
$table->describeFields(array(
// Id columns
'cid' => 'Primary Key: Unique comment ID.',
'pid' => 'The {comment}.cid to which this comment is a reply. If set to 0, this comment is not a reply to an existing comment.',
'nid' => 'The {node}.nid to which this comment is a reply.',
'uid' => 'The {users}.uid who authored the comment. If set to 0, this comment was created by an anonymous user.',
// Data columns
'subject' => 'The comment title.',
'hostname' => "The author's host name.",
'created' => 'The time that the comment was created, as a Unix timestamp.',
'changed' => 'The time that the comment was last edited, as a Unix timestamp.',
'status' => 'The published status of a comment. (0 = Not Published, 1 = Published)',
'thread' => "The vancode representation of the comment's place in a thread.",
'name' => "The comment author's name. Uses {users}.name if the user is logged in, otherwise uses the value typed into the comment form.",
'mail' => "The comment author's e-mail address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on.",
'homepage' => "The comment author's home page address from the comment form, if user is anonymous, and the 'Anonymous users may/must leave their contact information' setting is turned on.",
'language' => 'The {languages}.language of this comment.',
));
// Indexes
$table->index('comment_status_pid', array('pid', 'status'));
$table->index('comment_num_new', array('nid', 'status', 'created', 'cid', 'thread'));
$table->index('comment_uid', array('uid'));
$table->index('comment_nid_language', array('nid', 'language'));
$table->index('comment_created', array('created'));
}
Implementation
The implementation can be copied and adapted directly from injapi module.
The objects ($schema, $table, $field) used in here are just temporary for the purpose of this hook, and will not be used anywhere else in the db engine.
The $schema object might be created new for every invocation, or we use the same all over.
The result of a hook invocation will still be a huge array, after all is done.
Benefits
- Nicer to read (imo).
- Faster to type.
- Less scrolling.
- More visual structure in the code.
- Easier to focus on the intention of things, rather than the technical details.
- Well-known patterns such as "auto-increment id" are shortcutted, so you don't have to guess that from the values.
- Possibility for immediate sanity checks in the various methods, so that the array structure collected in $schema can be always sane.
Cons
- A tiny overhead, but that doesn't matter, because it is only on rebuild.
- No idea if we have anything other planned for hook_schema().
- Not sure what to do with hook_schema_alter().
Thoughts
Memory concerns?
We still have the option to run this thing separately per module, with a new $schema object per invocation. I don't think this is actually necessary, but the option exists.