A while ago I found out from a post that Dbix::Class
Perls default ORM is abandoned dbixclass not updated. So I started to look around to see what else is out there on metacpan and can be used as an alternative.
This is how I found Rose::DB an ridiculously fast and fun ORM.
In this post I plan to show to you how get started with this.
The first step is to create a DB for this I will use SQLite.
So lets start by installing dependenceies:
# install sqlite3
sudo apt install sqlite3
# verify Installation
sqlite3 --version
cpanm Rose::DB
cpanm Rose::DB::Object
cpanm Rose::DB::Object::Manager
Now create a lib directory an place in it a file namedDB.pm
with the following content:
package DB;
use strict;
use warnings;
use Rose::DB;
use base qw(Rose::DB);
# Use a private registry for this class
__PACKAGE__->use_private_registry;
# Register your lone data source using the default 'main' and domain 'development'
__PACKAGE__->register_db(
domain => 'development',
type => 'main',
driver => 'sqlite',
database => './file.db',
connect_options =>
{
RaiseError => 1,
AutoCommit => 1,
PrintError => 0,
sqlite_trace => 1,
}
);
1;
At this point we can use the DB.pm package to build our database.
So create a db file and to it this content:
use strict;
use warnings;
use lib 'lib';
use DB;
my $db = DB->new(domain => 'development', type => 'main');
my $sql_media_type = <<"SQL";
CREATE TABLE IF NOT EXISTS media_types (
id INTEGER PRIMARY KEY,
media_type VARCHAR(10) NOT NULL
);
SQL
my $sql_license = <<"SQL";
CREATE TABLE IF NOT EXISTS licenses (
id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
allows_commercial BOOLEAN NOT NULL
);
SQL
my $sql_media = <<"SQL";
CREATE TABLE IF NOT EXISTS media (
id INTEGER PRIMARY KEY,
name VARCHAR(255) NOT NULL,
location VARCHAR(255) NOT NULL,
source VARCHAR(511) NOT NULL,
attribution VARCHAR(255) NOT NULL,
media_type_id INTEGER NOT NULL,
license_id INTEGER NOT NULL,
FOREIGN KEY (media_type_id) REFERENCES media_types(id),
FOREIGN KEY (license_id)
REFERENCES licenses(id)
);
SQL
# create db;
$db->dbh->do($sql_media_type) or die $db->dbh->errstr;
$db->dbh->do($sql_license) or die $db->dbh->errstr;
$db->dbh->do($sql_media) or die $db->dbh->errstr;
Now the db is created we need to define the DB objects and insert some data to play around with it. So we need to created this files structure:
── lib
├── DB
├── Licenses.pm
├── Media.pm
└── Media_Types.pm
Each class corresponds to a table and we need to add the code to make this work for each of them. So lets start:
package DB::Media;
use strict;
use warnings;
use DB;
use base qw(Rose::DB::Object);
my $db = DB->new(domain => 'development', type => 'main');
__PACKAGE__->meta->setup
(
table => 'media',
columns => [
id => { type => 'serial', primary_key => 1 },
name => { type => 'varchar', length => 255 },
location => { type => 'varchar', length => 255 },
source => { type => 'varchar', length => 511 },
attribution => { type => 'varchar', length => 255 },
media_type_id => { type => 'integer' },
license_id => { type => 'integer' },
],
relationships => [
media_type => {
type => 'one to one',
class => 'DB::Media_Types',
column_map => { media_type_id => 'id' },
},
licenses => {
type => 'one to one',
class => 'DB::Licenses',
# media.media_id -> license.id
column_map => { license_id => 'id' },
},
]
);
sub init_db { $db };
package DB::Licenses;
use strict;
use warnings;
use DB;
use base qw(Rose::DB::Object);
my $db = DB->new(domain => 'development', type => 'main');
__PACKAGE__->meta->setup
(
table => 'licenses',
columns => [
id => { type => 'serial', primary_key => 1 },
name => { type => 'varchar', length => 255 },
allows_commercial => { type => 'varchar', length => 255 },
],
relationships => [
licenses => {
type => 'one to many',
class => 'DB::Media',
# licenses.id -> media.license_id
column_map => { id => 'license_id' },
},
]
);
sub init_db { $db };
1;
package DB::Media_Types;
use strict;
use warnings;
use DB;
use base qw(Rose::DB::Object);
my $db = DB->new(domain => 'development', type => 'main');
__PACKAGE__->meta->setup
(
table => 'media_types',
columns => [
id => { type => 'serial', primary_key => 1 },
media_type => { type => 'varchar', length => 10 },
],
relationships => [
media_type => {
type => 'one to many',
class => 'DB::Media',
column_map => { id => 'media_type_id' },
},
]
);
sub init_db { $db };
1;
With this step done we can start to insert data into our tables using this script:
use strict;
use warnings;
use lib 'lib';
use DB::Media_Types;
use DB::Licenses;
use DB::Media;
my @types = qw(video audio image);
my %media_type_id_for;
my %license_id_for;
foreach my $media (@types) {
my $media_obj = DB::Media_Types->new(media_type => $media);
$media_obj->save();
$media_type_id_for{$media} = $media_obj->id();
}
my @licenses =
(['Public Domain', 1], ['Attribution CC BY', 1], ['Attribution CC BY-SA', 1], ['Attribution-NonCommercial CC BY-NC', 0],);
foreach my $license (@licenses) {
my $license_obj = DB::Licenses->new(
name => $license->[0],
allows_commercial => $license->[1]
);
$license_obj->save();
$license_id_for{$license->[0]} = $license_obj->id();
}
my @media = ([
'Anne Frank Stamp',
'/data/images/anne_fronk_stamp.jpg',
'http://commons.wikimedia.org/wiki/File:Anne_Frank_stamp.jpg',
'Deutsche Post',
$media_type_id_for{'image'},
$license_id_for{'Public Domain'},
],
[
'Clair de Lune',
'/data/audio/claire_de_lune.ogg',
'http://commons.wikimedia.org/wiki/File:Sonate_Clair_de_lune.ogg',
'Schwarzer Stern',
$media_type_id_for{'audio'},
$license_id_for{'Public Domain'},
],
);
foreach my $media (@media) {
my $media_record_object = DB::Media->new(
name => $media->[0],
location => $media->[1],
source => $media->[2],
attribution => $media->[3],
media_type_id => $media->[4],
license_id => $media->[5],
);
$media_record_object->save();
}
With data inserted into our data base we can now try a select:
use strict;
use warnings;
use Data::Dumper;
use lib 'lib';
use feature 'say';
use DB::Media;
my $media = DB::Media->new(id => 1);
$media->load or die $media->error;
say "Media Name: ", $media->name;
say "Media Type: ", $media->media_type->media_type();
say "Allows_commercial: ", $media->licenses->allows_commercial();
Please notice how the Media object has access to data from media_type and licenses table.
In the background it will run this queries to fetch the data
SELECT id, name, location, source, attribution, media_type_id, license_id FROM media WHERE id = 1
SELECT id, media_type FROM media_types WHERE id = 3
SELECT id, name, allows_commercial FROM licenses WHERE id = 1
How ever the recommended approach is to created an manger class.
package DB::Media::Manager;
use strict;
use warnings;
use base 'Rose::DB::Object::Manager';
sub object_class { 'DB::Media' };
__PACKAGE__->make_manager_methods('media');
1;
This class will open your add to your code the following methods:
get_media
get_media_iterator
get_media_count
update_media
delete_media
This will also allow to create more complicated queries:
my $products = DB::Media::Manager->get_media(
with_objects => [ 'media_type', 'licenses' ],
query => [
't2.id' => 2,
]
);
This will result in this select:
SELECT
t1.id,
t1.name,
t1.location,
t1.source,
t1.attribution,
t1.media_type_id,
t1.license_id,
t2.id,
t2.media_type,
t3.id,
t3.name,
t3.allows_commercial
FROM
media t1
LEFT OUTER JOIN media_types t2 ON
(t1.media_type_id = t2.id)
LEFT OUTER JOIN licenses t3 ON
(t1.license_id = t3.id)
WHERE
t2.id = 2
Bibliography
- Beginning Perl by Curtis 'Ovid' Poe
- Rose::DB
- Rose::DB::Object
It reminds me of my post on similar topic published in the year 2011.
blogs.perl.org/users/mohammad_s_an...