NAME

DBIx::QuickORM - Composable ORM builder.

DESCRIPTION

DBIx::QuickORM allows you to define ORMs with reusable and composible parts.

With this ORM builder you can specify:

SYNOPSIS

The common use case is to create an ORM package for your app, then use that ORM package any place in the app that needs ORM access.

YOUR ORM PACKAGE

MANUAL SCHEMA

package My::ORM;
use DBIx::QuickORM;

# Define your ORM
orm my_orm => sub {
    # Define your object
    db my_db => sub {
        dialect 'PostgreSQL'; # Or MySQL, MariaDB, SQLite
        host 'mydb.mydomain.com';
        port 1234;

        # Best not to hardcode these, read them from a secure place and pass them in here.
        user $USER;
        pass $PASS;
    };

    # Define your schema
    schema myschema => sub {
        table my_table => sub {
            column id => sub {
                identity;
                primary_key;
                not_null;
            };

            column name => sub {
                type \'VARCHAR(128)';    # Exact SQL for the type
                affinity 'string';       # required if other information does not make it obvious to DBIx::QuickORM
                unique;
                not_null;
            };

            column added => sub {
                type 'Stamp';            # Short for DBIx::QuickORM::Type::Stamp
                not_null;

                # Exact SQL to use if DBIx::QuickORM generates the table SQL
                default \'NOW()';

                # Perl code to generate a default value when rows are created by DBIx::QuickORM
                default sub { ... };
            };
        };
    };
};

AUTOMAGIC SCHEMA

package My::ORM;
use DBIx::QuickORM;

# Define your ORM
orm my_orm => sub {
    # Define your object
    db my_db => sub {
        dialect 'PostgreSQL'; # Or MySQL, MariaDB, SQLite
        host 'mydb.mydomain.com';
        port 1234;

        # Best not to hardcode these, read them from a secure place and pass them in here.
        user $USER;
        pass $PASS;
    };

    # Define your schema
    schema myschema => sub {
        # The class name is optional, the one shown here is the default
        autofill 'DBIx::QuickORM::Schema::Autofill' => sub {
            autotype 'UUID';    # Automatically handle UUID fields
            autotype 'JSON';    # Automatically handle JSON fields

            # Do not autofill these tables
            autoskip table => qw/foo bar baz/;

            # Will automatically create My::Row::Table classes for you with
            # accessors for links and fields If My::Table::Row can be
            # loaded (IE My/Row/Table.pm exists) it will load it then
            # autofill anything missing.
            autorow 'My::Row';

            # autorow can also take a subref that accepts a table name as
            # input and provides the class name for it, here is the default
            # one used if none if provided:
            autorow 'My::Row' => sub {
                my $name = shift;
                my @parts = split /_/, $name;
                return join '' => map { ucfirst(lc($_)) } @parts;
            };

            # You can provide custom names for tables. It will still refer
            # to the correct name in queries, but will provide an alternate
            # name for the orm to use in perl code.
            autoname table => sub {
                my %params = @_;
                my $table_hash = $params{table}; # unblessed ref that will become a table
                my $name = $params{name}; # The name of the table
                ...
                return $new_name;
            };

            # You can provide custom names for link (foreign key) accessors when using autorow
            autoname link_accessor => sub {
                my %params = @_;
                my $link = $params{link};

                return "obtain_" . $link->other_table if $params{link}->unique;
                return "select_" . $link->other_table . "s";
            };

            # You can provide custom names for field accessors when using autorow
            autoname field_accessor => sub {
                my %params = @_;
                return "get_$params{name}";
            };
        };
    };
};

YOUR APP CODE

package My::App;
use My::Orm qw/orm/;

# Get a connection to the orm
# Note: This will return the same connection each time, no need to cache it yourself.
# See DBIx::QuickORM::Connection for more info.
my $orm = orm('my_orm');

# See DBIx::QuickORM::Handle for more info.
my $h = $orm->handle('people', {surname => 'smith'});
for my $person ($handle->all) {
    print $person->field('first_name') . "\n"
}

my $new_h = $h->limit(5)->order_by('surname')->omit(@large_fields);
my $iterator = $new_h->iterator; # Query is actually sent to DB here.
while (my $row = $iterator->next) {
    ...
}

