NAME

ActiveRecord::Simple

DESCRIPTION

ActiveRecord::Simple is a simple lightweight implementation of ActiveRecord pattern. It's fast, very simple and very light.

SYNOPSIS

# easy way:

package MyModel:Person;
use base 'ActiveRecord::Simple';

__PACKAGE__->auto_load()

1;

# hardcore:

package MyModel::Person;
use base 'ActiveRecord::Simple';

__PACKAGE__->table_name('persons');
__PACKAGE__->fields(
    id_person => {
        data_type => 'int',
        is_auto_increment => 1,
        is_primary_key => 1
    },
    first_name => {
        data_type => 'varchar',
        size => 64,
        is_nullable => 0
    },
    second_name => {
        data_type => 'varchar',
        size => 64,
        is_nullable => 0,
    },
    registered => {
        data_type => 'timestamp',
        is_nullable => 0,
    });
__PACKAGE__->primary_key('id_person');

That's it! Now you're ready to use your active-record class in the application:

use MyModel::Person;

# to create a new record:
my $person = MyModel::Person->new({ name => 'Foo', registered => \'NOW()' })->save();
# (use a scalarref to pass non-quoted data to the database, as is).

# to update the record:
$person->name('Bar')->save();

# to get the record (using primary key):
my $person = MyModel::Person->get(1);

# to get the record with specified fields:
my $person = MyModel::Person->find(1)->only('first_name', 'second_name')->fetch;

# to find records by parameters:
my @persons = MyModel::Person->find({ first_name => 'Foo' })->fetch();

# to find records by sql-condition:
my @persons = MyModel::Person->find('first_name = ?', 'Foo')->fetch();

# also you can do something like this:
my $persons = MyModel::Person->find('first_name = ?', 'Foo');
while ( my $person = $persons->next() ) {
    say $person->name;
}

# You can add any relationships to your tables:
__PACKAGE__->has_many(cars => 'MyModel::Car' => 'id_preson');
__PACKAGE__->belongs_to(wife => 'MyModel::Wife' => 'id_person');

# And then, you're ready to go:
say $person->cars->fetch->id; # if the relation is one to many
say $person->wife->name; # if the relation is one to one
$person->wife(Wife->new({ name => 'Jane', age => '18' })->save)->save; # change wife ;-)

METHODS

ActiveRecord::Simple provides a variety of techniques to make your work with data little easier. It contains set of operations, such as search, create, update and delete data.

If you realy need more complicated solution, just try to expand on it with your own methods.

Class Methods

Class methods mean that you can't do something with a separate row of the table, but they need to manipulate of the table as a whole object. You may find a row in the table or keep database handler etc.

new

Creates a new object, one row of the data.

MyModel::Person->new({ name => 'Foo', second_name => 'Bar' });

It's a constructor of your class and it doesn't save a data in the database, just creates a new record in memory.

You can pass as a parameter related object, ActiveRecord::Simple will do the rest:

my $Adam = Customer->find({name => 'Adam'})->fetch;

my $order = Order->new(sum => 100, customer => $Adam);
### This is the same:
my $order = Order->new(sum => 100, customer_id => $Adam->id);
### but here you have to know primary and foreign keys.

### much easier using objects:
my $order = Order->new(sum => 100, customer => $Adam); # ARS will find all keys automatically

columns

__PACKAGE__->columns([qw/id_person first_name second_name]);
# or
__PACKAGE__->columns('id_person', 'first_name', 'second_name');
# or
__PACKAGE__->columns({
    id_person => {
        # ...
    },
    first_name => {
        # ...
    },
    second_name => {
        # ...
    }
});
# or
__PACKAGE__->columns(
    id_person => {
        # ...
    },
    first_name => {
        # ...
    },
    second_name => {
        # ...
    }
);

This method is required. Set names of the table columns and add accessors to object of the class. If you set a hash or a hashref with additional parameters, the method will be dispatched to another method, "fields".

fields

__PACKAGE__->fields(
    id_person => {
        data_type => 'int',
        is_auto_increment => 1,
        is_primary_key => 1
    },
    first_name => {
        data_type => 'varchar',
        size => 64,
        is_nullable => 0
    },
    second_name => {
        data_type => 'varchar',
        size => 64,
        is_nullable => 0,
    }
);

This method requires SQL::Translator to be installed. Create SQL-Schema and data type validation for each specified field using SQL::Translator features. You don't need to call "columns" method explicitly, if you use "fields".

See SQL::Translator for more information about schema and SQL::Translator::Field for information about available data types.

