public function validateValues() { if (is_array($this->value) && !Condition::isValidMultipleModelsOperator($this->operator)) { throw new InvalidOperatorException(); } else { if (!is_array($this->value) && !Condition::isValidSingleModelOperator($this->operator)) { throw new InvalidOperatorException(); } } }
public function get(Request $request) { $modelClassName = $this->modelClassName; $query = $modelClassName::getModelQuery(); $limit = null; $page = null; $sort = null; // variables to build our links later on $jsonapi = new JsonApiRootNode(); // We start by adding the link to this request $baseUrl = $request->getSchemeAndHttpHost() . $request->getPathInfo(); // this might not work with a different port than 80, check later // Get requests can either be a list of models, or an individual model, so we must check the slug if (empty($this->slug)) { // when we don't have a slug, we are expected to always return an array response, // even when the result is a single object $jsonapi->asArray(true); // when the slug is empty, we must check for extra variables if ($request->query->has('limit') && is_numeric($request->query->get('limit'))) { $limit = $request->query->get('limit'); } // Additional check for the page variable, we potentially need to adjust the query start if ($request->query->has('page') && is_numeric($request->query->get('page'))) { $page = $request->query->get('page'); if (!empty($limit)) { $start = ($page - 1) * $limit; $query->setRange($start, $limit); } else { $start = ($page - 1) * $this->maxLimit; $query->setRange($start, $this->maxLimit); } } else { // no page, we can just set a limit if (empty($limit)) { // no limit, lets set the default one $query->setLimit($this->maxLimit); } else { $query->setLimit($limit); } } // Lets get the pretty to regular field mapping for use in either sort or filter $prettyToFieldsMap = $modelClassName::getPrettyFieldsToFieldsMapping(); // Lets also check for an order if ($request->query->has('sort')) { // sort params are split by ',' so lets evaluate them individually $sort = $request->query->get('sort'); $sortQueryFields = explode(',', $sort); foreach ($sortQueryFields as $sortQueryField) { // the json-api spec tells us, that all fields are sorted ascending, unless the field is prepended by a '-' // http://jsonapi.org/format/#fetching-sorting $direction = !empty($sortQueryField) && $sortQueryField[0] === '-' ? 'DESC' : 'ASC'; $prettyField = ltrim($sortQueryField, '-'); // lets remove the '-' from the start of the field if it exists $prettyFieldParts = explode('.', $prettyField); // if the pretty field exists, lets add it to the sort order if (array_key_exists($prettyFieldParts[0], $prettyToFieldsMap)) { $field = $prettyToFieldsMap[$prettyFieldParts[0]]; if (sizeof($prettyFieldParts) > 1) { $typePrettyToFieldsMap = $modelClassName::getTypePrettyFieldToFieldsMapping(); // meaning we have a extra column present $fieldDefinition = $modelClassName::getFieldDefinition($field); $fieldType = $fieldDefinition->getType(); if (array_key_exists($fieldType, $typePrettyToFieldsMap) && array_key_exists($prettyFieldParts[1], $typePrettyToFieldsMap[$fieldType])) { $column = $typePrettyToFieldsMap[$fieldType][$prettyFieldParts[1]]; $query->addSortOrder($field . '.' . $column, $direction); } } else { $query->addSortOrder($field, $direction); } } } } // Before we fetch the collection, lets check for filters if ($request->query->has('filter')) { $filter = $request->query->get('filter'); if (is_array($filter)) { foreach (array_keys($filter) as $prettyField) { // lets start by making sure the field exists // we explode, because we have a potential field with a column (like address.city) as opposed to just a field (like name) $prettyFieldParts = explode('.', $prettyField); if (array_key_exists($prettyFieldParts[0], $prettyToFieldsMap)) { $field = $prettyToFieldsMap[$prettyFieldParts[0]]; $operator = null; $value = null; $filterValue = $filter[$prettyField]; // the filter value can either be the specific value, or an array with extra attributes if (is_array($filterValue)) { // we found an array, meaning we must check for 'operator' as well $operator = array_key_exists('operator', $filterValue) && Condition::isValidSingleModelOperator($filterValue['operator']) ? $filterValue['operator'] : null; $value = array_key_exists('value', $filterValue) ? $filterValue['value'] : null; } else { // no array, so it will just be the value $operator = '='; $value = $filterValue; } if (!empty($operator) && !empty($value) && !empty($field)) { if (sizeof($prettyFieldParts) > 1) { // this means we have a field with a column (like address.city) $typePrettyToFieldsMap = $modelClassName::getTypePrettyFieldToFieldsMapping(); // meaning we have a extra column present $fieldDefinition = $modelClassName::getFieldDefinition($field); $fieldType = $fieldDefinition->getType(); if (array_key_exists($fieldType, $typePrettyToFieldsMap) && array_key_exists($prettyFieldParts[1], $typePrettyToFieldsMap[$fieldType])) { $column = $typePrettyToFieldsMap[$fieldType][$prettyFieldParts[1]]; $condition = new Condition($field . '.' . $column, $operator, $value); $query->addCondition($condition); } } else { // just a field, no column (like name) $condition = new Condition($field, $operator, $value); $query->addCondition($condition); } } } } } } // Here we build the links of the request $this->addSingleLink($jsonapi, 'self', $baseUrl, $limit, $page, $sort); // here we add the self link $result = $query->fetchCollection(); if (!$result->isEmpty) { // we must include pagination links when there are more than the maximum amount of results if ($result->size === $this->maxLimit) { $previousPage = empty($page) ? 0 : $page - 1; // the first link is easy, it is the first page $this->addSingleLink($jsonapi, 'first', $baseUrl, 0, 1, $sort); // the previous link, checks if !empty, so pages with value 0 will not be displayed if (!empty($previousPage)) { $this->addSingleLink($jsonapi, 'previous', $baseUrl, 0, $previousPage, $sort); } // next we check the total count, to see if we can display the last & next link $totalCount = $query->fetchTotalCount(); if (!empty($totalCount)) { $lastPage = ceil($totalCount / $this->maxLimit); $currentPage = empty($page) ? 1 : $page; $this->addSingleLink($jsonapi, 'last', $baseUrl, 0, $lastPage, $sort); // let's include some meta data $jsonapi->addMeta('count', (int) $result->size); $jsonapi->addMeta('total-count', (int) $totalCount); $jsonapi->addMeta('page-count', (int) $lastPage); $jsonapi->addMeta('page-size', (int) $this->maxLimit); $jsonapi->addMeta('page-current', (int) $currentPage); if (!empty($previousPage)) { $jsonapi->addMeta('page-prev', (int) $previousPage); } // and finally, we also check if the next page isn't larger than the last page $nextPage = empty($page) ? 2 : $page + 1; if ($nextPage <= $lastPage) { $this->addSingleLink($jsonapi, 'next', $baseUrl, 0, $nextPage, $sort); $jsonapi->addMeta('page-next', (int) $nextPage); } $jsonapi->addMeta('result-row-first', (int) (($currentPage - 1) * $this->maxLimit) + 1); $jsonapi->addMeta('result-row-last', (int) $result->size < $this->maxLimit ? ($currentPage - 1) * $this->maxLimit + $result->size : $currentPage * $this->maxLimit); } } else { if (!empty($limit)) { // we must also include pagination links when we have a limit defined $previousPage = empty($page) ? 0 : $page - 1; // the first link is easy, it is the first page $this->addSingleLink($jsonapi, 'first', $baseUrl, $limit, 1, $sort); // the previous link, checks if !empty, so pages with value 0 will not be displayed if (!empty($previousPage)) { $this->addSingleLink($jsonapi, 'prev', $baseUrl, $limit, $previousPage, $sort); } // next we check the total count, to see if we can display the last & next link $totalCount = $query->fetchTotalCount(); if (!empty($totalCount)) { $lastPage = ceil($totalCount / $limit); $currentPage = empty($page) ? 1 : $page; $this->addSingleLink($jsonapi, 'last', $baseUrl, $limit, $lastPage, $sort); // let's include some meta data $jsonapi->addMeta('count', (int) $result->size); $jsonapi->addMeta('total-count', (int) $totalCount); $jsonapi->addMeta('page-count', (int) $lastPage); $jsonapi->addMeta('page-size', (int) $limit); $jsonapi->addMeta('page-current', (int) $currentPage); if (!empty($previousPage)) { $jsonapi->addMeta('page-prev', (int) $previousPage); } // and finally, we also check if the next page isn't larger than the last page $nextPage = empty($page) ? 2 : $page + 1; if ($nextPage <= $lastPage) { $this->addSingleLink($jsonapi, 'next', $baseUrl, $limit, $nextPage, $sort); $jsonapi->addMeta('page-next', (int) $nextPage); } $jsonapi->addMeta('result-row-first', (int) (($currentPage - 1) * $limit) + 1); $jsonapi->addMeta('result-row-last', (int) $result->size < $limit ? ($currentPage - 1) * $limit + $result->size : $currentPage * $limit); } } else { $jsonapi->addMeta('count', (int) $result->size); $jsonapi->addMeta('total-count', (int) $result->size); $jsonapi->addMeta('page-count', (int) 1); $jsonapi->addMeta('page-size', (int) $this->maxLimit); $jsonapi->addMeta('page-current', (int) 1); $jsonapi->addMeta('result-row-first', (int) 1); $jsonapi->addMeta('result-row-last', (int) $result->size); } } $node = $result->getJsonApiNode(); $jsonapi->setData($node); if ($request->query->has('include')) { // includes are comma seperated $includes = explode(',', $request->query->get('include')); if (!empty($includes)) { $this->checkForIncludes($result, $jsonapi, $includes); } } } } else { $query->addCondition(new Condition($modelClassName::$idField, '=', $this->slug)); $result = $query->fetchSingleModel(); $this->addSingleLink($jsonapi, 'self', $baseUrl); if (!empty($result)) { $jsonapi->addNode($result->getJsonApiNode()); // let's check for includes as well if ($request->query->has('include')) { // includes are comma seperated $includes = explode(',', $request->query->get('include')); if (!empty($includes)) { $this->checkForIncludes($result, $jsonapi, $includes); } } } else { // we musn't do anything, json api provides an empty node out of the box } } return new Response(json_encode($jsonapi->serialize()), 200, array()); }