49
|
1 package Form::Container;
|
|
2 use strict;
|
|
3 use Common;
|
|
4 use Form::Filter;
|
|
5 use base qw(Form::Item);
|
|
6
|
|
7 BEGIN {
|
|
8 DeclareProperty Schema => ACCESS_READ;
|
|
9 DeclareProperty Children => ACCESS_READ;
|
|
10 }
|
|
11
|
|
12 sub CTOR {
|
|
13 my ($this,%args) = @_;
|
|
14 $args{Schema} or die new Exception('A schema is required');
|
|
15
|
|
16 $this->SUPER::CTOR(@args{qw(Id Form Parent Attributes)});
|
|
17 $this->{$Schema} = $args{Schema};
|
|
18 }
|
|
19
|
|
20 sub ResolveItem {
|
|
21 my ($this,$ItemId) = @_;
|
|
22
|
|
23 if (my $schemaChild = $this->{$Schema}->FindChild($ItemId->Name)) {
|
|
24 if ($schemaChild->isMulti) {
|
|
25 defined $ItemId->InstanceID or die new Exception('Instance id is required for a muti element');
|
|
26 if (my $child = $this->{$Children}{$ItemId->Name}[$ItemId->InstanceID]){
|
|
27 return $child;
|
|
28 } else {
|
|
29 return undef if not $this->Form->AutoCreate;
|
|
30 return $this->{$Children}{$ItemId->Name}[$ItemId->InstanceID] = $this->Form->CreateInstance($schemaChild,$ItemId,$this);
|
|
31 }
|
|
32
|
|
33 } else {
|
|
34 defined $ItemId->InstanceID and die new Exception('The child is a single element',$this->Id->Canonical,$ItemId->Name);
|
|
35 if(my $child = $this->{$Children}{$ItemId->Name}) {
|
|
36 return $child;
|
|
37 } else {
|
|
38 return undef if not $this->Form->AutoCreate;
|
|
39 return $this->{$Children}{$ItemId->Name} = $this->Form->CreateInstance($schemaChild,$ItemId,$this);
|
|
40 }
|
|
41 }
|
|
42 } else {
|
|
43 die new Exception('The requested item isn\'t exists in the schema', $this->Id->Canonical,$ItemId->Name);
|
|
44 }
|
|
45 }
|
|
46
|
|
47 sub isEmpty {
|
|
48 my ($this) = @_;
|
|
49
|
|
50 foreach my $child (values %{$this->{$Children} || {} }) {
|
|
51 if (ref $child eq 'ARRAY') {
|
|
52 foreach my $inst (@$child) {
|
|
53 return 0 if not $child->isEmpty;
|
|
54 }
|
|
55 } else {
|
|
56 return 0 if not $child->isEmpty;
|
|
57 }
|
|
58 }
|
|
59
|
|
60 return 1;
|
|
61 }
|
|
62
|
|
63 =pod
|
|
64 Получает дочерние контенеры в виде списка, при том только не пустые контейнеры.
|
|
65 Если дочернний контейнер не множественный, то список будет состоять из одного элемента.
|
|
66 =cut
|
|
67 sub GetChild {
|
|
68 my ($this,$name) = @_;
|
|
69 return unless exists $this->{$Children}{$name};
|
|
70 return( grep $_, map { UNIVERSAL::isa($_,'ARRAY') ? @{$_} : $_ } $this->{$Children}{$name} );
|
|
71 }
|
|
72
|
|
73 =pod
|
|
74 Выполняет фильтры по схеме для себя и всех детей.
|
|
75 Фильтры определяются по схеме и вызываются в различнх контекстах
|
|
76
|
|
77 * сначала для группы,
|
|
78 * потом для детишек, причем если
|
|
79 * детишки множественные, то
|
|
80 * снсчала для набора детишек, а потом
|
|
81 * для каждого в отдельности
|
|
82 =cut
|
|
83 sub Validate {
|
|
84 my ($this,$rhDisableFilters) = @_;
|
|
85
|
|
86 $rhDisableFilters ||= {};
|
|
87
|
|
88 my @errors;
|
|
89
|
|
90 foreach my $filter (grep {$_->SUPPORTED_CONTEXT & (Form::Filter::CTX_SINGLE) and not exists $rhDisableFilters->{$_}} map {$_->Instance} $this->{$Schema}->Filters) {
|
|
91 my $result = $filter->Invoke($this,Form::Filter::CTX_SINGLE | Form::Filter::CTX_EXISTENT,$this->{$Schema});
|
|
92 if ($result->State == Form::FilterResult::STATE_SUCCESS_STOP) {
|
|
93 return ();
|
|
94 } elsif ($result->State == Form::FilterResult::STATE_ERROR) {
|
|
95 push @errors,$result;
|
|
96 }
|
|
97 }
|
|
98
|
|
99 CHILD_LOOP: foreach my $schemaChild ($this->{$Schema}->Children) {
|
|
100
|
|
101 if ($schemaChild->isMulti) {
|
|
102 my %DisableFilters;
|
|
103 foreach my $filter (grep {$_->SUPPORTED_CONTEXT & Form::Filter::CTX_SET} map {$_->Instance} $schemaChild->Filters) {
|
|
104
|
|
105 my $result = $filter->Invoke($this->{$Children}{$schemaChild->Name},Form::Filter::CTX_SET,$schemaChild,$this);
|
|
106 if ($result->State == Form::FilterResult::STATE_ERROR) {
|
|
107 push @errors,$result;
|
|
108 # не проверять другие фильтры вообще
|
|
109 next CHILD_LOOP;
|
|
110 } elsif ($result->State == Form::FilterResult::STATE_SUCCESS_STOP) {
|
|
111 # не проверять другие фильтры вообще
|
|
112 next CHILD_LOOP;
|
|
113 } elsif ($result->State == Form::FilterResult::STATE_SUCCESS_STAY) {
|
|
114 # не проверять данный фильтр на каждом экземпляре
|
|
115 $DisableFilters{$filter} = 1;
|
|
116 } else {
|
|
117 # STATE_SUCCESS - все ок
|
|
118 }
|
|
119 }
|
|
120
|
|
121 $_ and push @errors,$_->Validate(\%DisableFilters) foreach grep !$_->isEmpty, $this->GetChild($schemaChild->Name);
|
|
122
|
|
123 } else {
|
|
124 my %DisableFilters;
|
|
125
|
|
126 # проверяем фильтры, которые могут применяться на несуществующем значении
|
|
127 foreach my $filter (grep { $_->SUPPORTED_CONTEXT & Form::Filter::CTX_SINGLE and not $_->SUPPORTED_CONTEXT & Form::Filter::CTX_EXISTENT} map {$_->Instance} $schemaChild->Filters) {
|
|
128 my $result = $filter->Invoke($this->{$Children}{$schemaChild->Name},Form::Filter::CTX_SINGLE,$schemaChild,$this);
|
|
129
|
|
130 if ($result->State == Form::FilterResult::STATE_ERROR) {
|
|
131 push @errors,$result;
|
|
132 # не проверять другие фильтры вообще
|
|
133 next CHILD_LOOP;
|
|
134 } elsif ($result->State == Form::FilterResult::STATE_SUCCESS_STOP) {
|
|
135 # не проверять другие фильтры вообще
|
|
136 next CHILD_LOOP;
|
|
137 } else {
|
|
138 # STATE_SUCCESS(_STAY) - все ок
|
|
139 $DisableFilters{$filter} = 1;
|
|
140 }
|
|
141 }
|
|
142
|
|
143 # если значение существует, то применяем оставшиеся фильтры
|
|
144 push @errors,$this->{$Children}{$schemaChild->Name}->Validate(\%DisableFilters) if $this->{$Children}{$schemaChild->Name};
|
|
145 }
|
|
146
|
|
147 }
|
|
148
|
|
149 return @errors;
|
|
150 }
|
|
151
|
|
152 sub Dispose {
|
|
153 my ($this) = @_;
|
|
154
|
|
155 foreach my $child (values %{ $this->{$Children} || {} }) {
|
|
156 if (ref $child eq 'ARRAY') {
|
|
157 foreach my $inst (@$child) {
|
|
158 $inst->Dispose;
|
|
159 }
|
|
160 } else {
|
|
161 die new IMPL::Exception("Child is null",%{ $this->{$Children} }) if not $child;
|
|
162 $child->Dispose;
|
|
163 }
|
|
164 }
|
|
165
|
|
166 delete @$this{$Schema,$Children};
|
|
167
|
|
168 $this->SUPER::Dispose;
|
|
169 }
|
|
170 1;
|