407
|
1 package IMPL::DOM::Transform::QueryToDOM;
|
|
2 use strict;
|
|
3
|
|
4 use IMPL::Const qw(:prop);
|
|
5 use IMPL::declare {
|
|
6 require => {
|
|
7 OutOfRangeException => '-IMPL::OutOfRangeException'
|
|
8 },
|
|
9 base => [
|
|
10 'IMPL::DOM::Transform::ObjectToDOM' => '@_'
|
|
11 ],
|
|
12 props => [
|
|
13 prefix => PROP_RO,
|
|
14 delimiter => PROP_RO
|
|
15 ]
|
|
16 };
|
|
17
|
|
18 our $MAX_INDEX = 1024;
|
|
19
|
|
20 sub CTOR {
|
|
21 my ($this) = @_;
|
|
22
|
|
23 $this->templates->{'CGI'} = 'TransformCGI';
|
|
24 $this->templates->{'IMPL::Web::Application::Action'} = 'TransformAction';
|
|
25
|
|
26 $this->delimiter('[.]');
|
|
27 $this->prefix('');
|
|
28 }
|
|
29
|
|
30 # inflate simple properties
|
|
31 sub TransformPlain {
|
|
32 my ($this,$data) = @_;
|
|
33
|
|
34 $this->currentNode->nodeProperty( rawValue => $data );
|
|
35 $this->currentNode->nodeValue( $data );
|
|
36 return $this->currentNode;
|
|
37 }
|
|
38
|
|
39 # do not store complex data as node values
|
|
40 sub StoreObject {
|
|
41 my ($this,$node,$data) = @_;
|
|
42
|
|
43 return $node;
|
|
44 }
|
|
45
|
|
46 #TODO: support a.b[0][1].c[1]
|
|
47
|
|
48 sub TransformCGI {
|
|
49 my ($this,$query) = @_;
|
|
50
|
|
51 my $data={};
|
|
52
|
|
53 my $prefix = $this->prefix;
|
|
54 my $delim = $this->delimiter;
|
|
55
|
|
56 foreach my $param (grep index($_,$prefix) >= 0 , $query->param()) {
|
|
57
|
|
58 my @value = grep length($_), $query->param($param) or next;
|
|
59
|
|
60 my @parts = split /$delim/,$param;
|
|
61
|
|
62 my $node = $data;
|
|
63 while ( my $part = shift @parts ) {
|
|
64 if (my ($name,$index) = ($part =~ m/^(\w+)(?:\[(\d+)\])?$/) ) {
|
|
65 if (@parts) {
|
|
66 if(defined $index) {
|
|
67 $this->ValidateIndex($index);
|
|
68 $node = ($node->{$name}[$index] ||= {});
|
|
69 } else {
|
|
70 $node = ($node->{$name} ||= {});
|
|
71 }
|
|
72 } else {
|
|
73 if(defined $index) {
|
|
74 $this->ValidateIndex($index);
|
|
75 $node->{$name}[$index] = (@value == 1 ? $value[0] : \@value);
|
|
76 } else {
|
|
77 $node->{$name} = (@value == 1 ? $value[0] : \@value);
|
|
78 }
|
|
79 }
|
|
80 }
|
|
81 }
|
|
82 }
|
|
83
|
|
84 return $this->Transform($data);
|
|
85 }
|
|
86
|
|
87 sub ValidateIndex {
|
|
88 my ($this,$index) = @_;
|
|
89
|
|
90 die OutOfRangeException->new()
|
|
91 unless $index >= 0 and $index <= $MAX_INDEX;
|
|
92 }
|
|
93
|
|
94 sub TransformAction {
|
|
95 my ($this,$action) = @_;
|
|
96
|
|
97 return $this->Transform($action->isJson ? $action->jsonData : $action->query);
|
|
98 }
|
|
99
|
|
100 1;
|
|
101
|
|
102 __END__
|
|
103
|
|
104 =pod
|
|
105
|
|
106 =head1 NAME
|
|
107
|
|
108 C<IMPL::DOM::Transform::QueryToDOM> - преобразование CGI запроса в DOM документ.
|
|
109
|
|
110 =head1 SYNOPSIS
|
|
111
|
|
112 =begin code
|
|
113
|
|
114 use CGI();
|
|
115 use IMPL::require {
|
|
116 Schema => 'IMPL::DOM::Schema',
|
|
117 Config => 'IMPL::Config',
|
|
118 QueryToDOM => 'IMPL::DOM::Transform::QueryToDOM'
|
|
119 }
|
|
120
|
|
121 my $q = CGI->new();
|
|
122
|
|
123 my $schema = Schema->LoadSchema(Config->AppBase('schemas','person.xml'));
|
|
124 my $transorm = QueryToDOM->new('edit', $schema);
|
|
125
|
|
126 my $form = $transform->Transform($q);
|
|
127
|
|
128 my @errors;
|
|
129
|
|
130 push @errors, $transform->buildErrors;
|
|
131 push @errors, $schema->Validate($doc);
|
|
132
|
|
133
|
|
134 =end code
|
|
135
|
|
136 =head1 DESCRIPTION
|
|
137
|
|
138 Наследует C<IMPL::DOM::Transform::ObjectToDOM>. Добавляет метод
|
|
139 C<TransformCGI> который применятеся к объектам типа C<CGI> (и производных).
|
|
140
|
|
141 Запрос C<CGI> сначала приводится к хешу, затем полученный хеш преобразуется
|
|
142 в DOM документ при помощи вызова метода C<Transform>.
|
|
143
|
|
144 Для этого выбираются параметры запроса, затем, имя каждого параметра
|
|
145 рассматривается в виде пути к свойству, создается структура из хешей и массивов
|
|
146 в которую по указанному пути кладется значение.
|
|
147
|
|
148 Если параметр имеет несколько значений, значит свойство является массивом.
|
|
149
|
|
150 Также изменено поведение некоторых методов преобразования.
|
|
151
|
|
152 =over
|
|
153
|
|
154 =item * C<TransformPlain($value)>
|
|
155
|
|
156 Преобразование для простого значения свойства. Посокльку в запросе передаются
|
|
157 строковые значения, а схема документа может предполпгать другие типы, при
|
|
158 преобразовании значения параметра из запроса к значению узла используется
|
|
159 метод C<< $this->inflateNodeValue($value) >>, также помимо значения
|
|
160 C<< $this->currentNode->nodeValue >> задается атрибут
|
|
161 C<< $this->currentNode->nodeProperty( rawValue => $value) >>, для того, чтобы
|
|
162 была возможность получить оригинальное значение параметра запроса (например,
|
|
163 в случае когда его формат был не верным и C<nodeValue> будет C<undef>).
|
|
164
|
|
165 =item * C<StoreObject($node,$object)>
|
|
166
|
|
167 Данный метод вызывается если текущий узел (переданный в параметре C<$node>)
|
|
168 предполагает простое значение, однако в запросе для него было передано сложное
|
|
169 содержимое. Данная реализация просто игнорирует переданный объект C<$object>
|
|
170 и возвращает C<$node> без изменений.
|
|
171
|
|
172 =back
|
|
173
|
|
174 =head1 MEMBERS
|
|
175
|
|
176 =head2 C<[get]delimiter>
|
|
177
|
|
178 REGEX. Разделитель свойств в имени параметра, по-умолчанию C<'[.]'>
|
|
179
|
|
180 =head2 C<[get]prefix>
|
|
181
|
|
182 Строка, префикс имен параметров, которые участвуют в формировании документа.
|
|
183 По-умолчанию пусто.
|
|
184
|
|
185 =cut |