package Catalyst::Model::DBIC::Schema;

use Moose;
use mro 'c3';
extends 'Catalyst::Model';
with 'CatalystX::Component::Traits';

our $VERSION = '0.65';

use namespace::autoclean;
use Carp::Clan '^Catalyst::Model::DBIC::Schema';
use Data::Dumper;
use DBIx::Class ();
use Module::Runtime qw/use_module/;

use Catalyst::Model::DBIC::Schema::Types
    qw/ConnectInfo SchemaClass Schema/;

use MooseX::Types::Moose qw/Str/;
use MooseX::Types::LoadableClass qw/LoadableClass/;

=head1 NAME

Catalyst::Model::DBIC::Schema - DBIx::Class::Schema Model Class


First, prepare your database schema using L<DBIx::Class>, see
L<Catalyst::Helper::Model::DBIC::Schema> for how to generate a
L<DBIx::Class::Schema> from your database using the Helper script, and

A typical usage of the helper script would be:

    script/ model FilmDB DBIC::Schema MyApp::Schema::FilmDB \
        create=static dbi:mysql:filmdb dbusername dbpass \

If you are unfamiliar with L<DBIx::Class>, see L<DBIx::Class::Manual::Intro>

These examples assume that you already have a schema called
C<MyApp::Schema::FilmDB>, which defines some Result classes for tables in
C<MyApp::Schema::FilmDB::Result::Actor> and
C<MyApp::Schema::FilmDB::Result::Film>. Either created by the helper script (as
shown above) or manually.

The helper also creates a Model in C<lib/MyApp/Model/>, if you already
have a schema you can create just the Model using:

    script/ model FilmDB DBIC::Schema MyApp::Schema::FilmDB
        dbi:mysql:filmdb dbusername dbpass

The connect_info is optional and will be hardcoded into the Model if provided.
It's better to configure it in your L<Catalyst> config file, which will also
override any hardcoded config, see L</connect_info> for examples.

Now you have a working Model which accesses your separate DBIC Schema. This can
be used/accessed in the normal Catalyst manner, via C<< $c->model() >>:

  my $db_model = $c->model('FilmDB');         # a Catalyst::Model
  my $dbic     = $c->model('FilmDB')->schema; # the actual DBIC object

There is also a shortcut, which returns a L<DBIx::Class::ResultSet> directly,
instead of a L<Catalyst::Model>:

  my $rs = $c->model('FilmDB::Actor');

See L<DBIx::Class::ResultSet> to find out more about which methods can be
called on ResultSets.

You can also define your own ResultSet methods to encapsulate the
database/business logic of your applications. These go into, for example,
C<lib/MyApp/Schema/FilmDB/ResultSet/>. The class must inherit from
L<DBIx::Class::ResultSet> and is automatically loaded.

Then call your methods like any other L<DBIx::Class::ResultSet> method:


=head2 Some examples:

  # to access schema methods directly:

  # to access the source object, resultset, and class:

  # For resultsets, there's an even quicker shortcut:
  # is the same as $c->model('FilmDB')->resultset('Actor')

  # To get the composed schema for making new connections:
  my $newconn = $c->model('FilmDB')->composed_schema->connect(...);

  # Or the same thing via a convenience shortcut:
  my $newconn = $c->model('FilmDB')->connect(...);

  # or, if your schema works on different storage drivers:
  my $newconn = $c->model('FilmDB')->composed_schema->clone();

  # and again, a convenience shortcut
  my $newconn = $c->model('FilmDB')->clone();

To set up authentication, see L</"Setting up DBIC authentication"> below.


This is a Catalyst Model for L<DBIx::Class::Schema>-based Models.  See
the documentation for L<Catalyst::Helper::Model::DBIC::Schema> for
information on generating these Models via Helper scripts.

When your Catalyst app starts up, a thin Model layer is created as an interface
to your DBIC Schema. It should be clearly noted that the model object returned
by C<< $c->model('FilmDB') >> is NOT itself a DBIC schema or resultset object,
but merely a wrapper proving L<methods|/METHODS> to access the underlying

In addition to this model class, a shortcut class is generated for each 
source in the schema, allowing easy and direct access to a resultset of the 
corresponding type. These generated classes are even thinner than the model 
class, providing no public methods but simply hooking into Catalyst's 
model() accessor via the 
L<ACCEPT_CONTEXT|Catalyst::Component/ACCEPT_CONTEXT> mechanism. The complete 
contents of each generated class is roughly equivalent to the following:

  package MyApp::Model::FilmDB::Actor
      my ($self, $c) = @_;