mixins

Use this method when you need to add optional fields, computed fields etc. Method takes hash, key is a name of field, value is a subroutine that returns SQL:

__PACKAGE__->mixins(
    sum_of_items => sub {

        return 'SUM(`item`)';
    }
);

# specify mixin as a field in the query:
my @items = Model->find->fields('id', 'name', 'sum_of_items')->fetch;

primary_key

__PACKAGE__->primary_key('id_person');

Set name of the primary key. This method is not required to use in the child (your model) classes.

secondary_key

__PACKAGE__->secondary_key('some_id');

If you don't need to use primary key, but need to insert or update data, using specific parameters, you can try this one: secondary key. It doesn't reflect schema, it's just about the code.

index

__PACKAGE__->index('index_id_person', ['id_person']);

Create an index and add it to the schema. Works only when method "fields" is using.

table_name

__PACKAGE__->table_name('persons');

Set name of the table. This method is required to use in the child (your model) classes.

auto_load

Load table info using DBI methods: table_name, primary_key, foreign_key, columns

load_info

Same as "auto_load". DEPRECATED.

belongs_to

__PACKAGE__->belongs_to(home => 'Home');

This method describes one-to-one objects relationship. By default ARS think that primary key name is "id", foreign key name is "[table_name]_id". You can specify it by parameters:

__PACKAGE__->belongs_to(home => 'Home', {
    primary_key => 'id',
    foreign_key => 'home_id'
});

has_many

__PACKAGE__->has_many(cars => 'Car');
__PACKAGE__->has_many(cars => 'Car', {
    primary_key => 'id',
    foreign_key => 'car_id'
})

This method describes one-to-many objects relationship.

has_one

__PACKAGE__->has_one(wife => 'Wife');
__PACKAGE__->has_one(wife => 'Wife', {
    primary_key => 'id',
    foreign_key => 'wife_id'
});

You can specify one object via another one using "has_one" method. It works like that:

say $person->wife->name; # SELECT name FROM Wife WHERE person_id = $self._primary_key

relations

__PACKAGE__->relations({
    cars => {
        class => 'MyModel::Car',
        key   => 'id_person',
        type  => 'many'
    },
});

It's not a required method and you don't have to use it if you don't want to use any relationships in your tables and objects. However, if you need to, just keep this simple schema in youre mind:

__PACKAGE__->relations({
    [relation key] => {
        class => [class name],
        key   => [column that refferers to the table],
        type  => [many or one]
    },
})

[relation key] - this is a key that will be provide the access to instance
of the another class (which is specified in the option "class" below),
associated with this relationship. Allowed to use as many keys as you need:

$package_instance->[relation key]->[any method from the related class];

generic

__PACKAGE__->generic(photos => { release_date => 'pub_date' });

Creates a generic relations.

my $single = Song->find({ type => 'single' })->fetch();
my @photos = $single->photos->fetch();  # fetch all photos with pub_date = single.release_date

auto_save

This method provides two features:

1. Check the changes of object's data before saving in the database.
   Won't save if data didn't change.

2. Automatic save on object destroy (You don't need use "save()" method
   anymore).

 __PACKAGE__->auto_save;

use_smart_saving

Same as "auto_save". DEPRECATED.

find

There are several ways to find someone in your database using ActiveRecord::Simple:

# by "nothing"
# just leave attributes blank to recieve all rows from the database:
my @all_persons = MyModel::Person->find->fetch;

# by primary key:
my $person = MyModel::Person->find(1)->fetch;

# by multiple primary keys
my @persons = MyModel::Person->find([1, 2, 5])->fetch;

# by simple condition:
my @persons = MyModel::Person->find({ name => 'Foo' })->fetch;

# by where-condtions:
my @persons = MyModel::Person->find('first_name = ? and id_person > ?', 'Foo', 1);

If you want to get a few instances by primary keys, you should put it as arrayref, and then fetch from resultset:

my @persons = MyModel::Person->find([1, 2])->fetch();

# you don't have to fetch it immidiatly, of course:
my $resultset = MyModel::Person->find([1, 2]);
while ( my $person = $resultset->fetch() ) {
    say $person->first_name;
}

To find some rows by simple condition, use a hashref:

my @persons = MyModel::Person->find({ first_name => 'Foo' })->fetch();

Simple condition means that you can use only this type of it:

{ first_name => 'Foo' } goes to "first_type = 'Foo'";
{ first_name => 'Foo', id_person => 1 } goes to "first_type = 'Foo' and id_person = 1";

