129
my $invocant = shift;
129
my $invocant = shift;
130
my $class = ref($invocant) || $invocant;
130
my $class = ref($invocant) || $invocant;
131
my ($id_list) = @_;
131
my ($id_list) = @_;
132
my $dbh = Bugzilla->dbh;
133
my $columns = join(',', $class->DB_COLUMNS);
134
my $table = $class->DB_TABLE;
135
my $order = $class->LIST_ORDER;
136
my $id_field = $class->ID_FIELD;
132
my $id_field = $class->ID_FIELD;
138
my $objects;
134
my @detainted_ids;
139
if (@$id_list) {
135
foreach my $id (@$id_list) {
140
my @detainted_ids;
136
detaint_natural($id) ||
141
foreach my $id (@$id_list) {
137
ThrowCodeError('param_must_be_numeric',
142
detaint_natural($id) ||
138
{function => $class . '::new_from_list'});
143
ThrowCodeError('param_must_be_numeric',
139
push(@detainted_ids, $id);
144
{function => $class . '::new_from_list'});
140
}
145
push(@detainted_ids, $id);
141
# We don't do $invocant->match because some classes have
146
}
142
# their own implementation of match which is not compatible
147
$objects = $dbh->selectall_arrayref(
143
# with this one. However, match() still needs to have the right $invocant
148
"SELECT $columns FROM $table WHERE "
144
# in order to do $class->DB_TABLE and so on.
149
. $dbh->sql_in($id_field, \@detainted_ids)
145
return match($invocant, { $id_field => \@detainted_ids });
150
. "ORDER BY $order", {Slice=>{}});
155
foreach my $object (@$objects) {
156
bless($object, $class);
161
# Note: Future extensions to this could be:
148
# Note: Future extensions to this could be:
162
# * Accept arrays for an IN clause
163
# * Add a MATCH_JOIN constant so that we can join against
149
# * Add a MATCH_JOIN constant so that we can join against
164
# certain other tables for the WHERE criteria.
150
# certain other tables for the WHERE criteria.
165
sub match {
151
sub match {
166
my ($invocant, $criteria) = @_;
152
my ($invocant, $criteria) = @_;
167
my $class = ref($invocant) || $invocant;
153
my $class = ref($invocant) || $invocant;
168
my $dbh = Bugzilla->dbh;
154
my $dbh = Bugzilla->dbh;
169
my $id = $class->ID_FIELD;
170
my $table = $class->DB_TABLE;
172
return [$class->get_all] if !$criteria;
156
return [$class->get_all] if !$criteria;
174
my (@terms, @values);
158
my (@terms, @values);
175
foreach my $field (keys %$criteria) {
159
foreach my $field (keys %$criteria) {
176
my $value = $criteria->{$field};
160
my $value = $criteria->{$field};
177
if ($value eq NOT_NULL) {
161
if (ref $value eq 'ARRAY') {
162
# IN () is invalid SQL, and if we have an empty list
163
# to match against, we're just returning an empty
165
return [] if !scalar @$value;
167
my @qmarks = ("?") x @$value;
168
push(@terms, $dbh->sql_in($field, \@qmarks));
169
push(@values, @$value);
171
elsif ($value eq NOT_NULL) {
178
push(@terms, "$field IS NOT NULL");
172
push(@terms, "$field IS NOT NULL");
180
elsif ($value eq IS_NULL) {
174
elsif ($value eq IS_NULL) {
189
my $where = join(' AND ', @terms);
183
my $where = join(' AND ', @terms);
190
my $ids = $dbh->selectcol_arrayref(
184
return $class->_do_list_select($where, \@values);
191
"SELECT $id FROM $table WHERE $where", undef, @values)
185
}
193
187
sub _do_list_select {
194
return $class->new_from_list($ids);
188
my ($class, $where, $values) = @_;
189
my $table = $class->DB_TABLE;
190
my $cols = join(',', $class->DB_COLUMNS);
191
my $order = $class->LIST_ORDER;
193
my $sql = "SELECT $cols FROM $table";
194
if (defined $where) {
195
$sql .= " WHERE $where ";
197
$sql .= " ORDER BY $order";
199
my $dbh = Bugzilla->dbh;
200
my $objects = $dbh->selectall_arrayref($sql, {Slice=>{}}, @$values);
201
bless ($_, $class) foreach @$objects;
197
###############################
205
###############################
350
sub get_all {
358
sub get_all {
351
my $class = shift;
359
my $class = shift;
352
my $dbh = Bugzilla->dbh;
360
return @{$class->_do_list_select()};
353
my $table = $class->DB_TABLE;
354
my $order = $class->LIST_ORDER;
355
my $id_field = $class->ID_FIELD;
357
my $ids = $dbh->selectcol_arrayref(qq{
358
SELECT $id_field FROM $table ORDER BY $order});
360
my $objects = $class->new_from_list($ids);
364
###############################
363
###############################
51
products.defaultmilestone
51
products.defaultmilestone
54
###############################
55
#### Constructors #####
56
###############################
58
# This is considerably faster than calling new_from_list three times
59
# for each product in the list, particularly with hundreds or thousands
63
my %prods = map { $_->id => $_ } @$products;
64
my @prod_ids = keys %prods;
65
return unless @prod_ids;
67
my $dbh = Bugzilla->dbh;
68
foreach my $field (qw(component version milestone)) {
69
my $classname = "Bugzilla::" . ucfirst($field);
70
my $objects = $classname->match({ product_id => \@prod_ids });
72
# Now populate the products with this set of objects.
73
foreach my $obj (@$objects) {
74
my $product_id = $obj->product_id;
75
$prods{$product_id}->{"${field}s"} ||= [];
76
push(@{$prods{$product_id}->{"${field}s"}}, $obj);
55
###############################
81
###############################
56
#### Methods ####
82
#### Methods ####
303
=item C<components()>
329
=item C<components>
305
Description: Returns an array of component objects belonging to
331
Description: Returns an array of component objects belonging to
306
the product.
332
the product.
319
Returns: A hash with group id as key and hash containing
345
Returns: A hash with group id as key and hash containing
320
a Bugzilla::Group object and the properties of group
346
a Bugzilla::Group object and the properties of group
321
relative to the product.
347
relative to the product.
323
=item C<groups_mandatory_for>
349
=item C<groups_mandatory_for>
359
=item C<versions()>
385
=item C<versions>
361
Description: Returns all valid versions for that product.
387
Description: Returns all valid versions for that product.
365
Returns: An array of Bugzilla::Version objects.
391
Returns: An array of Bugzilla::Version objects.
367
=item C<milestones()>
393
=item C<milestones>
369
Description: Returns all valid milestones for that product.
395
Description: Returns all valid milestones for that product.
446
When passed an arrayref of C<Bugzilla::Product> objects, preloads their
447
L</milestones>, L</components>, and L</versions>, which is much faster
448
than calling those accessors on every item in the array individually.
450
This function is not exported, so must be called like
451
C<Bugzilla::Product::preload($products)>.
418
=item C<check_product($product_name)>
453
=item C<check_product($product_name)>
420
Description: Checks if the product name was passed in and if is a valid
455
Description: Checks if the product name was passed in and if is a valid