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;