MultipleActionController.php
1 <?php
2 /**
3  * wCMF - wemove Content Management Framework
4  * Copyright (C) 2005-2015 wemove digital solutions GmbH
5  *
6  * Licensed under the terms of the MIT License.
7  *
8  * See the LICENSE file distributed with this work for
9  * additional information.
10  */
12 
17 
18 /**
19  * MultipleActionController executes multiple actions by passing them do the
20  * appropriate controllers and returning all results as once.
21  *
22  * The controller supports the following actions:
23  *
24  * <div class="controller-action">
25  * <div> __Action__ _default_ </div>
26  * <div>
27  * Execute the given actions.
28  * | Parameter | Description
29  * |------------------------|-------------------------
30  * | _in_ `data` | An associative array with unique/sortable keys and values that describe an action to perform
31  * | _out_ `data` | An associative array with the same keys and values that describe the resonse of each action
32  * | __Response Actions__ | |
33  * | `ok` | In all cases
34  * </div>
35  * </div>
36  *
37  * The data array may contain the following special variables, that will be replaced by the described values:
38  * - `{last_created_oid:type}` will be replaced by the oid lastly created object of the given type
39  *
40  * An example of input data in JSON:
41  * @code
42  data: {
43  action1: {
44  action: "create",
45  params: {
46  oid: "Author:wcmffb298f3784dd49548a05d43d7bf88590",
47  name: "Ingo Herwig"
48  }
49  },
50  action2: {
51  action: "read",
52  params: {
53  oid: "{last_created_oid:Author}"
54  }
55  }
56  }
57  * @endcode
58  *
59  * The output data for the preceding request could look like
60  * @code
61  data: {
62  action1: {
63  oid: "Author:123",
64  ...
65  },
66  action2: {
67  object: {
68  oid: "Author:123",
69  modified: "2001-01-01 01:01",
70  creator: "admin"
71  ...
72  }
73  }
74  }
75  * @endcode
76  *
77  * @author ingo herwig <ingo@wemove.com>
78  */
80 
81  /**
82  * @see Controller::validate()
83  */
84  protected function validate() {
85  // check if we have an array of arrays
86  $request = $this->getRequest();
87  $response = $this->getResponse();
88  if ($request->hasValue('data')) {
89  $data = $request->getValue('data');
90  foreach($data as $key => $value) {
91  if (!is_array($value)) {
92  $response->addError(ApplicationError::get('PARAMETER_INVALID',
93  array('invalidParameters' => array('data.'.$key))));
94  return false;
95  }
96  }
97  }
98  else {
99  $response->addError(ApplicationError::get('PARAMETER_MISSING',
100  array('missingParameters' => array('data'))));
101  return false;
102  }
103  return true;
104  }
105 
106  /**
107  * @see Controller::doExecute()
108  */
109  protected function doExecute() {
110  // create and execute requests for the actions given in data
111  $request = $this->getRequest();
112  $response = $this->getResponse();
113  $logger = $this->getLogger();
114 
115  $results = array();
116  $data = $request->getValue('data');
117  $actions = array_keys($data);
118  $numActions = sizeof($actions);
119  $exceptions = array();
120 
121  for($i=0; $i<$numActions; $i++) {
122  $actionId = $actions[$i];
123  if ($logger->isDebugEnabled()) {
124  $logger->debug("processing action: ".$actionId.":\n".StringUtil::getDump($data[$actionId]));
125  }
126  // replace special variables
127  $this->replaceVariables($data[$actionId]);
128 
129  // create the request
130  $actionData = $data[$actionId];
131  $context = isset($actionData['context']) ? $actionData['context'] : '';
132  $action = isset($actionData['action']) ? $actionData['action'] : '';
133  // since serializer may replace the params key by the actual
134  // node data, we use actionData as fallback
135  $params = isset($actionData['params']) ? $actionData['params'] : $actionData;
136  $requestPart = ObjectFactory::getInstance('request');
137  $requestPart->setContext($context);
138  $requestPart->setAction($action);
139  $requestPart->setValues($params);
140  $requestPart->setFormat('null');
141  $requestPart->setResponseFormat('null');
142 
143  // execute the request
144  try {
145  $responsePart = $this->getActionMapper()->processAction($requestPart);
146  }
147  catch (\Exception $ex) {
148  $exceptions[] = $ex;
149  }
150 
151  // collect the result
152  $results[$actionId] = $responsePart != null ? $responsePart->getValues() : array();
153  }
154  if ($logger->isDebugEnabled()) {
155  $logger->debug($results);
156  }
157  // add errors from exceptions
158  foreach ($exceptions as $ex) {
159  $response->addError(ApplicationError::fromException($ex));
160  }
161  $response->setValue('data', $results);
162  $response->setAction('ok');
163  }
164 
165  /**
166  * Check the given data array for special variables to replace
167  * Variables have either the form 'variable_name' or 'variable_name:column_separated_parameters'
168  * @param $data A reference to the associative data array
169  */
170  private function replaceVariables(&$data) {
171  $logger = $this->getLogger();
172  $keys = array_keys($data);
173  for($i=0; $i<sizeof($keys); $i++) {
174  $key = $keys[$i];
175  $value = $data[$key];
176 
177  // replace variables
178  $newKey = $this->replaceVariablesString($key);
179  $newValue = $this->replaceVariablesString($value);
180 
181  // replace entry
182  if ($key != $newKey || $value != $newValue) {
183  if ($logger->isDebugEnabled()) {
184  if ($key != $newKey) {
185  $logger->debug("Replace $key by $newKey");
186  }
187  if ($value != $newValue) {
188  $logger->debug("Replace $value by $newValue");
189  }
190  }
191  unset($data[$key]);
192  $data[$newKey] = $newValue;
193  }
194  }
195  }
196 
197  /**
198  * Check the given string for special variables to replace
199  * Variables have either the form 'variable_name' or 'variable_name:column_separated_parameters'
200  * @param $value The string
201  * @return String
202  */
203  private function replaceVariablesString($value) {
204  if (!is_string($value)) {
205  return $value;
206  }
207  preg_match_all('/\{([^\{]+)\}/', $value, $variableMatches);
208  $variables = $variableMatches[1];
209  foreach($variables as $variable) {
210  preg_match('/^([^:]+)[:]*(.*)$/', $variable, $matches);
211  if (sizeof($matches > 0)) {
212  $variableName = $matches[1];
213  $parameters = $matches[2];
214  $persistenceFacade = $this->getPersistenceFacade();
215 
216  // last_created_oid
217  if ($variableName == 'last_created_oid') {
218  $type = $parameters;
219  if ($persistenceFacade->isKnownType($type)) {
220  $oid = $persistenceFacade->getLastCreatedOID($type);
221  $value = preg_replace("/{".$variable."}/", $oid, $value);
222  }
223  }
224 
225  // oid reference
226  if ($persistenceFacade->isKnownType($variableName)) {
227  $type = $variableName;
228  $oid = $persistenceFacade->getLastCreatedOID($type);
229  $value = $oid;
230  }
231  }
232  }
233  return $value;
234  }
235 }
236 ?>
getRequest()
Get the Request instance.
Definition: Controller.php:190
Controller is the base class of all controllers.
Definition: Controller.php:48
MultipleActionController executes multiple actions by passing them do the appropriate controllers and...
static getInstance($name, $dynamicConfiguration=array())
static fromException(\Exception $ex)
Factory method for transforming an exception into an ApplicationError instance.
Application controllers.
Definition: namespaces.php:3
static get($code, $data=null)
Factory method for retrieving a predefind error instance.
getLogger()
Get the Logger instance.
Definition: Controller.php:206
getActionMapper()
Get the ActionMapper instance.
Definition: Controller.php:238
getResponse()
Get the Response instance.
Definition: Controller.php:198
getPersistenceFacade()
Get the PersistenceFacade instance.
Definition: Controller.php:222
static getDump($var)
Get the dump of a variable as string.
Definition: StringUtil.php:25