In short, there are three techniques available for obtaining a DBIC 
resultset object: 

  # the long way
  my $rs = $c->model('FilmDB')->schema->resultset('Actor');

  # using the shortcut method on the model object
  my $rs = $c->model('FilmDB')->resultset('Actor');

  # using the generated class directly
  my $rs = $c->model('FilmDB::Actor');

In order to add methods to a DBIC resultset, you cannot simply add them to 
the source (row, table) definition class; you must define a separate custom 
resultset class. This is just a matter of making a
C<lib/MyApp/Schema/ResultSet/> class that inherits from
L<DBIx::Class::ResultSet>, if you are using
L<DBIx::Class::Schema/load_namespaces>, the default for helper script generated

See L<DBIx::Class::Manual::Cookbook/"Predefined searches"> 
for information on definining your own L<DBIx::Class::ResultSet> classes for
use with L<DBIx::Class::Schema/load_classes>, the old default.


=head2 schema_class

This is the classname of your L<DBIx::Class::Schema> Schema.  It needs
to be findable in C<@INC>, but it does not need to be inside the 
C<Catalyst::Model::> namespace.  This parameter is required.

=head2 connect_info

This is a hashref or arrayref of connection parameters, which are specific to
your C<storage_type> (see your storage type documentation for more details). If
you only need one parameter (e.g. the DSN), you can just pass a string.

