DBIx-EAV

 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 ]);



( run in 0.592 second using v1.01-cache-2.11-cpan-49f99fa48dc )