File Coverage

blib/lib/DBI/Easy.pm
Criterion Covered Total %
statement 245 278 88.1
branch 62 104 59.6
condition 28 52 53.8
subroutine 27 30 90.0
pod 1 5 20.0
total 363 469 77.4


line stmt bran cond sub pod time code
1             package DBI::Easy;
2              
3 6     6   848346 use Class::Easy::Base;
  6         28128  
  6         42  
4              
5 6     6   16623 use DBI 1.611;
  6         64147  
  6         401  
6              
7             #use Hash::Util;
8              
9 6     6   52 use vars qw($VERSION);
  6         17  
  6         490  
10             $VERSION = '0.24';
11              
12             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
13             # interface splitted to various sections:
14             # sql generation stuff prefixed with sql and located
15             # at DBI::Class::SQL
16             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
17              
18 6     6   5734 use DBI::Easy::SQL;
  6         17  
  6         216  
19              
20             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
21             # real dbh operations contains methods fetch_* and no_fetch
22             # and placed in DBI::Class::DBH
23             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
24              
25 6     6   4425 use DBI::Easy::DBH;
  6         20  
  6         187  
26              
27 6     6   3632 use DBI::Easy::DriverPatcher;
  6         16  
  6         150  
28              
29 6     6   3276 use DBI::Easy::Helper;
  6         22  
  6         7961  
