Cataly...:Schema(3pm)

πŸ“¦
version 0.66 Β· 5 versions
, in debian trixie, ubuntu noble, ubuntu resolute, ubuntu stonking

πŸ’– NAME πŸ’–

okay so bestie, this is Catalyst::Model::DBIC::Schema β€” it's a DBIx::Class::Schema Model Class. that's the whole vibe. it's from the User Contributed Perl Documentation, and the manual page is literally called Cataly...:Schema(3pm) (they truncated it, iconic of them honestly).


✨ SYNOPSIS ✨

first things first: build your database schema with DBIx::Class. if you wanna know how to generate a DBIx::Class::Schema from your actual database using the Helper script, go look at Catalyst::Helper::Model::DBIC::Schema and DBIx::Class::Schema::Loader::Base. they got you.

a typical run of the helper script looks like this, no cap:

script/myapp_create.pl model FilmDB DBIC::Schema MyApp::Schema::FilmDB \
    create=static dbi:mysql:filmdb dbusername dbpass \
    quote_names=1

if DBIx::Class is giving you "who is she" energy, please go read DBIx::Class::Manual::Intro first. like actually. do it. πŸ™ˆ

these examples are all assuming you already have a schema called MyApp::Schema::FilmDB that defines some Result classes for tables in MyApp::Schema::FilmDB::Result::Actor and MyApp::Schema::FilmDB::Result::Film. either the helper script made them (see above, bestie) or you made them by hand like a legend.

the helper also makes a Model in lib/MyApp/Model/FilmDB.pm. but if you already have a schema, you can just make the Model on its own:

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

the connect_info is optional, and if you give it, it gets hardcoded into the Model. ngl it's better to configure it in your Catalyst config file instead β€” that also overrides any hardcoded config, so it wins. check connect_info for examples. πŸ’…

so now you have a working Model that accesses your separate DBIC Schema. use it the normal Catalyst way, via $c->model():

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

there's also a shortcut that hands you a DBIx::Class::ResultSet directly, no Catalyst::Model middleman:

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

want to know what methods you can call on ResultSets? that's DBIx::Class::ResultSet, go read it.

you can also define your own ResultSet methods to wrap up your app's database/business logic. those live in, for example, lib/MyApp/Schema/FilmDB/ResultSet/Actor.pm. the class must inherit from DBIx::Class::ResultSet, and it gets loaded automatically. slay.

then you just call your methods like any other DBIx::Class::ResultSet method:

$c->model('FilmDB::Actor')->SAG_members

some examples πŸ¦‹

# to access schema methods directly:
$c->model('FilmDB')->schema->source(...);

# to access the source object, resultset, and class:
$c->model('FilmDB')->source(...);
$c->model('FilmDB')->resultset(...);
$c->model('FilmDB')->class(...);

# For resultsets, there's an even quicker shortcut:
$c->model('FilmDB::Actor')
# 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();
$newconn->storage_type('::LDAP');
$newconn->connection(...);

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

to set up authentication, scroll down to "Setting up DBIC authentication" below. πŸ‘‘


🌸 DESCRIPTION 🌸

this is a Catalyst Model for DBIx::Class::Schema-based Models. if you want the tea on generating these Models via Helper scripts, the documentation for Catalyst::Helper::Model::DBIC::Schema has it.

when your Catalyst app starts up, a thin Model layer gets created as an interface to your DBIC Schema. and important, listen up: the model object that $c->model('FilmDB') hands back is NOT itself a DBIC schema or resultset object. it's literally just a wrapper that proves methods to access the underlying schema. don't get it twisted.

on top of this model class, a shortcut class gets generated for every source in the schema, so you can get easy, direct access to a resultset of the corresponding type. and these generated classes are even thinner than the model class β€” like, zero public methods, they just hook into Catalyst's model() accessor via the ACCEPT_CONTEXT mechanism. the complete contents of each generated class is roughly equivalent to this:

package MyApp::Model::FilmDB::Actor
sub ACCEPT_CONTEXT {
    my ($self, $c) = @_;
    $c->model('FilmDB')->resultset('Actor');
}

so basically, there are three techniques for getting yourself 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');

now, if you wanna add methods to a DBIC resultset, you cannot just slap them onto the source (row, table) definition class. nope. you have to define a separate custom resultset class. it's easy though β€” just make a lib/MyApp/Schema/ResultSet/Actor.pm class that inherits from DBIx::Class::ResultSet, assuming you're using load_namespaces in DBIx::Class::Schema, which is the default for helper script generated schemas. ✨