If you want to use a real sql where-condition:

my $res = MyModel::Person->find('first_name = ? or id_person > ?', 'Foo', 1);
# select * from persons where first_name = "Foo" or id_person > 1;

You can use the ordering of results, such as ORDER BY, ASC and DESC:

my @persons = MyModel::Person->find('age > ?', 21)->order_by('name')->desc->fetch;
my @persons = MyModel::Person->find('age > ?', 21)->order_by('name', 'age')->fetch;
my @persons = MyModel::Person->find->order_by('age')->desc->order_by('id')->asc->fetch;

You can pass objects as a parameters. In this case parameter name is the name of relation. For example:

package Person;

# some declarations here

__PACKAGE__->has_many(orders => Order);

# ...

package Order;

# some declarations here

__PACKAGE__->belongs_to(person => Person);

Now, get person:

my $Bill = Person->find({ name => 'Bill' })->fetch;

### .. and get all his orders:
my @bills_orders = Order->find({ customer => $Bill })->fetch;

### the same, but not so cool:
my @bills_orders = Order->find({ customer_id => $Bill->id })->fetch;

fetch

When you use the "find" method to get a few rows from the table, you get the meta-object with a several objects inside. To use all of them or only a part, use the "fetch" method:

my @persons = MyModel::Person->find('id_person != ?', 1)->fetch();

You can also specify how many objects you want to use at a time:

my @persons = MyModel::Person->find('id_person != ?', 1)->fetch(2);
# fetching only 2 objects.

Another syntax of command "fetch" allows you to make read-only objects:

my @persons = MyModel::Person->find->fetch({ read_only => 1, limit => 2 });
# all two object are read-only

select

Yet another way to select data from the database:

my $criteria = { name => 'Bill' };
my $select_options = { order_by => 'id', only => ['name', 'age', 'id'] };

my @bills = Person->select($criteria, $select_options);

upload

Loads fetched object into the variable:

my $finder = Person->find({ name => 'Bill' }); # now $finder isa ARS::Find
# you can continue using this variable as an ARS::Find object:
$finder->order_by('age');
$finder->with('orders');
# now, insted of creating yet another variable like this:
my $persons = $finder->fetch;
# .. you just upload the result into $finder:
$finder->upload; # now $finder isa Person

count

Returns count of records that match the rule:

say MyModel::Person->find->count;
say MyModel::Person->find({ zip => '12345' })->count;
say MyModel::Person->find('age > ?', 55)->count;
say MyModel::Person->find({city => City->find({ name => 'NY' })->fetch })->count;

exists

Returns 1 if record is exists in database:

say "Exists" if MyModel::Person->find({ zip => '12345' })->count;
say "Exists" if MyModel::Person->find('age > ?', 55)->count;

first

Returns the first record (records) ordered by the primary key:

my $first_person = MyModel::Person->find->first;
my @ten_persons  = MyModel::Person->find->first(10);

last

Returns the last record (records) ordered by the primary key:

my $last_person = MyModel::Person->find->last;
my @ten_persons = MyModel::Person->find->last(10);

increment

Increment the field value:

my $person = MyModel::Person->get(1);
say $person->age;  # prints e.g. 99
$person->increment('age');
say $person->age; # prints 100

decrement

Decrement the field value:

my $person = MyModel::Person->get(1);
say $person->age;  # prints e.g. 100
$person->decrement('age');
say $person->age; # prints 99

as_sql

say MyModel::Person->as_sql('PostgreSQL');

This method requires SQL::Translator to be installed. Create an SQL-schema using method "fields". See SQL::Translator for more details.

dbh

Keeps a database connection handler. It's not a class method actually, this is an attribute of the base class and you can put your database handler in any class:

Person->dbh($dbh);

Or even rigth in base class:

ActiveRecord::Simple->dbh($dht);

This decision is up to you. Anyway, this is a singleton value, and keeps only once at the session.

connect

Creates connection to the database and shares with child classes. Simple to use:

package MyModel;

use parent 'ActiveRecord::Simple';
__PACKAGE__->connect(...);

... and then:

package MyModel::Product;

use parent 'MyModel';

... and then:

my @products = MyModel::Product->find->fetch; ## you don't need to set dbh() anymore!

with

Left outer join.

my $artist = MyModel::Artist->find(1)->with('manager')->fetch;
say $person->name; # persons.name in DB
say $rerson->manager->name; managers.name in DB

The method can take a list of parameters:

