Skip to content

Commit

Permalink
Setup commands and eager loading (#83)
Browse files Browse the repository at this point in the history
* Allow dependency injection

* Expose methods

* Apply fixes from StyleCI (#79)

* Resolve relatable entities as relationships

* Dynamically load exception handler

* Setup commands

* Apply fixes from StyleCI (#82)
  • Loading branch information
Lupacescu Eduard authored Jan 11, 2020
1 parent b05b320 commit cb65bb4
Show file tree
Hide file tree
Showing 28 changed files with 892 additions and 138 deletions.
12 changes: 12 additions & 0 deletions config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,16 @@
DispatchRestifyStartingEvent::class,
AuthorizeRestify::class,
],

/*
|--------------------------------------------------------------------------
| Restify Exception Handler
|--------------------------------------------------------------------------
|
| These will override the main application exception handler,
| set to null, it will not override it.
| Having RestifyHandler as a global exception handler is a good approach, since it
| will return the exceptions in an API pretty format.
*/
'exception_handler' => \Binaryk\LaravelRestify\Exceptions\RestifyHandler::class,
];
12 changes: 8 additions & 4 deletions docs/.vuepress/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ module.exports = {
title: 'Quick Start',
path: '/docs/'
},
{
title: 'Repository',
path: '/docs/repository-pattern/repository-pattern',
},
{
title: 'Field',
path: '/docs/repository-pattern/field',
},
{
title: 'REST methods',
path: '/docs/rest-methods/rest-methods',
Expand All @@ -31,10 +39,6 @@ module.exports = {
title: 'Auth service',
path: '/docs/auth/auth',
},
{
title: 'Repository',
path: '/docs/repository-pattern/repository-pattern',
},
]
},
plugins: [
Expand Down
1 change: 1 addition & 0 deletions docs/docs/repository-pattern/field.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Field
257 changes: 255 additions & 2 deletions docs/docs/repository-pattern/repository-pattern.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,256 @@
# Repository pattern
# Repository

## Implementing contract
[[toc]]

## Introduction

The Repository is the main core of the Laravel Restify, included with Laravel provides the easiest way of
CRUD over your resources than you can imagine. It works along with
[Laravel API Resource](https://laravel.com/docs/6.x/eloquent-resources), which means you can use all helpers from there right away.

## Quick start
The follow command will generate you the Repository which will take the control over the post resource.

```shell script
php artisan restify:repository Post
```

The newly created repository could be found in the `app/Restify` directory.

## Defining Repositories

```php

use Binaryk\LaravelRestify\Repositories\Repository;

class Post extends Repository
{
/**
* The model the repository corresponds to.
*
* @var string
*/
public static $model = 'App\\Post';
}
```

### Actions handled by the Repository

Having this in place you're basically ready for the CRUD actions over posts.
You have available the follow endpoints:

| Verb | URI | Action |
| :------------- |:--------------------------- | :-------|
| GET | `/restify-api/posts` | index |
| GET | `/restify-api/posts/{post}` | show |
| POST | `/restify-api/posts` | store |
| PATCH | `/restify-api/posts/{post}` | update |
| DELETE | `/restify-api/posts/{post}` | destroy |

### Fields
When storing or updating a repository Restify will retrieve from the request all attributes defined in the `fillable`
array of the model and will fill all of these fields as they are sent through the request.
If you want to customize some fields before they are filled to the model `attribute`,
you can interact with fields by defining them in the `fields` method:
```php
use Binaryk\LaravelRestify\Fields\Field;
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;

class Post extends Repository
{
/**
* The model the repository corresponds to.
*
* @var string
*/
public static $model = 'App\\Post';

/**
* Resolvable attributes before storing/updating
*
* @param RestifyRequest $request
* @return array
*/
public function fields(RestifyRequest $request)
{
return [
Field::make('title')->storeCallback(function ($requestValue) {
return is_string($requestValue) ? $requestValue : `N/A`;
})
];
}
}
```

:::tip

`Field` class has many mutations, validators and interactions you can use, these are documented [here](/laravel-restify/docs/repository-pattern/field)

:::


### Dependency injection

The Laravel [service container](https://laravel.com/docs/6.x/container) is used to resolve all Laravel Restify repositories.
As a result, you are able to type-hint any dependencies your `Repository` may need in its constructor.
The declared dependencies will automatically be resolved and injected into the repository instance:

:::tip
Don't forget to to call the parent `contructor`
:::

```php
use Binaryk\LaravelRestify\Repositories\Repository;

class Post extends Repository
{
/**
* The model the repository corresponds to.
*
* @var string
*/
public static $model = 'App\\Post';

/**
* @var PostService
*/
private $postService;

/**
* Post constructor.
* @param PostService $service
*/
public function __construct(PostService $service)
{
parent::__construct();
$this->postService = $service;
}

}
```

## Restify Repository Conventions
Let's diving deeper into the repository, and take step by step each of its available tools and customizable
modules. Since this is just a helper, it should not break your normal development flow.

### Model name
As we already noticed, each repository basically works as a wrapper over a specific resource.
The fancy naming `resource` is nothing more than a database entity (posts, users etc.). Well, to make the
repository aware of the entity it should take care of, we have to define the model property:

```php
/**
* The model the repository corresponds to.
*
* @var string
*/
public static $model = 'App\\Post';
```

## CRUD Methods overriding

Laravel Restify magically made all "CRUD" operations for you. But sometimes you may want to intercept, or override the
entire logic of a specific action. Let's say your `save` method has to do something different than just storing
the newly created entity in the database. In this case you can easily override each action ([defined here](#actions-handled-by-the-repository)) from the repository:

### index

```php
public function index(RestifyRequest $request, Paginator $paginated)
{
// Custom response
}
```

### show

```php
public function show(RestifyRequest $request, $repositoryId)
{
// Custom finding
}
```

### store

```php
/**
* @param RestifyRequest $request
* @return \Illuminate\Http\JsonResponse|void
*/
public function store(Binaryk\LaravelRestify\Http\Requests\RestifyRequest $request)
{
// custom storing

return $this->response();
}
```

### update

```php
public function update(RestifyRequest $request, $model)
{
// Custom updating
}
```

### destroy

```php
public function destroy(RestifyRequest $request, $repositoryId)
{
}
```

## Transformation layer

When you call the `posts/{post}` endpoint, the repository will return the following primary
data for a single resource object:

```json
{
"data": {
"type": "post",
"id": "1",
"attributes": {
// ... this post's attributes
},
"meta": {
// ... by default meta includes information about user authorizations over the entity
"authorizedToView": true,
"authorizedToCreate": true,
"authorizedToUpdate": true,
"authorizedToDelete": true
}
}
}
```

This response is made according to [JSON:API format](https://jsonapi.org/format/). You can change it for all
repositories at once by modifying the `resolveDetails` method of the abstract Repository:

```php
/**
* Resolve the response for the details
*
* @param $request
* @param $serialized
* @return array
*/
public function resolveDetails($request, $serialized)
{
return $serialized;
}
```

Since the repository extends the [Laravel Resource](https://laravel.com/docs/6.x/eloquent-resources) you may
may conditionally return a field:

```php

```
## Response customization

## Scaffolding repository
67 changes: 67 additions & 0 deletions src/Commands/BaseRepositoryCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace Binaryk\LaravelRestify\Commands;

use Illuminate\Console\GeneratorCommand;

class BaseRepositoryCommand extends GeneratorCommand
{
/**
* The console command name.
*
* @var string
*/
protected $name = 'restify:base-repository';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Create a new base repository class';

/**
* Indicates whether the command should be shown in the Artisan command list.
*
* @var bool
*/
protected $hidden = true;

/**
* The type of class being generated.
*
* @var string
*/
protected $type = 'Repository';

/**
* Execute the console command.
*
* @return bool|null
*/
public function handle()
{
parent::handle();
}

/**
* Get the stub file for the generator.
*
* @return string
*/
protected function getStub()
{
return __DIR__.'/stubs/base-repository.stub';
}

/**
* Get the default namespace for the class.
*
* @param string $rootNamespace
* @return string
*/
protected function getDefaultNamespace($rootNamespace)
{
return $rootNamespace.'\Restify';
}
}
Loading

0 comments on commit cb65bb4

Please sign in to comment.