The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

DBIx::Class::ResultDDL - Sugar methods for declaring DBIx::Class::Result data definitions

VERSION

version 0.01

SYNOPSIS

  package MyApp::Schema::Result::Artist;
  use DBIx::Class::ResultDDL -V0;
  table 'artist';
  col id   => integer, unsigned, auto_inc;
  col name => varchar(25), null;
  primary_key 'id';
  
  has_many albums => { id => 'Album.artist_id' };
  rel_many impersonators => { name => 'Artist.name' };

DESCRIPTION

This is Yet Another Sugar Module for building DBIC result classes. It provides a domain-specific-language that feels almost like writing DDL.

This module heavily pollutes your symbol table in the name of extreme convenience, so the -V0 option has the added feature of automatically removing those symbols at end-of-scope as if you had said use namespace::clean;.

This module has a versioned API, to help prevent name collisions. If you request the -v0 behavior, you can rely on that to remain the same across upgrades.

EXPORTED FEATURES

This module is based on Exporter::Extensible, allowing all the import notations that module provides. Every export beginning with a dash indicates custom behavior, rather than just a simple export of symbols.

-swp

Enable use strict and use warnings (unless those flags have been changed from the default via other means), and use parent "DBIx::Class::Core" unless __PACKAGE__->can('add_column').

-autoclean

Remove all added symbols at the end of current scope.

-V0

Implies -swp, :V0, and -autoclean.

EXPORTED COLLECTIONS

:V0

This tag selects the following symbols:

  table
  col
    null default auto_inc fk
    integer unsigned tinyint smallint bigint decimal numeric
    char varchar nchar nvarchar binary varbinary blob text ntext
    tinyblob mediumblob longblob tinytext mediumtext longtext
    date datetime timestamp enum bool boolean
    inflate_json
  primary_key
  rel_one rel_many has_one might_have has_many belongs_to many_to_many
    ddl_cascade dbic_cascade

EXPORTED METHODS

table

  table 'foo';
  # becomes...
  __PACKAGE__->table('foo');

col

  col $name, @options;
  # becomes...
  __PACKAGE__->add_column($name, { is_nullable => 0, @merged_options });

Define a column. This calls add_column after sensibly merging all your options. It defaults the column to not-null for you, but you can override that by saying null in your options. You will probably use many of the methods below to build the options for the column:

null
  is_nullable => 1
auto_inc
  is_auto_increment => 1
fk
  is_foreign_key => 1
default($value | @value)
  default_value => $value
  default_value => [ @value ] # if more than one param
integer, integer($size)
  data_type => 'integer', size => $size // 11
unsigned
  extra => { unsigned => 1 }

MySQL specific flag to be combined with integer

tinyint
  data_type => 'tinyint', size => 4
smallint
  data_type => 'smallint', size => 6
bigint
  data_type => 'bigint', size => 22
decimal( $whole, $deci )
  data_type => 'decimal', size => [ $whole, $deci ]
numeric( $whole, $deci )
  data_type => 'numeric', size => [ $whole, $deci ]
char, char($size)
  data_type => 'char', size => $size // 1
varchar, varchar($size), varchar(MAX)
  data_type => 'varchar', size => $size // 255
nchar

SQL Server specific type for unicode char

nvarchar, nvarchar($size), nvarchar(MAX)

SQL Server specific type for unicode character data.

  data_type => 'nvarchar', size => $size // 255
MAX

Constant for 'MAX', used by SQL Server for varchar(MAX).

binary, binary($size)
  data_type => 'binary', size => $size // 255
varbinary, varbinary($size)
  data_type => 'varbinary', size => $size // 255
blob, blob($size)
  data_type => 'blob',
  size => $size if defined $size

Note: For MySQL, you need to change the type according to '$size'. A MySQL blob is 2^16 max length, and probably none of your binary data would be that small. Consider mediumblob or longblob, or consider overriding My::Schema::sqlt_deploy_hook to perform this conversion automatically according to which DBMS you are connected to.

For SQL Server, newer versions deprecate blob in favor of VARCHAR(MAX). This is another detail you might take care of in sqlt_deploy_hook.

tinyblob

MySQL-specific type for small blobs

  data_type => 'tinyblob', size => 0xFF
mediumblob

MySQL-specific type for larger blobs

  data_type => 'mediumblob', size => 0xFFFFFF
longblob

MySQL-specific type for the longest supported blob type

  data_type => 'longblob', size => 0xFFFFFFFF
text, text($size)
  data_type => 'text',
  size => $size if defined $size

See MySQL notes in blob. For SQL Server, you might want ntext or varchar(MAX) instead.

tinytext
  data_type => 'tinytext', size => 0xFF
mediumtext
  data_type => 'mediumtext', size => 0xFFFFFF
longtext
  data_type => 'longtext', size => 0xFFFFFFFF
ntext

