By extending the Model
and using this inside my route
files, I can easily call these functions inside the corresponding HTTP functions. But I feel there might be some improvements and I find it very important this basis is as well written as it can be.
ExtendedModel.php
<?php namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use App\Helpers\QueryStringHelper;
use Illuminate\Http\Request;
class ExtendedModel extends Model {
public static function get_by_id ($id) {
$response = array();
$http_status = 200;
$error = false;
try {
$response['data'] = self::find($id);
if (!$response['data']) {
QueryStringHelper::invalid_parameter('object does not exist', 404);
}
} catch (Exception $e) {
QueryStringHelper::invalid_parameter($error = $e->getMessage());
}
$response['error'] = $error;
return response($response, $http_status);
}
public static function get_by_query_string_filters ($request) {
$response = array();
$http_status = 200;
$error = false;
$query_string_array = $request->input();
$Query = self::query();
$data = true;
$count = false;
$select = false;
$paginate = false;
try {
if ($query_string_array) {
foreach ($query_string_array as $query_string_array_key => $query_string_array_value) {
// If the query argument is not one of the "SQL" keywords below, check for operator
if (!in_array($query_string_array_key, ['count', 'data', 'limit', 'offset', 'select', 'orderby', 'paginate'])) {
$url_operator = QueryStringHelper::filter_url_operator($query_string_array_value);
$operator = QueryStringHelper::eloquent_query_operator($url_operator);
$query_string_array_value = QueryStringHelper::remove_operator($url_operator, $query_string_array_value);
// Needs 2 arguments
if ($url_operator == 'bt') {
$between_values = explode(',', $query_string_array_value);
if (count($between_values) != 2) {
QueryStringHelper::invalid_parameter('"between" needs exaclty 2 values seperated by a "," to filter');
}
$Query = $Query->whereBetween($query_string_array_key, $between_values);
} else {
$Query = $Query->where($query_string_array_key, $operator, $query_string_array_value);
}
} else {
// If count is true in the query argument add a count element to the response
if ($query_string_array_key == 'count' && QueryStringHelper::absolute_bool($query_string_array_value)) {
$count = true;
}
// If data is false in the query argument do no add data element to the response
if ($query_string_array_key == 'data' && !QueryStringHelper::absolute_bool($query_string_array_value)) {
$data = false;
}
// If limit is set, return a limited result set
if ($query_string_array_key == 'limit') {
if(!QueryStringHelper::is_int($query_string_array_value)) {
QueryStringHelper::invalid_parameter('limit has to be an integer');
}
$Query = $Query->take(abs(intval($query_string_array_value)));
}
// If offset is set, return a offset result set
if ($query_string_array_key == 'offset') {
if(!QueryStringHelper::is_int($query_string_array_value)) {
QueryStringHelper::invalid_parameter('offset has to be an integer');
}
$Query = $Query->offset(abs(intval($query_string_array_value)));
}
// If select is set, return a limited result set
if ($query_string_array_key == 'select') {
if (!is_array($query_string_array_value)) {
QueryStringHelper::invalid_parameter('select has to be an array');
}
$select = $query_string_array_value;
}
// If orderby is set, order by the value
if ($query_string_array_key == 'orderby') {
$url_operator = QueryStringHelper::filter_url_operator($query_string_array_value);
$query_string_array_value = QueryStringHelper::remove_operator($url_operator, $query_string_array_value);
if (!in_array($url_operator, ['asc', 'desc'])) {
QueryStringHelper::invalid_parameter('orderby has to be "asc" or "desc"');
}
$Query = $Query->orderBy($query_string_array_value, $url_operator);
}
if ($query_string_array_key == 'paginate') {
if(!QueryStringHelper::is_int($query_string_array_value)) {
QueryStringHelper::invalid_parameter('paginate has to be an integer, the amount of records per page to show');
}
$paginate = abs(intval($query_string_array_value));
}
}
}
}
if ($paginate) {
$Query = $Query->paginate($paginate);
} else {
$Query = $select ? $Query->get($select) : $Query->get();
}
} catch (Exception $e) {
QueryStringHelper::invalid_parameter($e->getMessage());
}
if ($data) {
$response['data'] = $Query;
}
if ($count) {
$response['count'] = $Query->count();
}
if ($error) {
$response['error'] = $error;
}
return response($response, $http_status);
}
public static function create_model_object ($request) {
$object = self::create($request::all());
$response = array();
//$response['data'] = $object;
$response['primaryKey'] = $object->getKey();
$response['message'] = 'success';
return response($response, 200);
}
public static function update_model_object ($id, $request) {
return self::find($id)->update($request::all());
}
public static function delete_model_object ($id) {
return self::find($id)->delete();
}
}
QueryStringHelper.php
<?php
namespace App\Helpers;
class QueryStringHelper {
private static $query_string_operators = [
'eq' => '=',
'ne' => '!=',
'lt' => '<',
'le' => '<=',
'ge' => '>=',
'gt' => '>',
'lk' => 'like',
'bt' => 'between'
];
/**
* [eloquent_query_operator description]
* @method eloquent_query_operator Translate a Query String operator in 2 character syntax to the Eloquent equivalent
* @param string $query_string_operator The Query String operator to be translated
* @return string Eloquent equivalent operator
*/
public static function eloquent_query_operator ($query_string_operator) {
if (!$query_string_operator) {
self::invalid_parameter('missing operator in query string');
}
if (!array_key_exists($query_string_operator, self::$query_string_operators)) {
self::invalid_parameter('undefined operator in query string');
}
return self::$query_string_operators[$query_string_operator];
}
/**
* [remove_operator description]
* @method remove_operator Remove the url operator part of the Query String value
* @param string $url_operator The to be removed url operator
* @param string $full_query_value The Query String Parameter
* @return string The value to be used stripped from it's operator
*/
public static function remove_operator ($url_operator, $full_query_value) {
return str_replace('[' . $url_operator . ']' , '' , $full_query_value);
}
/**
* [filter_url_operator description]
* @method filter_url_operator Get the operator between the brackets
* @param string $full_query_value The value part of a Query String parameter
* @return string or bool The operator between the brackets or false if no match is found
*/
public static function filter_url_operator ($full_query_value) {
// Use a regular expression to filter the operator
preg_match_all('/\[([a-z]+)\]/', $full_query_value, $matches);
// If no match is found just return false
return $matches[1][0] ?? false;
}
/**
* [absolute_bool description]
* @method absolute_bool Convert a string to a boolean based on content. E.g. "false" is false and "true" is true
* @param string $value The value part of a Query String parameter
* @return bool The boolean interpretation of said string
*/
public static function absolute_bool ($value) {
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
}
/**
* [is_int description]
* @method is_int Checks if the $var is a positive integer (PHP's is_int will see this value as a string)
* @param integer $var To be checked
* @return boolean Result of being a positive integer
*/
public static function is_int ($var) {
preg_match_all('/[0-9]+/', $var, $matches);
// If not a number return false
return $matches[0][0] == $var;
}
/**
* [invalid_parameter description]
* @method invalid_parameter return an error message if the user input is invalid
* @param string $error_message [description]
* @param integer $http_status_code [description]
* @return [type] [description]
*/
public static function invalid_parameter ($error_message, $http_status_code = 400) {
$response = array();
$response['error'] = $error_message;
return response($response, $http_status_code);
}
}
Example implementation for routes
<?php
Route::get('/examples/{id}', function ($id) {
return Example::get_by_id($id);
});
Route::get('/examples', function (Request $request) {
return Example::get_by_query_string_filters($request);
});
Route::post('/examples', function (Request $request) {
return Example::create_model_object($request);
});
Route::put('/examples/{id}', function ($id, Request $request) {
return Example::update_model_object($id, $request);
});
Route::delete('/examples/{id}', function($id) {
return Example::delete_model_object($id);
});
orderby
- its on user to format data, not API. If your insist on having such functionality I can help you make this code cleaner. \$\endgroup\$