File Coverage

blib/lib/SLOOPS/Factory.pm
Criterion Covered Total %
statement 81 407 19.9
branch 2 122 1.6
condition 1 51 1.9
subroutine 27 47 57.4
pod 12 20 60.0
total 123 647 19.0


line stmt bran cond sub pod time code
1             package SLOOPS::Factory ;
2              
3             our $VERSION='0.01' ;
4              
5             =head1 NAME
6              
7             SLOOPS::Factory - a general persistent object managing class.
8              
9             =head1 DESCRIPTION
10              
11             This is the main class to use this persistance framework.
12             Use it to seek objects, to save them, to delete them ...
13              
14             =head1 AUTHOR
15              
16             jerome@eteve.net
17              
18             =head1 SYNOPSIS
19              
20             use SLOOPS::Factory [ { debug => 0|1 } ];
21              
22             my $f = SLOOPS::Factory->instance();
23              
24             [ $f->setCache(0|1) ; ]
25              
26             [ my $dbDriver = # A valid dbDriver ;
27             # Only one time needed for the life of the instance.
28             $f->dbDriver($dbDriver);
29             ]
30              
31             # Then use the methods..
32              
33             =head1 METHODS
34              
35             =cut
36              
37              
38 1     1   1139 use strict ;
  1         2  
  1         33  
39 1     1   5 use Carp ;
  1         2  
  1         64  
40              
41 1     1   5 use base qw/Class::AutoAccess/ ;
  1         2  
  1         642  
