NAME
DBIx::EAV::Entity - Represents an entity record.
SYNOPSIS
DESCRIPTION
This class can be used by itself or as base class for your entity objects.
CUSTOM CLASS
DBIx::EAV lets you define your entities via custom classes, which are subclasses of DBIx::EAV::Entity. Unlike DBIx::Class, the custom classes are not loaded upfront. They are lazy loaded whenever a call to "type" in DBIx::EAV is made. Directly or indirectly (i.e. via other DBIx::EAV methods like "resultset()").
Custom classes are used not only define the entity attributes and relationships, but also to add define you application's business logic, via custom entity methods.
Okay, an example. Lets mimic the namespaces used by DBIx::Class:
my $eav = DBIx::EAV->connect($dsn, $user, $pass, $attrs, {
entity_namespaces => 'MyApp::Schema::Result',
resultset_namespaces => 'MyApp::Schema::ResultSet',
});
Now lets create a 'User' entity class.
package MyApp::Schema::Result::User;
use Moo;
BEGIN { extends 'DBIx::EAV::Entity' }
__PACKAGE__->attribute('first_name');
__PACKAGE__->attribute('last_name');
__PACKAGE__->attribute('email');
__PACKAGE__->attribute('birth_date:datetime');
__PACKAGE__->attribute('is_verified:boolean:0');
# can also define relationships
#__PACKAGE__->has_one( ... );
#__PACKAGE__->has_many( ... );
#__PACKAGE__->many_to_many( ... );
# custom methods
sub full_name {
my $self = shift;
return join ' ', $self->get('first_name'), $self->get('last_name');
}
1;
Done. You have just defined the User
entity type, and also a custom class for instances of the this type.
my $user = $eav->resultset('User')->create({
first_name => 'Carlos',
last_name => 'Gratz'
});
print $user->full_name; # Carlos Gratz
# obviously, all other DBIx::EAV::Entity are also available :]
As you could have noted in the first code snippet, its also possible to create custom resultset classes.
package MyApp::Schema::ResultSet::User;
use Moo;
extends 'DBIx::EAV::ResultSet';
sub verified_only {
my $self = shift;
$self->search({ is_verified => 1 });
}
1;
Now a call to $eav->resultset('User')
returns an instance of MyApp::Schema::ResultSet::User
.
my $users_rs = $eav->resultset('User');
$users_rs->isa('MyApp::Schema::ResultSet::User'); # 1
my $verified_user = $users_rs->verified_only
->find({ email => 'user@example.com'});
CUSTOM CLASS INHERITANCE
DBIx::EAV supports entity type inheritance. When working with custom classes all you need to do is set you custom base class by normal perl means. DBIx::EAV will inspect your class @ISA
and get the parent entity name.
package MyApp::DB::Result::UserSubclass;
BEGIN { extends 'MyApp::DB::Result::User' }
# define attributes, relationships and methods for 'UserSubclass'
1;
For more information on how entity type inheritance works in DBIx::EAV, read DBIx::EAV::Manual::Inheritance.
METHODS
in_storage
Returns true if a database id is present.
sub in_storage {
my $self = shift;
exists $self->raw->{id} && defined $self->raw->{id};
}
id
Returns the entity database id or undef
if entity is not in storage.
# new_entity() doesn't call save(). $cd1 has no id in this case
my $cd1 = $eav->resultset('CD')->new_entity({ title => 'CD1' });
$cd1->id; # undef
$cd1->save;
$cd1->id; # <database id>
$cd1->delete;
$cd1->id; # undef
get
Returns a attribute value or related entities.
set
- Arguments: $name, $value \%values
- Arguments: \%values
- Return Value: $self
Set a new value for the attribute or relationship $name
. Returns $self
to allow method chaining. Even though subsequent calls to "get" will return the new value you have just "set", changes are not saved in the database until you call "save". Use "update" if you wan't to set and save in one call.
$cd->set('title' => 'New title');
$cd->get('title'); # New title
$cd->save; # or $cd->discard_changes
# set multiple values
$cd->set({
title => 'New Title',
year => 2016
});
When setting the value for a relationship, this method replaces the existing set of related entities by the new one (relationship bindings are deleted, not the related entities themselves). Valid values for relationships are existing entities or hashref suitable for inserting the related entity, or a arrayref of those (for *_many relationships). Passing an entity instance which is not of the correct type for the relationship or not "in_storage" is a fatal error.
# set (and replace) the cd tracks
$cd->set('tracks', [
{ title => 'Track1', duration => ... },
{ title => 'Track2', duration => ... },
{ title => 'Track3', duration => ... },
]);
# set its tags
my @tags = $eav->resultset('Tag')->find( name => [qw/ Foo Bar Baz /]);
$cd->set('tags', \@tags);
You can obviously set attribute and relationships at the same time:
$cd->set({
title => 'New Title',
year => 2016,
tracks => \@tracks
});
Se also "add_related" if you want to add (instead of replace) related entities.
save
Save all changes to the database.
# modify
$entity->set( ... );
$entity->save;
First thing save
does is insert the entity (in the entities table) if its not already "in_storage". Then it saves the non-static attributes: attributes values (in the values tables) are inserted, updated or deleted, whether the value is new (undef -> value), existing (value -> value), or undef (value -> undef).
Then relationship bindings are inserted/deleted according with each relationship type and rules. Related entities in the form of hashref is inserted before the bindings takes place.
Last but not least, modifications to static attributes are saved on the entities table.
update
- Arguments: $name, $value \%values
- Arguments: \%values
- Return Value: $self
A shortcut for set()
and save()
.
# set and save in one call
$cd->update({
title => 'New CD Title',
year => 2016
});
load_attributes
Fetches the attributes values from database "value tables" and stores in entity's "raw" data structure. If this method is called without arguments all attributes will be loaded.
NOTE: In the current version of DBIx::EAV this method is called internally by "next" in DBIx::EAV::Cursor, which makes all attributes to be loaded everytime. Its planned for a future version to make the attributes get lazy-loaded, which will make this method relevant.
add_related
Available only for has_many and many_to_many relationships, this method binds entities via the $rel_name
relationship. $related_data
must be a entity instance (of the proper type for the relationship) or a hashref of data to be inserted (again, suitable for the related type), or a arrayref of those. Passing Entity objects which are not "in_storage" results in a fatal error.
# add tracks to a cd
$cd->add_related('tracks', [
{ title => 'Track1', duration => ... },
{ title => 'Track2', duration => ... },
{ title => 'Track3', duration => ... },
]);
# also accepts existing entities
my @tracks = $eav->resultset('Track')->populate( ... );
$cd->add_related('tracks', \@tracks);
remove_related
Unbinds $related_entities
from the relationship $rel_name
. Note that it doesn't delete the related entities.
my @tags = $eav->resultset('Tag')->find( name => [qw/ Foo Bar Baz /]);
$article->remove_related('tags', \@tags);
discard_changes
Reverts all modified attributes to the its original value. Note that the internal memory of modified attributes is reset after a call to "save".
delete
- Arguments: none
- Return Value: $result
Throws an exception if the object is not in the database according to "in_storage".
The object is still perfectly usable, but "in_storage" will now return 0 and the object will be reinserted (same attrs, new id) if you call "save".
If you delete an object in a class with a has_many
or has_one
relationship, an attempt is made to delete all the related objects as well. To turn this behaviour off, pass cascade_delete => 0
in the $attr
hashref of the relationship, see "Relationships" in DBIx::EAV.
Since a entity is represented by data not only in the entities table, but also in value tables and relationship links table, those related rows must be deleted before the main row.
First a DELETE
is executed for the relationship links table where this entity is the right-side entity, unbinding from "parent" relationships. Then a DELETE
query is executed for each value table, unless this entity has no attributes of that data type.
Those extra DELETE
operations are unneccessary if you are using database-level ON DELETE CASCADE
. See "DATABASE-LEVEL CASCADE DELETE" in DBIx::EAV.
See also "delete" in DBIx::EAV::ResulutSet.
LICENSE
Copyright (C) Carlos Fernando Avila Gratz.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
AUTHOR
Carlos Fernando Avila Gratz <cafe@kreato.com.br>