my $person = MyModel::Person->find(1)->with('car', 'home', 'dog')->fetch;
say $person->name;
say $person->dog->name;
say $person->home->addres;

This method allows to use just one request to the database (using left outer join) to create the main object with all relations. For example, without "with":

my $person = MyModel::Person->find(1)->fetch; # Request no 1:
# select * from persons where id = ?
say $person->name; # no requests, becouse the objects is loaded already

say $person->dog->name; # request no 2. (to create Dog object):
# select * from dogs where person_id = ?
say $person->dog->burk; # no requests, the object Dog is loaded too

Using "with":

my $person = MyModel::Person->find(1)->with('dog')->fetch; # Just one request:
# select * fom persons left join dogs on dogs.person_id = perosn.id
#     where person.id = ?

say $person->name; # no requests
say $person->dog->name; # no requests too! The object Dog was loaded by "with"

left_join

Same as "with" method.

only

Get only those fields that are needed:

my $person = MyModel::Person->find({ name => 'Alex' })->only('address', 'email')->fetch;
### SQL:
###     SELECT `address`, `email` from `persons` where `name` = "Alex";

get

This is shortcut method for "find":

my $person = MyModel::Person->get(1);
### is the same:
my $person = MyModel::Person->find(1)->fetch;

order_by

Order your results by specified fields:

my @persons = MyModel::Person->find({ city => 'NY' })->order_by('name')->fetch();

This method uses as many fields as you want:

my @fields = ('name', 'age', 'zip');
my @persons = MyModel::Person->find({ city => 'NY' })->order_by(@fields)->fetch();

Use chain "order_by" if you would like to order your data in different ways:

my @persons = Model->find->order_by('name', 'age')->asc->order_by('zip')->desc->fetch;
# This is equal to ... ORDER BY name, age ASC, zip DESC;

asc

Use this attribute to order your results ascending:

MyModel::Person->find([1, 3, 5, 2])->order_by('id')->asc->fetch();

desc

Use this attribute to order your results descending:

MyModel::Person->find([1, 3, 5, 2])->order_by('id')->desc->fetch();

limit

Use this attribute to limit results of your requests:

MyModel::Person->find()->limit(10)->fetch; # select only 10 rows

offset

Offset of results:

MyModel::Person->find()->offset(10)->fetch; # all next after 10 rows

group_by

Group by specified fields:

Model->find->group_by('name')->fetch;

Object Methods

Object methods are intended for management of each row of your table separately as an object.

save

To insert or update data in the table, use only one method. It detects automatically what do you want to do with it. If your object was created by the new method and never has been saved before, method will insert your data.

If you took the object using the find method, "save" will mean "update".

my $person = MyModel::Person->new({
    first_name  => 'Foo',
    second_name => 'Bar',
});

$person->save() # -> insert

$person->first_name('Baz');
$person->save() # -> now it's update!

### or

my $person = MyModel::Person->find(1);
$person->first_name('Baz');
$person->save() # update

update

To quick update object's fields, use "update":

$person->update({
    first_name  => 'Foo',
    second_name => 'Bar'
});
$person->save;

delete

$person->delete();

Delete row from the table.

exists

Checks for a record in the database corresponding to the object:

my $person = MyModel::Person->new({
    first_name => 'Foo',
    secnd_name => 'Bar',
});

$person->save() unless $person->exists;

to_hash

Convert objects data to the simple perl hash:

use JSON::XS;

say encode_json({ person => $peron->to_hash });

to_sql

Convert aobject to SQL-query:

my $sql = Person->find({ name => 'Bill' })->limit(1)->to_sql;
# select * from persons where name = ? limit 1;

my ($sql, $binds) = Person->find({ name => 'Bill' })->to_sql;
# sql: select * from persons where name = ? limit 1;
# binds: ['Bill']

is_defined

Checks weather an object is defined:

my $person = MyModel::Person->find(1);
return unless $person->is_defined;

SEE ALSO

L<DBIx::ActiveRecord>, L<SQL::Translator>

MORE INFO

perldoc ActiveRecord::Simple::Tutorial

AUTHOR

shootnix, <shootnix at cpan.org>

BUGS

Please report any bugs or feature requests to shootnix@cpan.org, or through the github: https://github.com/shootnix/activerecord-simple/issues

SUPPORT

You can find documentation for this module with the perldoc command.

perldoc ActiveRecord::Simple

You can also look for information at:

ACKNOWLEDGEMENTS

LICENSE AND COPYRIGHT

Copyright 2013-2017 shootnix.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.