42              
43             my $instance = SLOOPS::Factory->new();
44             my $debug = undef ;
45              
46              
47             sub import{
48             #my $callerPack = caller ;
49 1     1   8 my ($class, $options) = @_ ;
50 1 50       6 if( ! defined $debug ){
51 1   50     8 $debug = $options->{'debug'} || 0 ;
52             }
53 1 50       14 print "\n\nDebug option : $debug \n\n" if ($debug);
54             }
55              
56              
57              
58             sub instance{
59 0     0 0 0 return $instance ;
60             }
61              
62             =head2 setCache
63              
64             Sets the cache on 1 /off 0
65             : $f->setCache(1);
66             $f->setCache(0);
67              
68             Setting the cache on allows to get always the same instance of object that old exactly the
69             same data when you fetch object.
70              
71             =cut
72              
73             sub setCache{
74 0     0 1 0 my ($self, $on ) = @_ ;
75 0 0       0 if( ! $on ){
76 0 0       0 $self->cache()->clear() if ( $self->cache() );
77 0         0 $self->{'cache'} = undef ;
78 0         0 return ;
79             }
80            
81 0         0 eval{
82 0         0 require Cache::FastMemoryCache;
83 0         0 require Cache::Cache ;
84             };
85 0 0       0 if( $@ ){
86 0         0 carp("No Cache::FastMemoryCache available in system. Skipping");
87 0         0 return ;
88             }
89            
90 0         0 $self->cache(new Cache::FastMemoryCache({ 'namespace' => '-'.$self.'-' }));
91            
92             }
93              
94             sub new{
95 1     1 0 2 my ($class) = @_ ;
96 1         7 return bless {
97             'dbDriver' => undef,
98             'cache' => undef
99             } , $class ;
100             }
101              
102             sub dbh{
103 0     0 0   my ($self, $dbh ) = @_ ;
104 0 0         if( defined $dbh ) {
105 0           $self->dbDriver()->dbh($dbh);
106             }
107 0           return $self->dbDriver()->dbh();
108             }
109              
110              
111             =head2 createObject
112              
113             Usage:
114              
115             my $o = $f->createObject("ObjectClass");
116              
117             =cut
118              
119             sub createObject{
120 0     0 1   my ($self, $oclass ) = @_ ;
121            
122 0           return $oclass->new();
123             }
124              
125             =head2 fetchObject
126              
127             Fetch an object of class $oclass identified by $id from the database.
128              
129             usage :
130              
131             my $o = $f->fetchObject($oclass,$id);
132              
133             =cut
134              
135             sub fetchObject{
136 0     0 1   my ($self, $oclass , $dbid ) = @_ ;
137            
138 0 0         print "Fetching $dbid for class $oclass\n" if ( $debug );
139             #my $oclass = $self->getRealClass($oclass);
140             # Getting real class for object $oclass with id .
141              
142 0           my $baseClass = $self->findBaseClass($oclass);
143            
144 0 0         if( $self->cache() ){
145 0           my $o = $self->cache()->get($oclass.'-'.$dbid);
146 0 0         if( $o ) {
147 0           return $o ;
148             }
149             }
150            
151 1     1   6 no strict 'refs' ;
  1         2  
  1         44  
152 0           my $hashBase = ${"$baseClass".'::PERSIST'};
  0            
153 1     1   5 use strict 'refs';
  1         2  
  1         290  
154 0           my $table = $hashBase->{'table'};
155             # Retrieving real class
156 0           my $sql = "SELECT dbRealClass FROM ".$table." WHERE dbid = ".$self->dbh()->quote($dbid);
157 0           my $realClass = undef ;
158 0           eval{
159 0           my $sth = $self->dbh()->prepare($sql);
160 0           $sth->execute();
161 0           $realClass = $sth->fetch()->[0];
162             };
163 0 0         if( $@ ){
164 0           confess("Cannot retrieve realClass for id $dbid, class $oclass: $@");
165             }
166            
167 0 0         print "Base class is : $baseClass\n" if ($debug);
168 0 0         print "Real class is : $realClass\n" if ($debug);
169            
170 0           my $o = $self->fetchObjectReal($realClass,$dbid);
171 0 0         if( $self->cache() ){
172 0           $self->cache()->set($oclass.'-'.$dbid, $o , $Cache::Cache::EXPIRES_NEVER );
173             }
174 0           return $o ;
175             }
176              
177             =head2 findBaseClass
178              
179             Utility function.
180             Returns the base class of any persistent class.
181              
182             =cut
183              
184             sub findBaseClass{
185 0     0 1   my( $self , $oclass ) = @_ ;
186 1     1   5 no strict 'refs' ;
  1         2  
  1         58  
187 0           my $hash = ${"$oclass".'::PERSIST'} ;
  0            
188 1     1   6 use strict 'refs';
  1         1  
  1         104  
189 0 0         if( ! defined $hash->{'base'} ) { return $oclass ;}
  0            
190 0           return $self->findBaseClass($hash->{'base'});
191            
192             }
193              
194              
195             =head2 fetchObjectReal
196              
197             Fetch the object $dbid. $oclass is the real class of this object.
198              
199             =cut
200              
201             sub fetchObjectReal{
202 0     0 1   my ($self, $oclass , $dbid , $o ) = @_ ;
203            
204 0   0       $o ||= $oclass->new();
205            
206             # Set $o attributes from this table;
207 1     1   5 no strict 'refs' ;
  1         2  
  1         45  
208 0           my $hash = ${"$oclass".'::PERSIST'} ;
  0            
209 1     1   5 use strict 'refs';
  1         2  
  1         455  
210 0           my $table = $hash->{'table'} ;
211 0 0         if( ! defined $table ){ confess("No table defined for class $oclass") ; }
  0            
212              
213 0           my $sql = 'SELECT ';
214            
215 0 0         my @fields = keys %{$hash->{'fields'} || {} } ;
  0            
216 0 0         my @refs = keys %{$hash->{'references'} || {} };
  0            
217            
218 0           foreach my $field ( @fields ){
219 0           $sql .= $field.',';
220             }
221 0           foreach my $ref ( @refs ){
222 0           $sql .= $ref.',' ;
223             }
224 0           chop($sql) ;
225 0           $sql .= ' FROM '.$table.' WHERE dbid = '.$self->dbh()->quote($dbid);
226              
227 0           my @row = () ;
228 0 0         print "Exec: $sql\n" if ($debug);
229 0           eval{
230 0           my $sth = $self->dbh()->prepare($sql);
231 0           $sth->execute();
232 0           @row = $sth->fetchrow_array()
233             };
234 0 0         if ( $@ ) {
235 0           confess("SQL ERROR : $@");
236             }
237 0 0         if( ! @row ){
238 0           confess("No row with id = $dbid for class $oclass");
239             }
240            
241 0           my $fielditem = 0 ;
242 0           foreach my $field ( @fields){
243 0           $o->$field($row[$fielditem]);
244 0           $fielditem ++ ;
245             }
246              
247 0           foreach my $ref ( @refs ){
248 0           $o->$ref($row[$fielditem]);
249 0           $fielditem++ ;
250             }
251              
252 0           $o->{'_dbid_'} = $dbid ;
253            
254             # Calling recursively.
255 0 0         if( $hash->{'base'} ){
256 0           $self->fetchObjectReal($hash->{'base'} , $dbid , $o );
257             }
258              
259 0           return $o ;
260             }
261              
262             =head2 saveObject
263              
264             Stores the object in the database and add an _dbid_ to it.
265             If _dbid_ is allready set, redirect to syncObject .
266              
267             Returns the object database id (_dbid_)
268              
269             SQL equiv: insert.
270              
271             =cut
272              
273             sub saveObject{
274 0     0 1   my ($self, $o ) = @_ ;
275 0 0         if( exists $o->{'_dbid_'} ){
276 0           return $self->syncObject($o) ;
277             }
278            
279             # Do a simple insert, get the new id and sync the object.
280            
281 0           my $oclass = ref ( $o ) ;
282            
283 1     1   6 no strict 'refs' ;
  1         2  
  1         50  
284            
285 0           my $hash = ${"$oclass".'::PERSIST'} ;
  0            
286 1     1   5 use strict 'refs';
  1         3  
  1         110  
287            
288 0 0         if ( ! defined $hash ){
289 0           confess("Class $oclass is not set to be persistant");
290             }
291              
292 0           $o->{'_dbid_'} = $self->createTuple($oclass); # generated id.
293 0           return $self->syncObject($o);
294             }
295              
296              
297             sub createTuple{
298 0     0 0   my ($self , $class , $realclass ) = @_ ;
299            
300 0   0       $realclass ||= $class ;
301            
302 1     1   6 no strict 'refs' ;
  1         2  
  1         47  
303 0           my $persist = ${"$class".'::PERSIST'} ;
  0            
304 1     1   6 use strict 'refs';
  1         1  
  1         383  
305              
306 0           my $table = $persist->{'table'} ;
307            
308 0           my $id = undef ;
309 0           my $sql = undef ;
310            
311 0 0         if( defined $persist->{'base'} ){
312 0           $id = $self->createTuple($persist->{'base'}, $realclass );
313             }
314            
315 0 0         if( $id ){
316             # It is not a base class.
317 0           $sql = 'INSERT INTO '.$table.' (dbid) values('.$id.')';
318 0 0         print "Exec with id : $sql\n" if($debug);
319 0           eval{
320 0           my $sth = $self->dbh()->prepare($sql);
321 0           $sth->execute();
322             #$id = $self->dbDriver()->lastInsertId($table);
323             };
324 0 0         if( $@ ){
325 0           confess("Insert failed: $@");
326             }
327            
328 0           return $id ;
329             }
330            
331             # Insert into table $table
332             # a void tuple. i.e base class.
333             #$sql = 'INSERT INTO '.$table.'(dbRealClass) values ('.$self->dbh()->quote($realClass).')' ;
334             # No need of this since it s done in the updateTuple method.
335            
336 0           $sql = 'INSERT INTO '.$table.' values ()';
337 0 0         print "Exec: $sql\n" if ($debug);
338 0           eval{
339 0           my $sth = $self->dbh()->prepare($sql);
340 0           $sth->execute();
341 0           $id = $self->dbDriver()->lastInsertId($table);
342              
343             };
344 0 0         if( $@ ){
345 0           confess("Insert failed: $@");
346             }
347            
348 0           return $id;
349             }
350              
351              
352             =head2 saveObject
353              
354             Synchronize the object value with the database.
355             Returns the object database id ( _dbid_ )
356              
357             SQL equiv: update.
358              
359             =cut
360              
361             sub syncObject{
362 0     0 0   my ($self , $o ) = @_ ;
363 0 0         if( ! exists $o->{'_dbid_'} ){
364 0           confess("Cannot sync object $o since its not saved yet");
365             }
366 0           my $oclass = ref( $o );
367 1     1   6 no strict 'refs' ;
  1         3  
  1         64  
368 0           my $persist = ${"$oclass".'::PERSIST'} ;
  0            
369 1     1   6 use strict 'refs';
  1         3  
  1         123  
370 0 0         if( ! defined $persist){
371 0           confess("Class $oclass is not set to be persistant");
372             }
373            
374 0           $self->updateTuple($o,$oclass);
375 0           return $o->{'_dbid_'} ;
376             }
377              
378             sub updateTuple{
379 0     0 0   my ($self , $o , $class , $realclass ) = @_ ;
380            
381 0   0       $realclass ||= $class ;
382              
383 1     1   5 no strict 'refs' ;
  1         2  
  1         51  
384 0           my $persist = ${"$class".'::PERSIST'} ;
  0            
385 1     1   6 use strict 'refs';
  1         2  
  1         533  
386              
387 0           my $table = $persist->{'table'} ;
388 0 0         if( defined $persist->{'base'} ){
389 0           $self->updateTuple($o , $persist->{'base'}, $realclass );
390             }
391            
392 0           my $sql = 'UPDATE '.$table.' SET' ;
393            
394 0 0         if( ! defined $persist->{'base'} ){
395             # Base class, got to store the real class.
396 0           $sql .= ' dbRealClass = '.$self->dbh()->quote($realclass).',';
397             }
398              
399 0           my @fields = keys ( %{$persist->{'fields'}} );
  0            
400            
401 0           foreach my $field ( @fields ) {
402 0           $sql .= ' '.$field.' = '.$self->dbh()->quote($o->$field()).',';
403             }
404              
405             # references
406 0 0         my @refs = keys ( %{$persist->{'references'} || {} } );
  0            
407 0           foreach my $refs ( @refs ){
408 0           $sql .= ' '.$refs.' = '.$self->dbh()->quote($o->$refs()).',' ;
409             }
410              
411 0           chop($sql);
412            
413 0           $sql .= ' WHERE dbid = '.$self->dbh()->quote($o->{'_dbid_'}) ;
414            
415 0 0         print 'Exec: '.$sql."\n" if($debug);
416 0           eval{
417 0           my $sth = $self->dbh()->prepare($sql);
418 0           $sth->execute();
419             };
420 0 0         if( $@ ){
421 0           confess("Update of table ".$table." failed: ".$@);
422             }
423            
424             }
425              
426              
427             =head3 deleteObject
428              
429             Removes the object from the database.
430              
431             Empty the object from all database properties, turning it into
432             a plain perl-space object.
433              
434              
435             SQL equiv: delete
436              
437             =cut
438              
439             sub deleteObject{
440 0     0 1   my ($self , $o ) = @_ ;
441 0 0         if( ! exists $o->{'_dbid_'} ){
442 0           confess("Object $o is not persistent in database. Cannot delete");
443             }
444            
445 0           my $class = ref($o);
446 0           my $id = $o->_dbid_();
447            
448 0 0         print "Deleting $o\n" if($debug);
449            
450 0           $self->deleteTuple($class,$id);
451            
452 0           delete $o->{'_dbid_'} ;
453             }
454              
455             sub deleteTuple{
456 0     0 0   my ($self,$class,$id) = @_ ;
457            
458 1     1   6 no strict 'refs' ;
  1         3  
  1         65  
459 0           my $persist = ${"$class".'::PERSIST'} ;
  0            
460 1     1   6 use strict 'refs';
  1         2  
  1         862  
461            
462 0   0       my $table = $persist->{'table'} || confess("No table defined for class $class");
463            
464 0           my $sql = 'DELETE FROM '.$table.' WHERE dbid = '.$self->dbh()->quote($id) ;
465 0           eval{
466 0           my $sth = $self->dbh()->prepare($sql);
467 0           $sth->execute();
468             };
469 0 0         if( $@ ){
470 0           confess("Cannot delete tuple $id from table $table: $@");
471             }
472            
473             # Remove super tuple.
474 0           my $base = $persist->{'base'} ;
475 0 0         if( defined $base ){
476 0           $self->deleteTuple($base,$id);
477             }
478            
479             }
480              
481             =head2 find
482              
483             Returns the only instance found with the given constraints and existence.
484             Returns Undef if none found.
485             Dies if more than one instance is found.
486              
487             Usage:
488              
489             my $contraints = { ... } ; # See seekIds for syntax
490             my $existences = { ... } ; # See seekIds for syntax
491              
492             my $o = $f->find($class,$constraints,$existences);
493              
494              
495             =cut
496              
497             sub find{
498 0     0 1   my ($self , $class , $constraints , $existence ) = @_ ;
499 0   0       $constraints ||= {};
500 0   0       $existence ||= {} ;
501 0           my @ids = @{$self->seekIds($class,$constraints,$existence)};
  0            
502            
503 0 0         if( @ids > 1 ){
504 0           my $msg = "More than one object of class $class fullfills the constraints:";
505 0           while ( my ($key , $value ) = each %{$constraints} ){
  0            
506 0           $msg .= ' '.$key.' '.$value->[0].' '.$value->[1].' ,';
507             }
508 0           confess($msg);
509             }
510            
511 0 0         if( @ids == 1 ){
512 0 0         print "One object found !\n" if ($debug );
513 0           return $self->fetchObject($class,$ids[0]);
514             }
515 0           return undef ;
516             }
517              
518              
519             =head2 findOrCreate
520              
521             Returns a newly created object with the equality constraints and the reference
522             constraints used to initiate the object if it doesn't exists in the database.
523              
524             Returns the object from the database if it's allready there.
525              
526             Dies if more than one object fullfills the given constraints.
527              
528             Usage:
529              
530             my $contraints = { ... } ; # See seekIds for syntax
531             my $existences = { ... } ; # See seekIds for syntax
532              
533             my $o = $f->findOrCreate($class,$constraints,$existences);
534              
535             =cut
536              
537             sub findOrCreate{
538 0     0 1   my ($self, $class, $constraints , $existence ) = @_ ;
539 0   0       $constraints ||= {};
540 0   0       $existence ||= {} ;
541              
542             # If object is found, return !!
543 0           my $o = $self->find($class,$constraints , $existence );
544            
545 0 0         if ( defined $o ){
546 0           return $o ;
547             }
548            
549 0 0         print "Constructing object\n" if($debug);
550             # Ok, object does not exists in the database.
551 0           $o = $self->createObject($class);
552             # Set the value from the equality constraints.
553            
554 0           while( my ($key , $value ) = each %{$constraints} ){
  0            
555             # If a value is set ..
556 0 0 0       if( ref($value) eq 'ARRAY' && $value->[0] eq '=' ){
557 0           $o->$key($value->[1]);
558 0           next ;
559             }
560             # If this is a reference on an object ..
561 0 0         if( ref($value) ){
562 0           my $acc = $key.'_O';
563 0           $o->$acc($value);
564             }
565             }
566            
567             # Save and return !
568 0           $self->saveObject($o);
569 0           return $o ;
570            
571             }
572              
573             =head2 seekObjects
574              
575             Same usage as seekIds, but return a set of allready constructed objects.
576              
577             =cut
578              
579             sub seekObjects{
580 0     0 1   my ($self, $class, $constraints, $existence) = @_ ;
581 0           my $ids = $self->seekIds($class,$constraints, $existence );
582            
583 0           my @res = () ;
584 0           foreach my $id ( @{$ids}){
  0            
585 0           push @res , $self->fetchObject($class,$id);
586             }
587 0           return \@res ;
588             }
589              
590              
591             =head2 seekIds
592              
593             Returns a collection of id of object for the class $class in the database.
594             These object matches the constraints.
595              
596             Contraints can concern super class attributes and references.
597              
598             usage :
599              
600             my $constraint = {
601             'field1' => [ $operator , $value ],
602             ...
603             'reference1' => $referencedObject
604             };
605              
606             my $existence = {
607             'field1' => 'exist' , # Field is set
608             'field2' => undef , # field is not set
609             'reference1' => 'exist' , # idem
610             'reference2' => undef # idem
611             ...
612             };
613            
614             my $ids = $self->seekIds('ClassName' , $constraint , $existence );
615              
616              
617              
618             =cut
619              
620             sub seekIds{
621 0     0 1   my ($self, $class , $constraints , $existence ) = @_ ;
622            
623 0   0       $constraints ||= {};
624 0   0       $existence ||= {};
625            
626             # Lets construct a query .
627 1     1   14 no strict 'refs' ;
  1         2  
  1         56  
628 0           my $persist = ${"$class".'::PERSIST'} ;
  0            
629 1     1   7 use strict 'refs';
  1         2  
  1         953  
630            
631 0           my $dbdriver = $self->dbDriver();
632            
633 0   0       my $table = $persist->{'table'} || confess("No table defined for class $class");
634 0   0       my $fields = $persist->{'fields'} || {};
635 0   0       my $references = $persist->{'references'} || {};
636              
637 0           my $sql = 'SELECT dbid FROM '.$table.' WHERE ';
638            
639             # Constraints.
640 0           my $mustSuper = 0 ;
641            
642 0           my $constraintsRest = {} ;
643 0           while (my ($key,$value) = each %{$constraints}) {
  0            
644             # if $key in fields, value must be an array [ operator , value ]
645             # if $key in references , value must be an object with _dbid_() setted.
646 0 0         if( defined $fields->{$key} ){
647 0   0       my $operator = $value->[0] || confess ("No operator defined for $key");
648 0 0         if (! defined $value->[1] ){
649 0           confess ("No value defined for $key");
650             }
651 0           my $cmpvalue = $value->[1] ;
652             # substr EXPR,OFFSET,LONGUEUR
653 0 0 0       if ( (length ($cmpvalue) > $dbdriver->MaxLength()) &&
654             ( $fields->{$key}->[0] eq $dbdriver->String()) ){
655 0           carp("Truncating value : $cmpvalue");
656 0           $cmpvalue = substr $cmpvalue , 0 , $dbdriver->MaxLength() ;
657             }
658 0           $sql .= $key.' '.$operator.' '.$self->dbh()->quote($cmpvalue).' AND ';
659 0           next;
660             }
661            
662            
663 0 0         if( defined $references->{$key} ){
664 0           my $o = $value ;
665 0 0         if( ! defined $o ){
666 0           confess("Given object for key $key is not defined");
667             }
668 0           eval{
669 0           my $dbid = $o->_dbid_();
670 0           $sql .= $key.' = '.$self->dbh()->quote($dbid).' AND ';
671             };
672 0 0         if ( $@ ){ confess("Considered key: $key . No dbid for given object $o : $@ ");}
  0            
673 0           next ;
674             }
675             #confess("Unknown $key as attribute or reference for $class");
676 0           $constraintsRest->{$key} = $value ;
677 0           $mustSuper = 1 ;
678             }
679            
680             # Existence
681            
682 0           my $existRest = {};
683 0           while( my ($key,$value) = each %{$existence} ){
  0            
684            
685 0 0 0       if( defined $fields->{$key} || defined $references->{$key} ){
686            
687 0           $sql .= ' '.$key.' ';
688 0 0         if( $value ){
689 0           $sql .= ' IS NOT NULL ';
690             }
691             else{
692 0           $sql .= ' IS NULL ';
693             }
694 0           $sql .= ' AND ';
695 0           next;
696             }
697 0           $existRest->{$key} = $value ;
698 0           $mustSuper = 1 ;
699             }
700 0           $sql .= ' 1 ';
701 0 0         print "Exec :$sql\n" if($debug);
702 0           my @res = () ;
703 0           eval{
704 0           my $sth = $self->dbh()->prepare($sql);
705 0           $sth->execute();
706 0           while( my $tuple = $sth->fetch() ){
707 0           push @res , $tuple->[0];
708             }
709             };
710 0 0         if( $@ ){
711 0           confess("Cannot execute $sql : $@");
712             }
713            
714             # If something is here in contraintsRest or existRest, go to seek ids in
715             # super class. If no super class, then its an error
716             # The res should be the intersection of the returns ids.
717 0 0         if( $mustSuper ){
718 0   0       my $base = $persist->{'base'} || confess("Must have a super class for $class since it has extra contraints");
719 0           my $superRes = $self->seekIds($base,$constraintsRest,$existRest);
720 0           my ($onlya, $onlyb, $both, $either ) = _listy($superRes , \@res);
721 0 0         @res = @{$both || [] };
  0            
722             }
723            
724 0           return \@res ;
725             }
726              
727             =head2 findDistinctFieldsFrom
728              
729             Finds the distinct values of the given $field in the instances
730             of the $class that match the constraints $constraints and $existence
731             like in seekIds .
732              
733             If $field is a name of reference instead of a plain field, it returns the collection
734             of corresponding object.
735              
736             Usage:
737              
738             my $ObjsOrScalars = $f->findDistinctFieldsFrom($field , $class , $constraints , $existence );
739            
740            
741              
742             =cut
743              
744             sub findDistinctFieldsFrom{
745 0     0 1   my ($self, $field, $class, @rest ) = @_ ;
746            
747             # Find objects
748 0           my $objs = $self->seekObjects($class,@rest);
749            
750             # Lets construct a query .
751 1     1   6 no strict 'refs' ;
  1         2  
  1         43  
752 0           my $persist = ${"$class".'::PERSIST'} ;
  0            
753 1     1   5 use strict 'refs';
  1         2  
  1         174  
754            
755             # Then unique of asked field.
756 0           my %uniq = ();
757 0           foreach my $o (@{$objs}) {
  0            
758 0           $uniq{$o->$field()} = 1 ;
759             }
760            
761 0           my @values = sort keys %uniq;
762            
763 0 0         if( defined $persist->{'references'}->{$field} ){
764             # Fetch objects from the right class.
765 0           my $refClass = $persist->{'references'}->{$field} ;
766 0           my @objs = ();
767 0           foreach my $v ( @values ){
768 0           push @objs , $self->fetchObject($refClass,$v);
769             }
770 0           @values = @objs ;
771             }
772            
773 0           return \@values ;
774             }
775              
776             sub nbInstances{
777 0     0 0   my ($self, $class ) = @_ ;
778            
779 1     1   6 no strict 'refs' ;
  1         1  
  1         62  
780 0           my $persist = ${"$class".'::PERSIST'} ;
  0            
781 1     1   23 use strict 'refs';
  1         2  
  1         240  
782            
783 0   0       my $table = $persist->{'table'} || confess("No table for class $class");
784            
785 0           my $sql = "SELECT COUNT(*) FROM ".$table ;
786 0           my $count = undef ;
787 0           eval {
788 0           my $sth = $self->dbh()->prepare($sql);
789 0           $sth->execute() ;
790 0           $count = $sth->fetch()->[0];
791             };
792 0 0         if( $@ ){
793 0           confess ("Database error: ".$@);
794             }
795            
796 0           return $count ;
797             }
798              
799             sub _listy {
800 0     0     my %tmp1;
801 0           for (0..1) {
802 0           for my $k (@{$_[$_]}) {
  0            
803 0           $tmp1{$k} .= $_;
804             }
805             }
806 0           my %tmp2;
807 0           while (my($k, $v) = each %tmp1) {
808 0           push @{$tmp2{$v}}, $k;
  0            
809             }
810 0           return @tmp2{"0", "1", "01"}, [keys %tmp1];
811             }
812              
813              
814              
815             1;