DBIx-EAV
view release on metacpan or search on metacpan
lib/DBIx/EAV/ResultSet.pm view on Meta::CPAN
sub inflate_entity {
my ($self, $data) = @_;
my $type = $self->type;
$type = $self->eav->type_by_id($data->{entity_type_id})
if $data->{entity_type_id} && $data->{entity_type_id} != $type->id;
my $entity = $self->entity_class->new( eav => $self->eav, type => $type, raw => $data );
$entity->load_attributes;
$entity;
}
{
no warnings;
*create = \&insert;
}
sub insert {
my ($self, $data) = @_;
$self->new_entity($data)->save;
}
sub populate {
my ($self, $data) = @_;
die 'Call populate(\@items)' unless ref $data eq 'ARRAY';
my @result;
foreach my $item (@$data) {
push @result, $self->insert($item);
}
return wantarray ? @result : \@result;
}
sub update {
my ($self, $data, $where) = @_;
$where //= {};
$where->{entity_type_id} = $self->type->id;
# do a direct update for static attributes
}
sub delete {
my $self = shift;
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' }] } }
);
# delete attributes:
# - group attrs by data type so only one DELETE command is sent per data type
# - restrict by entity_type_id so we dont delete parent/sibiling/child data
my %types;
push @{ $types{$_->{data_type}} }, $_->{id}
for $type->attributes(no_static => 1);
while (my ($data_type, $ids) = each %types) {
my $value_table = $eav->table('value_'.$data_type);
$value_table->delete(
{
attribute_id => $ids,
$entities_table->name.'.entity_type_id' => $type->id
},
{ join => { $entities_table->name => { 'me.entity_id' => 'their.id' } } }
);
}
}
$entities_table->delete({ entity_type_id => $type->id });
}
sub delete_all {
my $self = shift;
my $rs = scalar @_ > 0 ? $self->search_rs(@_) : $self;
my $i = 0;
while (my $entity = $rs->next) {
$entity->delete;
$i++;
}
$i;
}
sub find {
my ($self, $criteria, $options) = @_;
croak "Missing find() criteria."
unless defined $criteria;
# simple id search
return $self->search_rs({ id => $criteria }, $options)->next
unless ref $criteria;
lib/DBIx/EAV/ResultSet.pm view on Meta::CPAN
sub distinct {
die "distinct() not implemented";
}
sub storage_size {
die "storage_size() not implemented";
}
1;
__END__
=encoding utf-8
=head1 NAME
DBIx::EAV::ResultSet - Represents a query used for fetching a set of entities.
=head1 SYNOPSIS
# resultsets are bound to an entity type
my $cds_rs = $eav->resultset('CD');
# insert CDs
my $cd1 = $cds_rs->insert({ title => 'CD1', tracks => \@tracks });
my $cd2 = $cds_rs->insert({ title => 'CD2', tracks => \@tracks });
my $cd3 = $cds_rs->insert({ title => 'CD3', tracks => \@tracks });
# ... or use populate() to insert many
my (@cds) = $cds_rs->populate(\@cds);
# find all 2015 cds
my @cds = $eav->resultset('CD')->search({ year => 2015 });
foreach my $cd (@cds) {
printf "CD '%s' has %d tracks.\n",
$cd->get('title'),
$cd->get('tracks')->count;
}
# find one
my $cd2 = $cds_rs->search_one({ name => 'CD2' });
# find by related attribute
my $cd2 = $cds_rs->search_one({ 'tracks.title' => 'Some CD2 Track' });
# 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.
A basic ResultSet representing the data of an entire table is returned
by calling C<resultset> on a L<DBIx::EAV> and passing in a
L<type|DBIx::EntityType> name.
my $users_rs = $eav->resultset('User');
A new ResultSet is returned from calling L</search> on an existing
ResultSet. The new one will contain all the conditions of the
original, plus any new conditions added in the C<search> call.
A ResultSet also incorporates an implicit iterator. L</next> and L</reset>
can be used to walk through all the L<entities|DBIx::EAV::Entity> the ResultSet
represents.
The query that the ResultSet represents is B<only> executed against
the database when these methods are called:
L</find>, L</next>, L</all>, L</first>, L</count>.
If a resultset is used in a numeric context it returns the L</count>.
However, if it is used in a boolean context it is B<always> true. So if
you want to check if a resultset has any results, you must use C<if $rs
!= 0>.
=head1 METHODS
=head2 new_entity
=over 4
=item Arguments: \%entity_data
=item Return Value: L<$entity|DBIx::EAV::EntityType>
=back
Creates a new entity object of the resultset's L<type|DBIx::EAV::EntityType> and
returns it. The row is not inserted into the database at this point, call
L<DBIx::EAV::Entity/save> to do that. Calling L<DBIx::EAV::Entity/in_storage>
will tell you whether the entity object has been inserted or not.
# create a new entity, do some modifications...
my $cd = $eav->resultset('CD')->new_entity({ title => 'CD1' });
$cd->set('year', 2016);
# now insert it
$cd->save;
=head2 insert
lib/DBIx/EAV/ResultSet.pm view on Meta::CPAN
=over
=item WARNING
When subclassing ResultSet never attempt to override this method. Since
it is a simple shortcut for C<< $self->new_entity($data)->save >>, a
lot of the internals simply never call it, so your override will be
bypassed more often than not. Override either L<DBIx::EAV::Entity/new>
or L<DBIx::EAV::Entity/save> depending on how early in the
L</insert> process you need to intervene.
=back
=head2 populate
=over 4
=item Arguments: \@entites
=item Return Value: L<@inserted_entities|DBIx:EAV::Entity>
=back
Shortcut for inserting multiple entities at once. Returns a list of inserted
entities.
my @cds = $eav->resultset('CD')->populate([
{ title => 'CD1', ... },
{ title => 'CD2', ... },
{ title => 'CD3', ... }
]);
=head2 count
=over 4
=item Arguments: \%where, \%options
=item Return Value: $count
=back
Performs an SQL C<COUNT> with the same query as the resultset was built
with to find the number of elements. Passing arguments is equivalent to
C<< $rs->search($cond, \%attrs)->count >>
=head2 delete
=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
calls L</delete_all> if SQLite database is detected.
=back
=head2 delete_all
=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
=back
Specifies the maximum number of rows for direct retrieval or the number of
rows per page if the page option or method is used.
=head2 offset
=over 4
=item Value: $offset
=back
Specifies the (zero-based) row number for the first row to be returned, or the
of the first row of the first page if paging is used.
=head2 page
NOT IMPLEMENTED.
=head2 group_by
=over 4
=item Value: \@columns
=back
A arrayref of columns to group by. Can include columns of joined tables.
group_by => [qw/ column1 column2 ... /]
=head2 having
=over 4
=item Value: \%condition
=back
The HAVING operator specifies a B<secondary> condition applied to the set
after the grouping calculations have been done. In other words it is a
constraint just like L</QUERY> (and accepting the same
L<SQL::Abstract syntax|SQL::Abstract/WHERE CLAUSES>) applied to the data
as it exists after GROUP BY has taken place. Specifying L</having> without
L</group_by> is a logical mistake, and a fatal error on most RDBMS engines.
Valid fields for criteria are all known attributes, relationships and related
attributes for the type this cursor is bound to.
E.g.
( run in 1.060 second using v1.01-cache-2.11-cpan-5a3173703d6 )