SQL-Server specific type for unicode text. Note that newer versions prefer nvarchar(MAX).

  data_type => 'ntext', size => 0x3FFFFFFF
enum( @values )
  data_type => 'enum', extra => { list => [ @values ] }
bool, boolean
  data_type => 'boolean'

Note that SQL Server has 'bit' instead.

bit, bit($size)
  data_type => 'bit', size => $size // 1

To be database agnostic, consider using 'bool' and override My::Scema::sqlt_deploy_hook to rewrite it to 'bit' when deployed to SQL Server.

date, date($timezone)
  data_type => 'date'
  time_zone => $timezone if defined $timezone
datetime, datetime($timezone)
  data_type => 'datetime'
  time_zone => $timezone if defined $timezone
timestamp, timestamp($timezone)
  date_type => 'timestamp'
  time_zone => $timezone if defined $timezone
inflate_json
  serializer_class => 'JSON'

Also adds the component 'InflateColumn::Serializer' to the current package if it wasn't added already.

primary_key

  primary_key(@cols)

Shortcut for __PACKAGE__->set_primary_key(@cols)

belongs_to

  belongs_to $rel_name, $peer_class, $condition, @attr_list;
  belongs_to $rel_name, { colname => "$ResultClass.$colname" }, @attr_list;
  # becomes...
  __PACKAGE__->belongs_to($rel_name, $peer_class, $condition, { @attr_list });

Note that the normal DBIC belongs_to requires conditions to be of the form

  { "foreign.$their_col" => "self.$my_col" }

but all these sugar functions allow it to be written the other way around, and use a table name in place of "foreign.".

might_have

  might_have $rel_name, $peer_class, $condition, @attr_list;
  might_have $rel_name, { colname => "$ResultClass.$colname" }, @attr_list;
  # becomes...
  __PACKAGE__->might_have($rel_name, $peer_class, $condition, { @attr_list });

has_one

  has_one $rel_name, $peer_class, $condition, @attr_list;
  has_one $rel_name, { colname => "$ResultClass.$colname" }, @attr_list;
  # becomes...
  __PACKAGE__->has_one($rel_name, $peer_class, $condition, { @attr_list });

has_many

  has_many $rel_name, $peer_class, $condition, @attr_list;
  has_many $rel_name, { colname => "$ResultClass.$colname" }, @attr_list;
  # becomes...
  __PACKAGE__->has_one($rel_name, $peer_class, $condition, { @attr_list });

many_to_many

  many_to_many $name => $rel_to_linktable, $rel_from_linktable;
  # becomes...
  __PACKAGE__->many_to_many(@_);

rel_one

Declares a single-record left-join relation without implying ownership. Note that the DBIC relations that do imply ownership like might_have cause an implied deletion of the related row if you delete a row from this table that references it, even if your schema did not have a cascading foreign key. This DBIC feature is controlled by the cascading_delete option, and using this sugar function to set up the relation defaults that feature to "off".

  rel_one $rel_name, $peer_class, $condition, @attr_list;
  rel_one $rel_name, { $mycol => "$ResultClass.$fcol", ... }, @attr_list;
  # becomes...
  __PACKAGE__->add_relationship(
    $rel_name, $peer_class, { "foreign.$fcol" => "self.$mycol" },
    {
      join_type => 'LEFT',
      accessor => 'single',
      cascade_copy => 0,
      cascade_delete => 0,
      is_depends_on => $is_f_pk, # auto-detected, unless specified
      ($is_f_pk? fk_columns => { $mycol => 1 } : ()),
      @attr_list
    }
  );

rel_many

  rel_many $name => { $my_col => "$class.$col", ... }, @options;

Same as "rel_one", but generates a one-to-many relation with a multi-accessor.

ddl_cascade

  ddl_cascade;     # same as ddl_cascade("CASCADE");
  ddl_cascade(1);  # same as ddl_cascade("CASCADE");
  ddl_cascade(0);  # same as ddl_cascade("RESTRICT");
  ddl_cascade($mode);

Helper method to generate @options for above. It generates

  on_update => $mode, on_delete => $mode

This does not affect client-side cascade, and is only used by Schema::Loader to generate DDL for the foreign keys when the table is deployed.

dbic_cascade

  dbic_cascade;  # same as dbic_cascade(1)
  dbic_cascade($enabled);

Helper method to generate @options for above. It generates

  cascade_copy => $enabled, cascade_delete => $enabled

This re-enables the dbic-side cascading that was disabled by default in the rel_ functions.

MISSING FUNCTIONALITY

The methods above in most cases allow you to insert plain-old-DBIC notation where appropriate, instead of relying purely on sugar methods. If you are missing your favorite column flag or something, feel free to contribute a patch.

AUTHOR

Michael Conrad <mconrad@intellitree.com>

COPYRIGHT AND LICENSE

This software is copyright (c) 2019 by Michael Conrad, IntelliTree Solutions llc.

This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.