want more? check out "Predefined searches" in DBIx::Class::Manual::Cookbook for how to define your own DBIx::Class::ResultSet classes for use with load_classes in DBIx::Class::Schema β€” that's the old default, fyi.


πŸŽ€ CONFIG PARAMETERS πŸŽ€

schema_class

this is the classname of your DBIx::Class::Schema Schema. it needs to be findable in @INC, but honestly it does not need to live inside the Catalyst::Model:: namespace. this parameter is required. periodt.

connect_info

this is a hashref or arrayref of connection parameters, and they're specific to your storage_type (go check your storage type documentation for the deets). if you only need one parameter β€” like the DSN, for example β€” you can literally just pass a string.

this is not required if schema_class already has connection information defined inside itself. (which, ngl, isn't highly recommended, but it can be done. do with that what you will. 🫢)

for DBIx::Class::Storage::DBI β€” which is the only supported storage_type in DBIx::Class at the time of this writing β€” the parameters are your dsn, username, password, and connect options hashref.

for a detailed explanation of the arguments supported, see "connect_info" in DBIx::Class::Storage::DBI.

examples πŸ’œ

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 Config::General:

<Model::FilmDB>
    schema_class   MyApp::Schema::FilmDB
    traits Caching
    <connect_info>
        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
    </connect_info>
    user_defined_schema_accessor foo
</Model::FilmDB>

or:

<Model::FilmDB>
    schema_class   MyApp::Schema::FilmDB
    connect_info   dbi:SQLite:dbname=foo.db
</Model::FilmDB>

or, using YAML:

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

and yes, the old arrayref style with hashrefs for DBI then DBIx::Class options is also supported, because we respect the classics:

connect_info => [
  'dbi:Pg:dbname=mypgdb',
  'postgres',
  '',
  {
    pg_enable_utf8 => 1,
  },
  {
    auto_savepoint => 1,
    on_connect_do => [
      'some SQL statement',
      'another SQL statement',
    ],
  }
]

traits

an Array of Traits to apply to the instance. Traits are Moose::Roles. πŸ’…

they're relative to the MyApp::TraitFor::Model::DBIC::Schema::, then the Catalyst::TraitFor::Model::DBIC::Schema:: namespaces β€” unless they're prefixed with "+", in which case they're taken to be a fully qualified name. like so:

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

a new instance gets created at application time, so any consumed required attributes, coercions and modifiers will work. love that for us.

Traits are applied at "COMPONENT" in Catalyst::Component time using CatalystX::Component::Traits.

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

when you're writing a Trait, the interesting points to modify are "BUILD", setup and ACCEPT_CONTEXT.

Traits that come with the distribution:

  • Catalyst::TraitFor::Model::DBIC::Schema::Caching
  • Catalyst::TraitFor::Model::DBIC::Schema::Replicated
  • Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy
  • Catalyst::TraitFor::Model::DBIC::Schema::PerRequestSchema

compose_namespaces

this model calls "compose_namespace" in DBIx::Class::Schema by default to install classes into the model namespaces. you can turn that off by setting this attribute to false. Default is true.

install_model_shortcuts

if you don't want shortcut models β€” so you can't do stuff like $c->model('DB::Book') β€” set this attribute to false. Default is true.

storage_type

this lets you use a different "storage_type" than whatever is set in your schema_class (which in turn defaults to "::DBI" if it's not set in current DBIx::Class). Completely optional, and probably unnecessary for most people until other storage backends become available for DBIx::Class. 🌈


...to be continued, bestie β€” part 2 is coming πŸ’–

ATTRIBUTES ✨

okay so literally every key you drop into your model config? bestie, it just... becomes an attribute. that's the vibe. πŸ’…

And then there's a lil bonus roster of attributes waiting for you:

connect_info

Your connect_info args, but normalized into hashref form (with dsn/user/password in there 🫢). Want the actual deets on what the hashref form of connect_info looks like? Run, don't walk, to "connect_info" in DBIx::Class::Storage::DBI.

model_name

The model name Catalyst uses to resolve this model β€” specifically the part after ::Model:: or ::M:: in your class name. So like, if your class name is MyApp::Model::DB, then your model_name is DB. iconic. ✨

_default_cursor_class

What to reset your cursor_class in DBIx::Class::Storage::DBI to when a custom one just doesn't work out (it happens, we don't talk about it πŸ’€). Defaults to DBIx::Class::Storage::DBI::Cursor.


ATTRIBUTES FROM MooseX::Traits::Pluggable 🌈

_original_class_name

The class name of your model before any traits get applied to it. E.g. MyApp::Model::DB.

_traits

Unresolved arrayref of traits you passed in the config.

_resolved_traits

The traits you actually used, resolved all the way out to their full class names. πŸ’–


CONFIGURING YOUR SCHEMA AND RESULTSETS πŸŽ€

Wanna know how to pass config values from your Catalyst config straight into your DBIx::Class::Schema and/or DBIx::Class::ResultSet classes? Go read the docs for Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy β€” it's all laid out there, no cap.


METHODS πŸ‘‘

new

Instantiates the Model based on the ->config parameters we documented up above. The only required parameter is schema_class. Now β€” connect_info is required in the case that schema_class doesn't already have connection info defined for it. so like, if it's already got connection deets, you're free. πŸ¦‹

schema

Accessor that returns the connected schema this model is using. And bestie, there are direct shortcuts on the model class itself for schema->resultset, schema->source, and schema->class. no extra steps, periodt.

composed_schema

Accessor that returns the composed schema β€” which has no connection info β€” that was used in constructing the schema. Super useful for spinning up new connections based on the same schema/model. And there are direct shortcuts from the model object for composed_schema->clone and composed_schema->connect.

Heads up tho: if compose_namespaces is not true, then composed_schema is equivalent to $model->schema_class->clone. πŸ“Œ

clone

Shortcut for ->composed_schema->clone

connect

Shortcut for ->composed_schema->connect

source

Shortcut for ->schema->source

class

Shortcut for ->schema->class

resultset

Shortcut for ->schema->resultset

txn_do

Shortcut for ->schema->txn_do

txn_scope_guard

Shortcut for ->schema->txn_scope_guard

storage

Provides an accessor for the connected schema's storage object. πŸ—„οΈ

See DBIx::Class::Storage and DBIx::Class::Storage::DBI.

setup

Called at BUILD time, before configuration but after connect_info is set. Want to do something after configuration instead? Use after BUILD =>. πŸ’«

Receives a hashref of args that were passed to BUILD.

ACCEPT_CONTEXT

The point of extension for doing things at $c->model time with context β€” returns the model instance. For more info, see "ACCEPT_CONTEXT" in Catalyst::Manual::Intro. 🫢


ENVIRONMENT 🌸

  • CMDS_NO_SOURCES β€” Set this variable if you're gonna be using schemas with no sources (Result classes), and it'll disable the warning. Why is there a warning at all? Because having no Result classes is usually a mistake, bestie. 😭

Setting up DBIC authentication πŸ”

You can set this whole thing up with Catalyst::Authentication::Store::DBIx::Class in MyApp.pm:

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',
                      }
                  }
              });

