[Bio] / Sprout / ERDBObject.pm Repository:
ViewVC logotype

Annotation of /Sprout/ERDBObject.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.16 - (view) (download) (as text)

1 : parrello 1.1 package ERDBObject;
2 :    
3 :     use strict;
4 :     use DBKernel;
5 :     use Tracer;
6 :    
7 :     =head1 Entity-Relationship Database Package Instance Object
8 :    
9 :     =head2 Introduction
10 :    
11 : parrello 1.7 This package defines the instance object for the Entity-Relationship Database
12 : parrello 1.9 Package (L<ERDB>. This object can be created directly, returned by the
13 : parrello 1.7 C<Fetch> method of the B<ERDBQuery> object, or returned by the C<Cross> method
14 :     of this object. An object created directly is considered I<transient>. An object
15 :     created by one of the database methods is considered I<persistent>.
16 :    
17 :     An instance object allows the user to access the fields in the current instance.
18 :     The instance consists of zero or more entity and/or relationship objects and a
19 :     map of field names to locations. Some entity fields require additional queries
20 :     to the database. If the entity object is present, the additional queries are
21 :     executed automatically. Otherwise, the value is treated as missing.
22 :    
23 : parrello 1.9 Each L<ERDBObject> has at least one object called the I<target object>. This
24 : parrello 1.7 can be specified directly in the constructor or it can be computed implicity
25 : parrello 1.16 from the query that created the object. This object name is used as the
26 : parrello 1.7 default when parsing field names.
27 : parrello 1.1
28 :     =head2 Public Methods
29 :    
30 :     =head3 new
31 :    
32 : parrello 1.7 my $dbObject = ERDBObject->new($erdb, $objectName, \%fields);
33 : parrello 1.1
34 : parrello 1.7 Create a new transient object. A transient object maps fields to values, but is
35 :     not read from a database. The parameter list should be an entity name
36 :     followed by a set of key-value pairs. Each key should be in the
37 : parrello 1.9 L<ERDB/Standard Field Name Format>.
38 : parrello 1.1
39 : parrello 1.7 =over 4
40 : parrello 1.1
41 : parrello 1.7 =item erdb
42 : parrello 1.1
43 : parrello 1.9 L<ERDB> object for accessing the database. If undefined, the object will
44 : parrello 1.7 be considered transient.
45 : parrello 1.1
46 : parrello 1.7 =item objectName
47 : parrello 1.1
48 : parrello 1.7 Default object name that should be used when resolving field name specifiers.
49 :     If undefined, no object will be considered the default.
50 : parrello 1.1
51 : parrello 1.7 =item fields
52 : parrello 1.1
53 : parrello 1.7 Reference to a hash mapping field names to field values. For a multi-valued
54 :     field, the value should be a list reference.
55 : parrello 1.1
56 :     =back
57 :    
58 :     =cut
59 : parrello 1.7
60 :     sub new {
61 : parrello 1.1 # Get the parameters.
62 : parrello 1.7 my ($class, $erdb, $objectName, $fields) = @_;
63 :     # Create the value hash.
64 :     my %values;
65 :     # Loop through the fields.
66 :     for my $fieldName (keys %$fields) {
67 :     # Normalize the field name.
68 :     my $normalizedName = ERDB::ParseFieldName($fieldName, $objectName);
69 :     # Get the field value.
70 :     my $list = $fields->{$fieldName};
71 :     # Convert it to a list. A single-valued field is stored as a singleton
72 :     # list.
73 :     if (ref $list ne 'ARRAY') {
74 :     $list = [$list];
75 :     }
76 :     # Store the field.
77 :     $values{$normalizedName} = $list;
78 :     }
79 :     # Create this object.
80 :     my $retVal = {
81 :     _db => $erdb,
82 :     _targetObject => $objectName,
83 :     _values => \%values,
84 : parrello 1.12 _parsed => {}
85 : parrello 1.7 };
86 :     # Bless and return it.
87 :     bless $retVal, $class;
88 :     return $retVal;
89 : parrello 1.1 }
90 :    
91 :     =head3 Attributes
92 :    
93 : parrello 1.4 my @attrNames = $dbObject->Attributes();
94 : parrello 1.1
95 :     This method will return a sorted list of the attributes present in this object.
96 :     The list can be used in the L</Values> method to get all the values stored.
97 :    
98 :     If the ERDBObject was created by a database query, the attributes returned will
99 :     only be those which occur on the primary relation. Additional fields may get
100 :     loaded into the object if the client has asked for them in a L</Value> or
101 :     L</Values> command. Initially, however, only the primary fields-- each of which
102 :     has one and only one value-- will be found in the attribute list.
103 :    
104 :     =cut
105 :     #: Return Type @;
106 :     sub Attributes {
107 :     # Get the parameters.
108 :     my ($self) = @_;
109 :     # Get the keys of the value hash.
110 :     my @retVal = sort keys %{$self->{_values}};
111 :     # Return the result.
112 :     return @retVal;
113 :     }
114 :    
115 :     =head3 HasField
116 :    
117 : parrello 1.4 my $flag = $dbObject->HasField($fieldSpec);
118 : parrello 1.1
119 :     Return TRUE if this object has the specified field available, else FALSE.
120 :     This method can be used to determine if a value is available without
121 :     requiring an additional database query.
122 :    
123 :     =over 4
124 :    
125 :     =item fieldSpec
126 :    
127 : parrello 1.9 A standard field specifier. See L<ERDB/Standard Field Name Format>. The
128 : parrello 1.7 default table name is the object's target entity.
129 : parrello 1.1
130 :     =item RETURN
131 :    
132 :     Returns TRUE if there's a value for the field in this object, else FALSE.
133 :    
134 :     =back
135 :    
136 :     =cut
137 :    
138 :     sub HasField {
139 :     # Get the parameters.
140 :     my ($self, $fieldName) = @_;
141 : parrello 1.7 # Parse the field name.
142 :     my $normalizedName = ERDB::ParseFieldName($fieldName, $self->{_targetObject});
143 : parrello 1.1 # Get the field hash.
144 :     my $fields = $self->{_values};
145 :     # Return the result.
146 : parrello 1.7 return exists $fields->{$normalizedName};
147 : parrello 1.1 }
148 :    
149 :     =head3 AddValues
150 :    
151 : parrello 1.4 $dbObject->AddValues($name, @values);
152 : parrello 1.1
153 :     Add one or more values to a specified field.
154 :    
155 :     =over 4
156 :    
157 :     =item name
158 :    
159 : parrello 1.9 Name of the field to receive the new values, in the L<ERDB/Standard Field Name Format>.
160 : parrello 1.7 If the field does not exist, it will be created.
161 : parrello 1.1
162 :     =item values
163 :    
164 :     List of values to put in the field.
165 :    
166 :     =back
167 :    
168 :     =cut
169 :    
170 :     sub AddValues {
171 :     # Get the parameters.
172 :     my ($self, $name, @values) = @_;
173 : parrello 1.7 # Parse the value name.
174 :     my $normalizedName = ERDB::ParseFieldName($name, $self->{_targetObject});
175 : parrello 1.1 # Get the field hash.
176 :     my $fields = $self->{_values};
177 :     # Add the new values.
178 : parrello 1.5 push @{$fields->{$name}}, @values;
179 : parrello 1.1 }
180 :    
181 : parrello 1.3 =head3 PrimaryValue
182 :    
183 : parrello 1.4 my $value = $dbObject->PrimaryValue($name);
184 : parrello 1.3
185 : parrello 1.7 Return the primary value of a field. This will be its first value in a standard
186 :     value list.
187 : parrello 1.3
188 :     This method is a more convenient version of L</Value>. Basically, the call
189 :    
190 :     my ($value) = $dbObject->Value($name);
191 :    
192 :     is equivalent to
193 :    
194 :     my $value = $dbObject->PrimaryValue($name);
195 :    
196 :     but the latter is syntactically more convenient.
197 :    
198 :     =over 4
199 :    
200 :     =item name
201 :    
202 : parrello 1.9 Name of the field whose value is desired, in the L<ERDB/Standard Field Name Format>.
203 : parrello 1.3
204 :     =item RETURN
205 :    
206 :     Returns the value of the specified field, or C<undef> if the field has no value.
207 :    
208 :     =back
209 :    
210 :     =cut
211 :    
212 :     sub PrimaryValue {
213 :     # Get the parameters.
214 :     my ($self, $name) = @_;
215 :     # Get the value.
216 :     my ($retVal) = $self->Value($name);
217 :     # Return it.
218 :     return $retVal;
219 :     }
220 :    
221 : parrello 1.1 =head3 Value
222 :    
223 : parrello 1.14 my @values = $dbObject->Value($attributeName, $rawFlag);
224 : parrello 1.1
225 :     Return a list of the values for the specified attribute.
226 :    
227 :     =over 4
228 :    
229 :     =item attributeName
230 :    
231 : parrello 1.9 Name of the desired attribute, in the L<ERDB/Standard Field Name Format>.
232 : parrello 1.1
233 : parrello 1.14 =item rawFlag (optional)
234 :    
235 :     If TRUE, then the data will be returned in raw form, without decoding from the
236 :     database format.
237 :    
238 : parrello 1.1 =item RETURN
239 :    
240 : parrello 1.7 Returns a list of the values for the specified attribute.
241 : parrello 1.1
242 :     =back
243 :    
244 :     =cut
245 :    
246 :     sub Value {
247 :     # Get the parameters.
248 : parrello 1.14 my ($self, $attributeName, $rawFlag) = @_;
249 :     # Get the database.
250 :     my $erdb = $self->{_db};
251 : parrello 1.1 # Declare the return variable.
252 :     my @retVal = ();
253 : parrello 1.12 # Normalize the field name. We keep the normalized name data in a hash so we only
254 :     # need to compute it once per field name.
255 : olson 1.10 my $parsed = $self->{_parsed}->{$attributeName};
256 : parrello 1.11 if (!defined($parsed)) {
257 : parrello 1.12 $parsed = [ ERDB::ParseFieldName($attributeName, $self->{_targetObject}) ];
258 :     $self->{_parsed}->{$attributeName} = $parsed;
259 : olson 1.10 }
260 :     my($alias, $fieldName) = @$parsed;
261 : parrello 1.7 my $normalizedName = "$alias($fieldName)";
262 : parrello 1.8 # Insure the field name is valid.
263 :     if (! defined $fieldName) {
264 :     Confess("Invalid field name \"$fieldName\".");
265 : parrello 1.1 } else {
266 : parrello 1.8 # Look for the field in the values hash.
267 :     my $fieldHash = $self->{_values};
268 :     my $retValRef = $fieldHash->{$normalizedName};
269 :     if (defined $retValRef) {
270 : parrello 1.14 # Here we have the field already. Split out the real field name and the value.
271 :     my ($fieldName, $fieldValue) = @$retValRef;
272 :     # Find out if we need to convert.
273 :     if (defined $erdb && ! $rawFlag) {
274 :     # Yes. Map the values to decoded fields.
275 :     @retVal = $erdb->DecodeField($fieldName, $fieldValue);
276 :     } else {
277 :     # No. Copy the value to the output.
278 :     @retVal = $fieldValue;
279 :     }
280 : parrello 1.8 } else {
281 :     # Here the field is not in the hash. If we don't have a database, we are
282 :     # done. The user will automatically get an empty list handed back to him.
283 : parrello 1.14 if (defined $erdb) {
284 : parrello 1.13 # We must first find the field's data structure.
285 : parrello 1.8 # If the field name is invalid, this will throw an error.
286 : parrello 1.13 my $fieldData = $erdb->_FindField($attributeName, $alias);
287 : parrello 1.8 # Insure we have an ID for this entity.
288 :     my $idName = "$alias(id)";
289 :     if (! exists $fieldHash->{$idName}) {
290 :     Confess("Cannot retrieve a field from \"$alias\": it is not part of this query.");
291 :     } else {
292 :     # Get the ID value. The field hash points to a list of IDs, but of
293 :     # course, there is only one, so we just take the first.
294 : parrello 1.15 my $idValue = $fieldHash->{$idName}[1];
295 : parrello 1.8 # We need to encode the ID because we're using it as a query parameter.
296 : parrello 1.13 my $id = $erdb->EncodeField("$alias(id)", $idValue);
297 : parrello 1.8 # Determine the name of the relation that contains this field.
298 :     my $relationName = $fieldData->{relation};
299 :     # Compute the actual name of the field in the database.
300 :     my $fixedFieldName = ERDB::_FixName($fieldName);
301 :     # Create the SELECT statement for the desired relation and execute it.
302 :     my $command = "SELECT $fixedFieldName FROM $relationName WHERE id = ?";
303 :     my $sth = $erdb->_GetStatementHandle($command, [$id]);
304 :     # Loop through the query results creating a list of the values found.
305 :     my $rows = $sth->fetchall_arrayref;
306 :     for my $row (@{$rows}) {
307 : parrello 1.14 # Are we decoding?
308 :     if ($rawFlag) {
309 :     # No, stuff the value in the result list unmodified.
310 :     push @retVal, $row->[0];
311 :     } else {
312 :     # Yes, decode it before stuffing.
313 : parrello 1.16 push @retVal, $erdb->DecodeField($normalizedName, $row->[0])
314 : parrello 1.14 }
315 : parrello 1.8 }
316 : parrello 1.7 }
317 : parrello 1.1 }
318 :     }
319 :     }
320 :     # Return the field values found.
321 :     return @retVal;
322 :     }
323 :    
324 :     =head3 Values
325 :    
326 : parrello 1.4 my @values = $dbObject->Values(\@attributeNames);
327 : parrello 1.1
328 : parrello 1.7 This method returns a list of all the values for a list of field specifiers.
329 :     Essentially, it calls the L</Value> method for each element in the parameter
330 :     list and returns a flattened list of all the results.
331 :    
332 :     For example, let us say that C<$feature> contains a feature with two links and a
333 :     translation. The following call will put the feature links in C<$link1> and
334 :     C<$link2> and the translation in C<$translation>.
335 : parrello 1.1
336 : parrello 1.4 my ($link1, $link2, $translation) = $feature->Values(['Feature(link)', 'Feature(translation)']);
337 : parrello 1.1
338 :     =over 4
339 :    
340 :     =item attributeNames
341 :    
342 : parrello 1.7 Reference to a list of attribute names, or a space-delimited string of attribute names.
343 : parrello 1.1
344 :     =item RETURN
345 :    
346 :     Returns a flattened list of all the results found for each specified field.
347 :    
348 :     =back
349 :    
350 :     =cut
351 :    
352 :     sub Values {
353 :     # Get the parameters.
354 :     my ($self, $attributeNames) = @_;
355 :     # Create the return list.
356 :     my @retVal = ();
357 : parrello 1.7 # Create the attribute name list.
358 :     my @attributes;
359 :     if (ref $attributeNames eq 'ARRAY') {
360 :     @attributes = @$attributeNames;
361 :     } else {
362 :     @attributes = split /\s+/, $attributeNames;
363 :     }
364 : parrello 1.1 # Loop through the specifiers, pushing their values into the return list.
365 : parrello 1.7 for my $specifier (@attributes) {
366 : parrello 1.1 push @retVal, $self->Value($specifier);
367 :     }
368 :     # Return the resulting list.
369 :     return @retVal;
370 :     }
371 :    
372 : parrello 1.7 =head2 Internal Methods
373 : parrello 1.1
374 : parrello 1.13 =head3 _new
375 : parrello 1.1
376 : parrello 1.7 my $erdbObject = ERDBObject->_new($dbquery, @values);
377 : parrello 1.1
378 : parrello 1.13 Create an B<ERDBObject> for the current database row.
379 : parrello 1.1
380 :     =over 4
381 :    
382 :     =item dbquery
383 :    
384 : parrello 1.9 L<ERDBQuery> object for the relevant query.
385 : parrello 1.1
386 : parrello 1.12 =item RETURN
387 : parrello 1.1
388 : parrello 1.13 Returns an B<ERDBObject> that can be used to access fields from this row of data.
389 : parrello 1.1
390 :     =back
391 :    
392 :     =cut
393 :    
394 : parrello 1.13 sub _new {
395 : olson 1.10 # Get the parameters.
396 : parrello 1.13 my ($class, $dbquery, @values) = @_;
397 :     # Create the field hash.
398 :     my %fh;
399 :     # Get the database.
400 : olson 1.10 my $erdb = $dbquery->{_db};
401 : parrello 1.13 # Check for a search relevance field in the results.
402 :     if ($dbquery->{_fullText}) {
403 :     # Create the special search relevance field from the first element of
404 :     # the row values. Note that the object name for this field is the
405 :     # stored in the query object's _fullText property.
406 :     my $relevanceName = "$dbquery->{_fullText}(search-relevance)";
407 :     $fh{$relevanceName} = [shift @values];
408 :     }
409 :     # Loop through the field information array, copying in the field values.
410 :     for my $finfo (@{$dbquery->{_fieldInfo}}) {
411 :     my($fieldName, $extFieldName, $fieldKey) = @$finfo;
412 : parrello 1.14 $fh{$fieldKey} = [$extFieldName, shift @values];
413 : olson 1.10 }
414 : parrello 1.13 # Create this object.
415 :     my $self = {
416 :     _db => $erdb,
417 :     _targetObject => $dbquery->{_targetObject},
418 :     _values => \%fh,
419 :     _parsed => $dbquery->{_parsed}
420 : parrello 1.11 };
421 : parrello 1.13 # Bless and return it.
422 :     bless $self, $class;
423 :     return $self;
424 : parrello 1.1 }
425 :    
426 : parrello 1.7 =head3 DB
427 :    
428 :     my $erdb = $dbObject->DB();
429 :    
430 :     Return the database for this result object.
431 :    
432 :     =cut
433 :    
434 :     sub DB {
435 :     # Get the parameters.
436 :     my ($self) = @_;
437 :     # Return the result.
438 :     return $self->{_db};
439 :     }
440 :    
441 : parrello 1.4 1;

MCS Webmaster
ViewVC Help
Powered by ViewVC 1.0.3