File Coverage

blib/lib/ORM.pm
Criterion Covered Total %
statement 520 687 75.6
branch 194 356 54.4
condition 69 138 50.0
subroutine 69 95 72.6
pod 22 29 75.8
total 874 1305 66.9


line stmt bran cond sub pod time code
1             #
2             # DESCRIPTION
3             # PerlORM - Object relational mapper (ORM) for Perl. PerlORM is Perl
4             # library that implements object-relational mapping. Its features are
5             # much similar to those of Java's Hibernate library, but interface is
6             # much different and easier to use.
7             #
8             # AUTHOR
9             # Alexey V. Akimov
10             #
11             # COPYRIGHT
12             # Copyright (C) 2005-2006 Alexey V. Akimov
13             #
14             # This library is free software; you can redistribute it and/or
15             # modify it under the terms of the GNU Lesser General Public
16             # License as published by the Free Software Foundation; either
17             # version 2.1 of the License, or (at your option) any later version.
18             #
19             # This library is distributed in the hope that it will be useful,
20             # but WITHOUT ANY WARRANTY; without even the implied warranty of
21             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22             # Lesser General Public License for more details.
23             #
24             # You should have received a copy of the GNU Lesser General Public
25             # License along with this library; if not, write to the Free Software
26             # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
27             #
28              
29             package ORM;
30              
31 5     5   38415 use 5.006001;
  5         18  
  5         240  
32 5     5   409 use strict;
  5         176  
  5         356  
33 5     5   27 use warnings;
  5         8  
  5         284  
34 5     5   28 use Carp;
  5         7  
  5         525  
35 5     5   24 use base 'Class::Data::Inheritable';
  5         10  
  5         5734  
36              
37 5     5   4774 use ORM::Error;
  5         152  
  5         143  
38 5     5   5196 use ORM::Cache;
  5         12  
  5         129  
39 5     5   5174 use ORM::Broken;
  5         10  
  5         127  
40 5     5   2488 use ORM::Date;
  5         18  
  5         145  
41 5     5   37 use ORM::Datetime;
  5         13  
  5         104  
42 5     5   5086 use ORM::Ta;
  5         15  
  5         201  
43 5     5   2390 use ORM::Const;
  5         17  
  5         165  
44 5     5   3267 use ORM::Ident;
  5         14  
  5         118  
45 5     5   29 use ORM::Expr;
  5         7  
  5         101  
46 5     5   2765 use ORM::Order;
  5         18  
  5         162  
47 5     5   42 use ORM::Metaprop;
  5         12  
  5         93  
48 5     5   3245 use ORM::MetapropBuilder;
  5         15  
  5         128  
49 5     5   3060 use ORM::ResultSet;
  5         12  
  5         131  
50 5     5   3614 use ORM::StatResultSet;
  5         18  
  5         55353  