30              
31             # bwahahahaha
32             our %GREP_COLUMN_INFO = qw(TYPE_NAME 1 mysql_values 1);
33              
34             our $wrapper = 1;
35              
36             our $H = 'DBI::Easy::Helper';
37              
38             sub new {
39 77     77 1 7143 my $class = shift;
40            
41 77         125 my $params;
42             my $init_params;
43            
44 77 100       1238 $init_params = $class->_init (@_)
45             if $class->can ('_init');
46            
47             $params = $init_params || {(@_ == 1 && ref $_[0] && ref $_[0] eq 'HASH')
48 77   100     576 ? %{$_[0]}
49             : @_
50             };
51            
52 77         783 bless $params, $class;
53             }
54              
55             sub import {
56 33     33   161 my $class = shift;
57            
58 33 100       61 unless (${"${class}::imported"}) {
  33         414  
59 23         261 make_accessor ($class, 'dbh', is => 'rw', global => 1);
60             make_accessor ($class, 'dbh_modify', is => 'rw', global => 1, default => sub {
61 42     42   137 return shift->dbh;
62 23         1212 });
63             }
64              
65 33 100 66     998 if (! ${"${class}::wrapper"} and $class ne __PACKAGE__ and ! ${"${class}::imported"} ) {
  33   100     688  
  21         147  
66            
67 17         104 debug "importing $class";
68            
69 17         1375 my $t = timer ('init_class');
70 17         1040 $class->_init_class;
71            
72 17         824 $t->lap ('init_db');
73            
74             # we call _init_db from package before real db
75 17         289 $class->_init_db;
76            
77 17         1075 $t->lap ("init_collection $class");
78            
79 17 100       246 $class->_init_collection
80             if $class->is_collection;
81            
82 17         421 $t->lap ("dbh check and accessors $class");
83            
84 17 50 33     145 die "can't use database class '$class' without db connection: $DBI::errstr"
85             if ! $class->dbh or $class->dbh eq '0E0';
86              
87 17 50       722 die "can't retrieve table '".$class->table_name."' columns for '$class'"
88             unless $class->_init_make_accessors;
89            
90 17         118 $t->lap ("init_last $class");
91            
92 17         299 $class->_init_last;
93            
94 17         69 $t->end;
95            
96             # my $driver = $class->dbh->get_info (17);
97             # warn "driver name from
98             # get_info ($DBI::Const::GetInfoType{SQL_DBMS_NAME}): $driver";
99             }
100            
101 33         133 ${"${class}::imported"} = 1;
  33         138  
102            
103 33 50       506 $class::SUPER->import (@_)
104             if (defined $class::SUPER);
105            
106             }
107              
108             sub _init_db {
109 0     0   0 my $self = shift;
110 0         0 $self->dbh (DBI->connect);
111             }
112              
113             sub _init_class {
114 17     17   44 my $self = shift;
115              
116 17   33     667 my $ref = ref $self || $self;
117            
118 17         111 my @pack_chunks = split /\:\:/, $ref;
119            
120 17         40 my $is_collection = 0;
121            
122             # fix for collections
123 17 100       82 if ($pack_chunks[-1] eq 'Collection') {
    50          
124 6         12 pop @pack_chunks;
125            
126 6         30 make_accessor ($ref, 'record_package', is => 'rw', global => 1);
127            
128 6         215 $is_collection = 1;
129            
130             } elsif ($pack_chunks[-1] eq 'Record') {
131 0         0 pop @pack_chunks;
132             }
133            
134 17         71 make_accessor ($ref, 'is_collection', default => $is_collection);
135            
136 17         992 my $table_name = DBI::Easy::Helper::table_from_package ($pack_chunks[-1]);
137            
138             # dies when this method called without object reference;
139             # expected behaviour
140            
141 17         43 my $common_table_prefix = '';
142            
143 17 100       570 $common_table_prefix = $ref->common_table_prefix
144             if $ref->can ('common_table_prefix');
145            
146 17 50       647 make_accessor (
147             $ref, 'table_name', is => 'rw', global => 1,
148             default => $common_table_prefix . $table_name
149             ) unless $ref->can ('table_name');
150            
151 17         723 make_accessor ($ref, '_date_format', is => 'rw', global => 1);
152            
153 17 50       786 make_accessor (
154             $ref, 'column_prefix', is => 'rw', global => 1,
155             default => $ref->table_name . "_"
156             ) unless $ref->can ('column_prefix');
157            
158 17         773 make_accessor ($ref, 'fieldset', is => 'rw', default => '*');
159            
160 17         749 make_accessor ($ref, 'prepare_method', is => 'rw', global => 1,
161             default => 'prepare_cached');
162 17         598 make_accessor ($ref, 'prepare_param', is => 'rw', global => 1,
163             default => 3);
164 17         613 make_accessor ($ref, 'undef_as_null', is => 'rw', global => 1,
165             default => 0);
166            
167             }
168              
169             sub _init_collection {
170 6     6   132 my $self = shift;
171            
172 6         27 my $rec_pkg = $self->record_package;
173            
174 6 50       135 unless ($rec_pkg) {
175 6   33     39 my $ref = ref $self || $self;
176            
177 6         35 my @pack_chunks = split /\:\:/, $ref;
178            
179 6         13 pop @pack_chunks;
180            
181 6         22 $rec_pkg = join '::', @pack_chunks;
182            
183             # TODO: move to Class::Easy
184 6 50       26 unless (try_to_use ($rec_pkg)) {
185 0 0       0 die unless try_to_use ($rec_pkg . '::Record');
186             }
187            
188 6         5727 $self->record_package ($rec_pkg);
189             }
190            
191             }
192              
193             sub _detect_vendor {
194 17     17   35 my $class = shift;
195            
196 17         60 my $dbh = $class->dbh;
197            
198 17         390 my $vendor = lc ($dbh->get_info(17));
199            
200 17         243 make_accessor ($class, 'dbh_vendor', default => $vendor);
201            
202 17         869 my $vendor_pack = "DBI::Easy::Vendor::$vendor";
203 17         76 my $have_vendor_pack = try_to_use_quiet ($vendor_pack);
204            
205 17 50       15926 unless ($have_vendor_pack) {
206 17         41 $vendor_pack = "DBI::Easy::Vendor::Base";
207 17 50       60 die unless try_to_use_quiet ($vendor_pack);
208             }
209            
210 6     6   65 no strict 'refs';
  6         12  
  6         390  
211            
212 17         2348 push @{"$class\::ISA"}, $vendor_pack;
  17         754  
213            
214 6     6   36 use strict 'refs';
  6         13  
  6         16417  
215            
216 17         256 $class->_init_vendor;
217            
218             }
219              
220             # here we retrieve fields and create make_accessors
221             sub _init_make_accessors {
222 17     17   38 my $class = shift;
223            
224 17         86 my $table_name = $class->table_name;
225 17         125 my $column_prefix = $class->column_prefix;
226            
227             # detecting vendor
228 17         220 $class->_detect_vendor;
229            
230 17         150 my $t = timer ('columns info wrapper');
231            
232 17         1106 my $columns = $class->_dbh_columns_info;
233            
234 17         65 $t->end;
235            
236 17         153 make_accessor ($class, 'columns', default => $columns);
237 17         962 make_accessor ($class, 'column_values', is => 'rw');
238            
239 17         584 my $fields = {};
240            
241 17         65 make_accessor ($class, 'fields', default => $fields);
242 17         633 make_accessor ($class, 'field_values', is => 'rw');
243            
244 17         786 my $pri_key;
245             my $pri_key_column;
246            
247 17         76 foreach my $col_name (keys %$columns) {
248 81         1892 my $col_meta = $columns->{$col_name};
249             # here we translate rows
250 81         315 my $field_name = lc ($col_name); # oracle fix
251            
252 81 100 33     1304 if (
      66        
253             defined $column_prefix
254             and $column_prefix ne ''
255             and $col_name =~ /^$column_prefix(.*)/i
256             ) {
257 32         166 $field_name = lc($1);
258             }
259            
260             # field meta referenced to column meta
261             # no we can use $field_meta->{col_name} and $col_meta->{field_name}
262 81         182 $fields->{$field_name} = $col_meta;
263            
264 81         175 $col_meta->{field_name} = $field_name;
265            
266 81 50 33     251 if ($col_meta->{type_name} eq 'ENUM' and $#{$col_meta->{mysql_values}} >= 0) {
  0         0  
267 0         0 make_accessor ($class, "${field_name}_variants",
268             default => $col_meta->{mysql_values});
269             }
270            
271             # attach decoder for complex datatypes, as example date, datetime, timestamp
272 81         387 $class->attach_decoder ($col_meta);
273            
274 81 100 66     383 if (exists $col_meta->{X_IS_PK} and $col_meta->{X_IS_PK} == 1) {
275            
276 17 50       64 if ($pri_key) {
277 0         0 warn "multiple pri keys: $fields->{$pri_key}->{column_name} and $field_name";
278             } else {
279 17         35 $pri_key = $field_name;
280 17         48 $pri_key_column = $col_name;
281             }
282            
283             my $fetch_by_pk_sub = sub {
284 5     5   97 my $package = shift;
285 5         9 my $value = shift;
286            
287 5         74 return $package->fetch ({$field_name => $value}, @_);
288 17         99 };
289            
290 17         83 make_accessor ($class, "fetch_by_$field_name", default => $fetch_by_pk_sub);
291 17         608 make_accessor ($class, "fetch_by_pk", default => $fetch_by_pk_sub);
292             }
293            
294             # access to the precise field value or column value without cool accessors
295            
296             make_accessor ($class, $field_name, default => sub {
297 47     47   6823 my $self = shift;
298            
299 47 100       163 unless (@_) {
300             # bad style?
301             return
302 44   66     436 $self->{field_values}->{$field_name} || (
303             exists $self->columns->{$col_name}->{decoder}
304             ? $self->columns->{$col_name}->{decoder}->($self) # ($self->{column_values}->{$col_name});
305             : $self->{column_values}->{$col_name});
306             }
307              
308 3 50       18 die "too many parameters" if @_ > 1;
309              
310 3         49 $self->assign_values ($field_name => $_[0]);
311            
312 81         920 });
313            
314             make_accessor ($class, "_fetched_${field_name}", default => sub {
315 1     1   564 my $self = shift;
316            
317 1 50       5 unless (@_) {
318 1         7 return $self->{column_values}->{$col_name};
319             }
320              
321 0         0 die "too many parameters";
322 81         3123 });
323              
324             make_accessor ($class, "_raw_${field_name}", default => sub {
325 1     1   3 my $self = shift;
326            
327 1 50       6 die "you must supply one parameter" unless @_ == 1;
328            
329 1         5 $self->{field_values}->{$field_name} = $_[0];
330 81         3476 });
331              
332             }
333            
334 17         1238 make_accessor ($class, '_pk_', default => $pri_key);
335 17         559 make_accessor ($class, '_pk_column_', default => $pri_key_column);
336              
337 17         950 return $class;
338             }
339              
340             sub assign_values {
341 3     3 0 6 my $self = shift;
342 3         12 my $to_assign = {@_};
343            
344 3         15 foreach my $k (keys %$to_assign) {
345 3         26 $self->{field_values}->{$k} = $to_assign->{$k};
346             }
347             }
348              
349             sub attach_decoder {
350 81     81 0 113 my $class = shift;
351 81         98 my $col_meta = shift;
352            
353 81         132 my $type = $col_meta->{type_name};
354            
355 81 50 33     452 if (defined $type and $H->is_rich_type ($type)) {
356            
357             $col_meta->{decoder} = sub {
358 0     0   0 my $self = shift;
359 0         0 my $value = $self->column_values->{$col_meta->{column_name}};
360 0         0 return $H->value_from_type ($type, $value, $self);
361             }
362 0         0 }
363             }
364              
365              
366 17     17   29 sub _init_last {
367              
368             }
369              
370             sub _dbh_columns_info {
371 17     17   41 my $class = shift;
372            
373 17         58 my $ts = timer ('inside columns info');
374              
375 17         853 my $dbh = $class->dbh;
376              
377 17         128 my $table_name = $class->table_name;
378            
379 17         115 $ts->lap ('make accessor');
380            
381             # preparations
382 17         300 make_accessor (
383             $class, 'table_quoted',
384             default => $class->quote_identifier ($table_name)
385             );
386            
387 17         1536 my $real_row_count = 0;
388            
389 17         39 my $column_info = {};
390            
391 17         68 $ts->lap ('eval column info');
392            
393 17         106 eval {
394            
395 17         56 my $t = timer ('column info call');
396            
397 17         795 my $sth = $dbh->column_info(
398             undef, undef, $table_name, '%'
399             );
400            
401 17         53856 $t->lap ('execute');
402            
403 17 50       243 $sth->execute
404             unless $sth->{Executed};
405            
406 17         1347 $t->lap ('fetchrow hashref');
407            
408 17         275 while (my $row = $sth->fetchrow_hashref) {
409 81         2361 $real_row_count ++;
410            
411 81         150 my $column_name = $row->{COLUMN_NAME};
412            
413 81         907 $column_info->{$column_name} = {
414             (map {
415 1458         2710 lc($_) => $row->{$_}
416             } grep {
417 81         354 exists $GREP_COLUMN_INFO{$_}
418             } keys %$row),
419            
420             column_name => $column_name,
421             quoted_column_name => $dbh->quote_identifier ($column_name),
422             nullable => $row->{NULLABLE},
423             };
424            
425 81         2414 my $default_val = $row->{COLUMN_DEF};
426 81 100       1084 if (defined $default_val) {
427 5         89 $default_val =~ s/^"(.*)"$/$1/;
428 5         84 $column_info->{$column_name}->{default} = $default_val;
429             }
430            
431             }
432            
433 17         630 $t->end;
434            
435 17         674 $t->total;
436            
437 17 50       967 if ($real_row_count == 0) {
438 0         0 die "no rows for table '$table_name' fetched";
439             }
440             };
441            
442 17         79 $ts->lap ('_dbh_error');
443            
444             return
445 17 50       753 if $class->_dbh_error ($@);
446            
447 17         31 $real_row_count = 0;
448            
449 17         66 $ts->lap ('primary_key_info');
450            
451 17         109 eval {
452            
453 17         76 my $t = timer ('primary key');
454            
455             # fuckin oracle
456 17         1105 my $schema = $class->vendor_schema;
457            
458 17         152 my $sth = $dbh->primary_key_info(
459             undef, $schema, $table_name
460             );
461            
462 17         25351 $t->lap ('execute');
463            
464 17 50       168 if ($sth) {
465 17 50       116 $sth->execute
466             unless $sth->{Executed};
467            
468 17         512 $t->lap ('fetchrow');
469              
470 17         466 while (my $row = $sth->fetchrow_hashref) {
471 17         330 $real_row_count ++;
472             # here we translate rows
473 17         45 my $pri_key_name = $row->{COLUMN_NAME};
474            
475 17         64 $column_info->{$row->{COLUMN_NAME}}->{X_IS_PK} = 1;
476 17         212 $column_info->{$row->{COLUMN_NAME}}->{nullable} = 0;
477            
478             }
479             }
480            
481 17         303 $t->end;
482            
483 17         109 $t->total;
484            
485 17 50       796 if ($real_row_count == 0) {
486 0         0 warn "no primary keys for table '$table_name'";
487             }
488             };
489            
490             return
491 17 50       75 if $class->_dbh_error ($@);
492              
493 17         62 $ts->end;
494            
495             #Hash::Util::lock_hash_recurse (%$column_info);
496            
497 17         127 return $column_info;
498             }
499              
500             sub _dbh_error {
501 102     102   251 my $self = shift;
502 102         219 my $error = shift;
503 102         193 my $statement = shift;
504            
505 102 50       584 return unless $error;
506            
507 0         0 my @caller = caller (1);
508 0         0 my @caller2 = caller (2);
509            
510 0 0 0     0 if ($DBI::Easy::ERRHANDLER and ref $DBI::Easy::ERRHANDLER eq 'CODE') {
511 0         0 &$DBI::Easy::ERRHANDLER ($self, $error, $statement);
512             } else {
513 0         0 warn ("[db error at $caller[3] ($caller[2]) called at $caller2[3] ($caller2[2])] ",
514             $error
515             );
516             }
517            
518 0 0       0 if ($self->{in_transaction}) {
519 0         0 eval {$self->rollback};
  0         0  
520 0         0 die $error;
521             }
522            
523 0         0 return 1;
524             }
525              
526              
527             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
528             # we always work with one table or view.
529             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
530              
531             sub _prefix_manipulations {
532 107     107   160 my $self = shift;
533 107         149 my $dir = shift;
534 107         246 my $values = shift;
535 107   50     474 my $in_place = shift || 0;
536            
537 107         445 my $entities;
538             my $ent_key;
539 0         0 my $convert;
540 107 50       309 if ($dir eq 'fields2cols') {
    0          
541 107         633 $entities = $self->fields;
542 107         554 $ent_key = 'column_name';
543 107         291 $convert = 'value_to_type';
544 107 100       508 $values = $self->field_values
545             unless $values;
546             } elsif ($dir eq 'cols2fields') {
547 0         0 $entities = $self->cols;
548 0         0 $ent_key = 'field_name';
549 0         0 $convert = 'value_from_type';
550 0 0       0 $values = $self->column_values
551             unless $values;
552             } else {
553 0         0 die "you can't call _prefix_manipulations without direction";
554             }
555              
556 107 100       575 return $values if ! ref $values;
557            
558 79         110 my $place = $values;
559 79 50       362 unless ($in_place) {
560 79         257 $place = {};
561             }
562            
563 79         289 foreach (keys %$values) {
564            
565 84 50 66     467 next unless exists $entities->{$_} or /^[_:-]\w+$/;
566             #&& ($self->undef_as_null || defined $entities->{$_})
567              
568 84 100       302 if (/^:\w+$/) { # copy placeholders
569 1 50       4 unless ($in_place) {
570 1         3 $place->{$_} = $values->{$_};
571             }
572 1         4 next;
573             }
574             # next if $ent->{$ent_key} eq $_ and $in_place; #
575 83         498 my ($column_prefix, $k) = (/^(_?)(\w+)$/);
576 83 50       228 $column_prefix = ''
577             unless defined $column_prefix;
578            
579             # debug $k, $_;
580            
581 83         171 my $ent = $entities->{$k};
582 83         174 my $value = $values->{$_};
583            
584 83 50       193 if ($in_place) {
585 0         0 delete $values->{$_};
586             }
587            
588 83         351 my $v = $value;
589            
590             # we must convert only convertible values
591             # field => 'value'
592             # _field => {'>', 'value'}
593 83 100 66     364 if ($column_prefix eq '') {
    100 66        
      66        
594 73         1151 $v = $H->$convert (
595             $ent->{type_name}, $value, $self
596             );
597             } elsif (
598             $column_prefix eq '_' and ref $value and ref $value eq 'HASH'
599             and keys %$value == 1
600             ) {
601 3         8 my $condition = (keys %$value)[0];
602 3         23 $v = $condition . $self->quote ($H->$convert (
603             $ent->{type_name}, $value->{$condition}, $self
604             ));
605             }
606            
607 83 50       317 next unless exists $ent->{$ent_key};
608            
609             # warn "$prefix/$ent_key => $ent->{$ent_key}";
610 83         382 $place->{$column_prefix . $ent->{$ent_key}} = $v;
611             }
612            
613 79 50       662 return $place
614             unless $in_place;
615            
616             }
617              
618             sub fields_to_columns {
619 107     107 0 191 my $self = shift;
620            
621 107         541 $self->_prefix_manipulations ('fields2cols', shift, 0);
622             }
623              
624             sub columns_to_fields {
625 0     0 0   my $self = shift;
626            
627 0           $self->_prefix_manipulations ('cols2fields', shift, 0);
628             }
629              
630             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
631             # we always work with one table or view.
632             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
633              
634              
635             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
636             # simplified sql execute
637             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
638              
639              
640              
641             1;
642              
643             =head1 NAME
644              
645             DBI::Easy - yet another perl ORM for SQL databases
646              
647             =head1 DESCRIPTION
648              
649             DBI::Easy is another ORM, aimed at making the life of the developer
650             using it a lot easier.
651              
652             =head1 INTRODUCTION
653              
654             The key notions of DBI::Easy are data records, collection of data records
655             and relations between them. A data record is a presentation of SQL result:
656             row or blessed hash, depending on how you look at it. Data records collection
657             is a set of records limited by certain criteria or without any limitations.
658             the differentiation between collections and records has to do with
659             different relations between them: one-to-one, one-to-many, many-to-many.
660              
661             For Example: Within a domain auction based on DBI::Easy, every user may
662             have a few bids, but each bid belongs to just one concrete user.
663              
664             It's also worth mentioning the relations between DBI::Easy and SQL. DBI::Easy
665             is currently using a small set of sql, limited to tables and views,
666             including four operations to work with data: insert, update, select, delete.
667             The relations between SQL objects are not formed automatically with the help
668             of constraints.
669              
670             Also it's important that DBI::Easy is not trying to hide SQL from you.
671             If you need it you can use it fully. However, it allows carrying out the vast
672             majority of simple operations with data without the participation of SQL.
673              
674             =head1 SYNOPSIS
675              
676             Let's start from the most simple things. To start the work you will need two
677             modules that will return database handler ($dbh) upon request.
678              
679             To avoid unpleasant consequences it's recommended to cache the returned
680             connection only after the fork, if there is a fork in your code.
681             for the case when CL environment variables for DBI_DSN and DBI_* are defined,
682             and they can be used to establish a connection that doesn't need to be cached,
683             you can do without these modules at all. The main task for 'Entity' is to
684             acquire DBI::Easy::Record[::Collection] or one of the child classes.
685              
686             package DBEntity;
687             use strict;
688             use DBI;
689             use DBI::Easy::Record;
690             use base qw(DBI::Easy::Record);
691             sub dbh { # optional. You don't have to write a procedure similar
692             # to this one since DBI->connect is requested
693             # when a ready $dbh hasn't been provided
694             return DBI->connect;
695             };
696             1;
697              
698             #-----------------------------------------
699              
700             package DBEntity::Collection;
701             use strict;
702             use DBI::Easy::Record::Collection;
703             use base qw(DBI::Easy::Record::Collection);
704             1;
705              
706             Now let's get down to something concrete. Let's assume we have a user and his
707             passport data (one-to-one relation) and some contact data (one-to-many)
708             NOTE: the many-to-many relations hasn't been realized yet.
709              
710              
711             package Entity::Passport;
712             use strict;
713             use DBEntity;
714             use base qw(DBEntity);
715             1;
716              
717             #-----------------------------------------
718              
719             package Entity::Contact;
720             use strict;
721             use DBEntity;
722             use base qw(DBEntity);
723             1;
724              
725             #-----------------------------------------
726              
727             package Entity::Contact::Collection;
728             use strict;
729             use DBEntity::Collection;
730             use base qw(DBEntity::Collection);
731             1;
732              
733             #-----------------------------------------
734              
735             package Entity::Account;
736             use strict;
737             use DBEntity;
738             use base qw(DBEntity);
739             use Entity::Passport;
740             use Entity::Contact::Collection;
741              
742             sub _init_last {
743             my $self = shift;
744             $self->is_related_to (
745             passport => 'Entity::Passport'
746             );
747             $self->is_related_to (
748             contacts => 'Entity::Contact::Collection'
749             );
750             }
751             1;
752              
753             #-----------------------------------------
754              
755             package Entity::Account::Collection;
756             use strict;
757             use DBEntity::Collection;
758             use base qw(DBEntity::Collection);
759             1;
760              
761             #-----------------------------------------
762              
763             Now let's create some SQL tables for our test application (using SQLite):
764              
765             create table account (
766             account_id serial not null primary key,
767             account_login varchar (50) not null
768             );
769             create table pasport (
770             passport_id serial not null primary key,
771             passport_serial varchar (50) not null,
772             account_id integer
773             );
774             create table contact (
775             contact_id serial not null primary key,
776             contact_proto varchar (10) not null,
777             contact_address varchar (200) not null,
778             account_id integer
779             );
780              
781             And now the funniest part: the script itself:
782              
783             #-----------------------------------------
784              
785             #!/usr/bin/perl
786              
787             use strict;
788             use Entity::Account;
789              
790             # here it doesn`t matter whether there is a user with such a login in
791             # the database, if needed we can create it.
792              
793             my $account = Entity::Account->fetch_or_create ({login => 'apla'});
794              
795             # here fetch_or_create is implicitly activated with the parameters
796             # {id => $account->id, serial => 'aabbcc'}
797              
798             $account->passport ({serial => 'aabbcc'});
799              
800             my $acc_contacts = $account->contacts;
801              
802             my $contact = $acc_contacts->new_record ({
803             proto => 'email', address => 'apla@localhost'
804             });
805             $contact->save;
806             $acc_contacts->count;
807              
808             1;
809              
810              
811              
812             =head1 AUTHOR
813              
814             Ivan Baktsheev, C<< >>
815              
816             =head1 BUGS
817              
818             Please report any bugs or feature requests to my email address,
819             or through the web interface at L.
820             I will be notified, and then you'll automatically be notified
821             of progress on your bug as I make changes.
822              
823             =head1 SUPPORT
824              
825              
826              
827             =head1 ACKNOWLEDGEMENTS
828              
829              
830              
831             =head1 COPYRIGHT & LICENSE
832              
833             Copyright 2008-2009 Ivan Baktsheev
834              
835             This program is free software; you can redistribute it and/or modify it
836             under the same terms as Perl itself.
837              
838             =cut
839