METHOD PROXYING 🧚

okay so important lore: the automatic proxying to the underlying DBIx::Class::Schema was removed as of version 0.34. πŸ’€ If you want that feature back, add SchemaProxy to traits.

See Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy.


SEE ALSO πŸ‘€

General Catalyst stuff:

  • Catalyst::Manual
  • Catalyst::Test
  • Catalyst::Request
  • Catalyst::Response
  • Catalyst::Helper
  • Catalyst

Stuff related to DBIC and this Model style:

  • DBIx::Class
  • DBIx::Class::Schema
  • DBIx::Class::Schema::Loader
  • Catalyst::Helper::Model::DBIC::Schema
  • CatalystX::Component::Traits
  • MooseX::Traits::Pluggable

Traits:

  • Catalyst::TraitFor::Model::DBIC::Schema::Caching
  • Catalyst::TraitFor::Model::DBIC::Schema::Replicated
  • Catalyst::TraitFor::Model::DBIC::Schema::SchemaProxy
  • Catalyst::TraitFor::Model::DBIC::Schema::PerRequestSchema
  • Catalyst::TraitFor::Model::DBIC::Schema::QueryLog

AUTHOR ✍️

Brandon L Black blblack at gmail.com

CONTRIBUTORS πŸ’–

  • caelum: Rafael Kitover rkitover at cpan.org
  • dandv: Dan Dascalescu dandv at cpan.org
  • bluefeet: Aran Deltac bluefeet@cpan.org
  • t0m: Tomas Doran bobtfish@bobtfish.net
  • osfameron: osfameron@cpan.org
  • ozum: Ozum Eldogan ozum@ozum.net
  • Pavel I. Shaydo zwon@trinitum.org
  • SineSwiper: Brendan Byrd <byrd.b@insightcom.com>

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

LICENSE πŸŽ€

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


and that's a wrap, bestie!! 🫢 this whole thing shipped with perl v5.36.0 back on 2023-10-26, and yeah the page title got truncated to Cataly...:Schema(3pm) because the manpage world simply cannot handle a long name. iconic behaviour honestly. go build something slay βœ¨πŸ’…

πŸ“¦ where this page comes from