resourceManager = $resource; $this->legacyApi = $legacyApi; $this->converter = $converter; $this->request = $request; $this->db = $database; } /** * @param string $action Controller-Action * * @return Response */ public function dispatch($action) { if (substr($action, -6) !== 'Action') { throw new \RuntimeException(sprintf( 'API controller action "%s" is not dispatchable.', $action )); } if (!method_exists($this, $action)) { throw new \RuntimeException(sprintf( 'API controller method "%s" not found', $action )); } $this->response = $this->$action(); if ($this->response === null) { throw new \RuntimeException('Controller must return a Response object. Null given.'); } if (!$this->response instanceof Response) { throw new \RuntimeException('Controller must return a Response object.'); } return $this->response; } /** * @param string $className */ public function setResourceClass($className) { $this->resourceClass = $className; } /** * ID aus der URL (Route) bekommen * * @return int */ protected function getResourceId() { return (int)$this->request->attributes->getDigits('id'); } /** * @param string|null $className * * @return AbstractResource */ protected function getResource($className = null) { return $this->resourceManager->get($className !== null ? $className : $this->resourceClass); } /** * Request-Body in Array wandeln * * @return array */ protected function getRequestData() { try { return $this->converter->toArray($this->getContentType(), $this->request->getContent()); } catch (ConvertionException $e) { throw new BadRequestException( sprintf('%s could not be decoded.', strtoupper($this->getContentType())), ApiError::CODE_MALFORMED_REQUEST_BODY ); } } /** * @return null|string [json|xml] */ protected function getContentType() { return $this->request->getContentType(); } /** * @param AbstractResult $result * @param int $statusCode * * @return Response */ protected function sendResult(AbstractResult $result, $statusCode = Response::HTTP_OK) { $contentType = $this->determineResponseContentType(); $data = []; if ($contentType === 'xml') { if ($result->isCollection()) { $data['items'] = $result->getData(); $data['pagination'] = $result->getPagination(); } else { $data['item'] = $result->getData(); } } if ($contentType === 'json') { $data = $result->getResult(); } return $this->sendResponse($data, $contentType, $statusCode); } /** * Content-Type für die Ausgabe bestimmen * * @return string [xml|json] */ protected function determineResponseContentType() { // Accept-Header auslesen $acceptable = $this->request->getAcceptableContentTypes(); switch ($acceptable[0]) { // Client ist vermutlich ein Browser > JSON ausliefern case 'text/html': $contentType = 'json'; break; // Client hat JSON angefragt case 'application/json': $contentType = 'json'; break; // Client hat XML angefragt case 'application/xml': $contentType = 'xml'; break; // Nicht eindeutig > JSON bevorzugen default: if (in_array('application/xml', $acceptable)) { $contentType = 'xml'; break; } $contentType = 'json'; break; } return $contentType; } /** * @param array $data * @param string $contentType [xml|json] * @param int $statusCode HTTP-Statuscode * * @return Response */ protected function sendResponse($data, $contentType, $statusCode = Response::HTTP_OK) { if ($contentType === 'xml') { return new Response( $this->converter->arrayToXml($data, 'result'), $statusCode, ['Content-Type' => 'application/xml; charset=UTF-8'] ); } return new Response( $this->converter->arrayToJson($data), $statusCode, ['Content-Type' => 'application/json; charset=UTF-8'] ); } /** * Filterparameter aufbereiten * * @example /resource?title=123&project=1 * @example /resource?title_starts_with=123&project=1 * * @return array */ protected function prepareFilterParams() { $queryParams = $this->request->get->all(); // Reservierte Parameter ignorieren unset( $queryParams['sort'], $queryParams['page'], $queryParams['items'], $queryParams['filter'], $queryParams['include'] ); $filter = []; foreach ($queryParams as $filterKey => $filterValue) { $filter[$filterKey] = filter_var($filterValue, FILTER_SANITIZE_STRING, FILTER_FLAG_STRIP_LOW); } // Komplexe Suchfilter enthalten Array $filter['filter'] = $this->prepareComplexFilterParams(); return $filter; } /** * Filterparameter für komplexe Suche aufbereiten * * @example /resource?filter[0][property]=satz&filter[0][expression]=gte&filter[0][value]=10 * &filter[1][property]=bezeichnung&filter[1][value]=%Irland% * * @return array */ protected function prepareComplexFilterParams() { $filter = []; $params = $this->request->get->get('filter'); if (!is_array($params)) { return $filter; } ksort($params); $params = array_values($params); return $params; foreach ($params as $param) { echo "
";
var_dump($params);
echo "";
exit;
// @todo Sanitize
echo "";
var_dump($param);
echo "";
exit;
}
return $filter;
}
/**
* Sortierungsparameter aufbereiten
*
* @example /resource?sort=name,project
* @example /resource?sort=-name,project
*
* @return array
*/
protected function prepareSortingParams()
{
$sorting = [];
$sortQuery = filter_var($this->request->get->get('sort'), FILTER_SANITIZE_URL);
if (empty($sortQuery)) {
return $sorting;
}
/**
* Alte Syntax
*
* @example /resource?sort=title:desc|projekt:asc
*/
if (strpos($sortQuery, '|')) {
$sortParams = explode('|', $sortQuery);
foreach ($sortParams as $sortParam) {
if (strpos($sortParam, ':')) {
list($sortField, $sortOrder) = explode(':', $sortParam, 2);
} else {
$sortField = $sortParam;
$sortOrder = 'asc';
}
if (empty($sortField) || $sortField === ':') {
throw new InvalidArgumentException('Sorting parameter can not be empty');
}
if (!in_array(strtolower($sortOrder), ['asc', 'desc'], true)) {
throw new InvalidArgumentException(sprintf(
'Sorting order "%s" is not valid. Use "asc" or "desc".', $sortOrder
));
}
$sortOrder = strtolower($sortOrder) === 'desc' ? 'DESC' : 'ASC';
$sorting[$sortField] = $sortOrder;
}
return $sorting;
}
/**
* Neue Syntax: Minuszeichen vor dem Feld kehrt die Sortierung um
*
* @example /resource?sort=-title,projekt
*/
$sortParams = explode(',', $sortQuery);
foreach ($sortParams as $sortParam) {
if (strpos($sortParam, '-') === 0) {
$sortField = substr_replace($sortParam, '', 0, 1);
$sortOrder = 'DESC';
} else {
$sortField = $sortParam;
$sortOrder = 'ASC';
}
if (empty($sortField) || $sortField === '-') {
throw new InvalidArgumentException('Sorting parameter can not be empty');
}
$sorting[$sortField] = $sortOrder;
}
return $sorting;
}
/**
* @return array
*/
protected function prepareIncludeParams()
{
$includesQuery = $this->request->get->get('include');
if (empty($includesQuery)) {
return [];
}
$includes = explode(',', $includesQuery);
$includes = array_map('trim', $includes);
$includes = array_map('htmlspecialchars', $includes);
return $includes;
}
/**
* @return int
*/
protected function getPaginationPage()
{
$page = $this->request->get->getInt('page');
return $page > 0 && $page <= 1000 ? $page : 1;
}
/**
* @return int
*/
protected function getPaginationCount()
{
$items = $this->request->get->getInt('items');
return $items > 0 && $items <= 1000 ? $items : 20;
}
}