The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

DBIx::Skinny::Manual::EN::Intro - Intro to DBIx::Skinny

DESCRIPTION

Caution

DBIx::Skinny runs on MySQL, SQLite, PostgreSQL and Oracle.

However it is designed to be extensible to other databases.

Be aware that APIs may change - this is alpha software!

Base Class Defintions

Let's work with an example project called Proj. Your Skinny model for this project would be:

    package Proj::Model;
    use DBIx::Skinny conect_info => +{
        dsn => 'dbi:SQLite:',
        username => '',
        password => '',
    };
    1;

DSN file usage

If you want to directly use DSN files then you can create your model like this:

    package Proj::Model;
    use DBIx::Skinny;
    1;

->connect_info

    Proj::Model->connect_info(....); 

->connect

    Proj::Model->connect(....);

Proj::Model->new($connection_info)

    my $model = Proj::Model->new($connection_info); 

->set_dbh

    Proj::Model->set_dbh($dbh);

Schema class definition

The recommended way to define your schema classes is automatically via DBIx::Skinny::Schema::Loader. However, read on for info about manual setup.

    package Proj::Model::Schema;
    use DBIx::Skinny::Schema;
    
    install_table 'user' => schema {
        pk 'id';
        columns qw/
            id guid login_id login_pw name mail created_at updated_at
        /;
    };
    1;

Unlike most OR mappers which require a class for every table, in Skinny you can install all your tables in a single class.

Dealing with multi-byte (utf-8) values for a column

Automatic processing of columns is achieved via install_utf8_columns:

    package Proj::Model::Schema;
    use DBIx::Skinny::Schema;
    
    install_utf8_columns qw/name/;
    install_table 'user' => schema {
        pk 'id';
        columns qw/
            id guid login_id login_pw name mail created_at updated_at
        /;
    };
    1;

Note that the name column will be processed automatically for all tables, not just the user table.

Inflate/deflate Processing

To enable inflate/deflate processing on the columns created_at and updated_at you can do the following:

    package Proj::Model::Schema;
    use DBIx::Skinny::Schema;
    use DateTime;
    use DateTime::Format::Strptime;
    use DateTime::Format::MySQL;
    use DateTime::TimeZone;
    
    my $timezone = DateTime::TimeZone->new(name => 'Asia/Tokyo');
    install_inflate_rule '^.+_at$' => callback {
        inflate {
            my $value = shift;
            my $dt = DateTime::Format::Strptime->new(
                pattern   => '%Y-%m-%d %H:%M:%S',
                time_zone => $timezone,
            )->parse_datetime($value);
            return DateTime->from_object( object => $dt );
        };
        deflate {
            my $value = shift;
            return DateTime::Format::MySQL->format_datetime($value);
        };
    };
    
    install_table 'user' => schema {
        pk 'id';
        columns qw/
            id guid login_id login_pw name mail created_at updated_at
        /;
    };
    1;

Triggers

Skinny supports insert/update/delete triggers:

    package Proj::Model::Schema;
    use DBIx::Skinny::Schema;
    use DateTime;
    
    install_table 'user' => schema {
        pk 'id';
        columns qw/
            id guid login_id login_pw name mail created_at updated_at
        /;
        trigger pre_insert => sub {
            my ( $class, $args ) = @_;
            $args->{created_at} ||= DateTime->now;
        };
    };
    1;

The available triggers are:

pre_insert, post_insert, pre_update, post_update, pre_delete, post_delete

Triggers are stackable, meaning you can have several triggers of the same type and they will fire in the order of definition.

new

    my $model = Proj::Model->new;
    $model->do();



    Proj::Model->do()

connection_info / connect /reconnect / set_dbh

connection_info

connect_info is used to set the database connection info:

    Proj::Model->connection_info({
        dsn      => 'dbi:mysql:test',
        username => 'username',
        password => 'password'
        connect_options => +{
            RaiseError => 1,
            PrintError => 0,
            AutoCommit => 1,
        },
    });

The default connect_options are:

RaiseError: 1

PrintError: 0

AutoCommit: 1

connect

If you want to explictly connect to the database then:

    Proj::Model->connect({
        dsn      => 'dbi:mysql:test',
        username => 'username',
        password => 'password'
        connect_options => +{
            RaiseError => 1,
            PrintError => 0,
            AutoCommit => 1,
        },
    });

reconnect

    Proj::Model->reconnect({
        dsn      => 'dbi:mysql:test',
        username => 'username',
        password => 'password'
        connect_options => +{
            RaiseError => 1,
            PrintError => 0,
            AutoCommit => 1,
        },
    });

set_dbh

    Proj::Model->set_dbh($dbh);

dbh

    my $dbh = Proj::Model->dbh;

