DBIx::NinjaORM - Flexible Perl ORM for easy transitions from inline SQL to objects.
Version 2.4.3
Caveat: if you're starting a new project, DBIx::NinjaORM is probably not the right ORM to use. Look instead at DBIx::Class for example, which has a more abstract model and can leverage a nicely normalized database schema.
DBIx::NinjaORM was designed with a few goals in mind:
Allow a progressive introduction of a separate Model layer in a legacy codebase.
Expand objects with data joined from other tables, to do less queries and prevent lazy-loading of ancillary information.
Have a short learning curve.
Let's take the example of a My::Model::Book class that represents a book. You would start My::Model::Book with the following code:
My::Model::Book
package My::Model::Book; use strict; use warnings; use base 'DBIx::NinjaORM'; use DBI; sub static_class_info { my ( $class ) = @_; # Retrieve defaults from DBIx::Ninja->static_class_info(). my $info = $class->SUPER::static_class_info(); # Set mandatory defaults. $info->{'table_name'} = 'books'; $info->{'primary_key_name'} = 'book_id'; $info->{'default_dbh'} = DBI->connect( "dbi:mysql:[database_name]:localhost:3306", "[user]", "[password]", ); # Add optional information. # Allow filtering SELECTs on books.name. $info->{'filtering_fields'} = [ 'name' ]; return $info; } 1;
Inheriting with use base 'DBIx::NinjaORM' and creating sub static_class_info (with a default database handle and a table name) are the only two requirements to have a working model.
use base 'DBIx::NinjaORM'
sub static_class_info
If you have more than one Model class to create, for example My::Model::Book and My::Model::Library, you probably want to create a single class My::Model to hold the defaults and then inherits from that main class.
My::Model::Library
My::Model
package My::Model; use strict; use warnings; use base 'DBIx::NinjaORM'; use DBI; use Cache::Memcached::Fast; sub static_class_info { my ( $class ) = @_; # Retrieve defaults from DBIx::Ninja->static_class_info(). my $info = $class->SUPER::static_class_info(); # Set defaults common to all your objects. $info->{'default_dbh'} = DBI->connect( "dbi:mysql:[database_name]:localhost:3306", "[user]", "[password]", ); $info->{'memcache'} = Cache::Memcached::Fast->new( { servers => [ 'localhost:11211', ], } ); return $info; } 1;
The various classes will then inherit from My::Model, and the inherited defaults will make static_class_info() shorter in the other classes:
static_class_info()
package My::Model::Book; use strict; use warnings; # Inherit from your base model class, not from DBIx::NinjaORM. use base 'My::Model'; sub static_class_info { my ( $class ) = @_; # Retrieve defaults from My::Model. my $info = $class->SUPER::static_class_info(); # Set mandatory defaults for this class. $info->{'table_name'} = 'books'; $info->{'primary_key_name'} = 'book_id'; # Add optional information. # Allow filtering SELECTs on books.name. $info->{'filtering_fields'} = [ 'name' ]; return $info; } 1;
This distribution currently supports:
SQLite
MySQL
PostgreSQL
Please contact me if you need support for another database type, I'm always glad to add extensions if you can help me with testing.
DBIx::NinjaORM is designed with inheritance in mind, and you can subclass most of its public methods to extend or alter its behavior.
This group of method covers the most commonly subclassed methods, with examples and use cases.
Clone the current object and return the clone.
my $cloned_book = $book->clone();
Convenience function to insert or update the object.
If the object has a primary key set, update() is called, otherwise insert() is called. If there's an error, the method with croak with relevant error information.
update()
insert()
$book->commit();
Arguments: (none).
Get the value corresponding to an object's field.
my $book_name = $book->get('name');
This method will croak if you attempt to retrieve a private field. It also detects if the object was retrieved from the database, in which case it has an exhaustive list of the fields that actually exist in the database and it will croak if you attempt to retrieve a field that doesn't exist in the database.
Insert a row corresponding to the data passed as first parameter, and fill the object accordingly upon success.
my $book = My::Model::Book->new(); $book->insert( { name => 'Learning Perl', } );
If you don't need the object afterwards, you can simply do:
My::Model::Book->insert( { name => 'Learning Perl', } );
This method supports the following optional arguments:
overwrite_created
A UNIX timestamp to be used instead of the current time for the value of 'created'.
generated_primary_key_value
A primary key value, in case the underlying table doesn't have an autoincremented primary key.
dbh
A different database handle than the default one specified in static_class_info(), but it has to be writable.
ignore
INSERT IGNORE instead of plain INSERT.
$book->insert( \%data, overwrite_created => $unixtime, generated_primary_key_value => $value, dbh => $dbh, ignore => $boolean, );
new() has two possible uses:
new()
Creating a new empty object
my $object = My::Model::Book->new();
Retrieving a single object from the database.
# Retrieve by ID. my $object = My::Model::Book->new( { id => 3 } ) // die 'Book #3 does not exist'; # Retrieve by unique field. my $object = My::Model::Book->new( { isbn => '9781449303587' } ) // die 'Book with ISBN 9781449303587 does not exist';
When retrieving a single object from the database, the first argument should be a hashref containing the following information to select a single row:
id
The ID for the primary key on the underlying table. id is an alias for the primary key field name.
my $object = My::Model::Book->new( { id => 3 } ) // die 'Book #3 does not exist';
A unique field
Allows passing a unique field and its value, in order to load the corresponding object from the database.
my $object = My::Model::Book->new( { isbn => '9781449303587' } ) // die 'Book with ISBN 9781449303587 does not exist';
Note that unique fields need to be defined in static_class_info(), in the unique_fields key.
unique_fields
This method also supports the following optional arguments, passed in a hash after the filtering criteria above-mentioned:
skip_cache (default: 0)
By default, if cache is enabled with object_cache_time() in static_class_info(), then new attempts to load the object from the cache first. Setting skip_cache to 1 forces the ORM to load the values from the database.
object_cache_time()
new
skip_cache
my $object = My::Model::Book->new( { isbn => '9781449303587' }, skip_cache => 1, ) // die 'Book with ISBN 9781449303587 does not exist';
lock (default: 0)
By default, the underlying row is not locked when retrieving an object via new(). Setting lock to 1 forces the ORM to bypass the cache if any, and to lock the rows in the database as it retrieves them.
lock
my $object = My::Model::Book->new( { isbn => '9781449303587' }, lock => 1, ) // die 'Book with ISBN 9781449303587 does not exist';
Delete in the database the row corresponding to the current object.
$book->remove();
This method accepts the following arguments:
A different database handle from the default specified in static_class_info(). This is particularly useful if you have separate reader/writer databases.
Dispatch of retrieve_list() when objects should not be retrieved from the cache.
See retrieve_list() for the parameters this method accepts.
retrieve_list()
Set fields and values on an object.
$book->set( { name => 'Learning Perl', isbn => '9781449303587', }, );
This method supports the following arguments:
force
Set the properties on the object without going through validate_data().
validate_data()
$book->set( { name => 'Learning Perl', isbn => '9781449303587', }, force => 1, );
This methods sets defaults as well as general information for a specific class. It allows for example indicating what table the objects will be related to, or what database handle to use.
Here's what a typical subclassed static_class_info() would look like:
sub static_class_info { my ( $class ) = @_; # Retrieve defaults coming from higher in the inheritance chain, up # to DBIx::NinjaORM->static_class_info(). my $info = $class->SUPER::static_class_info(); # Set or override information. $info->{'table_name'} = 'books'; $info->{'primary_key_name'} = 'book_id'; $info->{'default_dbh'} = DBI->connect( "dbi:mysql:[database_name]:localhost:3306", "[user]", "[password]", ); # Return the updated information hashref. return $info; }
Here's the full list of the options that can be set or overridden:
default_dbh
The database handle to use when performing queries. The methods that interact with the database always provide a dbh argument to allow using a specific database handle, but setting it here means you won't have to systematically pass that argument.
$info->{'default_dbh'} = DBI->connect( "dbi:mysql:[database_name]:localhost:3306", "[user]", "[password]", );
memcache
Optionally, DBIx::NinjaORM uses memcache to cache objects and queries, in conjunction with the list_cache_time and object_cache_time arguments.
DBIx::NinjaORM
list_cache_time
object_cache_time
If you want to enable the cache features, you can set this to a valid Cache::Memcached object (or a compatible module, such as Cache::Memcached::Fast).
Cache::Memcached
Cache::Memcached::Fast
$info->{'memcache'} = Cache::Memcached::Fast->new( { servers => [ 'localhost:11211', ], } );
table_name
Mandatory, the name of the table that this class will be the interface for.
# Interface with a 'books' table. $info->{'table_name'} = 'books';
primary_key_name
The name of the primary key on the table specified with table_name.
$info->{'primary_key_name'} = 'book_id';
Control the list cache, which is an optional cache system in retrieve_list() to store how search criteria translate into object IDs.
By default it is disabled (with undef), but it is activated by setting it to an integer that represents the cache time in seconds.
undef
# Cache for 10 seconds. $info->{'list_cache_time'} = 10; # Don't cache. $info->{'list_cache_time'} = undef;
A good use case for this would be retrieving a list of books for a given author. We would pass the author ID as a search criteria, and the resulting list of book objects does not change often. Provided that you can tolerate a 1 hour delay for a new book to show up associated with a given author, then it makes sense to set the list_cache_time to 3600 and save most of the queries to find what book otherwise belongs to the author.
Control the object cache, which is an optional cache system in retrieve_list() to store the objects returned and be able to look them up by object ID.
# Cache for 10 seconds. $info->{'object_cache_time'} = 10; # Don't cache. $info->{'object_cache_time'} = undef;
A good use case for this are objects that are expensive to build. You will see more in retrieve_list() on how to cache objects.
The list of unique fields on the object.
Note: DBIx::NinjaORM does not support unique indexes made of more than one field. If you add more than one field in this arrayref, the ORM will treat them as separate unique indexes.
# Declare books.isbn as unique. $info->{'unique_fields'} = [ 'isbn' ]; # Declare books.isbn and books.upc as unique. $info->{'unique_fields'} = [ 'isbn', 'upc' ];
filtering_fields
The list of fields that can be used to filter on in retrieve_list().
# Allow filtering based on the book name and author ID. $info->{'unique_fields'} = [ 'name', 'author_id' ];
readonly_fields
The list of fields that cannot be set directly. They will be populated in retrieve_list, but you won't be able to insert / update / set them directly.
retrieve_list
has_created_field
Indicate whether the table has a field name created to store the UNIX time at which the row was created. Default: 1.
created
# The table doesn't have a 'created' field. $info->{'has_created_field'} = 0;
has_modified_field
Indicate whether the table has a field name modified to store the UNIX time at which the row was modified. Default: 1.
modified
# The table doesn't have a 'modified' field. $info->{'has_modified_field'} = 0;
cache_key_field
By default, the object cache uses the primary key value to make cached objects available to look up, but this allows specifying a different field for that purpose.
For example, you may want to use books.isbn instead of books.book_id to cache objects:
$info->{'cache_key_field'} = 'isbn';
verbose
Add debugging and tracing information, 0 by default.
# Show debugging information for operations on this class. $info->{'verbose'} = 1;
verbose_cache_operations
Add information in the logs regarding cache operations and uses.
Update the row in the database corresponding to the current object, using the primary key and its value on the object.
$book->update( { name => 'Learning Perl', } );
skip_modified_update (default 0)
Do not update the 'modified' field. This is useful if you're using 'modified' to record when was the last time a human changed the row, but you want to exclude automated changes.
restrictions
The update statement is limited using the primary key. This parameter however allows adding extra restrictions on the update. Additional clauses passed here are joined with AND.
$book->update( { author_id => 1234, }, restrictions => { where_clauses => [ 'status != ?' ], where_values => [ 'protected' ], }, );
set
\%data contains the data to update the row with "SET field = value". It is however sometimes necessary to use more complex SETs, such as "SET field = field + value", which is what this parameter allows.
Important: you will need to subclass update() in your model classes and update manually the values upon success (or reload the object), as DBIx::NinjaORM cannot determine the end result of those complex sets on the database side.
$book->update( { name => 'Learning Perl', }, set => { placeholders => [ 'edits = edits + ?' ], values => [ 1 ], } );
Validate the hashref of data passed as first argument. This is used both by insert() and update to check the data before performing databse operations.
update
my $validated_data = $object->validate_data( \%data, ); If there is invalid data, the method will croak with a detail of the error.
Return a Dumper( ) of the current object.
my $string = $book->dump();
Return a hash with the requested key/value pairs based on the list of fields provided.
Note that non-native fields (starting with an underscore) are not allowed. It also protects sensitive fields.
#TODO: allow defining sensitive fields.
my $book_data = $book->flatten_object( [ 'name', 'isbn' ] );
Reload the content of the current object. This always skips the cache.
$book->reload();
Return an arrayref of objects matching all the criteria passed.
This method supports the following filtering criteria in a hashref passed as first argument:
An ID or an arrayref of IDs corresponding to the primary key.
# Retrieve books with ID 1. my $books = My::Model::Book->retrieve_list( { id => 1, } ); # Retrieve books with IDs 1, 2 or 3. my $books = My::Model::Book->retrieve_list( { id => [ 1, 2, 3 ] } );
Field names
A scalar value or an arrayref of values corresponding to a field listed in static_class_info() under either filtering_fields or unique_fields.
# Retrieve books for an author. my $books = My::Model::Book->retrieve_list( { author_id => 12, } ); # Retrieve books by ISBN. my $books = My::Model::Book->retrieve_list( { isbn => [ '9781449313142', '9781449393090', ] } );
Note that you can combine filters (which is the equivalent of AND in SQL) in that hashref:
# Retrieve books by ISBN for a specific author. my $books = My::Model::Book->retrieve_list( { isbn => [ '9781449313142', '9781449393090', ], author_id => 12, } );
Retrieve the data against a different database than the default one specified in static_class_info.
static_class_info
order_by
Specify an ORDER BY clause to sort the objects returned.
my $books = My::Model::Book->retrieve_list( { author_id => 12, }, order_by => 'books.name ASC', );
limit
Limit the number of objects to return.
# Get 10 books from author #12. my $books = My::Model::Book->retrieve_list( { author_id => 12, }, limit => 10, );
query_extensions
Add joins and support different filtering criteria:
where_clauses
An arrayref of clauses to add to WHERE.
where_values
An arrayref of values corresponding to the clauses.
joins
A string specifying JOIN statements.
joined_fields
A string of extra fields to add to the SELECT.
my $books = My::Model::Book->retrieve_list( { id => [ 1, 2, 3 ], }, query_extensions => { where_clauses => [ 'authors.name = ?' ], where_values => [ [ 'Randal L. Schwartz' ] ], joins => 'INNER JOIN authors USING (author_id)', joined_fields => 'authors.name AS _author_name', } );
pagination
Off by default. Paginate the results. You can control the pagination options by setting it to the following hashref:
{ total_count => $total_count, page => $page, page_max => $page_max, per_page => $per_page, }
Additionally, pagination can be set to '1' instead of {} and then the default options will be used.
lock (default 0)
Add a lock to the rows retrieved.
my $books = My::Model::Book->retrieve_list( { id => [ 1, 2, 3 ], }, lock => 1, );
allow_all (default 0)
Retrieve all the rows in the table if no criteria is passed. Off by default to prevent retrieving large tables at once.
# All the books! my $books = My::Model::Book->retrieve_list( {}, allow_all => 1, );
show_queries (default 0)
Set to '1' to see in the logs the queries being performed.
my $books = My::Model::Book->retrieve_list( { id => [ 1, 2, 3 ], }, show_queries => 1, );
allow_subclassing (default 0)
By default, retrieve_list() cannot be subclassed to prevent accidental infinite recursions and breaking the cache features provided by NinjaORM. Typically, if you want to add functionality to how retrieving a group of objects works, you will want to modify retrieve_list_nocache() instead.
retrieve_list_nocache()
If you really need to subclass retrieve_list(), you will then need to set allow_subclassing to 1 in subclassed method's call to its parent, to indicate that you've carefully considered the impact of this and that it is safe.
allow_subclassing
1
Return the name of the field that should be used in the cache key.
my $cache_time = $class->cache_key_field(); my $cache_time = $object->cache_key_field();
Return the default database handle to use with this class.
my $default_dbh = $class->get_default_dbh(); my $default_dbh = $object->get_default_dbh();
Returns the fields that can be used as filtering criteria in retrieve_list().
Notes:
Does not include the primary key.
Includes unique fields.
my $filtering_fields = $class->get_filtering_fields(); my $filtering_fields = $object->get_filtering_fields();
Return the duration for which a list of objects of the current class can be cached.
my $list_cache_time = $class->list_cache_time(); my $list_cache_time = $object->list_cache_time();
Return the memcache object to use with this class.
my $memcache = $class->get_memcache(); my $memcache = $object->get_memcache();
Return the duration for which an object of the current class can be cached.
my $object_cache_time = $class->get_object_cache_time(); my $object_cache_time = $object->get_object_cache_time();
Return the underlying primary key name for the current class or object.
my $primary_key_name = $class->get_primary_key_name(); my $primary_key_name = $object->get_primary_key_name();
Return an arrayref of fields that cannot be modified via set(), update(), or insert().
set()
my $readonly_fields = $class->get_readonly_fields(); my $readonly_fields = $object->get_readonly_fields();
Returns the underlying table name for the current class or object.
my $table_name = $class->get_table_name(); my $table_name = $object->get_table_name();
Return an arrayref of fields that are unique for the underlying table.
Important: this doesn't include the primary key name. To retrieve the name of the primary key, use $class-primary_key_name()>
$class-
my $unique_fields = $class->get_unique_fields(); my $unique_fields = $object->get_unique_fields();
Return a boolean to indicate whether the underlying table has a 'created' field.
my $has_created_field = $class->has_created_field(); my $has_created_field = $object->has_created_field();
Return a boolean to indicate whether the underlying table has a 'modified' field.
my $has_modified_field = $class->has_modified_field(); my $has_modified_field = $object->has_modified_field();
Return the value associated with the primary key for the current object.
my $id = $object->id();
Return if verbosity is enabled.
This method supports two types of verbosity:
general verbosity
Called with no argument, this returns whether code in general will be verbose.
$log->debug( 'This is verbose' ) if $class->is_verbose(); $log->debug( 'This is verbose' ) if $object->is_verbose();
verbosity for a specific type of operations
Called with a specific type of operations as first argument, this returns whether that type of operations will be verbose.
$log->debug( 'Describe cache operation' ) if $class->is_verbose( $operation_type ); $log->debug( 'Describe cache operation' ) if $object->is_verbose( $operation_type );
Currently, the following types of operations are supported:
'cache_operations'
Return a cached version of the information retrieved by static_class_info().
my $static_class_info = $class->cached_static_class_info(); my $static_class_info = $object->cached_static_class_info();
Delete a key from the cache.
my $value = $class->delete_cache( key => $key );
Get a value from the cache.
my $value = $class->get_cache( key => $key );
Return the name of the cache key for an object or a class, given a field name on which a unique constraint exists and the corresponding value.
my $cache_key = $object->get_object_cache_key(); my $cache_key = $class->get_object_cache_key( unique_field => $unique_field, value => $value, );
Invalidate the cached copies of the current object across all the unique keys this object can be referenced with.
$object->invalidate_cached_object();
Dispatch of retrieve_list() when objects should be retrieved from the cache.
Set a value into the cache.
$class->set_cache( key => $key, value => $value, expire_time => $expire_time, );
Those methods are used internally by DBIx::NinjaORM, you should not subclass them.
Assert that there is a database handle, either a specific one passed as first argument to this function (if defined) or the default one specified via static_class_info(), and return it.
my $dbh = $class->assert_dbh(); my $dbh = $object->assert_dbh(); my $dbh = $class->assert_dbh( $custom_dbh ); my $dbh = $object->assert_dbh( $custom_dbh );
Note that this method also supports coderefs that return a DBI::db object when evaluated. That way, if no database connection is needed when running the code, no connection needs to be established.
DBI::db
Create a filtering clause using the field, operator and values passed.
my ( $clause, $clause_values ) = $class->build_filtering_clause( field => $field, operator => $operator, values => $values, );
Helper function that takes a list of fields and converts them into where clauses and values that can be used by retrieve_list().
my ( $where_clauses, $where_values, $filtering_field_keys_passed ) = @{ $class->parse_filtering_criteria( \%filtering_criteria ) };
$filtering_field_keys_passed indicates whether %values had keys matching at least one element of @field. This allows detecting whether any filtering criteria was passed, even if the filtering criteria do not result in WHERE clauses being returned.
When we retrieve fields via SELECT in retrieve_list_nocache(), by convention we use _[table_name]_[field_name] for fields that are not native to the underlying table that the object represents.
This method moves them to $object->{'_table_name'}->{'field_name'} for a cleaner organization inside the object.
$object->reorganize_non_native_fields();
Guillaume Aubert, <aubertg at cpan.org>.
<aubertg at cpan.org>
Please report any bugs or feature requests through the web interface at https://github.com/guillaumeaubert/DBIx-NinjaORM/issues/new. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
You can find documentation for this module with the perldoc command.
perldoc DBIx::NinjaORM
You can also look for information at:
GitHub's request tracker
https://github.com/guillaumeaubert/DBIx-NinjaORM/issues
AnnoCPAN: Annotated CPAN documentation
http://annocpan.org/dist/DBIx-NinjaORM
CPAN Ratings
http://cpanratings.perl.org/d/DBIx-NinjaORM
MetaCPAN
https://metacpan.org/release/DBIx-NinjaORM
Thanks to ThinkGeek (http://www.thinkgeek.com/) and its corporate overlords at Geeknet (http://www.geek.net/), for footing the bill while I write code for them!
Thanks to Kate Kirby <<kate at cpan.org> > for her help with the design of this module, various additions, and pair programming retrieve_list_cache() with me.
<<kate at cpan.org
retrieve_list_cache()
Thanks to Jennifer Pinkham <<jpinkham at cpan.org > > for creating separate cache times for objects and arguments to IDs translations.
<<jpinkham at cpan.org
Thanks to Jamie McCarthy for adding the 'ignore' argument to insert().
Copyright 2009-2013 Guillaume Aubert.
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License version 3 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/
To install DBIx::NinjaORM, copy and paste the appropriate command in to your terminal.
cpanm
cpanm DBIx::NinjaORM
CPAN shell
perl -MCPAN -e shell install DBIx::NinjaORM
For more information on module installation, please visit the detailed CPAN module installation guide.