NAME
DBIx::MoCo - Light & Fast Model Component
SYNOPSIS
# First, set up your db.
package Blog::DataBase;
use base qw(DBIx::MoCo::DataBase);
__PACKAGE__->dsn('dbi:mysql:dbname=blog');
__PACKAGE__->username('test');
__PACKAGE__->password('test');
1;
# Second, create a base class for all models.
package Blog::MoCo;
use base qw 'DBIx::MoCo'; # Inherit DBIx::MoCo
use Blog::DataBase;
__PACKAGE__->db_object('Blog::DataBase');
1;
# Third, create your models.
package Blog::User;
use base qw 'Blog::MoCo';
__PACKAGE__->table('user');
__PACKAGE__->has_many(
entries => 'Blog::Entry',
{ key => 'user_id' }
);
__PACKAGE__->has_many(
bookmarks => 'Blog::Bookmark',
{ key => 'user_id' }
);
1;
package Blog::Entry;
use base qw 'Blog::MoCo';
__PACKAGE__->table('entry');
__PACKAGE__->has_a(
user => 'Blog::User',
{ key => 'user_id' }
);
__PACKAGE__->has_many(
bookmarks => 'Blog::Bookmark',
{ key => 'entry_id' }
);
1;
package Blog::Bookmark;
use base qw 'Blog::MoCo';
__PACKAGE__->table('bookmark');
__PACKAGE__->has_a(
user => 'Blog::User',
{ key => 'user_id' }
);
__PACKAGE__->has_a(
entry => 'Blog::Entry',
{ key => 'entry_id' }
);
1;
# Now, You can use some methods same as in Class::DBI.
# And, all objects are stored in cache automatically.
my $user = Blog::User->retrieve(user_id => 123);
print $user->name;
$user->name('jkontan'); # update db immediately
print $user->name; # jkontan
my $user2 = Blog::User->retrieve(user_id => 123);
# $user is same as $user2
# You can easily get has_many objects array.
my $entries = $user->entries;
my $entries2 = $user->entries;
# $entries is same reference as $entries2
my $entry = $entries->first; # isa Blog::Entry
print $entry->title; # you can use methods in Entry class.
Blog::Entry->create(
user_id => 123,
title => 'new entry!',
);
# $user->entries will be flushed automatically.
my $entries3 = $user->entries;
# $entries3 isnt $entries
print ($entries->last eq $entries2->last); # 1
print ($entries->last eq $entries3->last); # 1
# same instance
# You can delay update/create query to database using session.
DBIx::MoCo->start_session;
$user->name('jkondo'); # not saved now. changed in cache.
print $user->name; # 'jkondo'
$user->save; # update db
print Blog::User->retrieve(123)->name; # 'jkondo'
# Or, update queries will be thrown automatically after ending session.
$user->name('jkontan');
DBIx::MoCo->end_session;
print Blog::User->retrieve(123)->name; # 'jkontan'
DESCRIPTION
Light & Fast Model Component
CACHE ALGORITHM
MoCo caches objects effectively. There are 3 functions to control MoCo's cache. Their functions are called appropriately when some operations are called to a particular object.
Here are the 3 functions.
- store_self_cache
-
Stores self instance for all own possible object ids.
- flush_self_cache
-
Flushes all caches for all own possible object ids.
- flush_belongs_to
-
Flushes all caches whose have has_many arrays including the object.
And, here are the triggers which call their functions.
- _after_create
-
Calls
store_self_cache
andflush_belongs_to
. - _before_update
-
Calls
flush_self_cache
. - _after_update
-
Calls
store_self_cache
. - _before_delete
-
Calls
flush_self_cache
andflush_belongs_to
.
CLASS METHODS
Here are common class methods of DBIx::MoCo.
- add_trigger
-
Adds triggers. Here are the types which called from DBIx::MoCo.
before_create after_create before_update after_update before_delete
You can add your trigger like this.
package Blog::User; __PACKAGE__->add_trigger(before_create => sub my ($class, $args) = @_; $args->{name} .= '-san'; }); # in your scripts my $u = Blog::User->create(name => 'ishizaki'); is ($u->name, 'ishizaki-san'); # ok.
before_create
passes a hash reference of new object data as the second argument, and all other triggers pass the instance$self
. - has_a
-
Defines has_a relationship between 2 models.
- has_many
-
Defines has_many relationship between 2 models. You can define additional conditions as below.
Blog::User->has_many( root_messages => 'Blog::Message', { key => {name => 'to_name'}, condition => 'reference_id is null', order => 'modified desc', }, );
condition
is additional sql statement will be used in where condition.order
is used for specifying order statement. In above case, SQL statement will be ..SELECT message_id FROM message WHERE to_name = 'myname' AND reference_id is null ORDER BY modified desc
And, all each results will be inflated as Blog::Message by retrieving (with using cache) all records again.
- retrieve_keys
-
Defines keys for retrieving by retrieve_all etc. If there aren't any unique keys in your table, please specify these keys.
package Blog::Bookmark; __PACKAGE__->retrieve_keys(['user_id', 'entry_id']); # When user can add multiple bookmarks onto same entry.
- start_session
- end_session
- is_in_session
- cache_status
-
Returns cache status hash reference. cache_status provides retrieve_count, retrieve_cache_count, retrieved_oids retrieve_all_count, has_many_count, has_many_cache_count,
- cache
-
Set or get cache.
- schema
-
Returns DBIx::MoCo::Schema object reference related with your model class.
- primary_keys
- unique_keys
- columns
- has_column(col_name)
-
Returns which the table has the column or not.
- retrieve
- retrieve_or_create
- retrieve_all
- retrieve_all_id_hash
- create
- delete_all
- search
-
You can specify search condition in 3 diferrent ways. "Hash reference style", "Array reference style" and "Scalar style".
Hash reference style is same as SQL::Abstract style and like this.
Blog::User->search(where => {name => 'jkondo'});
Array style is the most flexible. You can use placeholder.
Blog::User->search( where => ['name = ?', 'jkondo'], ); Blog::User->search( where => ['name in (?,?)', 'jkondo', 'cinnamon'], ); Blog::Entry->search( where => ['name = :name and date like :date'], name => 'jkondo', date => '2007-04%'], );
Scalar style is the simplest one, and most flexible in other word.
Blog::Entry->search( where => "name = 'jkondo' and DATE_ADD(date, INTERVAL 1 DAY) > NOW()', );
You can also specify
field
,order
,offset
,limit
,group
too. Full spec search statement will be like the following.Blog::Entry->search( field => 'entry_id', where => ['name = ?', 'jkondo'], order => 'created desc', offset => 0, limit => 1, group => 'title', );
- count
-
Returns the count of results matched with given condition. You can specify the condition in same way as
search
's where spec.Blog::User->count({name => 'jkondo'}); # Hash reference style Blog::User->count(['name => ?', 'jkondo']); # Array reference style Blog::User->count("name => 'jkondo'"); # Scalar style
- find
-
Similar to search, but returns only the first item as a reference (not array).
- retrieve_by_column(_and_column2)
- retrieve_by_column(_and_column2)_or_create
- retrieve_by_column_or_column2
- column_as_something
-
Inflate column value by using DBIx::MoCo::Column::* plugins. If you set up your plugin like this,
package DBIx::MoCo::Column::URI; sub URI { my $self = shift; return URI->new($$self); } sub URI_as_string { my $class = shift; my $uri = shift or return; return $uri->as_string; } 1;
Then, you can use column_as_URI method as following,
my $e = MyEntry->retrieve(..); print $e->uri; # 'http://test.com/test' print $e->uri_as_URI->host; # 'test.com'; my $uri = URI->new('http://www.test.com/test'); $e->uri_as_URI($uri); # set uri by using URI instance
The name of infrate method which will be imported must be same as the package name.
If you don't define "as string" method (such as
URI_as_string
), scalar evaluated value of given argument will be used for new value instead. - has_a, has_many auto generated methods
-
If you define has_a, has_many relationships,
package Blog::Entry; use base qw 'Blog::MoCo'; __PACKAGE__->table('entry'); __PACKAGE__->has_a( user => 'Blog::User', { key => 'user_id' } ); __PACKAGE__->has_many( bookmarks => 'Blog::Bookmark', { key => 'entry_id' } );
You can use those keys as methods.
my $e = Blog::Entry->retrieve(..); print $e->user; # isa Blog::User print $e->bookmarks; # isa ARRAY of Blog::Bookmark
CLASS OR INSTANCE METHODS
Here are common class or instance methods of DBIx::MoCo.
- object_id
- delete
- quote
INSTANCE METHODS
Here are common instance methods of DBIx::MoCo.
- flush_self_cache
-
Flush caches for self possible object ids.
- store_self_cache
-
Store self into cache for possible object ids.
- flush
-
Delete attribute from given attr. name.
- param
-
Set or get attribute from given attr. name.
- set
-
Set attribute which is not related with DB schema or set temporary.
- has_primary_keys
- save
-
Saves changed columns in session.
- object_ids
-
Returns all possible object-ids.
FORM VALIDATION
You can validate user parameters using moco's schema. For example you can define your validation profile using param like this,
package Blog::User;
__PACKAGE__->schema->param([
name => ['NOT_BLANK', 'ASCII', ['DBIC_UNIQUE', 'Blog::User', 'name']],
mail => ['NOT_BLANK', 'EMAIL_LOOSE'],
]);
And then,
# In your scripts
sub validate {
my $self = shift;
my $q = $self->query;
my $prof = Blog::User->schema->param('validation');
my $result = FormValidator::Simple->check($q => $prof);
# handle errors ...
}
SEE ALSO
SQL::Abstract, Class::DBI, Cache,
AUTHOR
Junya Kondo, <http://jkondo.vox.com/>, Naoya Ito, <naoya@hatena.ne.jp>
COPYRIGHT AND LICENSE
Copyright (C) Hatena Inc. All Rights Reserved.
This library is free software; you may redistribute it and/or modify it under the same terms as Perl itself.