do

Model->do is a shortcut for $dbh->do

    Proj::Model->do(q{
        CREATE TABLE foo (
            id   INT,
            name TEXT
        )
    });

insert / create

To insert records into a table:

    my $row = Proj::Model->insert('user',{
        name => 'nekokak',
        mail => 'nekokak _at_ gmail.com',
    });

->insert() returns a DBIx::Skinny::Row> class, so you can access columns like so:

    print $row->name; # nekokak
    print $row->mail; # nekokak _at_ gmail.com

    my $row = Proj::Model->create('user',{
        name => 'nekokak',
        mail => 'nekokak _at_ gmail.com',
    });

update ( $table, $values_href, $where )

    Proj::Model->update('user', {name => 'yappo'}, {id => 1})

You can also call the ->update method on the Row object returned from insert:

    my $row = Proj::Model->insert('user',{
        name => 'nekokak',
        mail => 'nekokak _at_ gmail.com',
    });
    $row->update({name => 'yappo'});

delete ( $table, $where )

    Proj::Model->delete('user', {id => 1});

    my $row = Proj::Model->insert('user',{
        name => 'nekokak',
        mail => 'nekokak _at_ gmail.com',
    });
    $row->delete;

bulk_insert

To insert multiple rows at once:

    Proj::Model->bulk_insert('user',
        [
            {
                name => 'nekokak',
                mail => 'nekokak _at_ gmail.com',
            },
            {
                name => 'yappo',
                mail => 'yappo _at_ example.com',
            },
        ]
    );

NOTE: triggers are not supported with bulk inserts

find_or_create / find_or_insert

    my $row = Proj::Model->find_or_create('user',{
        name => 'nekokak',
        mail => 'nekokak _at_ gmail.com',
    });


    my $row = Proj::Model->find_or_insert('user',{
        name => 'nekokak',
        mail => 'nekokak _at_ gmail.com',
    });

single / search / search_named /search_by_sql / count

single

Used when you want a single row:

    my $row = Proj::Model->single('user',{name => 'nekokak'});

search ( $table, $where, $limit_offset )

    my $itr = Proj::Model->search('user',
        {
            name => 'nekokak',
        },
        { }
    );

See DBIx::Class::Manual::EN::Resultset for more information on the $where clause.

search_named ( $sql, $placeholder_bindings, $sprintf_bindings )

This method supports search with named placeholders:

    my $itr = Proj::Model->search_named(q{SELECT * FROM user WHERE id > :id}, {id => 1});

It is also possible to interpolate data with sprintf control strings:

    my $itr = Proj::Model->search_named(q{SELECT * FROM user WHERE id > :id LIMIT %s}, {id => 1}, [10]);

A fourth argument can be used to specify a table for query options. (???)

    my $itr = Proj::Model->search_named(q{SELECT * FROM user WHERE id > :id LIMIT %s}, {id => 1}, [10], 'user');

search_by_sql

Typical DBI-style placeholders:

    my $itr = Proj::Model->search_by_sql(q{SELECT * FROM user WHERE id = ?}, [1], 'user');

count ( $table, $count_column, $where )

    my $count = Porj::Model->count('user' , 'id', {name => 'nekokak'});

resultset

Please see DBIx::Skinny::Manual::EN::Resultset

Transactions

Transactions are easy in Skinny:

    my $txn = Proj::Model->txn_scope;
    
    my $row = Proj::Model->single('user', {id => 1});
    $row->set({name => 'nekokak'});
    $row->update;
    
    $txn->commit;

Here's another way:

    Proj::Model->txn_begin;
    
    my $row = Proj::Model->single('user', {id => 1});
    $row->set({name => 'nekokak'});
    $row->update;
    
    Proj::Model->txn_commit;
    Proj::Model->txn_end;

It is expected that the database support transactions. For MySQL this means using the InnoDB engine.

Mixing in methods

Class level

    package Proj::Model;
    use DBIx::Skinny;
    use DBIx::Skinny::Mixin modules => ['+Mixin::Foo'];
    1;

    package Mixin::Foo;
    sub register_method {
        +{
            foo => sub { 'foo' },
        };
    }

Now

    Proj::Model->foo;

is possible

Row object level

    package Proj::Model::Row::User;
    use strict;
    use warnings;
    use utf8;
    use base 'DBIx::Skinny::Row';
    sub foo {
        say 'foo';
    }
    1;

Now

    $row->foo;

is possible.

Here's a more practical example:

    package Proj::Model::Row::User;
    use base 'DBIx::Skinny::Row';
    sub blogs {
        my $self = shift;
        $self->{skinny}->search('blog',{user_id => $self->id});
    }

Now you can call:

    $user->blogs;