This is not required if C<schema_class> already has connection information
defined inside itself (which isn't highly recommended, but can be done.)

For L<DBIx::Class::Storage::DBI>, which is the only supported
C<storage_type> in L<DBIx::Class> at the time of this writing, the
parameters are your dsn, username, password, and connect options hashref.

See L<DBIx::Class::Storage::DBI/connect_info> for a detailed explanation
of the arguments supported.


  connect_info => {
    dsn => 'dbi:Pg:dbname=mypgdb',
    user => 'postgres',
    password => ''

  connect_info => {
    dsn => 'dbi:SQLite:dbname=foo.db',
    on_connect_do => [
      'PRAGMA synchronous = OFF',

  connect_info => {
    dsn => 'dbi:Pg:dbname=mypgdb',
    user => 'postgres',
    password => '',
    pg_enable_utf8 => 1,
    on_connect_do => [
      'some SQL statement',
      'another SQL statement',

Or using L<Config::General>:

        schema_class   MyApp::Schema::FilmDB
        traits Caching
            dsn   dbi:Pg:dbname=mypgdb
            user   postgres
            password ""
            auto_savepoint 1
            quote_names 1
            on_connect_do   some SQL statement
            on_connect_do   another SQL statement
        user_defined_schema_accessor foo


        schema_class   MyApp::Schema::FilmDB
        connect_info   dbi:SQLite:dbname=foo.db

Or using L<YAML>:

      schema_class: MyDB
      traits: Caching
          dsn: dbi:Oracle:mydb
          user: mtfnpy
          password: mypass
          LongReadLen: 1000000
          LongTruncOk: 1
          on_connect_call: 'datetime_setup'
          quote_names: 1

The old arrayref style with hashrefs for L<DBI> then L<DBIx::Class> options is also

  connect_info => [
      pg_enable_utf8 => 1,
      auto_savepoint => 1,
      on_connect_do => [
        'some SQL statement',
        'another SQL statement',

=head2 traits

Array of Traits to apply to the instance. Traits are L<Moose::Role>s.

They are relative to the C<< MyApp::TraitFor::Model::DBIC::Schema:: >>, then
the C<< Catalyst::TraitFor::Model::DBIC::Schema:: >> namespaces, unless
prefixed with C<+> in which case they are taken to be a fully qualified name.

    traits Caching
    traits +MyApp::TraitFor::Model::Foo

A new instance is created at application time, so any consumed required
attributes, coercions and modifiers will work.

Traits are applied at L<Catalyst::Component/COMPONENT> time using

C<ref $self> will be an anon class if any traits are applied, C<<
$self->_original_class_name >> will be the original class.

When writing a Trait, interesting points to modify are C<BUILD>, L</setup> and

Traits that come with the distribution:

=over 4

=item L<Catalyst::TraitFor::Model::DBIC::Schema::Caching>

=item L<Catalyst::TraitFor::Model::DBIC::Schema::Replicated>

=item L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>

=item L<Catalyst::TraitFor::Model::DBIC::Schema::PerRequestSchema>


=head2 compose_namespaces

This model calls L<DBIx::Class::Schema/compose_namespaces> by default to
install classes into the model namespaces. You can turn that off by
setting this attribute to false. Default is true.

=head2 install_model_shortcuts

If you don't want shortcut models so you can do e.g. C<< $c->model('DB::Book')
>> set this attribute to false, Default is true.

=head2 storage_type

Allows the use of a different C<storage_type> than what is set in your
C<schema_class> (which in turn defaults to C<::DBI> if not set in current
L<DBIx::Class>).  Completely optional, and probably unnecessary for most
people until other storage backends become available for L<DBIx::Class>.


The keys you pass in the model configuration are available as attributes.

Other attributes available:

=head2 connect_info

Your connect_info args normalized to hashref form (with dsn/user/password.) See
L<DBIx::Class::Storage::DBI/connect_info> for more info on the hashref form of

=head2 model_name

The model name L<Catalyst> uses to resolve this model, the part after
C<::Model::> or C<::M::> in your class name. E.g. if your class name is
C<MyApp::Model::DB> the L</model_name> will be C<DB>.

=head2 _default_cursor_class

What to reset your L<DBIx::Class::Storage::DBI/cursor_class> to if a custom one
doesn't work out. Defaults to L<DBIx::Class::Storage::DBI::Cursor>.

=head1 ATTRIBUTES FROM L<MooseX::Traits::Pluggable>

=head2 _original_class_name

The class name of your model before any L</traits> are applied. E.g.

=head2 _traits

Unresolved arrayref of traits passed in the config.

=head2 _resolved_traits

Traits you used resolved to full class names.


See the documentation for
L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy> for instructions on how
to pass config values from your L<Catalyst> config to your
L<DBIx::Class::Schema> and/or L<DBIx::Class::ResultSet> classes.

=head1 METHODS

=head2 new

Instantiates the Model based on the above-documented ->config parameters.
The only required parameter is C<schema_class>.  C<connect_info> is
required in the case that C<schema_class> does not already have connection
information defined for it.

=head2 schema

Accessor which returns the connected schema being used by the this model.
There are direct shortcuts on the model class itself for
schema->resultset, schema->source, and schema->class.

=head2 composed_schema

Accessor which returns the composed schema, which has no connection info,
which was used in constructing the L</schema>. Useful for creating
new connections based on the same schema/model.  There are direct shortcuts
from the model object for composed_schema->clone and composed_schema->connect

If L</compose_namespaces> is not true, L</composed_schema> is equivalent to
C<< $model->schema_class->clone >>.

=head2 clone

Shortcut for ->composed_schema->clone

=head2 connect

Shortcut for ->composed_schema->connect

=head2 source

Shortcut for ->schema->source

=head2 class

Shortcut for ->schema->class

=head2 resultset

Shortcut for ->schema->resultset

=head2 txn_do

Shortcut for ->schema->txn_do

=head2 txn_scope_guard

Shortcut for ->schema->txn_scope_guard

=head2 storage

Provides an accessor for the connected schema's storage object.

See L<DBIx::Class::Storage> and L<DBIx::Class::Storage::DBI>.


has schema_class => (
    is => 'ro',
    isa => SchemaClass,
    required => 1

has compose_namespaces => (is => 'ro', default => sub { 1 });

has install_model_shortcuts => (is => 'ro', default => sub { 1 });

has storage_type => (is => 'rw', isa => Str);

has connect_info => (is => 'rw', isa => ConnectInfo, coerce => 1);

has model_name => (
    is => 'ro',
    isa => Str,
    required => 1,
    lazy_build => 1,

has _default_cursor_class => (
    is => 'ro',
    isa => LoadableClass,
    default => 'DBIx::Class::Storage::DBI::Cursor',

has schema => (is => 'rw', isa => Schema);

my $app_class;

before COMPONENT => sub {
    $app_class = ref $_[1] || $_[1];

sub app_class { $app_class }

sub BUILD {
    my ($self, $args) = @_;
    my $class = $self->_original_class_name;
    my $schema_class = $self->schema_class;

    if( !$self->connect_info ) {
        if($schema_class->storage && $schema_class->storage->connect_info) {
        else {
            die "Either ->config->{connect_info} must be defined for $class"
                  . " or $schema_class must have connect info defined on it."
		  . " Here's what we got:\n"
		  . Dumper($args);

    if (exists $self->connect_info->{cursor_class}) {
        eval { use_module($self->connect_info->{cursor_class}) }
            or croak "invalid connect_info: Cannot load your cursor_class"
        . " ".$self->connect_info->{cursor_class}.": $@";


    my $is_installed = defined $self->composed_schema;

    if (not $is_installed) {
        $self->composed_schema($self->compose_namespaces ?

        unless $self->schema;

        if $self->storage_type;


    if ((not $is_installed) && $self->install_model_shortcuts) {

sub clone { shift->composed_schema->clone(@_); }

sub connect { shift->composed_schema->connect(@_); }

# some proxy methods, see also SchemaProxy

sub resultset { shift->schema->resultset(@_); }

sub txn_do { shift->schema->txn_do(@_); }

sub txn_scope_guard { shift->schema->txn_scope_guard(@_); }

sub storage { shift->schema->storage(@_); }

=head2 setup

Called at C<BUILD> time before configuration, but after L</connect_info> is
set. To do something after configuuration use C<< after BUILD => >>.

Receives a hashref of args passed to C<BUILD>.


sub setup { 1 }


Point of extension for doing things at C<< $c->model >> time with context,
returns the model instance, see L<Catalyst::Manual::Intro/ACCEPT_CONTEXT> for
more information.


sub ACCEPT_CONTEXT { shift }

sub _install_rs_models {
    my $self  = shift;
    my $class = $self->_original_class_name;

    no strict 'refs';

    my @sources = $self->schema->sources;

    unless (@sources) {
        warn <<'EOF' unless $ENV{CMDS_NO_SOURCES};
******************************* WARNING ***************************************
* No sources found (did you forget to define your tables?)                    *
*                                                                             *
* To turn off this warning, set the CMDS_NO_SOURCES environment variable.     *

    foreach my $moniker (@sources) {
        my $classname = "${class}::$moniker";
        *{"${classname}::ACCEPT_CONTEXT"} = sub {

sub _reset_cursor_class {
    my $self = shift;

    if ($self->storage->can('cursor_class')) {
	    if $self->storage->cursor_class ne $self->_default_cursor_class;


    sub composed_schema {
	my $self = shift;
	my $class = $self->_original_class_name;
	my $store = \$COMPOSED_CACHE{$class}{$self->schema_class};

	$$store = shift if @_;

	return $$store

sub _build_model_name {
    my $self  = shift;
    my $class = $self->_original_class_name;
    (my $model_name = $class) =~ s/^[\w:]+::(?:Model|M):://;

    return $model_name;



=over 4


Set this variable if you will be using schemas with no sources (Result classes)
to disable the warning. The warning is there because having no Result classes
is usually a mistake.


=head1 Setting up DBIC authentication

You can set this up with 
L<Catalyst::Authentication::Store::DBIx::Class> in

  package MyApp;

  use Catalyst qw/... Authentication .../;


  __PACKAGE__->config('Plugin::Authentication' =>
                    default_realm => 'members',
                    members => {
                        credential => {
                            class => 'Password',
                            password_field => 'password',
                            password_type => 'hashed'
                            password_hash_type => 'SHA-256'
                        store => {
                            class => 'DBIx::Class',
                            user_model => 'DB::User',
                            role_relation => 'roles',
                            role_field => 'rolename',


The automatic proxying to the underlying L<DBIx::Class::Schema> has been
removed as of version C<0.34>, to enable this feature add C<SchemaProxy> to

See L<Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy>.

=head1 SEE ALSO

General Catalyst Stuff:

L<Catalyst::Manual>, L<Catalyst::Test>, L<Catalyst::Request>,
L<Catalyst::Response>, L<Catalyst::Helper>, L<Catalyst>,

Stuff related to DBIC and this Model style:

L<DBIx::Class>, L<DBIx::Class::Schema>,
L<DBIx::Class::Schema::Loader>, L<Catalyst::Helper::Model::DBIC::Schema>,
L<CatalystX::Component::Traits>, L<MooseX::Traits::Pluggable>



=head1 AUTHOR

Brandon L Black C<blblack at>


caelum: Rafael Kitover C<rkitover at>

dandv: Dan Dascalescu C<dandv at>

bluefeet: Aran Deltac C<>

t0m: Tomas Doran C<>

osfameron: C<>

ozum: Ozum Eldogan C<>

Pavel I. Shaydo C<>

SineSwiper: Brendan Byrd <>


Copyright (c) 2006 - 2010
the Catalyst::Model::DBIC::Schema L</AUTHOR> and L</CONTRIBUTORS>
as listed above.

=head1 LICENSE

This program is free software. You can redistribute it and/or modify it
under the same terms as Perl itself.


# vim:sts=4 sw=4 et tw=80: