| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package DBIx::EAV::Entity; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 10 |  |  | 10 |  | 36 | use Moo; | 
|  | 10 |  |  |  |  | 9 |  | 
|  | 10 |  |  |  |  | 46 |  | 
| 4 | 10 |  |  | 10 |  | 1827 | use strictures 2; | 
|  | 10 |  |  |  |  | 50 |  | 
|  | 10 |  |  |  |  | 426 |  | 
| 5 | 10 |  |  | 10 |  | 1618 | use Scalar::Util qw/ blessed /; | 
|  | 10 |  |  |  |  | 11 |  | 
|  | 10 |  |  |  |  | 418 |  | 
| 6 | 10 |  |  | 10 |  | 32 | use Data::Dumper; | 
|  | 10 |  |  |  |  | 10 |  | 
|  | 10 |  |  |  |  | 351 |  | 
| 7 | 10 |  |  | 10 |  | 34 | use Carp 'croak'; | 
|  | 10 |  |  |  |  | 11 |  | 
|  | 10 |  |  |  |  | 18580 |  | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | has 'eav', is => 'ro', required => 1; | 
| 10 |  |  |  |  |  |  | has 'type', is => 'ro', required => 1, handles => [qw/ is_type /]; | 
| 11 |  |  |  |  |  |  | has 'raw', is => 'ro', default => sub { {} }; | 
| 12 |  |  |  |  |  |  | has '_modified', is => 'ro', default => sub { {} }; | 
| 13 |  |  |  |  |  |  | has '_modified_related', is => 'ro', default => sub { {} }; | 
| 14 |  |  |  |  |  |  |  | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 |  |  |  |  |  |  | sub in_storage { | 
| 18 | 189 |  |  | 189 | 1 | 5439 | my $self = shift; | 
| 19 | 189 | 100 |  |  |  | 780 | exists $self->raw->{id} && defined $self->raw->{id}; | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | sub id { | 
| 23 | 890 |  |  | 890 | 1 | 2181 | my $self = shift; | 
| 24 | 890 | 50 |  |  |  | 1605 | return unless exists $self->raw->{id}; | 
| 25 | 890 |  |  |  |  | 3540 | $self->raw->{id}; | 
| 26 |  |  |  |  |  |  | } | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  |  | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | sub get { | 
| 31 | 107 |  |  | 107 | 1 | 595 | my $self = shift; | 
| 32 | 107 |  |  |  |  | 97 | my $name = shift; | 
| 33 | 107 |  |  |  |  | 141 | my $type = $self->type; | 
| 34 |  |  |  |  |  |  |  | 
| 35 | 107 | 100 |  |  |  | 203 | return $self->raw->{$name} | 
| 36 |  |  |  |  |  |  | if $type->has_attribute($name); | 
| 37 |  |  |  |  |  |  |  | 
| 38 | 20 | 50 |  |  |  | 42 | if ($type->has_relationship($name)) { | 
| 39 | 20 |  |  |  |  | 32 | my $rel = $type->relationship($name); | 
| 40 | 20 |  |  |  |  | 42 | my $rs = $self->_get_related($name, @_); | 
| 41 |  |  |  |  |  |  | # return an Entity for has_one and belongs_to; return Cursor otherwise | 
| 42 |  |  |  |  |  |  | return $rs->next if | 
| 43 | 20 | 100 | 66 |  |  | 110 | $rel->{is_has_one} || ($rel->{is_has_many} && $rel->{is_right_entity}); | 
|  |  |  | 66 |  |  |  |  | 
| 44 |  |  |  |  |  |  |  | 
| 45 |  |  |  |  |  |  | # *_many rel, return cursor or array of entities | 
| 46 | 16 | 50 |  |  |  | 60 | return wantarray ? $rs->all : $rs; | 
| 47 |  |  |  |  |  |  | } | 
| 48 |  |  |  |  |  |  |  | 
| 49 | 0 |  |  |  |  | 0 | die sprintf "get() error: '%s' is not a valid attribute/relationship for '%s'", $name, $self->type->name; | 
| 50 |  |  |  |  |  |  | } | 
| 51 |  |  |  |  |  |  |  | 
| 52 |  |  |  |  |  |  | sub _get_related { | 
| 53 | 41 |  |  | 41 |  | 50 | my ($self, $relname, $query, $options) = @_; | 
| 54 | 41 |  | 100 |  |  | 134 | $query //= {}; | 
| 55 | 41 |  |  |  |  | 91 | my $rel = $self->type->relationship($relname); | 
| 56 | 41 |  |  |  |  | 73 | $query->{$rel->{incoming_name}} = $self; | 
| 57 | 41 |  |  |  |  | 121 | $self->eav->resultset($rel->{entity})->search($query, $options); | 
| 58 |  |  |  |  |  |  | } | 
| 59 |  |  |  |  |  |  |  | 
| 60 |  |  |  |  |  |  |  | 
| 61 |  |  |  |  |  |  | sub load_attributes { | 
| 62 | 112 |  |  | 112 | 1 | 139 | my ($self, @attrs) = @_; | 
| 63 |  |  |  |  |  |  |  | 
| 64 | 112 | 50 |  |  |  | 164 | die "Can't load_attributes(): this entity has no id!" | 
| 65 |  |  |  |  |  |  | unless defined $self->id; | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 112 |  |  |  |  | 136 | my $eav = $self->eav; | 
| 68 | 112 |  |  |  |  | 116 | my $type = $self->type; | 
| 69 |  |  |  |  |  |  |  | 
| 70 | 112 | 50 |  |  |  | 372 | @attrs = $type->attributes( no_static => 1, names => 1 ) | 
| 71 |  |  |  |  |  |  | if @attrs == 0; | 
| 72 |  |  |  |  |  |  |  | 
| 73 |  |  |  |  |  |  | # build sql query: one aliases subselect for each attribute | 
| 74 |  |  |  |  |  |  | my $sql_query = 'SELECT ' . join(', ', map { | 
| 75 |  |  |  |  |  |  |  | 
| 76 | 112 |  |  |  |  | 139 | my $attr_spec = $type->attribute($_); | 
|  | 436 |  |  |  |  | 739 |  | 
| 77 | 436 |  |  |  |  | 6997 | my $value_table = $eav->table('value_'. $attr_spec->{data_type} ); | 
| 78 |  |  |  |  |  |  | sprintf "(SELECT value FROM %s WHERE entity_id = %d AND attribute_id = %d) AS %s", | 
| 79 |  |  |  |  |  |  | $value_table->name, | 
| 80 |  |  |  |  |  |  | $self->id, | 
| 81 |  |  |  |  |  |  | $attr_spec->{id}, | 
| 82 | 436 |  |  |  |  | 877 | $_; | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | } @attrs); | 
| 85 |  |  |  |  |  |  |  | 
| 86 |  |  |  |  |  |  | # fetch data | 
| 87 | 112 |  |  |  |  | 1626 | my ($rv, $sth) = $eav->dbh_do($sql_query); | 
| 88 | 112 |  |  |  |  | 1691 | my $data = $sth->fetchrow_hashref; | 
| 89 |  |  |  |  |  |  |  | 
| 90 | 112 | 50 |  |  |  | 376 | die "load_attributes() failed! No data returned from database!" | 
| 91 |  |  |  |  |  |  | unless ref $data eq 'HASH'; | 
| 92 |  |  |  |  |  |  |  | 
| 93 | 112 |  |  |  |  | 182 | my $raw = $self->raw; | 
| 94 | 112 |  |  |  |  | 114 | my $total = 0; | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | # adopt data | 
| 97 | 112 |  |  |  |  | 352 | for (keys %$data) { | 
| 98 | 436 |  |  |  |  | 444 | $raw->{$_} = $data->{$_}; | 
| 99 | 436 |  |  |  |  | 358 | $total++; | 
| 100 |  |  |  |  |  |  | } | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | # return the number os attrs loaded | 
| 103 | 112 |  |  |  |  | 1356 | $total; | 
| 104 |  |  |  |  |  |  | } | 
| 105 |  |  |  |  |  |  |  | 
| 106 |  |  |  |  |  |  | sub update { | 
| 107 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 108 | 0 |  |  |  |  | 0 | $self->set(@_)->save; | 
| 109 |  |  |  |  |  |  | } | 
| 110 |  |  |  |  |  |  |  | 
| 111 |  |  |  |  |  |  | sub set { | 
| 112 | 98 |  |  | 98 | 1 | 2069 | my $self = shift; | 
| 113 | 98 |  |  |  |  | 156 | my $numargs = scalar(@_); | 
| 114 |  |  |  |  |  |  |  | 
| 115 | 98 | 50 | 33 |  |  | 402 | die 'Call set(\%data) or set($attr, $value)' | 
| 116 |  |  |  |  |  |  | if 1 > $numargs || $numargs > 2; | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 98 | 100 |  |  |  | 252 | if ($numargs == 2) { | 
|  |  | 50 |  |  |  |  |  | 
| 119 | 2 |  |  |  |  | 8 | $self->_set(@_); | 
| 120 |  |  |  |  |  |  | } | 
| 121 |  |  |  |  |  |  | elsif ($numargs == 1) { | 
| 122 | 96 | 50 |  |  |  | 255 | die "You must pass a hashref set()" unless ref $_[0] eq 'HASH'; | 
| 123 | 96 |  |  |  |  | 92 | while (my ($k, $v) = each %{$_[0]}) { | 
|  | 247 |  |  |  |  | 690 |  | 
| 124 | 151 |  |  |  |  | 237 | $self->_set($k, $v); | 
| 125 |  |  |  |  |  |  | } | 
| 126 |  |  |  |  |  |  | } | 
| 127 |  |  |  |  |  |  |  | 
| 128 | 98 |  |  |  |  | 136 | $self; | 
| 129 |  |  |  |  |  |  | } | 
| 130 |  |  |  |  |  |  |  | 
| 131 |  |  |  |  |  |  | sub _set { | 
| 132 | 153 |  |  | 153 |  | 158 | my ($self, $attr_name, $value) = @_; | 
| 133 | 153 |  |  |  |  | 209 | my $type = $self->type; | 
| 134 |  |  |  |  |  |  |  | 
| 135 | 153 | 100 |  |  |  | 319 | if ($type->has_relationship($attr_name)) { | 
| 136 | 16 |  |  |  |  | 31 | return $self->_set_related($attr_name, $value); | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 | 137 |  |  |  |  | 281 | my $attr = $self->type->attribute($attr_name); | 
| 140 |  |  |  |  |  |  |  | 
| 141 | 137 | 50 |  |  |  | 320 | die "Sorry, you can't set the 'id' attribute." | 
| 142 |  |  |  |  |  |  | if $attr_name eq 'id'; | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | # same value | 
| 145 |  |  |  |  |  |  | return if defined $value && | 
| 146 |  |  |  |  |  |  | exists $self->raw->{$attr_name} && | 
| 147 |  |  |  |  |  |  | defined $self->raw->{$attr_name} && | 
| 148 | 137 | 50 | 66 |  |  | 762 | $value eq $self->raw->{$attr_name}; | 
|  |  |  | 100 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | # remember original value | 
| 151 |  |  |  |  |  |  | $self->_modified->{$attr_name} = $self->raw->{$attr_name} | 
| 152 | 137 | 100 |  |  |  | 428 | unless exists $self->_modified->{$attr_name}; | 
| 153 |  |  |  |  |  |  |  | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | # set | 
| 156 |  |  |  |  |  |  | # TODO use type-specific deflator | 
| 157 | 137 |  |  |  |  | 300 | $self->raw->{$attr_name} = $value; | 
| 158 |  |  |  |  |  |  | } | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | sub _set_related { | 
| 161 | 16 |  |  | 16 |  | 19 | my ($self, $relname, $data) = @_; | 
| 162 | 16 |  |  |  |  | 25 | my $type = $self->type; | 
| 163 | 16 |  |  |  |  | 36 | my $rel = $type->relationship($relname); | 
| 164 |  |  |  |  |  |  |  | 
| 165 | 16 | 0 | 66 |  |  | 74 | die "You can only pass related data in the form of a hashref, blessed Entity object, or an arrayref of it." | 
|  |  |  | 0 |  |  |  |  | 
|  |  |  | 33 |  |  |  |  | 
| 166 |  |  |  |  |  |  | unless ref $data eq 'HASH' || ref $data eq 'ARRAY' || (blessed $data && $data->isa('DBIx::EAV::Entity')); | 
| 167 |  |  |  |  |  |  |  | 
| 168 |  |  |  |  |  |  | die "You can't pass an arrayref for the '$rel->{name}' relationship." | 
| 169 | 16 | 50 | 33 |  |  | 78 | if ref $data eq 'ARRAY' && ( $rel->{is_has_one} || ($rel->{is_has_many} && $rel->{is_right_entity}) ); | 
|  |  |  | 66 |  |  |  |  | 
| 170 |  |  |  |  |  |  |  | 
| 171 | 16 |  |  |  |  | 29 | $self->raw->{$relname} = $data; | 
| 172 | 16 |  |  |  |  | 42 | $self->_modified_related->{$relname} = 1; | 
| 173 |  |  |  |  |  |  | } | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | sub save { | 
| 177 | 95 |  |  | 95 | 1 | 108 | my $self = shift; | 
| 178 | 95 |  |  |  |  | 112 | my $type = $self->type; | 
| 179 | 95 |  |  |  |  | 1617 | my $entities_table = $self->eav->table('entities'); | 
| 180 | 95 |  |  |  |  | 173 | my $is_new_entity = not $self->in_storage; | 
| 181 | 95 |  |  |  |  | 107 | my $raw = $self->raw; | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | # modified static attrs | 
| 184 | 3 |  |  |  |  | 17 | my %modified_static_attributes = map { $_ => $self->raw->{$_} } | 
| 185 | 133 |  |  |  |  | 409 | grep { $type->has_static_attribute($_) } | 
| 186 | 95 |  |  |  |  | 84 | keys %{$self->_modified}; | 
|  | 95 |  |  |  |  | 209 |  | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | # insert if its new entity | 
| 189 | 95 | 100 |  |  |  | 611 | if ($is_new_entity) { | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | # TODO insert default values | 
| 192 |  |  |  |  |  |  |  | 
| 193 | 91 |  |  |  |  | 346 | my $id = $entities_table->insert({ | 
| 194 |  |  |  |  |  |  | %modified_static_attributes, | 
| 195 |  |  |  |  |  |  | entity_type_id => $type->id, | 
| 196 |  |  |  |  |  |  | }); | 
| 197 |  |  |  |  |  |  |  | 
| 198 | 91 | 50 |  |  |  | 218 | die "Invalid ID returned ($id) while inserting new entity." | 
| 199 |  |  |  |  |  |  | unless $id > 0; | 
| 200 |  |  |  |  |  |  |  | 
| 201 | 91 |  |  |  |  | 301 | my $static_attributes = $entities_table->select_one({ id => $id }); | 
| 202 |  |  |  |  |  |  |  | 
| 203 |  |  |  |  |  |  | die "Error: could not fetch the entity row I've just inserted!" | 
| 204 | 91 | 50 |  |  |  | 953 | unless $static_attributes->{id} == $id; | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | $raw->{$_} = $static_attributes->{$_} | 
| 207 | 91 |  |  |  |  | 429 | for keys %$static_attributes; | 
| 208 |  |  |  |  |  |  |  | 
| 209 |  |  |  |  |  |  | # undirty those attrs | 
| 210 | 91 |  |  |  |  | 138 | delete $self->_modified->{$_} for keys %modified_static_attributes; | 
| 211 | 91 |  |  |  |  | 181 | %modified_static_attributes = (); | 
| 212 |  |  |  |  |  |  | } | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | # upsert attributes | 
| 215 | 95 |  |  |  |  | 87 | my $modified_count = 0; | 
| 216 |  |  |  |  |  |  |  | 
| 217 | 95 |  |  |  |  | 90 | while (my ($attr_name, $old_value) = each %{$self->_modified}) { | 
|  | 227 |  |  |  |  | 846 |  | 
| 218 |  |  |  |  |  |  |  | 
| 219 | 132 |  |  |  |  | 113 | $modified_count++; | 
| 220 | 132 |  |  |  |  | 140 | my $value = $raw->{$attr_name}; | 
| 221 | 132 |  |  |  |  | 385 | my $attr_spec = $self->type->attribute($attr_name); | 
| 222 |  |  |  |  |  |  |  | 
| 223 |  |  |  |  |  |  | # save static attrs later | 
| 224 | 132 | 100 |  |  |  | 255 | if ($attr_spec->{is_static}) { | 
| 225 | 2 |  |  |  |  | 3 | $modified_static_attributes{$attr_name} = $value; | 
| 226 | 2 |  |  |  |  | 4 | next; | 
| 227 |  |  |  |  |  |  | } | 
| 228 |  |  |  |  |  |  |  | 
| 229 | 130 |  |  |  |  | 2770 | my $values_table = $self->eav->table('value_'.$attr_spec->{data_type}); | 
| 230 |  |  |  |  |  |  |  | 
| 231 |  |  |  |  |  |  | my %attr_criteria = ( | 
| 232 |  |  |  |  |  |  | entity_id    => $self->id, | 
| 233 |  |  |  |  |  |  | attribute_id => $attr_spec->{id} | 
| 234 | 130 |  |  |  |  | 338 | ); | 
| 235 |  |  |  |  |  |  |  | 
| 236 |  |  |  |  |  |  | # undefined value, delete attribute row | 
| 237 | 130 | 100 |  |  |  | 294 | if (not defined $value) { | 
|  |  | 100 |  |  |  |  |  | 
| 238 | 1 |  |  |  |  | 6 | $values_table->delete(\%attr_criteria); | 
| 239 |  |  |  |  |  |  | } | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | # update or insert value | 
| 242 |  |  |  |  |  |  | elsif (defined $old_value) { | 
| 243 | 1 |  |  |  |  | 6 | $values_table->update({ value => $value }, \%attr_criteria); | 
| 244 |  |  |  |  |  |  | } | 
| 245 |  |  |  |  |  |  | else { | 
| 246 | 128 |  |  |  |  | 515 | $values_table->insert({ | 
| 247 |  |  |  |  |  |  | %attr_criteria, | 
| 248 |  |  |  |  |  |  | value => $value | 
| 249 |  |  |  |  |  |  | }); | 
| 250 |  |  |  |  |  |  | } | 
| 251 |  |  |  |  |  |  | } | 
| 252 |  |  |  |  |  |  |  | 
| 253 |  |  |  |  |  |  | # upset related | 
| 254 | 95 |  |  |  |  | 91 | foreach my $relname (keys %{$self->_modified_related}) { | 
|  | 95 |  |  |  |  | 265 |  | 
| 255 | 16 |  |  |  |  | 50 | $self->_save_related($relname, $self->raw->{$relname}); | 
| 256 |  |  |  |  |  |  | } | 
| 257 |  |  |  |  |  |  |  | 
| 258 |  |  |  |  |  |  |  | 
| 259 |  |  |  |  |  |  | # update static attributes | 
| 260 | 95 | 50 |  |  |  | 186 | if ($modified_count > 0) { | 
| 261 |  |  |  |  |  |  |  | 
| 262 | 95 | 100 |  |  |  | 169 | $entities_table->update(\%modified_static_attributes, { id => $self->id }) | 
| 263 |  |  |  |  |  |  | if keys(%modified_static_attributes) > 0; | 
| 264 |  |  |  |  |  |  | } | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | # undirty | 
| 267 | 95 |  |  |  |  | 87 | %{$self->_modified} = (); | 
|  | 95 |  |  |  |  | 177 |  | 
| 268 |  |  |  |  |  |  |  | 
| 269 | 95 |  |  |  |  | 279 | $self; | 
| 270 |  |  |  |  |  |  | } | 
| 271 |  |  |  |  |  |  |  | 
| 272 |  |  |  |  |  |  | sub _save_related { | 
| 273 | 18 |  |  | 18 |  | 27 | my ($self, $relname, $data, $options) = @_; | 
| 274 | 18 |  | 100 |  |  | 71 | $options //= {}; | 
| 275 |  |  |  |  |  |  |  | 
| 276 | 18 |  |  |  |  | 54 | my $rel = $self->type->relationship($relname); | 
| 277 | 18 |  |  |  |  | 66 | my $related_type = $self->eav->type($rel->{entity}); | 
| 278 | 18 | 50 |  |  |  | 42 | my ($our_side, $their_side) = $rel->{is_right_entity} ? qw/ right left / : qw/ left right /; | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | # delete any old links | 
| 281 | 18 |  |  |  |  | 362 | my $relationship_table = $self->eav->table('entity_relationships'); | 
| 282 |  |  |  |  |  |  | $relationship_table->delete({ | 
| 283 |  |  |  |  |  |  | relationship_id => $rel->{id}, | 
| 284 |  |  |  |  |  |  | $our_side."_entity_id" => $self->id | 
| 285 | 18 | 100 |  |  |  | 86 | }) unless $options->{keep_current_links}; | 
| 286 |  |  |  |  |  |  |  | 
| 287 |  |  |  |  |  |  | # link new objects | 
| 288 | 18 | 100 |  |  |  | 71 | foreach my $entity (ref $data eq 'ARRAY' ? @$data : ($data)) { | 
| 289 |  |  |  |  |  |  |  | 
| 290 |  |  |  |  |  |  | # if is a blessed object, check its a entity from the correct type | 
| 291 | 51 | 100 |  |  |  | 165 | if (blessed $entity) { | 
|  |  | 50 |  |  |  |  |  | 
| 292 |  |  |  |  |  |  |  | 
| 293 | 22 | 50 |  |  |  | 74 | die "Can't save data for relationship '$relname': unknown data type: ". ref $entity | 
| 294 |  |  |  |  |  |  | unless $entity->isa('DBIx::EAV::Entity'); | 
| 295 |  |  |  |  |  |  |  | 
| 296 | 22 | 50 |  |  |  | 67 | die sprintf("relationship '%s' requires '%s' objects, not '%s'", $relname, $related_type->name, $entity->type->name) | 
| 297 |  |  |  |  |  |  | unless $entity->type->id == $related_type->id; | 
| 298 |  |  |  |  |  |  |  | 
| 299 | 22 | 50 |  |  |  | 33 | die "Can't save data for relationship '$relname': related entity is not in_storage." | 
| 300 |  |  |  |  |  |  | unless $entity->in_storage; | 
| 301 |  |  |  |  |  |  |  | 
| 302 |  |  |  |  |  |  | # remove any links to it | 
| 303 |  |  |  |  |  |  | $relationship_table->delete({ | 
| 304 |  |  |  |  |  |  | relationship_id => $rel->{id}, | 
| 305 |  |  |  |  |  |  | $their_side."_entity_id" => $entity->id | 
| 306 |  |  |  |  |  |  |  | 
| 307 | 22 | 100 |  |  |  | 46 | }) unless $rel->{is_many_to_many}; | 
| 308 |  |  |  |  |  |  |  | 
| 309 |  |  |  |  |  |  | } | 
| 310 |  |  |  |  |  |  | elsif (ref $entity eq 'HASH') { | 
| 311 |  |  |  |  |  |  |  | 
| 312 |  |  |  |  |  |  | # insert new entity | 
| 313 | 29 |  |  |  |  | 116 | $entity = $self->eav->resultset($related_type->name)->insert($entity); | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  | else { | 
| 316 | 0 |  |  |  |  | 0 | die "Can't save data for relationship '$relname': unknown data type: ". ref $entity; | 
| 317 |  |  |  |  |  |  | } | 
| 318 |  |  |  |  |  |  |  | 
| 319 |  |  |  |  |  |  | # create link | 
| 320 |  |  |  |  |  |  | $relationship_table->insert({ | 
| 321 |  |  |  |  |  |  | relationship_id => $rel->{id}, | 
| 322 | 51 | 50 |  |  |  | 175 | $our_side."_entity_id"  => $self->id, | 
| 323 |  |  |  |  |  |  | $their_side."_entity_id" => $entity->id | 
| 324 |  |  |  |  |  |  | }) or die "Error creating link for relationship '$relname'"; | 
| 325 |  |  |  |  |  |  | } | 
| 326 |  |  |  |  |  |  | } | 
| 327 |  |  |  |  |  |  |  | 
| 328 |  |  |  |  |  |  | sub add_related { | 
| 329 | 3 |  |  | 3 | 1 | 20 | my ($self, $relname, $data) = @_; | 
| 330 | 3 |  |  |  |  | 13 | my $rel = $self->type->relationship($relname); | 
| 331 |  |  |  |  |  |  | die "Can't call add_related() for relationship '$rel->{name}'" | 
| 332 | 3 | 100 | 66 |  |  | 28 | if $rel->{is_has_one} || ($rel->{is_has_many} && $rel->{is_right_entity}); | 
|  |  |  | 66 |  |  |  |  | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 2 |  |  |  |  | 6 | $self->_save_related($relname, $data, { keep_current_links => 1 }); | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  |  | 
| 337 |  |  |  |  |  |  |  | 
| 338 |  |  |  |  |  |  | sub remove_related { | 
| 339 | 3 |  |  | 3 | 1 | 374 | my ($self, $relname, $data) = @_; | 
| 340 | 3 |  |  |  |  | 12 | my $rel = $self->type->relationship($relname); | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | die "Can't call add_related() for relationship '$rel->{name}'" | 
| 343 | 3 | 100 | 66 |  |  | 23 | if $rel->{is_has_one} || ($rel->{is_has_many} && $rel->{is_right_entity}); | 
|  |  |  | 66 |  |  |  |  | 
| 344 |  |  |  |  |  |  |  | 
| 345 | 2 |  |  |  |  | 40 | my $relationships_table = $self->eav->table('entity_relationships'); | 
| 346 | 2 | 50 |  |  |  | 8 | my ($our_side, $their_side) = $rel->{is_right_entity} ? qw/ right left / : qw/ left right /; | 
| 347 |  |  |  |  |  |  |  | 
| 348 | 2 | 50 |  |  |  | 7 | $data = [$data] unless ref $data eq 'ARRAY'; | 
| 349 |  |  |  |  |  |  |  | 
| 350 | 2 |  |  |  |  | 4 | foreach my $entity (@$data) { | 
| 351 |  |  |  |  |  |  |  | 
| 352 |  |  |  |  |  |  | die "remove_related() error: give me an instance of '$rel->{entity}' or an arrayref of it." | 
| 353 | 2 | 50 | 33 |  |  | 24 | unless blessed $entity && $entity->isa('DBIx::EAV::Entity') && $entity->type->name eq $rel->{entity}; | 
|  |  |  | 33 |  |  |  |  | 
| 354 |  |  |  |  |  |  |  | 
| 355 |  |  |  |  |  |  | $relationships_table->delete({ | 
| 356 |  |  |  |  |  |  | relationship_id          => $rel->{id}, | 
| 357 | 2 |  |  |  |  | 8 | $our_side  ."_entity_id" => $self->id, | 
| 358 |  |  |  |  |  |  | $their_side."_entity_id" => $entity->id | 
| 359 |  |  |  |  |  |  | }); | 
| 360 |  |  |  |  |  |  | } | 
| 361 |  |  |  |  |  |  | } | 
| 362 |  |  |  |  |  |  |  | 
| 363 |  |  |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | sub discard_changes { | 
| 365 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 366 |  |  |  |  |  |  |  | 
| 367 | 0 |  |  |  |  | 0 | while (my ($k, $v) = each %{$self->_modified}) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 368 | 0 |  |  |  |  | 0 | $self->raw->{$k} = $v; | 
| 369 | 0 |  |  |  |  | 0 | delete $self->raw->{$k}; | 
| 370 |  |  |  |  |  |  | } | 
| 371 |  |  |  |  |  |  |  | 
| 372 | 0 |  |  |  |  | 0 | $self; | 
| 373 |  |  |  |  |  |  | } | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  |  | 
| 376 |  |  |  |  |  |  | sub delete { | 
| 377 | 24 |  |  | 24 | 1 | 35 | my $self = shift; | 
| 378 | 24 | 50 |  |  |  | 44 | die "Can't delete coz I'm not in storage!" | 
| 379 |  |  |  |  |  |  | unless $self->in_storage; | 
| 380 |  |  |  |  |  |  |  | 
| 381 | 24 |  |  |  |  | 47 | my $eav  = $self->eav; | 
| 382 | 24 |  |  |  |  | 37 | my $type = $self->type; | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | # cascade delete child entities | 
| 385 | 24 |  |  |  |  | 60 | foreach my $rel ($type->relationships) { | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | next if $rel->{is_right_entity} | 
| 388 |  |  |  |  |  |  | || $rel->{is_many_to_many} | 
| 389 | 53 | 100 | 66 |  |  | 189 | || (exists $rel->{cascade_delete} && $rel->{cascade_delete} == 0); | 
|  |  |  | 33 |  |  |  |  | 
|  |  |  | 66 |  |  |  |  | 
| 390 |  |  |  |  |  |  |  | 
| 391 | 21 |  |  |  |  | 38 | my $rs = $self->_get_related($rel->{name}); | 
| 392 | 21 |  |  |  |  | 69 | while (my $related_entity = $rs->next) { | 
| 393 | 6 |  |  |  |  | 21 | $related_entity->delete; | 
| 394 |  |  |  |  |  |  | } | 
| 395 |  |  |  |  |  |  | } | 
| 396 |  |  |  |  |  |  |  | 
| 397 | 24 | 50 |  |  |  | 516 | unless ($eav->schema->database_cascade_delete) { | 
| 398 |  |  |  |  |  |  |  | 
| 399 |  |  |  |  |  |  | # delete relationship links | 
| 400 | 0 |  |  |  |  | 0 | $eav->table('entity_relationships')->delete([ | 
| 401 |  |  |  |  |  |  | { left_entity_id  => $self->id }, | 
| 402 |  |  |  |  |  |  | { right_entity_id => $self->id } | 
| 403 |  |  |  |  |  |  | ]); | 
| 404 |  |  |  |  |  |  |  | 
| 405 |  |  |  |  |  |  | # delete attributes | 
| 406 | 0 |  |  |  |  | 0 | my %data_types = map { $_->{data_type} => 1 } | 
|  | 0 |  |  |  |  | 0 |  | 
| 407 |  |  |  |  |  |  | $type->attributes( no_static => 1 ); | 
| 408 |  |  |  |  |  |  |  | 
| 409 | 0 |  |  |  |  | 0 | foreach my $data_type (keys %data_types) { | 
| 410 | 0 |  |  |  |  | 0 | $eav->table('value_'.$data_type)->delete({ entity_id => $self->id }); | 
| 411 |  |  |  |  |  |  | } | 
| 412 |  |  |  |  |  |  | } | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | # delete entity | 
| 415 | 24 |  |  |  |  | 521 | my $entities_table = $self->eav->table('entities'); | 
| 416 | 24 |  |  |  |  | 50 | my $rv = $entities_table->delete({ id => $self->id }); | 
| 417 | 24 |  |  |  |  | 83 | delete $self->raw->{id}; # not in_storage | 
| 418 | 24 |  |  |  |  | 64 | $rv; | 
| 419 |  |  |  |  |  |  | } | 
| 420 |  |  |  |  |  |  |  | 
| 421 |  |  |  |  |  |  |  | 
| 422 |  |  |  |  |  |  | ##               ## | 
| 423 |  |  |  |  |  |  | ## Class Methods ## | 
| 424 |  |  |  |  |  |  | ##               ## | 
| 425 |  |  |  |  |  |  |  | 
| 426 |  |  |  |  |  |  | sub is_custom_class { | 
| 427 | 20 |  |  | 20 | 0 | 4085 | my $class = shift; | 
| 428 | 20 | 50 |  |  |  | 26 | croak "is_custom_class() is a Class method." if ref $class; | 
| 429 | 20 |  |  |  |  | 46 | $class ne __PACKAGE__; | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  |  | 
| 432 |  |  |  |  |  |  | sub type_definition { | 
| 433 | 11 |  |  | 11 | 0 | 164 | my $class = shift; | 
| 434 |  |  |  |  |  |  |  | 
| 435 | 11 | 50 |  |  |  | 17 | croak "type_definition() is a Class method." if ref $class; | 
| 436 | 11 | 50 |  |  |  | 14 | croak "type_definition() must be called on DBIx::EAV::Entity subclasses." | 
| 437 |  |  |  |  |  |  | unless $class->is_custom_class; | 
| 438 |  |  |  |  |  |  |  | 
| 439 | 10 |  |  | 10 |  | 50 | no strict 'refs'; | 
|  | 10 |  |  |  |  | 9 |  | 
|  | 10 |  |  |  |  | 1378 |  | 
| 440 | 11 | 100 |  |  |  | 10 | unless (defined *{"${class}::__TYPE_DEFINITION__"}) { | 
|  | 11 |  |  |  |  | 36 |  | 
| 441 |  |  |  |  |  |  |  | 
| 442 | 3 |  |  |  |  | 2 | my %definition; | 
| 443 |  |  |  |  |  |  | # detect parent entity | 
| 444 | 3 |  |  |  |  | 4 | my $parent_class = ${"${class}::ISA"}[0]; | 
|  | 3 |  |  |  |  | 8 |  | 
| 445 | 3 | 100 |  |  |  | 12 | ($definition{extends}) = $parent_class =~ /::(\w+)$/ | 
| 446 |  |  |  |  |  |  | if $parent_class ne __PACKAGE__; | 
| 447 |  |  |  |  |  |  |  | 
| 448 | 3 |  |  |  |  | 5 | *{"${class}::__TYPE_DEFINITION__"} = \%definition; | 
|  | 3 |  |  |  |  | 9 |  | 
| 449 |  |  |  |  |  |  | } | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  |  | 
| 452 | 11 |  |  |  |  | 6 | \%{"${class}::__TYPE_DEFINITION__"}; | 
|  | 11 |  |  |  |  | 51 |  | 
| 453 |  |  |  |  |  |  | } | 
| 454 |  |  |  |  |  |  |  | 
| 455 |  |  |  |  |  |  | # install class methods for type definition | 
| 456 |  |  |  |  |  |  | foreach my $stuff (qw/ attribute has_many has_one many_to_many /) { | 
| 457 | 10 |  |  | 10 |  | 38 | no strict 'refs'; | 
|  | 10 |  |  |  |  | 11 |  | 
|  | 10 |  |  |  |  | 1107 |  | 
| 458 |  |  |  |  |  |  | *{$stuff} = sub { | 
| 459 | 7 |  |  | 7 |  | 8039 | my ($class, $spec) = @_; | 
| 460 |  |  |  |  |  |  |  | 
| 461 | 7 | 50 |  |  |  | 16 | croak "$stuff() is a Class method." if ref $class; | 
| 462 | 7 | 50 |  |  |  | 17 | croak "$stuff() must be called on DBIx::EAV::Entity subclasses." | 
| 463 |  |  |  |  |  |  | unless $class->is_custom_class; | 
| 464 |  |  |  |  |  |  |  | 
| 465 | 7 | 100 |  |  |  | 15 | my $key = $stuff eq 'attribute' ? 'attributes' : $stuff; | 
| 466 | 7 |  |  |  |  | 4 | push @{ $class->type_definition->{$key} }, $spec; | 
|  | 7 |  |  |  |  | 15 |  | 
| 467 |  |  |  |  |  |  | }; | 
| 468 |  |  |  |  |  |  | } | 
| 469 |  |  |  |  |  |  |  | 
| 470 |  |  |  |  |  |  |  | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | 1; | 
| 473 |  |  |  |  |  |  |  | 
| 474 |  |  |  |  |  |  |  | 
| 475 |  |  |  |  |  |  | __END__ |