Aniki - The ORM as our great brother.
use 5.014002; package MyProj::DB::Schema { use DBIx::Schema::DSL; create_table 'module' => columns { integer 'id', primary_key, auto_increment; varchar 'name'; integer 'author_id'; add_index 'author_id_idx' => ['author_id']; belongs_to 'author'; }; create_table 'author' => columns { integer 'id', primary_key, auto_increment; varchar 'name', unique; }; }; package MyProj::DB::Filter { use Aniki::Filter::Declare; use Scalar::Util qw/blessed/; use Time::Moment; # define inflate/deflate filters in table context. table author => sub { inflate name => sub { my $name = shift; return uc $name; }; deflate name => sub { my $name = shift; return lc $name; }; }; inflate qr/_at$/ => sub { my $datetime = shift; $datetime =~ tr/ /T/; $datetime .= 'Z'; return Time::Moment->from_string($datetime); }; deflate qr/_at$/ => sub { my $datetime = shift; return $datetime->at_utc->strftime('%F %T') if blessed $datetime and $datetime->isa('Time::Moment'); return $datetime; }; }; package MyProj::DB { use Mouse v2.4.5; extends qw/Aniki/; __PACKAGE__->setup( schema => 'MyProj::DB::Schema', filter => 'MyProj::DB::Filter', row => 'MyProj::DB::Row', ); }; package main { my $db = MyProj::DB->new(connect_info => ["dbi:SQLite:dbname=:memory:", "", ""]); $db->execute($_) for split /;/, MyProj::DB::Schema->output; my $author_id = $db->insert_and_fetch_id(author => { name => 'songmu' }); $db->insert(module => { name => 'DBIx::Schema::DSL', author_id => $author_id, }); $db->insert(module => { name => 'Riji', author_id => $author_id, }); my $module = $db->select(module => { name => 'Riji', }, { limit => 1, })->first; say '$module->name: ', $module->name; ## Riji say '$module->author->name: ', $module->author->name; ## SONGMU my $author = $db->select(author => { name => 'songmu', }, { limit => 1, prefetch => [qw/modules/], })->first; say '$author->name: ', $author->name; ## SONGMU say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji }; 1;
Aniki is ORM. Lite, but powerful.
You can read codes easily.
Inflates rows to Aniki::Result::Collection object. And inflates row to Aniki::Row object.
You can change result class, also we can change row class. Aniki dispatches result/row class by table. (e.g. foo table to MyDB::Row::Foo)
foo
MyDB::Row::Foo
Supports to execute raw SELECT SQL and fetch rows of result. Of course, Aniki can inflate to result/row also.
SELECT
Aniki includes query builder powered by SQL::Maker. SQL::Maker is fast and secure SQL builder.
Aniki includes DBI handler powered by DBIx::Handler.
Easy to handle execution errors by handle_error method. You can override it.
handle_error
You can extend Aniki by Mouse::Role. Aniki provides some default plugins as Mouse::Role.
Aniki supports relationship. Extracts relationship from schema class.
Example:
use 5.014002; package MyProj::DB::Schema { use DBIx::Schema::DSL; create_table 'module' => columns { integer 'id', primary_key, auto_increment; varchar 'name'; integer 'author_id'; add_index 'author_id_idx' => ['author_id']; belongs_to 'author'; }; create_table 'author' => columns { integer 'id', primary_key, auto_increment; varchar 'name', unique; }; };
A author has many modules. So you can access author row object to modules.
author
modules
my $author = $db->select(author => { name => 'songmu' })->first; say 'modules[]->name: ', $_->name for $author->modules; ## DBIx::Schema::DSL, Riji
Also module has a author. So you can access module row object to author also.
module
my $module = $db->select(module => { name => 'Riji' })->first; say "Riji's author is ", $module->author->name; ## SONGMU
And you can pre-fetch related rows.
my @modules = $db->select(module => {}, { prefetch => [qw/author/] }); say $_->name, "'s author is ", $_->author->name for @modules;
Install Aniki from CPAN:
cpanm Aniki
And run install-aniki command.
install-aniki
install-aniki --lib=./lib MyApp::DB
install-aniki creates skeleton modules.
setup(%args)
Initialize and customize Aniki class. schema is required. Others are optional.
schema
use_prepare_cached
If this method returns true value, Aniki uses preare_cached. This method returns true value default. So you don't need to use preare_cached, override it and return false value.
preare_cached
use_strict_query_builder
If this method returns true value, Aniki enables SQL::Maker's strict mode. This method returns true value default. So you need to disable SQL::Maker's strict mode, override it and return false value.
SEE ALSO: The JSON SQL Injection Vulnerability
preload_all_row_classes
Preload all row classes.
preload_all_result_classes
Preload all result classes.
guess_result_class($table_name) : ClassName
Guesses result class by table name.
guess_row_class($table_name) : ClassName
Guesses row class by table name.
new(%args) : Aniki
Create instance of Aniki.
handler : Aniki::Handler
Instance of Aniki::Hanlder. If this argument is given, not required to give connect_info for arguments.
connect_info
connect_info : ArrayRef
Auguments for DBI's connect method.
Execute SQL or CodeRef when connected/disconnected.
Enables to inject a caller information as SQL comment. SEE ALSO: DBIx::Handler
Ignore to inject the SQL comment when trace_ignore_if's return value is true. SEE ALSO: DBIx::Handler
suppress_row_objects : Bool
If this option is true, no create row objects. Aniki's methods returns hash reference instead of row object.
suppress_result_objects : Bool
If this option is true, no create result objects. Aniki's methods returns array reference instead of result object.
select($table_name, \%where, \%opt)
Execute SELECT query by generated SQL, and returns result object.
my $result = $db->select(foo => { id => 1 }, { limit => 1 }); # stmt: SELECT FROM foo WHERE id = ? LIMIT 1 # bind: [1]
There are the options of SELECT query. See also SQL::Maker.
And you can use there options:
If this option is true, no create row objects. This methods returns hash reference instead of row object.
If this option is true, no create result objects. This method returns array reference instead of result object.
columns : ArrayRef[Str]
List for retrieving columns from database.
prefetch : ArrayRef|HashRef
Pre-fetch specified related rows. See also "RELATIONSHIP" section.
select_named($sql, \%bind, \%opt)
select_by_sql($sql, \@bind, \%opt)
Execute SELECT query by specified SQL, and returns result object.
my $result = $db->select_by_sql('SELECT FROM foo WHERE id = ? LIMIT 1', [1]); # stmt: SELECT FROM foo WHERE id = ? LIMIT 1 # bind: [1]
You can use there options:
table_name: Str
This is table name using row/result class guessing.
columns: ArrayRef[Str]
prefetch: ArrayRef|HashRef
insert($table_name, \%values, \%opt)
Execute INSERT INTO query.
INSERT INTO
$db->insert(foo => { bar => 1 }); # stmt: INSERT INTO foo (bar) VALUES (?) # bind: [1]
insert_and_fetch_id($table_name, \%values, \%opt)
Execute INSERT INTO query, and returns last_insert_id.
last_insert_id
my $id = $db->insert_and_fetch_id(foo => { bar => 1 }); # stmt: INSERT INTO foo (bar) VALUES (?) # bind: [1]
insert_and_fetch_row($table_name, \%values, \%opt)
Execute INSERT INTO query, and SELECT it, and returns row object.
my $row = $db->insert_and_fetch_row(foo => { bar => 1 }); # stmt: INSERT INTO foo (bar) VALUES (?) # bind: [1]
insert_and_emulate_row($table_name, \%values, \%opt)
Execute INSERT INTO query, and returns row object created by $row and schema definition.
$row
This method is faster than insert_and_fetch_row.
insert_and_fetch_row
If you use SQL TRIGGER or dynamic default value, this method don't return the correct value, maybe. In this case, you should use insert_and_fetch_row instead of this method.
TRIGGER
insert_on_duplicate($table_name, \%insert, \%update)
Execute INSERT ... ON DUPLICATE KEY UPDATE query for MySQL.
INSERT ... ON DUPLICATE KEY UPDATE
my $row = $db->insert_on_duplicate(foo => { bar => 1 }, { bar => \'VALUE(bar) + 1' }); # stmt: INSERT INTO foo (bar) VALUES (?) ON DUPLICATE KEY UPDATE bar = VALUE(bar) + 1 # bind: [1]
SEE ALSO: INSERT ... ON DUPLICATE KEY UPDATE Syntax
insert_multi($table_name, \@values, \%opts)
Execute INSERT INTO ... (...) VALUES (...), (...), ... query for MySQL. Insert multiple rows at once.
INSERT INTO ... (...) VALUES (...), (...), ...
my $row = $db->insert_multi(foo => [{ bar => 1 }, { bar => 2 }, { bar => 3 }]); # stmt: INSERT INTO foo (bar) VALUES (?),(?),(?) # bind: [1, 2, 3]
SEE ALSO: INSERT Syntax
update($table_name, \%set, \%where)
Execute UPDATE query, and returns changed rows count.
UPDATE
my $count = $db->update(foo => { bar => 2 }, { id => 1 }); # stmt: UPDATE foo SET bar = ? WHERE id = ? # bind: [2, 1]
update($row, \%set)
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first; my $count = $db->update($row => { bar => 2 }); # stmt: UPDATE foo SET bar = ? WHERE id = ? # bind: [2, 1]
delete($table_name, \%where)
Execute DELETE query, and returns changed rows count.
DELETE
my $count = $db->delete(foo => { id => 1 }); # stmt: DELETE FROM foo WHERE id = ? # bind: [1]
delete($row)
my $row = $db->select(foo => { id => 1 }, { limit => 1 })->first; my $count = $db->delete($row); # stmt: DELETE foo WHERE id = ? # bind: [1]
schema : Aniki::Schema
filter : Aniki::Filter
query_builder : Aniki::QueryBuilder
root_row_class : Aniki::Row
root_result_class : Aniki::Result
on_connect_do : CodeRef|ArrayRef[Str]|Str
on_disconnect_do : CodeRef|ArrayRef[Str]|Str
dbh : DBI::db
txn_manager : DBIx::TransactionManager
I need to support documentation and reviewing my english. This module is developed on Github.
Copyright (C) karupanerura.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
karupanerura <karupa@cpan.org>
To install Aniki, copy and paste the appropriate command in to your terminal.
cpanm
CPAN shell
perl -MCPAN -e shell install Aniki
For more information on module installation, please visit the detailed CPAN module installation guide.