view release on metacpan or search on metacpan
lib/DBIx/EAV.pm view on Meta::CPAN
has '_types', is => 'ro', default => sub { {} };
has '_types_by_id', is => 'ro', default => sub { {} };
# group schema_config params
around BUILDARGS => sub {
my ( $orig, $class, @args ) = @_;
my $params = @args == 1 && ref $args[0] ? $args[0] : { @args };
my $schema_config = delete $params->{schema_config} || {};
my @schema_params = grep { exists $params->{$_} } qw/
tenant_id data_types database_cascade_delete static_attributes
table_prefix id_type default_attribute_type enable_multi_tenancy
/;
@{$schema_config}{@schema_params} = delete @{$params}{@schema_params};
$class->$orig(%$params, schema_config => $schema_config);
};
sub _build_schema {
lib/DBIx/EAV/Entity.pm view on Meta::CPAN
sub delete {
my $self = shift;
die "Can't delete coz I'm not in storage!"
unless $self->in_storage;
my $eav = $self->eav;
my $type = $self->type;
# cascade delete child entities
foreach my $rel ($type->relationships) {
next if $rel->{is_right_entity}
|| $rel->{is_many_to_many}
|| (exists $rel->{cascade_delete} && $rel->{cascade_delete} == 0);
my $rs = $self->_get_related($rel->{name});
while (my $related_entity = $rs->next) {
$related_entity->delete;
}
}
unless ($eav->schema->database_cascade_delete) {
# delete relationship links
$eav->table('entity_relationships')->delete([
{ left_entity_id => $self->id },
{ right_entity_id => $self->id }
]);
# delete attributes
my %data_types = map { $_->{data_type} => 1 }
$type->attributes( no_static => 1 );
lib/DBIx/EAV/Entity.pm view on Meta::CPAN
Throws an exception if the object is not in the database according to
L</in_storage>.
The object is still perfectly usable, but L</in_storage> will
now return 0 and the object will be reinserted (same attrs, new id) if you
call L</save>.
If you delete an object in a class with a C<has_many> or C<has_one>
relationship, an attempt is made to delete all the related objects as well.
To turn this behaviour off, pass C<< cascade_delete => 0 >> in the C<$attr>
hashref of the relationship, see L<DBIx::EAV/Relationships>.
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 C<DELETE> is executed for the relationship links table where this entity
is the right-side entity, unbinding from "parent" relationships. Then a
C<DELETE> query is executed for each value table, unless this entity has no
attributes of that data type.
lib/DBIx/EAV/ResultSet.pm view on Meta::CPAN
my $eav = $self->eav;
my $type = $self->type;
my $entities_table = $eav->table('entities');
# Call delete_all for SQLite since it doesn't
# support delete with joins.
# Better solution welcome.
return $self->delete_all if
$self->eav->schema->db_driver_name eq 'SQLite';
unless ($eav->schema->database_cascade_delete) {
# delete links by relationship id
my @ids = map { $_->{id} } $type->relationships;
$eav->table('entity_relationships')->delete(
{
relationship_id => \@ids,
$entities_table->name.'.entity_type_id' => $type->id
},
{ join => { $entities_table->name => [{ 'me.left_entity_id' => 'their.id' }, { 'me.right_entity_id' => 'their.id' }] } }
lib/DBIx/EAV/ResultSet.pm view on Meta::CPAN
# count
my $top_cds_count = $cds_rs->search({ rating => { '>' => 7 } })->count;
# update
# delete all entities
$cds_rs->delete; # fast, but doesn't deletes related entities
$cds_rs->delete_all; # cascade delete all cds and related entities
=head1 DESCRIPTION
A ResultSet is an object which stores a set of conditions representing
a query. It is the backbone of DBIx::EAV (i.e. the really
important/useful bit).
No SQL is executed on the database when a ResultSet is created, it
just stores all the conditions needed to create the query.
lib/DBIx/EAV/ResultSet.pm view on Meta::CPAN
=over 4
=item Arguments: \%where
=item Return Value: $underlying_storage_rv
=back
Deletes the entities matching \%where condition without fetching them first.
This will run faster, at the cost of related entities not being casdade deleted.
Call L</delete_all> if you want to cascade delete related entities.
When L<DBIx::EAV/database_cascade_delete> is enabled, the delete operation is
done in a single query. Otherwise one more query is needed for each of the
L<values table|DBIx::EAV::Schema> and another for the
L<relationship link table|DBIx::EAV::Schema>.
=over
=item WARNING
This method requires database support for C<DELETE ... JOIN>. Since the current
implementation of DBIx::EAV is only tested against MySQL and SQLite, this method
lib/DBIx/EAV/ResultSet.pm view on Meta::CPAN
=over 4
=item Arguments: \%where, \%options
=item Return Value: $num_deleted
=back
Fetches all objects and deletes them one at a time via
L<DBIx::EAV::Entity/delete>. Note that C<delete_all> will cascade delete related
entities, while L</delete> will not.
=head1 QUERY OPTIONS
=head2 limit
=over 4
=item Value: $rows
lib/DBIx/EAV/Schema.pm view on Meta::CPAN
our $SCHEMA_VERSION = 1;
my %driver_to_producer = (
mysql => 'MySQL'
);
has 'dbh', is => 'ro', required => 1;
has 'database_cascade_delete', is => 'ro', default => 1;
has 'table_prefix', is => 'ro', default => 'eav_';
has 'tenant_id', is => 'ro';
has 'enable_multi_tenancy', is => 'ro', default => 0;
has 'data_types', is => 'ro', default => sub { [qw/ int decimal varchar text datetime bool /] };
has 'static_attributes', is => 'ro', default => sub { [] };
has 'id_type', is => 'ro', default => 'int';
has 'translator', is => 'ro', init_arg => undef, lazy => 1, builder => 1;
has '_tables', is => 'ro', init_arg => undef, default => sub { {} };
sub BUILD {
my $self = shift;
# enable sqlite fk for cascade delete to work
$self->dbh_do("PRAGMA foreign_keys = ON;")
if $self->db_driver_name eq 'SQLite';
}
sub _build_translator {
my $self = shift;
my $sqlt = SQL::Translator->new;
$self->_build_sqlt_schema($sqlt->schema);
lib/DBIx/EAV/Schema.pm view on Meta::CPAN
unique => {
name => ['left_entity_type_id','name']
}
},
entity_relationships => {
columns => [qw/ relationship_id left_entity_id right_entity_id /],
pk => [qw/ relationship_id left_entity_id right_entity_id /],
fk => {
relationship_id => 'relationships',
left_entity_id => { table => 'entities', cascade_delete => $self->database_cascade_delete },
right_entity_id => { table => 'entities', cascade_delete => $self->database_cascade_delete },
}
},
type_hierarchy => {
columns => [qw/ parent_type_id child_type_id /],
pk => [qw/ parent_type_id child_type_id /],
fk => {
parent_type_id => { table => 'entity_types', cascade_delete => $self->database_cascade_delete },
child_type_id => { table => 'entity_types', cascade_delete => $self->database_cascade_delete },
}
},
map {
("value_$_" => {
columns => [qw/ entity_id attribute_id /, 'value:'.$_],
fk => {
entity_id => { table => 'entities', cascade_delete => $self->database_cascade_delete },
attribute_id => 'attributes'
}
})
} @{ $self->data_types }
);
for (my $i = 0; $i < @schema; $i += 2) {
# add table
my $table_name = $schema[$i];
lib/DBIx/EAV/Schema.pm view on Meta::CPAN
my $params = $table_schema->{fk}->{$fk_column};
$params = { table => $params } unless ref $params;
$table->add_constraint(
name => join('_', 'fk', $table_name, $fk_column, $params->{table}),
type => 'foreign_key',
fields => $fk_column,
reference_fields => 'id',
reference_table => $self->table_prefix . $params->{table},
on_delete => $params->{cascade_delete} ? 'CASCADE' : 'NO ACTION'
);
}
# # unique constraints
foreach my $name (keys %{ $table_schema->{unique} || {} }) {
$table->add_index(
name => join('_', 'unique', $table_name, $name),
type => 'unique',
fields => $table_schema->{unique}{$name},
lib/DBIx/EAV/Schema.pm view on Meta::CPAN
=over
=item Default: C<"eav_">
=back
Prefix added to our tables names to form the real database table name.
See L</TABLES>.
=head2 database_cascade_delete
=over
=item Default: C<1>
=back
When enabled, entities delete operations (via L<DBIx::EAV::Entity/delete> or
L<DBIx::EAV::ResultSet/delete>) are accomplished through a single C<DELETE> SQL command.
Also instructs L</deploy> to create the proper C<ON DELETE CASCADE> constraints.
lib/DBIx/EAV/Schema.pm view on Meta::CPAN
the L</entity_relationships> table. If an entity has attributes of 4 data types,
and has any relationship defined, a total of 6 (six!!) C<DELETE> commands will
be needed to delete a single entity. Four to the L</values> tables, one to the
L</entity_relationships> and one for the actual L</entities> table).
Those extra C<DELETE> commands can be avoided by using database-level
C<ON DELETE CASCADE> for the references from the B<values> and
B<entity_relationships> tables to the B<entities> table.
The current DBIx::EAV implementation can handle both situations, but defaults
to database-level cascade delete. See L</database_cascade_delete> option.
I'll probably drop support for no database-level cascade delete in the future...
if no one points otherwise.
=head1 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.
t/resultset.t view on Meta::CPAN
my $rs = $eav->resultset('CD');
$rs->populate([
{ title => 'CD1' },
{ title => 'CD2', tracks => [{ title => 'T1' }, { title => 'T2' }, { title => 'T3' }] }
]);
$rs->delete_all;
is $rs->count, 0, 'delete_all';
is $eav->resultset("Track")->count, 0, 'delete_all cascade delete';
}
sub test_count {
my $rs = $eav->resultset('Artist');
empty_database($eav);
$rs->populate([ map { +{ name => 'A'.$_ }} 1..6 ]);
$rs->populate([ map { +{ name => 'A'.$_ }} 1..6 ]);