51              
52             our $VERSION = 0.83;
53              
54             ORM->mk_classdata( '_class_hier' );
55             ORM->mk_classdata( '_db' );
56             ORM->mk_classdata( '_history_class' );
57             ORM->mk_classdata( '_default_prefer_lazy_load' );
58             ORM->mk_classdata( '_emulate_foreign_keys' );
59             ORM->mk_classdata( '_default_cache_size' );
60             ORM->mk_classdata( '_current_transaction' );
61              
62             ##
63             ## CONSTRUCTORS
64             ##
65              
66             ## use: $obj = $class->new
67             ## (
68             ## prop => { prop => [string|OBJECT] ... },
69             ## error => ORM::Error,
70             ## temporary => boolean,
71             ## suspended => boolean,
72             ## history => boolean,
73             ## )
74             ##
75             ## 'temporary' - if set to true, then created object will
76             ## not be stored in database.
77             ## You can store that kind of objects later using method
78             ## $object->make_permanent.
79             ##
80             ## 'suspended' - if set to true, then constructor's behavior
81             ## is similar to those with 'temporary'=1 but after creation
82             ## object appended to the internal list of suspended objects.
83             ##
84             ## Later you can flush all suspended objects into database
85             ## at one time by calling $class->flush_suspended. This allows to
86             ## optimize write of objects into database by means of database
87             ## server, e.g. ORM::Db::DBI::MySQL storage engine will use
88             ## multiple-rows form of INSERT statement:
89             ##
90             ## INSERT INTO table (a,b,c) VALUES (1,1,1),(2,2,2),(3,3,3)...
91             ##
92             sub new
93             {
94 21     21 1 148 my $class = shift;
95 21         85 my %arg = @_;
96 21         96 my $error = ORM::Error->new;
97 21         133 my $ta = $class->new_transaction( error=>$error );
98 21         42 my $self = {};
99 21 50       173 my $history = defined $arg{history} ? $arg{history} : $class->history_is_enabled;
100              
101 21 50       410 if( $class->_is_intermediate )
102             {
103 0         0 $error->add_fatal( "Can't create instance of intermediate class" );
104             }
105              
106 21 50       246 unless( $error->fatal )
107             {
108 21         44 my $prop;
109              
110 21         71 bless $self, $class;
111              
112 21 50       78 $self->{_ORM_tpm} = 1 if( $arg{temporary} );
113              
114             # Extract required DB properties from %arg
115 21         134 for $prop ( $class->_not_mandatory_props )
116             {
117 160 50       981 $self->{_ORM_data}{$prop} = $self->_normalize_prop_to_db_value
118             (
119             name => $prop,
120             error => $error,
121             value =>
122             (
123             exists $arg{prop}{$prop}
124             ? $arg{prop}{$prop}
125             : $class->_prop_default_value( $prop )
126             ),
127             );
128             }
129             }
130              
131 21 50       111 unless( $error->fatal )
132             {
133             # Check validity of object data
134 21         128 $self->_validate_prop( prop=>$self->{_ORM_data}, method=>'new', error=>$error );
135             }
136              
137 21 50 33     145 if( ! $arg{temporary} && ! $error->fatal )
138             {
139 21         108 $self->{_ORM_data}{id} = $class->_db->insert_object
140             (
141             id => $arg{repair_id},
142             object => $self,
143             error => $error,
144             );
145 21 50 33     113 if( ! $error->fatal && ! $self->{_ORM_data}{id} )
146             {
147 0         0 $error->add_fatal( "Failed to detect id of newly created object of class '$class'" );
148             }
149              
150             # Make record in history
151 21 100 66     76 if( !$error->fatal && $history )
152             {
153 2         9 $class->_history_class->new( obj=>$self, created=>1, error=>$error );
154             }
155              
156             # Cache object
157 21 50       81 $self->_cache->add( $self ) unless( $error->fatal );
158             }
159              
160 21         99 $error->upto( $arg{error} );
161 21 50       73 return $error->fatal ? undef : $self;
162             }
163              
164             ## use: $count = $class->count
165             ## (
166             ## filter => ORM::Filter,
167             ## error => ORM::Error,
168             ## )
169             ##
170             sub count
171             {
172 2     2 1 5 my $class = shift;
173 2         11 $class->_db->count( class=>$class, @_ );
174             }
175              
176             sub exists
177             {
178 0     0 0 0 my $class = shift;
179 0         0 my %arg = @_;
180              
181 0         0 return $class->count
182             (
183             filter => ( $class->M->id == $arg{id} ),
184             error => $arg{error},
185             );
186             }
187              
188             ## use: @obj = $class->find
189             ## (
190             ## filter => ORM::Filter,
191             ## order => ORM::Order,
192             ## lazy_load => boolean,
193             ## page => integer,
194             ## pagesize => integer,
195             ## error => ORM::Error,
196             ## return_ref => boolean,
197             ## return_res => boolean,
198             ## )
199             ##
200             ## If called in scalar context returns first object from result set.
201             ##
202             ## If called in array context returns array of found objects.
203             ##
204             ## If 'return_ref' is true then return value is reference to the array
205             ## of found objects with no respect to context.
206             ##
207             ## If 'return_res' is true then return value is object of class ORM::ResultSet,
208             ## found objects can be accesed one by one via $result->next. It is useful to
209             ## retrieve large amount of objects. Pays no respect to context and 'return_ref'.
210             ##
211             ## If 'pagesize' and 'page' is specified then result set is divided to pages
212             ## with 'pagesize' object per page and only page numbered 'page' will be returned.
213             ## First page number is 1.
214             ##
215             ## If 'lazy_load' specified then only data from tables corresponding to base class
216             ## $class will be loaded initially.
217             ##
218             sub find
219             {
220 2     2 1 5 my $class = shift;
221 2         11 my %arg = @_;
222 2         8 my $error = ORM::Error->new;
223 2   33     12 my $page = defined $arg{page} && int( $arg{page} );
224 2   33     9 my $pagesize = defined $arg{pagesize} && int( $arg{pagesize} );
225 2 50       9 my $lazy_load = defined $arg{lazy_load} ? $arg{lazy_load} : $class->prefer_lazy_load;
226 2 50       8 my $order = ( ref $arg{order} eq 'ARRAY' ) ? ORM::Order->new( @{$arg{order}} ) : $arg{order};
  0         0  
227 2         4 my @obj;
228             my $res;
229              
230 2 50 33     23 if( !wantarray && !$arg{return_ref} && !$arg{return_res} )
      33        
231             {
232 2         5 $page = ($page-1)*$pagesize+1;
233 2         4 $pagesize = 1;
234             }
235              
236 2 100 66     10 if( $class->_is_sealed || $lazy_load || $arg{return_res} )
      66        
237             {
238 1         19 $res = ORM::ResultSet->new
239             (
240             class => $class,
241             result => $class->_db->select_base
242             (
243             class => $class,
244             filter => $arg{filter},
245             order => $order,
246             page => $page,
247             pagesize => $pagesize,
248             error => $error,
249             ),
250             );
251 1 50 33     10 unless( $arg{return_res} || $error->fatal )
252             {
253 1         3 my $obj;
254 1         6 while( $obj = $res->next ) { push @obj, $obj; }
  1         4  
255             }
256             }
257             else
258             {
259 1         47 $res = $class->_db->select_full
260             (
261             class => $class,
262             filter => $arg{filter},
263             order => $order,
264             page => $page,
265             pagesize => $pagesize,
266             error => $error,
267             );
268 1 50       4 unless( $error->fatal )
269             {
270 1         1 my $data;
271             my $obj;
272 1         6 while( $data = $res->next_row )
273             {
274 1 50       5 if( ref $data eq 'HASH' )
275             {
276 0         0 $obj = bless { _ORM_data=>$data }, $data->{class};
277 0         0 delete $obj->{_ORM_data}{class};
278 0         0 $class->_cache->add( $obj );
279             }
280             else
281             {
282 1         3 $obj = $data;
283             }
284 1         4 push @obj, $obj;
285             }
286             }
287             }
288              
289 2         9 $error->upto( $arg{error} );
290            
291             return
292 2 50       136 $arg{return_res}
    50          
    50          
293             ? $res
294             : ( $arg{return_ref} ? \@obj : ( wantarray ? ( @obj ) : $obj[0] ) );
295             }
296              
297             ## use: $obj = $class->find_id
298             ## (
299             ## id => integer,
300             ## lazy_load => boolean,
301             ## error => ORM::Error,
302             ## );
303             ##
304             sub find_id
305             {
306 3     3 1 8 my $class = shift;
307 3         15 my %arg = @_;
308 3         6 my $self;
309              
310 3         11 $self = $class->_cache->get( $arg{id} );
311              
312 3 50       16 unless( $self )
313             {
314 3         15 $self = { _ORM_data=>{ id=>$arg{id} } };
315 3         18 for my $table ( $class->_db_tables )
316             {
317 4 50       48 if( scalar $class->_db_table_fields( $table ) )
318             {
319 4         63 $self->{_ORM_missing_tables}{$table} = 1;
320             }
321             }
322 3         15 bless $self, $class;
323              
324 3 100       13 unless( $arg{lazy_load} )
325             {
326 1         5 my $error = ORM::Error->new;
327 1         7 $self->finish_loading( error=>$error );
328 1 50 33     9 $self = undef if( ref $self eq 'ORM::Broken' || $error->fatal );
329 1         6 $error->upto( $arg{error} );
330             }
331              
332 3 50       20 $self && $class->_cache->add( $self );
333             }
334              
335 3         15 return $self;
336             }
337              
338             ## use: $obj = $class->find_or_new
339             ## (
340             ## prop => { prop_name => [string|OBJECT] ... },
341             ## lazy_load => boolean,
342             ## history => boolean,
343             ## error => ORM::Error,
344             ## )
345             ##
346             sub find_or_new
347             {
348 0     0 1 0 my $class = shift;
349 0         0 my %arg = @_;
350 0         0 my $error = ORM::Error->new;
351 0         0 my $filter = ORM::Expr->_and;
352 0         0 my @obj;
353              
354 0         0 for my $prop ( keys %{$arg{prop}} )
  0         0  
355             {
356 0 0       0 if( $class->_has_prop( $prop ) )
357             {
358 0         0 $filter->add_expr( $class->M->_prop( $prop ) == $arg{prop}{$prop} );
359             }
360             else
361             {
362 0         0 $error->add_fatal( "Non-existing prop '$prop' specified" );
363 0         0 last;
364             }
365             }
366              
367 0 0       0 unless( $error->fatal )
368             {
369 0         0 @obj = $class->find
370             (
371             filter => $filter,
372             error => $error,
373             pagesize => 2,
374             lazy_load => $arg{lazy_load},
375             );
376             }
377 0 0       0 unless( $error->fatal )
378             {
379 0 0       0 if( @obj > 1 )
380             {
381 0         0 $error->add_fatal( "More than 1 object were found" );
382             }
383             }
384 0 0       0 unless( $error->fatal )
385             {
386 0 0       0 if( ! @obj )
387             {
388 0         0 $obj[0] = $class->new( prop=>$arg{prop}, history=>$arg{history}, error=>$error );
389             }
390             }
391              
392 0         0 $error->upto( $arg{error} );
393 0 0       0 return $error->fatal ? undef : $obj[0];
394             }
395              
396             ##
397             ## OBJECT METHODS
398             ##
399              
400             ## use: $ta = $class->new_transaction( error=>ORM::Error );
401             ##
402             ## Begins transaction.
403             ## Transaction commits when object $ta is destroyed.
404             ##
405             sub new_transaction
406             {
407 57     57 1 122 my $class = shift;
408 57         239 my $iclass = $class->initial_class;
409 57         607 my %arg = @_;
410              
411 57         360 ORM::Ta->new( class=>$iclass, error=>$arg{error} );
412             }
413              
414             ## use: $self->update
415             ## (
416             ## prop => HASH,
417             ## old_prop => HASH,
418             ## history => boolean,
419             ## error => ORM::Error,
420             ## )
421             ##
422             sub update
423             {
424 6     6 1 197 my $self = shift;
425 6         18 my $class = ref $self;
426 6         25 my %arg = @_;
427 6         27 my $error = ORM::Error->new;
428 6         31 my $ta = $class->new_transaction( error=>$error );
429 6 50       57 my $history = defined $arg{history} ? $arg{history} : $class->history_is_enabled;
430 6         106 my %changed_prop;
431             my %expr_prop;
432 0         0 my %old_prop;
433              
434 6         37 $self->finish_loading( error=>$error );
435              
436             # Check if current properties match to those in 'old_prop' argument
437 6 50       19 unless( $error->fatal )
438             {
439 6         11 %old_prop = %{$self->{_ORM_data}};
  6         50  
440 6 100       25 if( $arg{old_prop} )
441             {
442 2         4 for my $prop ( keys %{$arg{old_prop}} )
  2         9  
443             {
444 4         19 my $old_normalized = $self->_normalize_prop_to_db_value
445             (
446             name => $prop,
447             value => $arg{old_prop}{$prop},
448             error => $error,
449             );
450 4 50       15 last if( $error->fatal );
451 4 100       14 if( $self->_values_are_not_equal( $self->{_ORM_data}{$prop}, $old_normalized ) )
452             {
453 1         6 $error->add_fatal
454             (
455             'Current properties of object #'.$self->id
456             . ' of class "'.$class.'" do not match '
457             . 'properties assumed by user',
458             );
459 1         3 last;
460             }
461             }
462             }
463             }
464              
465             # Detect data changes
466 6 100       23 unless( $error->fatal )
467             {
468 5         24 for my $prop ( $class->_not_mandatory_props )
469             {
470 21 100       101 if( exists $arg{prop}{$prop} )
471             {
472 7 100       65 if( UNIVERSAL::isa( $arg{prop}{$prop}, 'ORM::Expr' ) )
473             {
474 1         6 $expr_prop{$prop} = $arg{prop}{$prop};
475             }
476             else
477             {
478 6         29 my $new_normalized = $self->_normalize_prop_to_db_value
479             (
480             name => $prop,
481             value => $arg{prop}{$prop},
482             error => $error,
483             );
484 6 50       20 last if( $error->fatal );
485 6 50       46 if( $self->_values_are_not_equal( $self->{_ORM_data}{$prop}, $new_normalized ) )
486             {
487 6         14 $changed_prop{$prop} = 1;
488 6         15 $self->{_ORM_data}{$prop} = $new_normalized;
489 6         23 delete $self->{_ORM_cache}{$prop};
490             }
491             }
492             }
493             }
494             }
495             # User validations
496 6 100 66     33 if( %changed_prop && !$error->fatal )
497             {
498 4         21 $self->_validate_prop( prop=>\%changed_prop, old=>\%old_prop, method=>'update', error=>$error );
499             }
500             # Detect data changes again to consider changes in _validate_prop
501 6 100       24 unless( $error->fatal )
502             {
503 5         14 %changed_prop = ();
504 5         16 for my $prop ( $class->_not_mandatory_props )
505             {
506 21 100       95 if( $self->_values_are_not_equal( $old_prop{$prop}, $self->{_ORM_data}{$prop} ) )
    100          
507             {
508 6         15 $changed_prop{$prop} = $self->{_ORM_data}{$prop};
509 6 50       20 delete $expr_prop{$prop} if( exists $expr_prop{$prop} );
510             }
511             elsif( exists $expr_prop{$prop} )
512             {
513 1         5 $changed_prop{$prop} = $expr_prop{$prop};
514             }
515             }
516             }
517              
518 6 50 66     43 if( !$self->is_temporary && !$error->fatal && scalar( %changed_prop ) )
      100        
519             {
520 5         16 for my $prop ( keys %expr_prop )
521             {
522 1         8 $self->{_ORM_missing_tables}{ $class->_prop2table($prop) }{$prop} = 1;
523             }
524              
525             # Update object
526 5 50       27 unless( $error->fatal )
527             {
528 5         21 $class->_db->update_object
529             (
530             object => $self,
531             values => \%changed_prop,
532             old_values => \%old_prop,
533             error => $error,
534             );
535             }
536              
537             # Save changes to history
538 5 50 33     41 if( $history && !$error->fatal )
539             {
540 5         24 $self->finish_loading( error=>$error );
541             }
542 5 50 33     107 if( $history && !$error->fatal )
543             {
544 5         12 my %history;
545 5         19 for my $prop_name ( keys %changed_prop )
546             {
547 7         45 $history{$prop_name} =
548             [
549             $old_prop{$prop_name},
550             $self->{_ORM_data}{$prop_name}
551             ];
552             }
553             $class->_history_class->new
554             (
555 5         29 error => $error,
556             obj => $self,
557             changed => \%history,
558             );
559             }
560             }
561              
562 6 100       45 if( $error->fatal )
563             {
564             # Roll back update action if error occured
565 1         2 $self->{_ORM_data} = \%old_prop;
566             }
567              
568 6         30 $error->upto( $arg{error} );
569 6         48 return undef;
570             }
571              
572             ## use: $self->delete( error=>ORM::Error, history=>boolean )
573             ##
574             sub delete
575             {
576 2     2 1 152 my $self = shift;
577 2         6 my $class = ref $self;
578 2         7 my %arg = @_;
579 2         9 my $error = ORM::Error->new;
580 2         13 my $ta = $class->new_transaction( error=>$error );
581 2 50       20 my $history = defined $arg{history} ? $arg{history} : $class->history_is_enabled;
582              
583 2 50       44 unless( $self->is_temporary )
584             {
585 2 50       8 unless( $error->fatal )
586             {
587             # Make record in history
588 2 50       8 if( $history )
589             {
590 2         8 $class->_history_class->new( obj=>$self, deleted=>1, error=>$error );
591             }
592             }
593 2 50       9 unless( $error->fatal )
594             {
595 2         12 $class->_db->delete_object
596             (
597             object => $self,
598             error => $error,
599             emulate_foreign_keys => $class->_emulate_foreign_keys,
600             );
601             }
602 2 50       9 unless( $error->fatal )
603             {
604 2         25 $self->_rebless_to_broken( deleted=>1 );
605             }
606             }
607              
608 2         11 $error->upto( $arg{error} );
609 2         7 return undef;
610             }
611              
612             ## use: $object->refresh( error=>ORM::Error );
613             ##
614             sub refresh
615             {
616 0     0 1 0 my $self = shift;
617 0         0 my $class = ref $self;
618 0         0 my %arg = @_;
619              
620 0         0 $self->{_ORM_data} = { id=>$self->id };
621 0         0 delete $self->{_ORM_cache};
622 0         0 for my $table ( $class->_db_tables )
623             {
624 0 0       0 if( scalar $class->_db_table_fields( $table ) )
625             {
626 0         0 $self->{_ORM_missing_tables}{$table} = 1;
627             }
628             }
629              
630 0         0 $self->finish_loading( error=>$arg{error} );
631             }
632              
633             ## use: $object->finish_loading
634             ## or
635             ## use: $object->finish_loading( error=>ORM::Error );
636             ##
637             ## First form will rebless object to 'ORM::Broken' in case of error.
638             ##
639             sub finish_loading
640             {
641 16     16 1 32 my $self = shift;
642 16         41 my $class = ref $self;
643 16         53 my %arg = @_;
644 16         28 my $new_class;
645 16         40 my $prop = $arg{prop};
646 16   66     71 my $prop_table = $prop && $class->_prop2table( $prop );
647              
648 16 50 66     241 if
      66        
649             (
650             exists $self->{_ORM_missing_tables}
651             &&
652             (
653             ! defined $prop
654             ||
655             (
656             defined $prop_table
657             && $self->{_ORM_missing_tables}{$prop_table}
658             &&
659             (
660             !( ref $self->{_ORM_missing_tables}{$prop_table} eq 'HASH' )
661             || $self->{_ORM_missing_tables}{$prop_table}{$prop}
662             )
663             )
664             )
665             )
666             {
667 7         30 my $error = ORM::Error->new;
668 7         39 my $data = $class->_db->select_tables
669             (
670             id => $self->qc( $self->id ),
671             tables => $self->{_ORM_missing_tables},
672             error => $error,
673             );
674              
675 7   33     57 $data = $data && $data->next_row;
676              
677 7 50       290 if( $error->fatal )
    50          
678             {
679 0 0       0 if( $arg{error} )
680             {
681 0         0 $arg{error}->add( error=>$error );
682             }
683             else
684             {
685 0         0 $self->_rebless_to_broken( error=>$error );
686             }
687             }
688             elsif( !$data )
689             {
690 0         0 $self->_rebless_to_broken( deleted=>1 );
691             }
692             else
693             {
694 7         25 delete $self->{_ORM_missing_tables};
695              
696             # Fetch loaded properties
697 7 100       29 if( exists $data->{class} )
698             {
699 3         9 $new_class = $data->{class};
700 3         8 delete $data->{class};
701             }
702 7         29 for my $prop ( keys %$data )
703             {
704 24         218 $self->{_ORM_data}{$prop} = $data->{$prop};
705             }
706             }
707             }
708              
709             # If actual class of object is different than blessed class,
710             # then rebless object and upload residual tables if needed
711 16 100 100     103 if( $new_class && $new_class ne $class )
712             {
713 2         12 $class->_load_ORM_class( $new_class );
714              
715 2 50       36 if( UNIVERSAL::isa( $new_class, $class ) )
716             {
717 2         6 bless $self, $new_class;
718              
719 2         13 my $base_class_tables = $class->_db_tables_count;
720 2         24 my $class_tables = $new_class->_db_tables_count;
721              
722 2         22 for( my $i=$base_class_tables; $i<$class_tables; $i++ )
723             {
724 2         13 $self->{_ORM_missing_tables}{$new_class->_db_table($i)} = 1;
725             }
726              
727 2 100       39 $self->finish_loading( error=>$arg{error} ) unless( defined $prop );
728             }
729             else
730             {
731 0         0 $self->_rebless_to_broken( deleted=>1 );
732             }
733             }
734             }
735              
736             ##
737             ## PROPERTIES
738             ##
739              
740 89     89 1 710 sub id { $_[0]->{_ORM_data}{id}; }
741 0 0   0 1 0 sub class { ref $_[0] || $_[0]; }
742 8     8 1 50 sub is_temporary { $_[0]->{_ORM_tpm}; }
743              
744 0     0   0 sub __ORM_db_value { $_[0]->{_ORM_data}{id}; }
745             sub __ORM_new_db_value
746             {
747 0     0   0 my $class = shift;
748 0         0 my %arg = @_;
749 0         0 my $self;
750              
751 0 0       0 if( defined $arg{value} )
752             {
753 0         0 $self = $class->find_id( id=>$arg{value}, error=>$arg{error}, lazy_load=>$arg{lazy_load} );
754             }
755              
756 0         0 return $self;
757             }
758              
759             sub _class_info
760             {
761 1360   66 1360   6934 my $class = ref $_[0] || $_[0];
762 1360         4002 $class->_class_hier->{$class};
763             }
764              
765 0     0 1 0 sub base_class { $_[0]->_class_info->{BASE_CLASS}; }
766 40     40 1 140 sub primary_class { $_[0]->_class_info->{PRIMARY_CLASS}; }
767 57 50   57 1 259 sub initial_class { $_[0]->_is_initial ? $_[0] : $_[0]->_class_info->{INITIAL_CLASS}; }
768              
769             sub M
770             {
771 21     21 1 141 my $self = shift;
772 21   66     91 my $class = ref $self || $self;
773 21         34 my $prop = shift;
774              
775 21 100       60 if( $prop )
776             {
777 7         40 ORM::Metaprop->_new( prop_class=>$class, prop=>$prop );
778             }
779             else
780             {
781 14         130 ORM::Metaprop->_new_flat( class=>$class );
782             }
783             }
784              
785             ## use: $value = -$object->P( error=>$error )->prop1->prop2->prop3;
786             ##
787             sub P
788             {
789 0     0 1 0 my $self = shift;
790 0         0 my %arg = @_;
791              
792 0         0 ORM::MetapropBuilder->new
793             (
794             prop_class => (ref $self),
795             need_value => $self,
796             error => $arg{error},
797             );
798             }
799              
800 18     18 0 53 sub metaprop_class { $_[0]->_class_info->{METAPROP_CLASS}; }
801              
802 0     0 0 0 sub ql { $_[0]->_db->ql( $_[1] ); }
803 25     25 0 97 sub qc { $_[0]->_db->qc( $_[1] ); }
804 1     1 0 6 sub qi { $_[0]->_db->qi( $_[1] ); }
805 94     94 0 584 sub qt { $_[0]->_db->qt( $_[1] ); }
806 0     0 0 0 sub qf { $_[0]->_db->qf( $_[1] ); }
807              
808             ## use: $state = $class->history_is_enabled;
809             ## use: $state = $class->history_is_enabled( $new_state );
810             ##
811             ## If $new_state is specified then value of flag
812             ## 'history_is_enabled' will be replaced to $new_state.
813             ## $new_state can be undef, in that case global default value
814             ## will be used instead.
815             ##
816             sub history_is_enabled
817             {
818 29     29 1 76 my $class = shift;
819              
820 29 50       114 if( @_ )
821             {
822 0 0       0 if( defined $_[0] )
823             {
824 0 0       0 if( $class->_class_info )
825             {
826 0         0 $class->_class_info->{HISTORY_IS_ENABLED} = $_[0];
827             }
828             else
829             {
830 0         0 croak "Can't change global history settings";
831             }
832             }
833             else
834             {
835 0 0       0 delete $class->_class_info->{HISTORY_IS_ENABLED} if( $class->_class_info );
836             }
837             }
838              
839 29 100       89 exists $class->_class_info->{HISTORY_IS_ENABLED}
840             ? $class->_class_info->{HISTORY_IS_ENABLED}
841             : $class->_history_class;
842             }
843              
844             ## use: $state = $class->prefer_lazy_load;
845             ## use: $state = $class->prefer_lazy_load( $new_state );
846             ##
847             ## If $new_state is specified then value of flag
848             ## 'prefer_lazy_load' will be replaced to $new_state.
849             ## $new_state can be undef, in that case global default value
850             ## will be used instead.
851             ##
852             sub prefer_lazy_load
853             {
854 0     0 1 0 my $class = shift;
855              
856 0 0       0 if( @_ )
857             {
858 0 0       0 if( defined $_[0] )
859             {
860 0         0 $class->_class_info->{PREFER_LAZY_LOAD} = $_[0];
861             }
862             else
863             {
864 0         0 delete $class->_class_info->{PREFER_LAZY_LOAD};
865             }
866             }
867              
868 0 0       0 exists $class->_class_info->{PREFER_LAZY_LOAD}
869             ? $class->_class_info->{PREFER_LAZY_LOAD}
870             : $class->_default_prefer_lazy_load;
871             }
872              
873             sub _plain_prop
874             {
875 195     195   2426 my $class = shift;
876 195         292 my $prop = shift;
877              
878 195 50       515 exists( $class->_class_info->{PROP}{$prop} )
879             && ( ! $class->_class_info->{PROP}{$prop} );
880             }
881             sub _prop_is_ref
882             {
883 71     71   552 my $class = shift;
884 71         121 my $prop = shift;
885 71         203 my $pclass = $class->_prop_class( $prop );
886              
887 71 100 100     784 $pclass && $class->_class_hier->{$pclass} && $pclass;
888             }
889              
890 30     30   118 sub _is_sealed { $_[0]->_class_info->{SEALED}; }
891 133     133   368 sub _prop_class { $_[0]->_class_info->{PROP}{$_[1]}; }
892 0     0   0 sub _prop_default_value { $_[0]->_class_info->{PROP_DEFAULT_VALUE}{$_[1]}; }
893 211     211   694 sub _has_prop { exists $_[0]->_class_info->{PROP}{$_[1]}; }
894 73     73   190 sub _prop2table { $_[0]->_class_info->{PROP2TABLE_MAP}{$_[1]}; }
895 0     0   0 sub _prop2field { $_[0]->_class_info->{PROP2FIELD_MAP}{$_[1]}; }
896 21     21   77 sub _is_intermediate { $_[0]->_class_info->{INTERMEDIATE}; }
897 96     96   368 sub _is_initial { !$_[0]->_class_info; }
898 10     10   47 sub _db_table { $_[0]->_class_info->{TABLE}[$_[1]]; }
899 0     0   0 sub _db_tables_str { $_[0]->_class_info->{TABLES_STR}; }
900 7     7   16 sub _db_tables_count { scalar( @{$_[0]->_class_info->{TABLE}} ); }
  7         22  