# Start an async query
my $async = $h->async->iterator;

while (!$async->ready) {
    do_something_else();
}

while (my $item = $iterator->next) {
    ...
}

See DBIx::QuickORM::Connection for details on the object returned by my $orm = orm('my_orm');.

See DBIx::QuickORM::Handle for more details on handles, which are similar to ResultSets from DBIx::Class.

ESSENTIAL CONCEPTS

Knowing the basics about these concepts will help you understand how DBIx::QuickORM does things.

DIALECTS

A dielect tell DBIx::QuickORM hwo to interact with and communicate with a database. Not all SQL databases use identical syntax or extensions, a dialects job to to define behaviors a specific database understands.

DBIx::QuickORM::Dialect is the base class for dialects. All dialects should subclass this base class.

It is important to pick the dialect that best matches your situation. DBIx::QuickORM::Dialect::MySQL may work for all flavors of MySQL, but DBIx::QuickORM::Dialect::MySQL::MariaDB will work much better if you are using MariaDB, as the dialect will know about additions regular mysql does not support.

SCHEMA

This refers to DBIx::QuickORM::Schema, your entire database schema/structure should be represented in the ORM as a schema structure consisting of DBIx::QuickORM::Schema::Table objects, and others.

AFFINITY

Whenever you define a column in DBIx::QuickORM it is necessary for the ORM to know the affinity of the column. It may be any of these:

Much of the time the affinity can be derived from other data. The DBIx::QuickORM::Affinity package has an internal map for default affinities for many SQL types. Also if you use a class implementing DBIx::QuickORM::Role::Type it will often provide an affinity. You can override the affinity if necessary. If the affinity cannot be derived you must specify it.

CONNECTIONS

There are 2 primary interfaces your application will use to interact with the orm/database. The first of these is the DBIx::QuickORM::Connection object which represents a single connection to the database.

This connection maintains a connection specific copy of the schema, row cache, async status, and transaction state. You can have more than 1 connection to a specific database/schema, or connections to more than one database/schema and they will have completely independent row caches, transaction states, and so on.

Once you have a connection you will primarily use it to create handles that can be used to find or modify rows.

HANDLES

There are 2 primary interfaces your application will use to interact with the orm/database. The second of these is the DBIx::QuickORM::Handle object. This is very similar to the ResultSet object from DBIx::Class, though they are not identical or directly interchangable.

A handle is a combination of an Connection and a query state. The query state must include a Source, and may include things like a WHERE condition, a result limit, order-by directives, and so on.

The Handle object is your primary interface for row actions such as insert(), update(), upsert(), delete(), and fetching 1 or more rows.

ROWS

When you interact with rows in the database they will usually be presented as instances of DBIx::QuickORM::Row or a subclass of it. A row will usually be cached to the Connection that was used to fetch or create it.

See the "DEFINE TABLES IN THEIR OWN PACKAGES/FILES" section for info on how to create per-table row classes, which are probably what you want. Rows classes like this have methods added for all fields defined on the table so that you do can call $val = $row->foo(), and $row->foo($new_val) instead of needing to call $val = $row->field('foo') and $row->field(foo => $new_val).

RECIPES

RENAMING EXPORTS

When importing DBIx::QuickORM you can provide rename => { name => new_name } mapping to rename exports.

package My::ORM;
use DBIx::QuickORM rename => {
    pass  => 'password',
    user  => 'username',
    table => 'build_table',
};

Note If you do not want to bring in the import() method that normally gets produced, you can also add type => 'porcelain'.

use DBIx::QuickORM type => 'porcelain';

Really any 'type' other than 'orm' and undef (which becomes 'orm' by default) will work to prevent import() from being exported to your namespace.

DEFINE TABLES IN THEIR OWN PACKAGES/FILES

If you have many tables, or want each to have a custom row class (custom methods for items returned by tables), then you probably want to define tables in their own files.

When you follow this example you create the table My::ORM::Table::Foo. The package will automatically subclass DBIx::QuickORM::Row unless you use row_class() to set an alternative base.

Any methods added in the file will be callable on the rows returned when querying this table.

First create My/ORM/Table/Foo.pm:

package My::ORM::Table::Foo;
use DBIx::QuickORM type => 'table';

