Skip to content

Instantly share code, notes, and snippets.

@fenos
Last active October 15, 2018 20:04
Show Gist options
  • Save fenos/c1634f39ca61da26e6c0 to your computer and use it in GitHub Desktop.
Save fenos/c1634f39ca61da26e6c0 to your computer and use it in GitHub Desktop.
Custom Implementation of an Abstract RESTfull Repositories with Laravel.
<?php
interface FilterableInterface {
}
/**
*
* Implementation of RestApi Abstract Class
* on this Example repository
*/
<?php
use User;
/**
* Class ImplementationRest
*/
class ImplementationRest extends RestApi {
/**
* @var
*/
protected $user;
function __construct(User $user)
{
$this->user = $user;
}
/**
* @return mixed
*/
protected function model()
{
return $this->user;
}
/**
* Profile Filter
*
* @param $query
* @param RestData $data
* @return mixed
*/
public function profileFilter($query, RestData $data)
{
return $query->with('profile')->where('users.id',$data->user_id);
}
/**
* Only users who has got cars
*
* @param $query
* @param RestData $data
* @return mixed
*/
public function carFilter($query,RestData $data)
{
return $query->has('cars');
}
}
/**
* Scenario ->
* Given the following url: http://localhost.dev/user/1?filters=profile,cars
* You'll be able to request the user with ID 1 information applying the 2 following filters
* profile and cars
*---------------------------------------------------------------------------------------------
*
* Abstract class that will extends your Repository
*/
<?php
/**
* Class RestApi
* Extend this class to a repository
*/
abstract class RestApi implements FilterableInterface {
/**
* @var array
*/
protected $filters;
/**
* @var array
*/
protected $currentSort = array('created_at', 'desc');
/**
* @var int
*/
protected $perPage = 20;
/**
* @var int
*/
protected $limit;
/**
* @var array
*/
protected $eagerLoading = [];
/**
* @return mixed
*/
abstract protected function model();
/**
* Return a new query
*
* @return mixed
*/
protected function query()
{
return $this->model()->newQuery();
}
/**
* Return a item of a query
* generated via REST
*
* @param array $data
* @return
*/
public function item($data)
{
return $this->applyFilter($this->query(),$data)->first();
}
/**
* Return a collection of a query
* generated via REST
*
* @param array $data
*/
public function collection($data)
{
return $this->applyFilter($this->query(),$data)->get();
}
/**
* Create a pagination
*
* @param array $data
*/
public function paginate($data)
{
return $this->applyFilter($this->query(),$data)->paginate($this->perPage);
}
/**
* Create a new Record
*
* @param array $data
* @return mixed
*/
public function create(array $data)
{
return $this->model()->create($data);
}
/**
* Delete a record applying
* filters
*
* @param $data
* @return mixed
*/
public function delete($data)
{
return $this->applyFilter($this->query(),$data)->delete();
}
/**
* Update record filtering
*
* @param $data
* @param $update
* @return mixed
*/
public function update($data,$update)
{
return $this->applyFilter($this->query(),$data)->update($update);
}
/**
* Sort By Field and direction
*
* @param $field
* @param string $direction
* @return $this
*/
public function sortBy($field,$direction = 'DESC')
{
$direction = $direction ?: 'DESC';
$direction = (strtoupper($direction) == 'ASC') ? 'ASC' : 'DESC';
$field = $field ?: 'created_at';
$this->currentSort = array($field, $direction);
return $this;
}
public function limit($limit) {
$this->limit = $limit;
return $this;
}
/**
* Apply Filter Accordently
*
* @param $model
* @param $data
* @return mixed
*/
public function applyFilter($model,$data)
{
$model = $this->mapFilters($model,$data);
// Order By
list($sortField, $sortDir) = $this->currentSort;
$model->orderBy($sortField,$sortDir);
if (! is_null($this->limit))
{
$model->limit($this->limit);
}
// Check if some eager loading haas been required
if (count($this->eagerLoading) > 0)
{
$model->with($this->eagerLoading);
}
return $model;
}
/**
* Set eager Loading
*
* @return mixed
*/
public function with()
{
$parameters = func_get_args();
$parsed = ( is_array($parameters[0])) ? $parameters[0] : $parameters;
$this->eagerLoading = array_merge($this->eagerLoading,$parsed);
return $this;
}
/**
* @param $model
* @param $data
* @return mixed
*/
protected function mapFilters($model, $data)
{
// prepare data to pass
$restData = $this->getRestData();
$restData->pushData($data);
return $this->callFilterMethod(
$model,
$restData->requestedFilters($this),
$restData
);
return $model;
}
/**
* @param $model
* @param $neededFilters
* @param $restData
* @return mixed
*/
protected function callFilterMethod($model, $neededFilters, $restData)
{
// Apply all the filters to the query
foreach ( $neededFilters as $key => $filter )
{
$method = $key;
$model = $this->{$method}($model, $restData);
}
return $model;
}
/**
* @return RestData
*/
protected function getRestData()
{
$restData = app('rest.data');
return $restData;
}
}
//
// Container of your filters, and values
//
<?php
use RestMapperException;
/**
* Class RestData
*/
class RestData {
/**
* @var array
*/
protected $filters = [];
/**
* @var array
*/
protected $data = [];
/**
* @param $filters
*/
function __construct($filters)
{
$this->filters = $filters;
}
/**
* Parse the requested filters
*
* @param FilterableInterface $filterableInterface
* @return array
*/
public function requestedFilters(FilterableInterface $filterableInterface)
{
// parse filters followed by (,)
// example: filters=profile,tasks,cars
$this->filters = (count($this->filters) > 0)
? preg_split('/\s?,\s/', $this->filters)
: $this->filters;
$neededFilters = [];
// Set callables filters
foreach ( $this->filters as $filter )
{
$method = $this->getFilterMethod($filter);
if (method_exists($filterableInterface,$method))
{
$neededFilters[$method] = $filter;
}
}
return $neededFilters;
}
/**
* Get Value
*
* @param $name
* @return mixed
* @throws RestMapperException
*/
public function get($name)
{
if (array_key_exists($name,$this->data))
{
return $this->data[$name];
}
$error = "The query has [{$name}] value required";
throw new RestMapperException($error);
}
/**
* Add data to be available on the filters
*
* @param array $data
*/
public function pushData(array $data)
{
$this->data = array_merge($this->data,$data);
}
/**
* You can access to the data
* throught properties
*
* @param $name
* @return mixed
*/
function __get($name)
{
return $this->get($name);
}
/**
* Get a filter method name
*
* @param $name
* @return string
*/
protected function getFilterMethod($name)
{
return $name . "Filter";
}
}
<?php
use Illuminate\Support\ServiceProvider;
use RestData;
class RestApiServiceProvider extends ServiceProvider {
/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->app['rest.data'] = $this->app->share(function ($app)
{
$data = $app['request']->input('filters');
if ( count($data) == 0 )
{
$data = [];
}
return new RestData($data);
});
}
}
/**
* Controller Layer where you'll call
* your repository and pass the data to the repository
* L5 Controller
*\
<?php
use ImplementationRepository;
/**
* Class UserController
*/
class UserController extends BaseControllerApi {
/**
* @var UserTarget
*/
protected $userTarget;
/**
* @param UserTarget $userTarget
*/
function __construct(UserTarget $userTarget)
{
$this->userTarget = $userTarget;
}
public function getUser(ImplementationRest $rest,$user_id)
{
return $rest->item($compact('user_id')); // it will apply the filters provided and build the query for you
// Other methods: SortBy() and perPage() are not mandatory
$rest->sortBy($field,$direction)->collection($data);
$rest->perPage(20)->paginate($data);
$rest->create($data);
$rest->update($data,$fieldsToUpdate);
$rest->delete($data);
}
}
@rex
Copy link

rex commented Apr 21, 2015

This is great, thank you for creating this! A really great (even if a bit terse) overview of modular abstraction in Laravel :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment