Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions lib/QBit/Application/Model/DBManager.pm
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ sub get_all {
$self->{'__FOUND_ROWS__'} = $query->found_rows() if $opts{'calc_rows'};

if (@$result) {
$self->timelog->start(gettext('Preprocess easy fields'));
$self->pre_process_easy_fields($fields, $result);
$self->timelog->finish();

$self->timelog->start(gettext('Preprocess fields'));
$self->pre_process_fields($fields, $result);
$self->timelog->finish();
Expand Down Expand Up @@ -197,6 +201,92 @@ sub get_db_filter {

sub pre_process_fields { }

sub pre_process_easy_fields {
my ($self, $obj_fields, $data) = @_;

my $fields = $obj_fields->get_fields();

my @easy_fields = grep {$obj_fields->need($_) && exists($fields->{$_}{'model_accessor'})} keys(%$fields);

my $pre_process_fields = {};
foreach my $field (@easy_fields) {
my $model_accessor = $fields->{$field}{'model_accessor'};
my $fk_fields = join('#', @{$fields->{$field}{'fk_fields'}});
my $count = 1;
map {$pre_process_fields->{$model_accessor}{$fk_fields}{'fields'}{$_} = TRUE} @{$fields->{$field}{'fields'}},
grep {!($count++ & 1)} @{$fields->{$field}{'fk_fields'}};
$pre_process_fields->{$model_accessor}{$fk_fields}{'fk_fields'} = $fields->{$field}{'fk_fields'};
}

my $DATA;

foreach my $model (keys(%$pre_process_fields)) {
foreach my $fk_fields (keys(%{$pre_process_fields->{$model}})) {
next if exists($DATA->{$model}{$fk_fields});
my $filter = {};
my $i = 0;
while ($i < @{$pre_process_fields->{$model}{$fk_fields}{'fk_fields'}} - 1) {
my $j = $i + 1;
$filter->{$pre_process_fields->{$model}{$fk_fields}{'fk_fields'}[$j]} =
array_uniq(map {$_->{$pre_process_fields->{$model}{$fk_fields}{'fk_fields'}[$i]}} @$data);
$i += 2;
}

$DATA->{$model}{$fk_fields} = $self->$model->get_all(
fields => [keys(%{$pre_process_fields->{$model}{$fk_fields}{'fields'}})],
filter => $filter
);
}
}

foreach my $field (@easy_fields) {
my $model = $fields->{$field}{'model_accessor'};
my $fk_fields = join('#', @{$fields->{$field}{'fk_fields'}});
my $result = $fields->{$field}{'result'} || 'SCALAR';
if (($result eq 'SCALAR' || $result eq 'HASH')
&& !exists($obj_fields->{'__GROUP_DATA__'}{$model}{$fk_fields}{'SCALAR_HASH'}))
{
foreach $data (@{$DATA->{$model}{$fk_fields}}) {
my $count = 1;
my @key_list = map {$data->{$_}} grep {!($count++ & 1)} @{$fields->{$field}{'fk_fields'}};
$self->_add_key_with_hash(\$obj_fields->{'__GROUP_DATA__'}{$model}{$fk_fields}{'SCALAR_HASH'},
\@key_list, 1, $data);
}
} elsif ($result eq 'ARRAY' && !exists($obj_fields->{'__GROUP_DATA__'}{$model}{$fk_fields}{'ARRAY'})) {
foreach my $data (@{$DATA->{$model}{$fk_fields}}) {
my $count = 1;
my @key_list = map {$data->{$_}} grep {!($count++ & 1)} @{$fields->{$field}{'fk_fields'}};
$self->_add_key_with_array(\$obj_fields->{'__GROUP_DATA__'}{$model}{$fk_fields}{'ARRAY'},
\@key_list, 1, $data);
}
} else {
throw gettext('Unknown type of result "%s"', $result);
}
}
}

sub _add_key_with_hash {
my ($self, $hash, $key_list, $num, $value) = @_;

if (@$key_list == $num) {
$$hash->{$key_list->[$num - 1]} = $value;
return TRUE;
}

$self->_add_key_with_hash(\$$hash->{$key_list->[$num - 1]}, $key_list, ++$num, $value);
}

sub _add_key_with_array {
my ($self, $hash, $key_list, $num, $value) = @_;

if (@$key_list == $num) {
push(@{$$hash->{$key_list->[$num - 1]}}, $value);
return TRUE;
}

$self->_add_key_with_array(\$$hash->{$key_list->[$num - 1]}, $key_list, ++$num, $value);
}

sub _get_fields_obj {
my ($self, $fields) = @_;

Expand Down
38 changes: 38 additions & 0 deletions lib/QBit/Application/Model/DBManager/_Utils/Fields.pm
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,34 @@ sub process_data {
my $val;
if (exists($rec->{$field})) {
$val = $rec->{$field};
} elsif (exists($self->{'__FIELDS__'}{$field}{'model_accessor'})) {
my $model = $self->{'__FIELDS__'}{$field}{'model_accessor'};
my $fk_fields = join('#', @{$self->{'__FIELDS__'}{$field}{'fk_fields'}});
my $result = $self->{'__FIELDS__'}{$field}{'result'} || 'SCALAR';
my $count = 1;
my @key_list = map {$rec->{$_}} grep {$count++ & 1} @{$self->{'__FIELDS__'}{$field}{'fk_fields'}};
if ($result eq 'SCALAR') {
my $value =
$self->_get_value($self->{'__GROUP_DATA__'}{$model}{$fk_fields}{'SCALAR_HASH'}, \@key_list, 1);
$val = $value->{$self->{'__FIELDS__'}{$field}{'fields'}[0]};
} elsif ($result eq 'HASH') {
my $value =
$self->_get_value($self->{'__GROUP_DATA__'}{$model}{$fk_fields}{'SCALAR_HASH'}, \@key_list, 1);
$val = {map {$_ => $value->{$_}} @{$self->{'__FIELDS__'}{$field}{'fields'}}};
} elsif ($result eq 'ARRAY') {
my $value =
$self->_get_value($self->{'__GROUP_DATA__'}{$model}{$fk_fields}{'ARRAY'}, \@key_list, 1);
if (@{$self->{'__FIELDS__'}{$field}{'fields'}} == 1) {
$val = [map {$_->{$self->{'__FIELDS__'}{$field}{'fields'}[0]}} @$value];
} else {
$val = [];
foreach my $row (@$value) {
push(@$val, {map {$_ => $row->{$_}} @{$self->{'__FIELDS__'}{$field}{'fields'}}});
}
}
}
$val = $self->{'__FIELDS__'}{$field}{'get'}($self, $rec, $val)
if exists($self->{'__FIELDS__'}{$field}{'get'});
} elsif (exists($self->{'__FIELDS__'}{$field}{'get'})) {
$val = $rec->{$field} = $self->{'__FIELDS__'}{$field}{'get'}($self, $rec);
} elsif ($self->{'__FIELDS__'}{$field}{'i18n'}) {
Expand All @@ -105,6 +133,16 @@ sub process_data {
return \@res;
}

sub _get_value {
my ($self, $hash, $key_list, $num) = @_;

if (@$key_list == $num) {
return $hash->{$key_list->[$num - 1]};
} else {
return $self->_get_value($hash->{$key_list->[$num - 1]}, $key_list, ++$num);
}
}

sub need {
my ($self, $name) = @_;

Expand Down
60 changes: 60 additions & 0 deletions t/lib/Models/Model1.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package Models::Model1;

use qbit;
use base qw(QBit::Application::Model::DBManager); #QBit::Application

__PACKAGE__->model_fields(
id => {
default => TRUE,
db => TRUE,
pk => TRUE,
label => d_gettext('ID'),
},
domain => {
default => TRUE,
db => TRUE,
label => d_gettext('Domain'),
},
caption => {
db => TRUE,
label => d_gettext('Caption'),
}
);

sub get_all {
my ($self, %opts) = @_;

my %fields = map {$_ => TRUE} @{$opts{'fields'}};

foreach (@{$opts{'fields'}}) {
return [
{creator => "owner 1", id => 1, caption => 'short_caption 1'},
{creator => "owner 1", id => 2, caption => 'short_caption 1'},
{creator => "owner 2", id => 3, caption => 'short_caption 2'},
{creator => "owner 3", id => 4, caption => 'short_caption 3'},
{creator => "owner 3", id => 5, caption => 'short_caption 3'},
{creator => "owner 3", id => 6, caption => 'short_caption 3'},
{creator => "owner 4", id => 7, caption => 'short_caption 4'},
{creator => "owner 5", id => 8, caption => 'short_caption 5'},
{creator => "owner 5", id => 9, caption => 'short_caption 5'},
] if $_ eq 'creator' && !$fields{'domain'};
}

my $result = [
map {
my $value = $_;
{
id => $value,
map {$_ => "$_ $value"} grep {$_ ne 'id'} @{$opts{'fields'}}
}
} (1 .. 5)
];

foreach (@$result) {
$_->{'creator'} =~ s/creator/owner/ if $_->{'creator'};
};

return $result;
}

TRUE;
130 changes: 130 additions & 0 deletions t/lib/Models/Model2.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package Models::Model2;

use qbit;
use base qw(QBit::Application::Model::DBManager);

use Test::MockObject::Extends;

__PACKAGE__->model_accessors(model_1 => 'Models::Model1',);

__PACKAGE__->model_fields(
f_id => {
default => TRUE,
db => TRUE,
pk => TRUE,
label => d_gettext('ID'),
},
owner => {
default => TRUE,
db => TRUE,
label => d_gettext('Owner'),
},
short_caption => {
default => TRUE,
db => TRUE,
label => d_gettext('Short caption'),
},
name => {
depends_on => ['f_id'],
model_accessor => 'model_1',
fk_fields => ['f_id' => 'id'],
fields => ['caption'],
result => 'SCALAR',
},
name_array => {
depends_on => ['f_id'],
model_accessor => 'model_1',
fk_fields => ['f_id' => 'id'],
fields => ['caption'],
result => 'ARRAY',
},
name_hash => {
depends_on => ['f_id'],
model_accessor => 'model_1',
fk_fields => ['f_id' => 'id'],
fields => ['caption'],
result => 'HASH',
},
info => {
depends_on => ['f_id'],
model_accessor => 'model_1',
fk_fields => ['f_id' => 'id'],
fields => ['caption', 'domain'],
result => 'HASH',
},
ids_by_owner => {
depends_on => ['owner'],
model_accessor => 'model_1',
fk_fields => ['owner' => 'creator'],
fields => ['id'],
result => 'ARRAY',
},
ids_with_owner => {
depends_on => ['owner'],
model_accessor => 'model_1',
fk_fields => ['owner' => 'creator'],
fields => ['id', 'creator'],
result => 'ARRAY',
},
ids_string_by_owner => {
depends_on => ['owner'],
model_accessor => 'model_1',
fk_fields => ['owner' => 'creator'],
fields => ['id'],
result => 'ARRAY',
get => sub {
join(', ', @{$_[2]});
}
},
two_fk_fields => {
depends_on => ['f_id', 'owner'],
model_accessor => 'model_1',
fk_fields => ['f_id' => 'id', 'owner' => 'creator'],
fields => ['domain'],
result => 'SCALAR',
},
two_fk_fields_hash => {
depends_on => ['f_id', 'owner'],
model_accessor => 'model_1',
fk_fields => ['f_id' => 'id', 'owner' => 'creator'],
fields => ['domain', 'creator'],
result => 'HASH',
},
two_fk_fields_array => {
depends_on => ['short_caption', 'owner'],
model_accessor => 'model_1',
fk_fields => ['short_caption' => 'caption', 'owner' => 'creator'],
fields => ['id'],
result => 'ARRAY',
},
);

sub query {
my ($self, %opts) = @_;

my $fields = $opts{'fields'}->get_db_fields();

my $query = Test::MockObject::Extends->new();

$query->mock(
'get_all',
sub {

my @result = ();
for my $count (1 .. 5) {
my $var->{'f_id'} = $count;
foreach my $field (grep {$_ ne 'f_id'} keys(%$fields)) {
$var->{$field} = "$field $count";
}
push (@result, $var);
}
return \@result;
}
);

$query->mock('all_langs', sub {return $query});

return $query;
}

TRUE;
9 changes: 9 additions & 0 deletions t/lib/TestApp.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package TestApp;

use qbit;
use base qw(QBit::Application);

use Models::Model1 accessor => 'model_1';
use Models::Model2 accessor => 'model_2';

TRUE;
Loading