# Calling this will define the table. It will also:
#  * Remove all functions imported from DBIx::QuickORM
#  * Set the base class to DBIx::QuickORM::Row, or to whatever class you specify with 'row_class'.
table foo => sub {
    column a => sub { ... };
    column b => sub { ... };
    column c => sub { ... };

    ....

    # This is the default, but you can change it to set an alternate base class.
    row_class 'DBIx::QuickORM::Row';
};

sub custom_row_method {
    my $self = shift;
    ...
}

Then in your ORM package:

package My::ORM;

schema my_schema => sub {
    table 'My::ORM::Table::Foo'; # Bring in the table
};

Or if you have many tables and want to load all the tables under My::ORM::Table:: at once:

schema my_schema => sub {
    tables 'My::ORM::Table';
};

APP THAT CAN USE NEARLY IDENTICAL MYSQL AND POSTGRESQL DATABASES

Lets say you have a test app that can connect to nearly identical MySQL or PostgreSQL databases. The schemas are the same apart from minor differences required by the database engine. You want to make it easy to access whichever one you want, or even both.

package My::ORM;
use DBIx::QuickORM;

orm my_orm => sub {
    db myapp => sub {
        alt mysql => sub {
            dialect 'MySQL';
            driver '+DBD::mysql';     # Or 'mysql', '+DBD::MariaDB', 'MariaDB'
            host 'mysql.myapp.com';
            user $MYSQL_USER;
            pass $MYSQL_PASS;
            db_name 'myapp_mysql';    # In MySQL the db is named myapp_mysql
        };
        alt pgsql => sub {
            dialect 'PostgreSQL';
            host 'pgsql.myapp.com';
            user $PGSQL_USER;
            pass $PGSQL_PASS;
            db_name 'myapp_pgsql';    # In PostgreSQL the db is named myapp_pgsql
        };
    };

    schema my_schema => sub {
        table same_on_both => sub { ... };

        # Give the name 'differs' that can always be used to refer to this table, despite each db giving it a different name
        table differs => sub {
            # Each db has a different name for the table
            alt mysql => sub { db_name 'differs_mysql' };
            alt pgsql => sub { db_name 'differs_pgsql' };

            # Name for the column that the code can always use regardless of which db is in use
            column foo => sub {
                # Each db also names this column differently
                alt mysql => sub { db_name 'foo_mysql' };
                alt pgsql => sub { db_name 'foo_pgsql' };
                ...;
            };

            ...;
        };
    };
};

Then to use it:

use My::ORM;

my $orm_mysql = orm('my_orm:mysql');
my $orm_pgsql = orm('my_orm:pgsql');

Each ORM object is a complete and self-contained ORM with its own caching and db connection. One connects to MySQL and one connects to PostgreSQL. Both can ask for rows in the differs table, on MySQL it will query the differs_mysql, on PostgreSQL it will query the differs_pgsql table. You can use them both at the same time in the same code.

ADVANCED COMPOSING

You can define databases and schemas on their own and create multiple ORMs that combine them. You can also define a server that has multiple databases.

package My::ORM;
use DBIx::QuickORM;

server pg => sub {
    dialect 'PostgreSQL';
    host 'pg.myapp.com';
    user $USER;
    pass $PASS;

    db 'myapp';       # Points at the 'myapp' database on this db server
    db 'otherapp';    # Points at the 'otherapp' database on this db server
};

schema myapp => sub { ... };
schema otherapp => sub { ... };

orm myapp => sub {
    db 'pg.myapp';
    schema 'myapp';
};

orm otherapp => sub {
    db 'pg.otherapp';
    schema 'otherapp';
};

Then to use them:

use My::ORM;

my $myapp    = orm('myapp');
my $otherapp = orm('otherapp');

Also note that alt(variant => sub { ... }) can be used in any of the above builders to create MySQL/PostgreSQL/etc. variants on the databases and schemas. Then access them like:

my $myapp_pgsql = orm('myapp:pgsql');
my $myapp_mysql = orm('myapp:myql');

ORM BUILDER EXPORTS

You get all these when using DBIx::QuickORM.

YOUR ORM PACKAGE EXPORTS

RENAMING THE EXPORT

You can rename the orm() function at import time by providing an alternate name.

use My::ORM qw/renamed_orm/;

my $orm = renamed_orm('my_orm');