/** * Returns a list of records with the given values * * @history * 2013.09.30: * (AT) Initial release * 2013.10.25: * (AT) Improve thrown exceptions * 2013.11.25: * (AT) Add support for _limit and _offset query parameters * (AT) Set default limit to 10,000 rows * 2014.02.27: * (AT) Fix bad logic when setting limit and offset on OCI * 2014.03.04: * (AT) Split function into a query-generating function and a query * execution part to make it easier to extend query functionality * 2014.03.05: * (AT) Handle queryUnique flag * 2014.03.06: * (AT) Rename array keys when using the OCI driver * 2014.03.18: * (AT) Make sure the method is still persistent * * @version 2014.03.18 * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**> * * @param array $parameters * List of query parameters * @param string $class_name * Use array to return list as an array, or class name to return objects * @param array $ctorargs * Constructor arguments if returning objects * @return array Record list * @throws \Cougar\Exceptions\Exception; * @throws \Cougar\Exceptions\AccessDeniedException; */ public function query(array $parameters = array(), $class_name = "array", array $ctorargs = array()) { # Make sure we are still persistent if (!$this->__persistent) { throw new Exception("Querying is no longer allowed on this model"); } # See if querying is allowed if (!$this->__allowQuery) { throw new AccessDeniedException("This model does not support querying"); } # Set the view (if it hasn't been changed) if ($this->__currentView == "__default__" && $this->__queryView !== "__default__") { $this->__setView($this->__queryView); } # Extract the columns and aliases for the columns we can query $query_aliases = array_intersect($this->__alias, $this->__queryProperties); $columns = array(); $key_map = array(); foreach ($this->__queryProperties as $property) { if ($this->__visible[$property]) { if ($this->__exportAlias[$property] == $this->__columnMap[$property]) { $columns[$property] = $this->__columnMap[$property]; $key_map[$this->__columnMap[$property]] = $this->__columnMap[$property]; } else { $columns[$property] = $this->__columnMap[$property] . " AS " . $this->__exportAlias[$property]; $key_map[$this->__exportAlias[$property]] = $this->__exportAlias[$property]; } } } # Recursively iterate through the query parameters $this->iterateQueryParameters($parameters, $query_aliases, $columns, $key_map); # Prepare the array that will hold the parameter values $values = array(); # Set the default limit to 10,000 rows $limit = 10000; $offset = 0; $used_parameters = array(); # Prepare the query and execute the statement if ($this->__queryUnique) { $query = "SELECT DISTINCT "; } else { $query = "SELECT "; } $query .= implode(", ", $columns) . " FROM " . $this->__table . " " . implode(" ", $this->__joins); $where_clause = QueryParameter::toSql($parameters, $this->__columnMap, $query_aliases, $this->__caseInsensitive, $values, $used_parameters, $limit, $offset); if ($where_clause) { $query .= " WHERE " . $where_clause; } # Set the limit and offset $limit = (int) $limit; $offset = (int) $offset; if ($this->__pdo->getAttribute(PDO::ATTR_DRIVER_NAME) == "oci") { if ($where_clause) { $query .= " AND "; } else { $query .= " WHERE "; } $query .= "ROWNUM > " . $offset . " AND ROWNUM <= " . ($offset + $limit); } else { $query .= " LIMIT " . $limit . " OFFSET " . $offset; } # Execute the query $results = $this->executeQuery($query, $values, $class_name, $ctorargs); # Oracle will turn all column names as uppercase. This will rename them # if we are returning an array and are using OCI if ($class_name == "array" && count($results) > 0 && $this->__pdo->getAttribute(PDO::ATTR_DRIVER_NAME) == "oci") { # Change the keys in the key map to uppercase $key_map = array_change_key_case($key_map, CASE_UPPER); # Rename the keys $results = Arrays::renameKeys($results, $key_map); } # Return the result return $results; }
/** * @covers \Cougar\Util\QueryParameter::fromUri */ public function testToHtmlSingleMultipleOperators() { $query = "this=that&blue!=green|red>=circle"; $parameters = QueryParameter::fromUri("? " . $query); $this->assertEquals($query, QueryParameter::toHtml($parameters)); }
/** * Makes the REST call for the given operation * * @history * 2013.09.30: * (AT) Initial release * * @version 2013.09.30 * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**> * * @param string $operation One of CREATE, READ, UPDATE or DELETE * @return mixed REST response * @throws \Cougar\Exceptions\Exception * @throws \Cougar\Exceptions\NotImplementedException */ protected function makeRequest($operation) { # See which operation we have switch ($operation) { case "CREATE": $call = $this->__createCall; break; case "READ": $call = $this->__readCall; break; case "UPDATE": $call = $this->__updateCall; break; case "DELETE": $call = $this->__deleteCall; break; case "QUERY": $call = $this->__queryCall; break; default: throw new Exception("Invalid REST operation: " . $operation); break; } # Create the URI and add property values $uri = $this->__baseUri . $call["uri"]; $uri_param_list = array(); preg_match_all("/:[A-Za-z0-9_]+\\w/u", $uri, $uri_param_list); foreach ($uri_param_list[0] as $property_param) { $property = substr($property_param, 1); if (in_array($property, $this->__properties)) { $uri = str_replace($property_param, $this->{$property}, $uri); } } # See if we have query parameters if ($operation == "QUERY" && count($call["parameters"])) { # See if the URI has a ? already if (strpos($uri, "?") === false) { $uri .= "?" . QueryParameter::toHtml($call["parameters"]); } else { $uri .= "&" . QueryParameter::toHtml($call["parameters"]); } } # Gather the GET parameters $get_fields = array(); foreach ($call["get"] as $property) { $get_fields[$property] = $this->{$property}; } # See which kind of body we are sending switch ($call["bodyType"]) { case "json": $content_type = "application/json"; $body = json_encode($this); break; case "php": $content_type = "application/vnd.php.serialized"; $body = serialize((object) $this); break; case "xml": $content_type = "application/xml"; throw new NotImplementedException("XML REST calls not yet supported"); break; default: # Build an array of arguments $content_type = null; $body = null; if ($call["post"]) { $body = array(); foreach ($call["post"] as $property) { $body[$property] = $this->{$property}; } } break; } # Make the call and return the result return $this->__restClient->makeRequest($call["method"], $uri, null, $get_fields, $body, $content_type); }
/** * Returns the parsed GET query as a list of QueryParameters. * * @history * 2013.09.30: * (AT) Initial release * * @version 2013.09.30 * @author (AT) Alberto Trevino, Brigham Young Univ. <*****@*****.**> * * @return array List of QueryParameters with query information */ public function getQuery() { # See if we have parsed the query if ($this->queryParameters === null) { # Parse the query $this->queryParameters = QueryParameter::fromUri(parse_url($_SERVER["REQUEST_URI"], PHP_URL_QUERY)); } # Return the list of parsed query parameters return $this->queryParameters; }