901 65     65   121 sub _db_tables { @{$_[0]->_class_info->{TABLE}}; }
  65         256  
902 2     2   7 sub _db_tables_ref { $_[0]->_class_info->{TABLE}; }
903 26     26   42 sub _db_table_fields { keys %{$_[0]->_class_info->{TABLE_STRUCT}{$_[1]}}; }
  26         80  
904 0     0   0 sub _db_tables_inner_join { $_[0]->_class_info->{TABLES_INNER_JOIN}; }
905 33     33   54 sub _not_mandatory_props { keys %{$_[0]->_class_info->{PROP2FIELD_MAP}}; }
  33         100  
906 4     4   7 sub _all_props { ( 'id', 'class', keys %{$_[0]->_class_info->{PROP2FIELD_MAP}} ); }
  4         14  
907 40     40   2933 sub _cache { $_[0]->primary_class->_class_info->{CACHE}; }
908              
909             sub _rev_refs
910             {
911 6     6   26 my $class = shift;
912 6         8 my @refs = values %{$class->_class_info->{REV_REFS}};
  6         20  
913              
914 6 100       65 if( $class->_class_info->{BASE_CLASS} )
915             {
916 2         23 push @refs, $class->_class_info->{BASE_CLASS}->_rev_refs;
917             }
918              
919 6         61 return @refs;
920             }
921              
922             sub _has_rev_ref
923             {
924 0     0   0 my $class = shift;
925 0         0 my $rev_class = shift;
926 0         0 my $rev_prop = shift;
927              
928 0 0 0     0 $class->_class_info->{REV_REFS}{ $rev_class.' '.$rev_prop }
      0        
      0        
929             || (
930             $rev_class->base_class
931             && $class->_has_rev_ref( $rev_class->base_class, $rev_prop )
932             )
933             || (
934             $class->base_class
935             && $class->base_class->_has_rev_ref( $rev_class, $rev_prop )
936             );
937             }
938              
939             ## use: $class->stat
940             ## (
941             ## data => { alias=>ORM::Expr, ... },
942             ## preload => { alias=>boolean, ... },
943             ## filter => ORM::Expr,
944             ## group_by => [ ORM::Ident|ORM::Metaprop, ... ],
945             ## post_filter => ORM::Expr,
946             ## order => ORM::Order,
947             ## lazy_load => boolean,
948             ## page => integer,
949             ## pagesize => integer,
950             ## count => boolean,
951             ## error => ORM::Error,
952             ## return_res => boolean,
953             ## )
954             ##
955             sub stat
956             {
957 1     1 1 3 my $class = shift;
958 1         7 my %arg = @_;
959 1         7 my $error = ORM::Error->new;
960 1   33     5 my $page = defined $arg{page} && int( $arg{page} );
961 1   33     6 my $pagesize = defined $arg{pagesize} && int( $arg{pagesize} );
962 1 50       5 my $order = ( ref $arg{order} eq 'ARRAY' ) ? ORM::Order->new( @{$arg{order}} ) : $arg{order};
  0         0  
963 1 50       4 my %preload = $arg{preload} ? %{$arg{preload}} : ();
  1         13  
964 1         3 my %data;
965             my %conv;
966 0         0 my $res;
967              
968 1 50       2 if( ! %{$arg{data}} )
  1         11  
969             {
970 0         0 $error->add_fatal( "'data' argument is missing" );
971             }
972              
973 1 50       6 unless( $error->fatal )
974             {
975             # Prepare type converstions
976 1 50       8 if( $arg{count} )
    50          
977             {
978 0         0 %data = %{$arg{data}};
  0         0  
979             }
980             elsif( %preload )
981             {
982 1         2 for my $name ( keys %{$arg{data}} )
  1         4  
983             {
984 2 50 33     18 if( ! UNIVERSAL::isa( $arg{data}{$name}, 'ORM::Metaprop' ) )
    50          
985             {
986 0         0 $conv{$name} = undef;
987 0         0 $data{$name} = $arg{data}{$name};
988 0         0 delete $preload{$name};
989             }
990             elsif( $arg{data}{$name}->_prop_ref_class && $preload{$name} )
991             {
992 2         9 $conv{$name} = $arg{data}{$name}->_prop_class;
993 2         9 for my $prop ( $arg{data}{$name}->_prop_ref_class->_all_props )
994             {
995 13 100       49 if( $prop eq 'id' )
996             {
997 2         9 $data{$name} = $arg{data}{$name}->_prop( $prop );
998             }
999             else
1000             {
1001 11         35 $data{"_${name} ${prop}"} = $arg{data}{$name}->_prop( $prop );
1002             }
1003             }
1004             }
1005             else
1006             {
1007 0         0 $conv{$name} = $arg{data}{$name}->_prop_class;
1008 0         0 $data{$name} = $arg{data}{$name};
1009 0         0 delete $preload{$name};
1010             }
1011             }
1012             }
1013             else
1014             {
1015 0         0 %data = %{$arg{data}};
  0         0  
1016 0         0 for my $name ( keys %data )
1017             {
1018 0 0 0     0 if
1019             (
1020             UNIVERSAL::isa( $data{$name}, 'ORM::Metaprop' )
1021             && $data{$name}->_prop_class
1022             )
1023             {
1024 0         0 $conv{$name} = $data{$name}->_prop_class;
1025             }
1026             else
1027             {
1028 0         0 $conv{$name} = undef;
1029             }
1030             }
1031             }
1032              
1033             # Fetch result set
1034 1         11 $res = $class->_db->select_stat
1035             (
1036             class => $class,
1037             data => \%data,
1038             filter => $arg{filter},
1039             post_filter => $arg{post_filter},
1040             group_by => $arg{group_by},
1041             order => $order,
1042             page => $page,
1043             pagesize => $pagesize,
1044             error => $error,
1045             );
1046             }
1047              
1048             # Final step, prepare resulting data
1049 1 50 33     19 if( $res && !$error->fatal )
1050             {
1051 1 50       5 if( $arg{count} )
1052             {
1053 0         0 $res = $res->rows;
1054             }
1055             else
1056             {
1057 1         14 $res = ORM::StatResultSet->new
1058             (
1059             class => $class,
1060             result => $res,
1061             preload => \%preload,
1062             conv => \%conv,
1063             lazy_load => $arg{lazy_load},
1064             );
1065 1 50       6 if( !$arg{return_res} )
1066             {
1067 1         3 my @stat;
1068             my $stat;
1069              
1070 1         7 while( $stat = $res->next( error=>$error ) )
1071             {
1072 1 50       5 if( $error->fatal )
1073             {
1074 0         0 @stat = ();
1075 0         0 last;
1076             }
1077 1         4 push @stat, $stat;
1078             }
1079              
1080 1         4 $res = \@stat;
1081             }
1082             }
1083             }
1084              
1085 1         68 $error->upto( $arg{error} );
1086 1         91 return $res;
1087             }
1088              
1089             ## use: $prop = $obj->_property
1090             ## (
1091             ## name => string,
1092             ## error => ORM::Error,
1093             ## );
1094             ##
1095             ## 'name' - is name of the property corresponding to field name in DB table
1096             ##
1097             ## $prop - is either plain property,
1098             ## either object referenced by id in DB,
1099             ## or object referenced by value in DB
1100             ##
1101 0     0   0 sub _prop { shift->_property( @_ ); }
1102             sub _property
1103             {
1104 25     25   44 my $self = shift;
1105 25 50       181 my %arg = ( @_ == 1 ) ? () : @_;
1106 25 50       97 my $prop = ( @_ == 1 ) ? $_[0] : $arg{name};
1107 25         56 my $class = ref $self;
1108 25         119 my $error = ORM::Error->new;
1109 25         46 my $res;
1110             my $pclass;
1111              
1112              
1113 25 50       93 if( exists $arg{new_value} )
1114             {
1115 0         0 $self->update( prop=>{ $prop=>$arg{new_value} }, error=>$error );
1116             }
1117             else
1118             {
1119 25 100       97 if( exists $self->{_ORM_missing_tables} )
1120             {
1121 3         22 $self->finish_loading( prop=>$prop, error=>$error );
1122             }
1123              
1124 25 50       91 unless( $error->fatal )
1125             {
1126 25 50 33     176 if( $prop eq 'class' && $class->_is_sealed )
    50          
    0          
1127             {
1128 0         0 $res = $class;
1129             }
1130             elsif( $class->_plain_prop( $prop ) )
1131             {
1132 25         317 $res = $self->{_ORM_data}{$prop};
1133             }
1134             elsif( $pclass = $class->_prop_class( $prop ) )
1135             {
1136 0 0       0 if( defined $self->{_ORM_data}{$prop} )
1137             {
1138 0 0       0 unless( exists $self->{_ORM_cache}{$prop} )
1139             {
1140 0         0 $self->{_ORM_cache}{$prop} = $pclass->__ORM_new_db_value
1141             (
1142             value => $self->{_ORM_data}{$prop},
1143             error => $error,
1144             );
1145             }
1146 0         0 $res = $self->{_ORM_cache}{$prop};
1147             }
1148             }
1149             }
1150             }
1151              
1152 25         142 $error->upto( $arg{error} );
1153 25         562 return $res;
1154             }
1155              
1156             ## use: $prop = $obj->_property_id
1157             ## (
1158             ## name => string,
1159             ## error => ORM::Error,
1160             ## );
1161             ##
1162             ## 'name' - is name of the property corresponding to field name in DB table
1163             ##
1164             ## $prop - is either plain property,
1165             ## either object referenced by id in DB,
1166             ## or object referenced by value in DB
1167             ##
1168 0     0   0 sub _prop_id { shift->_property_id( @_ ); }
1169             sub _property_id
1170             {
1171 160     160   216 my $self = shift;
1172 160         191 my %arg;
1173             my $prop;
1174 0         0 my $value;
1175              
1176 160 50       406 if( @_ == 1 )
1177             {
1178 160         425 $prop = $_[0];
1179             }
1180             else
1181             {
1182 0         0 %arg = @_;
1183 0         0 $prop = $arg{name};
1184             }
1185              
1186 160 50       279 if( $prop eq 'class' )
1187             {
1188 0         0 $value = $self->class;
1189             }
1190             else
1191             {
1192 160 50       422 if( exists $self->{_ORM_missing_tables} )
1193             {
1194 0         0 $self->finish_loading( prop=>$prop, error=>$arg{error} );
1195             }
1196 160         305 $value = $self->{_ORM_data}{$prop};
1197             }
1198              
1199 160         573 return $value;
1200             }
1201              
1202 0     0   0 sub _rev { shift->_rev_prop( @_ ); }
1203             sub _rev_prop
1204             {
1205 0     0   0 my $self = shift;
1206 0         0 my $rev_class = shift;
1207 0         0 my $rev_prop = shift;
1208 0         0 my %arg = @_;
1209              
1210 0 0       0 if( (ref $self)->_has_rev_ref( $rev_class, $rev_prop ) )
1211             {
1212 0         0 $arg{filter} = $arg{filter} & ( $rev_class->M->_prop( $rev_prop ) == $self );
1213 0         0 $rev_class->find( %arg );
1214             }
1215             }
1216              
1217 0     0   0 sub _rev_count { shift->_rev_prop_count( @_ ); }
1218             sub _rev_prop_count
1219             {
1220 0     0   0 my $self = shift;
1221 0         0 my $rev_class = shift;
1222 0         0 my $rev_prop = shift;
1223 0         0 my %arg = @_;
1224              
1225 0 0       0 if( (ref $self)->_has_rev_ref( $rev_class, $rev_prop ) )
1226             {
1227 0         0 $arg{filter} = $arg{filter} & ( $rev_class->M->_prop( $rev_prop ) == $self );
1228 0         0 $rev_class->count( %arg );
1229             }
1230             }
1231              
1232             ## use: $prop = $obj->prop( error=>ORM::Error, new_value=>SCALAR );
1233             ##
1234             ## 'prop' - is name of the property corresponding to field name in DB table
1235             ##
1236             ## If 'new_value' is specified, then $obj will be updated with this value
1237             ## and new value will be returned.
1238             ##
1239             sub AUTOLOAD
1240             {
1241 25 50   25   2702 if( $ORM::AUTOLOAD =~ /^(.+)::(.+)$/ )
1242             {
1243 25         92 my $prop = $2;
1244 25         52 my $self = shift;
1245 25         61 my %arg = @_;
1246              
1247 25 50       100 croak "Called undefined static method '$ORM::AUTOLOAD' of class '$self'" unless( ref $self );
1248              
1249 25         125 $self->_property( name=>$prop, %arg );
1250             }
1251             }
1252              
1253             ##
1254             ## CLASS METHODS
1255             ##
1256              
1257             sub optimize_storage
1258             {
1259 0     0 1 0 my $class = shift;
1260 0         0 $class->_db->optimize_tables( class=>$class );
1261             }
1262              
1263             ##
1264             ## PROTECTED METHODS
1265             ##
1266              
1267             sub _find_constructor
1268             {
1269 3     3   30 my $class = shift;
1270 3         7 my $prop = shift;
1271 3         6 my $result_tables = shift;
1272 3         5 my $self;
1273              
1274 3 50       11 if( $prop->{id} )
1275             {
1276 3 50       12 if( $prop->{class} )
1277             {
1278 3         30 $class->_load_ORM_class( $prop->{class} );
1279 3         39 $self = bless { _ORM_data => $prop }, $prop->{class};
1280              
1281 3 50       24 if( $result_tables )
1282             {
1283 3         67 my $class_tables_count = $prop->{class}->_db_tables_count;
1284 3         31 my $loaded_tables_count = scalar( @$result_tables );
1285 3         12 for( my $i=$loaded_tables_count; $i<$class_tables_count; $i++ )
1286             {
1287 1         11 $self->{_ORM_missing_tables}{$prop->{class}->_db_table($i)} = 1;
1288             }
1289             }
1290              
1291 3         32 delete $self->{_ORM_data}{class};
1292             }
1293             else
1294             {
1295 0         0 $self = bless { _ORM_data => $prop }, $class;
1296             }
1297             }
1298              
1299 3         11 return $self;
1300             }
1301              
1302             sub _rebless_to_broken
1303             {
1304 2     2   5 my $self = shift;
1305 2         7 my %arg = @_;
1306            
1307 2         7 $self->_cache->delete( $self );
1308              
1309 2         9 $self->{class} = ref $self;
1310 2         7 $self->{id} = $self->id;
1311              
1312 2 50 0     8 if( $arg{deleted} )
    0          
1313             {
1314 2         4 $self->{deleted} = 1;
1315             }
1316             elsif( $arg{error} && $arg{error}->fatal )
1317             {
1318 0         0 $self->{error} = $arg{error};
1319             }
1320              
1321 2         4 delete $self->{_ORM_tmp};
1322 2         7 delete $self->{_ORM_data};
1323 2         5 delete $self->{_ORM_cache};
1324 2         4 delete $self->{_ORM_missing_tables};
1325              
1326 2         8 bless $self, 'ORM::Broken';
1327             }
1328              
1329             ## use: $self->_normalize_prop_to_db_value( name=>STRING, value=>SCALAR, error=>ORM::Error )
1330             ##
1331             ## Normalize specified value to be able to store it in database table.
1332             ## All arguments are necessary.
1333             ##
1334             sub _normalize_prop_to_db_value
1335             {
1336 170     170   231 my $self = shift;
1337 170         240 my $class = ref $self;
1338 170         554 my %arg = @_;
1339 170         514 my $error = ORM::Error->new;
1340 170         465 my $prop_name = $arg{name};
1341 170         382 my $prop_value = $arg{value};
1342 170         289 my $prop_ref = ref $prop_value;
1343              
1344 170 50       550 if( ! $class->_has_prop( $prop_name ) )
    100          
    100          
1345             {
1346 0         0 $error->add_fatal( "Superfluous property '$prop_name'" );
1347             }
1348             elsif( $class->_plain_prop( $prop_name ) )
1349             {
1350 132 50       1615 if( $prop_ref )
1351             {
1352 0         0 $error->add_fatal
1353             (
1354             "Property '$prop_name' should be scalar, not reference"
1355             );
1356             }
1357             }
1358             elsif( $class->_prop_is_ref( $prop_name ) )
1359             {
1360 19 100       321 if( ! defined $prop_value )
    50          
    50          
1361             {
1362             # leave NULL value
1363             }
1364             elsif( ! $prop_ref )
1365             {
1366 0         0 my $obj = $class->_prop_class( $prop_name )->exists
1367             (
1368             id => $prop_value,
1369             error => $error,
1370             );
1371 0 0       0 unless( $obj )
1372             {
1373 0         0 $error->add_fatal
1374             (
1375             "Property '$prop_name' of type '"
1376             . $class->_prop_class( $prop_name )
1377             . "' with id='$prop_value' was not found"
1378             );
1379             }
1380             }
1381             elsif( UNIVERSAL::isa( $prop_ref, $class->_prop_class( $prop_name ) ) )
1382             {
1383 10         129 $prop_value = $prop_value->id;
1384             }
1385             else
1386             {
1387 0         0 $error->add_fatal
1388             (
1389             "Property '$prop_name' should be of type "
1390             . "'" . $class->_prop_class( $prop_name ) . "' not '"
1391             . (ref $prop_value) . "'"
1392             );
1393             }
1394             }
1395             else # if( $class->_prop_class( $prop_name ) && ! $class->_prop_is_ref( $prop_name ) )
1396             {
1397 19 50       264 if( ! defined $prop_value )
    50          
    0          
1398             {
1399             # leave undef value
1400             }
1401             elsif( ! $prop_ref )
1402             {
1403 19         62 my $obj = $class->_prop_class( $prop_name )->__ORM_new_db_value
1404             (
1405             value => $prop_value,
1406             error => $error,
1407             );
1408 19 50       116 $prop_value = defined $obj ? $obj->__ORM_db_value : undef;
1409             }
1410             elsif( UNIVERSAL::isa( $prop_ref, $class->_prop_class( $prop_name ) ) )
1411             {
1412 0         0 $prop_value = $prop_value->__ORM_db_value;
1413             }
1414             else
1415             {
1416 0         0 $error->add_fatal
1417             (
1418             "Property '$prop_name' should be of type "
1419             . "'" . $class->_prop_class( $prop_name ) . "' not '"
1420             . (ref $prop_value) . "'"
1421             );
1422             }
1423             }
1424              
1425 170         703 $arg{error}->add( error=>$error );
1426 170 50       708 return $arg{error}->fatal ? undef : $prop_value;
1427             }
1428              
1429             ## use: $self->_validate_prop( prop=>HASH, method=>string, error=>ORM::Error )
1430             ##
1431 25     25   54 sub _validate_prop {}
1432              
1433             ## use: $self->_fix_prop( prop=>HASH, error=>ORM::Error )
1434             ##
1435             ## May be called from _validate_prop to change values of
1436             ## properties before commiting them to database.
1437             ##
1438             sub _fix_prop
1439             {
1440 0     0   0 my $self = shift;
1441 0         0 my %arg = @_;
1442 0         0 my $error = ORM::Error->new;
1443              
1444 0         0 for my $prop ( keys %{$arg{prop}} )
  0         0  
1445             {
1446 0 0       0 if( (ref $self)->_has_prop( $prop ) )
1447             {
1448 0         0 delete $self->{_ORM_cache}{$prop};
1449 0         0 $self->{_ORM_data}{$prop} = $self->_normalize_prop_to_db_value
1450             (
1451             name => $prop,
1452             value => $arg{prop}{$prop},
1453             error => $error,
1454             );
1455             }
1456             }
1457              
1458 0         0 $error->upto( $arg{error} );
1459 0         0 return undef;
1460             }
1461              
1462             ## use: ORM->_init
1463             ## (
1464             ## db => ORM::Db,
1465             ## history_class => string||undef,
1466             ## prefer_lazy_load => boolean,
1467             ## emulate_foreign_keys => boolean,
1468             ## default_cache_size => integer,
1469             ## )
1470             ##
1471             sub _init
1472             {
1473 4     4   13 my $class = shift;
1474 4         25 my %arg = @_;
1475              
1476 4 50       25 die "'db' argument not specified" unless( exists $arg{db} );
1477 4 50       40 die "'db' argument is specified but undefined" unless( $arg{db} );
1478 4 50       29 die "'db' argument specified is not descendant of 'ORM::Db'" unless( UNIVERSAL::isa( $arg{db}, 'ORM::Db' ) );
1479 4 50       16 die "'prefer_lazy_load' argument not specified" unless( exists $arg{prefer_lazy_load} );
1480 4 50       16 die "'emulate_foreign_keys' argument not specified" unless( exists $arg{emulate_foreign_keys} );
1481 4 50       16 die "'default_cache_size' argument not specified" unless( exists $arg{default_cache_size} );
1482              
1483 4         40 $class->_class_hier( {} );
1484 4         238 $class->_db( $arg{db} );
1485 4         112 $class->_history_class( $arg{history_class} );
1486 4         130 $class->_default_prefer_lazy_load( $arg{prefer_lazy_load} );
1487 4         155 $class->_emulate_foreign_keys( $arg{emulate_foreign_keys} );
1488 4         143 $class->_default_cache_size( $arg{default_cache_size} );
1489 4         148 $class->_current_transaction( undef );
1490             }
1491              
1492             ## use: $base_class->_derive
1493             ## (
1494             ## derived_class => string,
1495             ## intermediate => boolean,
1496             ## table => string,
1497             ##
1498             ## history_is_enabled => boolean,
1499             ## prefer_lazy_load => boolean,
1500             ## )
1501             ##
1502             sub _derive
1503             {
1504 10     10   25 my $class = shift;
1505 10         36 my %arg = @_;
1506 10         71 my $error = ORM::Error->new;
1507 10         47 my $base = $class->_class_info;
1508 10         76 my $derived;
1509             my $struct;
1510 0         0 my $defaults;
1511 0         0 my $table;
1512              
1513 10         23 $derived = {};
1514 10         30 $class->_class_hier->{$arg{derived_class}} = $derived;
1515              
1516             # Copy SQL configuration from base class
1517 10 100       80 if( $base )
1518             {
1519 5 50       37 if( $class->_is_sealed )
1520             {
1521 0         0 $error->add_fatal
1522             (
1523             "You cannot create class derived from '$class'"
1524             . " because '$class' is sealed. If you want to derive"
1525             . " from '$class' you should add column 'class' to"
1526             . " table '".$class->_db_table(0)."' and fill it with"
1527             . " '$class' values."
1528             );
1529             }
1530             else
1531             {
1532 5         52 $derived->{BASE_CLASS} = $class;
1533 5         17 $derived->{INITIAL_CLASS} = $base->{INITIAL_CLASS};
1534 5         16 $derived->{PRIMARY_CLASS} = $base->{PRIMARY_CLASS};
1535 5         15 $derived->{TABLES_STR} = $base->{TABLES_STR};
1536 5         13 $derived->{TABLES_INNER_JOIN} = $base->{TABLES_INNER_JOIN};
1537 5         10 %{$derived->{PROP2FIELD_MAP}} = %{$base->{PROP2FIELD_MAP}};
  5         30  
  5         21  
1538 5         10 %{$derived->{PROP2TABLE_MAP}} = %{$base->{PROP2TABLE_MAP}};
  5         27  
  5         24  
1539 5         11 %{$derived->{TABLE_STRUCT}} = %{$base->{TABLE_STRUCT}};
  5         20  
  5         14  
1540 5         9 %{$derived->{PROP}} = %{$base->{PROP}};
  5         20  
  5         17  
1541 5         10 %{$derived->{PROP_DEFAULT_VALUE}} = %{$base->{PROP_DEFAULT_VALUE}};
  5         23  
  5         17  
1542 5         9 @{$derived->{TABLE}} = @{$base->{TABLE}};
  5         18  
  5         12  
1543             }
1544             }
1545             else
1546             {
1547 5         16 $derived->{INITIAL_CLASS} = $class;
1548 5         14 $derived->{PRIMARY_CLASS} = $arg{derived_class};
1549 5   33     41 $derived->{CACHE} = ORM::Cache->new( size=>($arg{cache_size}||$class->_default_cache_size) );
1550             }
1551              
1552 10 50       44 unless( $error->fatal )
1553             {
1554 10         25 $derived->{REV_REFS} = {};
1555 10         21 $derived->{INTERMEDIATE} = $arg{intermediate};
1556              
1557             # History configuration
1558 10 100       57 if( exists $arg{history_is_enabled} )
    50          
1559             {
1560 2         8 $derived->{HISTORY_IS_ENABLED} = $arg{history_is_enabled};
1561             }
1562             elsif( exists $base->{HISTORY_IS_ENABLED} )
1563             {
1564 0         0 $derived->{HISTORY_IS_ENABLED} = $base->{HISTORY_IS_ENABLED};
1565             }
1566              
1567             # Lazy load configuration
1568 10 50       30 if( exists $arg{prefer_lazy_load} )
1569             {
1570 0         0 $derived->{PREFER_LAZY_LOAD} = $arg{prefer_lazy_load};
1571             }
1572              
1573             # Detect db table name
1574 10   33     80 $table = $arg{table} || $class->_guess_table_name( $arg{derived_class} );
1575             }
1576              
1577 10 50       162 if( $table )
1578             {
1579 10         47 ( $struct, $defaults ) = $class->_db->table_struct
1580             (
1581             class => $arg{derived_class},
1582             table => $table,
1583             error => $error,
1584             );
1585 10 100 66     61 if( $class->_history_class && $arg{derived_class} eq $class->_history_class )
1586             {
1587 2         43 $struct->{slaved_by} = $class->_history_class;
1588             }
1589             # Check whether table exists
1590 10 100       237 if( ! scalar( %$struct ) )
1591             {
1592 1         8 $error->add_fatal
1593             (
1594             "Table '$table' for class '$arg{derived_class}' not found."
1595             );
1596 1         3 $table = undef;
1597             }
1598             }
1599 10 100       38 if( $table )
1600             {
1601             # Check whether table format is correct
1602 9 50       32 unless( $error->fatal )
1603             {
1604 9 50       35 if( ! exists $struct->{id} )
1605             {
1606 0         0 $error->add_fatal( "Table '$table' should contain 'id' column" );
1607             }
1608             }
1609 9 50       29 unless( $error->fatal )
1610             {
1611 9 100 100     66 if
1612             (
1613             $class->_class_is_primary( $arg{derived_class} )
1614             && ! exists $struct->{class}
1615             )
1616             {
1617 2         28 $derived->{SEALED} = 1;
1618             }
1619             }
1620             # Initialize $derived->{TABLES_INNER_JOIN}
1621 9 50       160 unless( $error->fatal )
1622             {
1623 9 100       33 if( !$class->_class_is_primary( $arg{derived_class} ) )
1624             {
1625 4 50       47 $derived->{TABLES_INNER_JOIN} .= ' AND ' if( $derived->{TABLES_INNER_JOIN} );
1626 4         22 $derived->{TABLES_INNER_JOIN} .=
1627             $class->_db->qt( $table ).'.id = '.$class->_db->qt( $derived->{TABLE}[0] ).'.id';
1628             }
1629             }
1630             # Initialize
1631             # $derived->{PROP},
1632             # $derived->{PROP_DEFAULT_VALUE},
1633             # $derived->{PROP2FIELD_MAP},
1634             # $derived->{PROP2TABLE_MAP}
1635 9 50       74 unless( $error->fatal )
1636             {
1637 9         13 my $prop;
1638 9         38 for $prop ( keys %$struct )
1639             {
1640 43         93 $derived->{PROP}{$prop} = $struct->{$prop};
1641 43         101 $derived->{PROP_DEFAULT_VALUE}{$prop} = $defaults->{$prop};
1642             }
1643              
1644 9 100       53 $derived->{PROP2TABLE_MAP}{id} = $table unless( $derived->{PROP2TABLE_MAP}{id} );
1645 9         25 delete $struct->{id};
1646              
1647 9         29 for my $field ( keys %$struct )
1648             {
1649 34 50       143 unless( $derived->{PROP2FIELD_MAP}{$field} )
1650             {
1651 34         64 $derived->{PROP2TABLE_MAP}{$field} = $table;
1652 34 100       71 if( $field ne 'class' )
1653             {
1654 31         90 $derived->{PROP2FIELD_MAP}{$field} =
1655             $class->_db->qt( $table ) . '.' . $class->_db->qf( $field );
1656             }
1657             }
1658             else
1659             {
1660 0         0 $error->add_fatal
1661             (
1662             "Duplicate columns "
1663             . "'$derived->{PROP2FIELD_MAP}{$field}',"
1664             . " '".$class->_db->qt($table).'.'.$class->_db->qf($field)."'"
1665             );
1666 0         0 last;
1667             }
1668             }
1669             }
1670             # Initialize
1671             # $derived->{TABLE},
1672             # $derived->{TABLE_STR},
1673             # $derived->{TABLE_STRUCT},
1674 9         23 delete $struct->{class};
1675 9 50       34 unless( $error->fatal )
1676             {
1677 9 100       33 if( !$class->_class_is_primary( $arg{derived_class} ) )
1678             {
1679 4         43 $derived->{TABLES_STR} .= ',';
1680             }
1681 9         76 $derived->{TABLES_STR} .= $class->_db->qt( $table );
1682 9         37 $derived->{TABLE_STRUCT}{$table} = $struct;
1683 9         11 push @{$derived->{TABLE}}, $table;
  9         30  
1684             }
1685             }
1686              
1687 10 100       33 unless( $error->fatal )
1688             {
1689             # Load self metaprop class
1690 9         29 $derived->{METAPROP_CLASS} = "ORM::Meta::$arg{derived_class}";
1691 9 50       1075 if( ! eval "require $derived->{METAPROP_CLASS}" )
1692             {
1693 9 100       38 if( $derived->{BASE_CLASS} )
1694             {
1695 4         17 $derived->{METAPROP_CLASS} = $base->{METAPROP_CLASS};
1696             }
1697             else
1698             {
1699 5         13 $derived->{METAPROP_CLASS} = 'ORM::Metaprop';
1700             }
1701             }
1702             }
1703              
1704 10         39 my %require;
1705              
1706 10 100       48 unless( $error->fatal )
1707             {
1708             # Load referenced and referencing classes
1709             # and initialize reverse props
1710 9         15 for my $prop ( keys %{$derived->{TABLE_STRUCT}{$table}} )
  9         329  
1711             {
1712 31         135 my $pclass = $derived->{PROP}{$prop};
1713 31 100 100     109 if( $pclass && !$class->_class_hier->{$pclass} )
1714             {
1715 3         36 $require{$pclass} = 1;
1716             }
1717             }
1718 9         49 for my $pclass ( $class->_db->referencing_classes( class=>$arg{derived_class}, error=>$error ) )
1719             {
1720 2 100       13 $require{$pclass->{class}} = 1 unless( $class->_class_hier->{$pclass->{class}} );
1721 2         48 $derived->{REV_REFS}{ $pclass->{class}.' '.$pclass->{prop} }
1722             = [ $pclass->{class}, $pclass->{prop} ];
1723             }
1724             ## Following pease of code make sence only in mod_perl environment,
1725             ## it is necessary to avoid the following problem:
1726             ##
1727             ## If you have created and loaded new ORM-class My::Class2 that contain
1728             ## referencing property to class My::Class1, then My::Class1 does not
1729             ## know about new referer and therefore My::Class1->_rev_refs returns
1730             ## outdated data.
1731             ##
1732 9         21 for my $prop ( keys %{$derived->{TABLE_STRUCT}{$table}} )
  9         43  
1733             {
1734 31         87 my $pclass = $derived->{PROP}{$prop};
1735 31         65 my $key = "$arg{derived_class} $prop";
1736 31 100 100     118 if( $pclass && $class->_class_hier->{$pclass} && !$pclass->_class_info->{REV_REFS}{$key} )
      100        
1737             {
1738 2         29 $pclass->_class_info->{REV_REFS}{$key} = [ $arg{derived_class}, $prop ];
1739             }
1740             }
1741              
1742             # Load metaclasses of not ORM classes
1743 9         41 for my $prop ( keys %{$derived->{TABLE_STRUCT}{$table}} )
  9         32  
1744             {
1745 31         74 my $pclass = $derived->{PROP}{$prop};
1746 31 100 100     104 if( $pclass && !$class->_class_hier->{$pclass} )
1747             {
1748 3         47 ORM::Metaprop->_class2metaclass( $pclass );
1749             }
1750             }
1751             }
1752              
1753             # Print error message and exit if necessary
1754 10 100       55 die $error->text if( $error->any );
1755              
1756 9         555 return keys %require;
1757             }
1758              
1759             ##
1760             ## PRIVATE METHODS
1761             ##
1762              
1763             sub _values_are_not_equal
1764             {
1765 31     31   45 my $self = shift;
1766 31         40 my $val1 = shift;
1767 31         39 my $val2 = shift;
1768              
1769 31 100 33     360 ( ( defined $val1 ) xor ( defined $val2 ) )
      66        
      75        
1770             || ( defined $val1 && defined $val2 && ( $val1 ne $val2 ) );
1771             }
1772              
1773             ##
1774             ## METHODS AND PROPERTIES TO USE DURING CLASS INITIALISATION
1775             ## ( ORM->_derive )
1776             ##
1777              
1778 27     27   123 sub _class_is_primary { ! exists $_[1]->_class_info->{TABLE}; }
1779              
1780             ## use: $table_name = $class->_guess_table_name( $obj_class );
1781             ##
1782             sub _guess_table_name
1783             {
1784 0     0   0 my $class = shift;
1785 0         0 my $table = shift;
1786              
1787 0         0 $table =~ s/::/_/g;
1788              
1789 0         0 return $table;
1790             }
1791              
1792             ## use: $prop_class = $class->_db_type_to_class( $db_field_name, $db_type_name );
1793             ##
1794             sub _db_type_to_class
1795             {
1796 43     43   53 my $class = shift;
1797 43         50 my $field = shift;
1798 43         52 my $type = shift;
1799 43         35 my $prop_class;
1800              
1801             ## These classes will be used by default for columns
1802             ## of type 'date' and 'datetime' in database.
1803             ##
1804             ## '__ORM_new_db_value' method of classes should
1805             ## be able to return object constructed by value
1806             ## of 'time' function.
1807             ##
1808             ## This means:
1809             ##
1810             ## $class->__ORM_new_db_value( value=>1125850389 )->__ORM_db_value
1811             ## should return '2005-09-04 22:13:09'
1812             ##
1813 43 50       161 if( ( lc $type ) eq 'date' )
    100          
    50          
1814             {
1815 0         0 $prop_class = 'ORM::Date';
1816             }
1817             elsif( ( lc $type ) eq 'datetime' )
1818             {
1819 2         4 $prop_class = 'ORM::Datetime';
1820             }
1821             elsif( ( lc $type ) eq 'timestamp' )
1822             {
1823 0         0 $prop_class = 'ORM::Datetime';
1824             }
1825              
1826 43         174 return $prop_class;
1827             }
1828              
1829             ## use: $class->_load_ORM_class( $class );
1830             ##
1831             sub _load_ORM_class
1832             {
1833 5     5   11 my $class = shift;
1834 5         11 my $load_class = shift;
1835              
1836 5 50       21 unless( $class->_class_hier->{$load_class} )
1837             {
1838 0           $load_class .= '.pm';
1839 0           $load_class =~ s(::)(/)g;
1840 0           require $load_class;
1841             }
1842             }
1843              
1844             sub DESTROY
1845             {
1846 0 0 0 0     exists $_[0]->_class_hier->{PRIMARY_CLASS} && $_[0]->_cache && $_[0]->_cache->delete( $_[0] );
1847             }
1848              
1849             1;
1850             __END__