diff --git a/README.md b/README.md index 279493577..855f7f83f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Laravel magic REST API builder +

diff --git a/docs/.vuepress/1.0.js b/docs/.vuepress/1.0.js
new file mode 100644
index 000000000..bdf75649a
--- /dev/null
+++ b/docs/.vuepress/1.0.js
@@ -0,0 +1,37 @@
+module.exports = [
+ {
+ title: "Quick Start",
+ collapsable: false,
+ children: ['quickstart']
+ },
+ {
+ title: "Repository",
+ collapsable: false,
+ children: ['repository-pattern/repository-pattern']
+ },
+ {
+ title: 'Field',
+ collapsable: false,
+ children: ['repository-pattern/field'],
+ },
+ {
+ title: 'Rest Controller',
+ collapsable: false,
+ children: ['rest-methods/rest-methods'],
+ },
+ {
+ title: 'Filtering',
+ collapsable: false,
+ children: ['search/search'],
+ },
+ {
+ title: 'Auth service',
+ collapsable: false,
+ children: ['auth/auth'],
+ },
+ {
+ title: 'Error handler',
+ collapsable: false,
+ children: ['exception-handler/exception-handler'],
+ },
+];
diff --git a/docs/.vuepress/2.0.js b/docs/.vuepress/2.0.js
new file mode 100644
index 000000000..bdf75649a
--- /dev/null
+++ b/docs/.vuepress/2.0.js
@@ -0,0 +1,37 @@
+module.exports = [
+ {
+ title: "Quick Start",
+ collapsable: false,
+ children: ['quickstart']
+ },
+ {
+ title: "Repository",
+ collapsable: false,
+ children: ['repository-pattern/repository-pattern']
+ },
+ {
+ title: 'Field',
+ collapsable: false,
+ children: ['repository-pattern/field'],
+ },
+ {
+ title: 'Rest Controller',
+ collapsable: false,
+ children: ['rest-methods/rest-methods'],
+ },
+ {
+ title: 'Filtering',
+ collapsable: false,
+ children: ['search/search'],
+ },
+ {
+ title: 'Auth service',
+ collapsable: false,
+ children: ['auth/auth'],
+ },
+ {
+ title: 'Error handler',
+ collapsable: false,
+ children: ['exception-handler/exception-handler'],
+ },
+];
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 2bb8392d0..2176743c3 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -1,52 +1,48 @@
+var versions = ["1.0", "2.0"];
+
module.exports = {
title: 'Laravel Restify',
description: 'A package to start the REST API',
serviceWorker: true,
- base: '/laravel-restify/',
+ base: '/',
themeConfig: {
- logo: '/assets/img/logo.svg',
+ logo: '/assets/img/icon.png',
displayAllHeaders: true,
sidebarDepth: 2,
nav: [
- { text: 'Home', link: '/' },
- { text: 'Guide', link: '/docs/' },
+ { text: 'Docs', link: '/docs/2.0/' },
+ {
+ text: "Version",
+ link: "/",
+ items: [{ text: "1.0", link: "/docs/1.0/" }, { text: "2.0", link: "/docs/2.0/" }]
+ },
{ text: 'About us', link: 'https://binarcode.com', target: '_blank' }
],
- sidebar: [
- {
- title: 'Quick Start',
- path: '/docs/'
- },
- {
- title: 'Repository',
- path: '/docs/repository-pattern/repository-pattern',
- },
- {
- title: 'Field',
- path: '/docs/repository-pattern/field',
- },
- {
- title: 'Rest Controller',
- path: '/docs/rest-methods/rest-methods',
- },
- {
- title: 'Filtering',
- path: '/docs/search/search',
- },
- {
- title: 'Auth service',
- path: '/docs/auth/auth',
- },
- {
- title: 'Error handler',
- path: '/docs/exception-handler/exception-handler',
- },
- ]
+ sidebar: {
+ "/docs/1.0/": require("./1.0"),
+ "/docs/2.0/": require("./2.0")
+ },
},
plugins: [
'@vuepress/pwa',
+ (options = {}, context) => ({
+ extendPageData($page) {
+ const { regularPath, frontmatter } = $page;
+
+ frontmatter.meta = [];
+
+ versions.forEach(function(version) {
+ if ($page.regularPath.includes("/" + version + "/")) {
+ frontmatter.meta.push({
+ name: "docsearch:version",
+ content: version + ".0"
+ });
+ }
+ });
+ }
+ })
],
head: [
// Used for PWA
diff --git a/docs/.vuepress/public/android-chrome-192x192.png b/docs/.vuepress/public/android-chrome-192x192.png
index 8f66923ab..aa17157ad 100644
Binary files a/docs/.vuepress/public/android-chrome-192x192.png and b/docs/.vuepress/public/android-chrome-192x192.png differ
diff --git a/docs/.vuepress/public/android-chrome-512x512.png b/docs/.vuepress/public/android-chrome-512x512.png
index ea7e43f3f..aa17157ad 100644
Binary files a/docs/.vuepress/public/android-chrome-512x512.png and b/docs/.vuepress/public/android-chrome-512x512.png differ
diff --git a/docs/.vuepress/public/assets/img/icon.png b/docs/.vuepress/public/assets/img/icon.png
new file mode 100644
index 000000000..edaca7fa2
Binary files /dev/null and b/docs/.vuepress/public/assets/img/icon.png differ
diff --git a/docs/.vuepress/public/assets/img/logo.png b/docs/.vuepress/public/assets/img/logo.png
new file mode 100644
index 000000000..aa17157ad
Binary files /dev/null and b/docs/.vuepress/public/assets/img/logo.png differ
diff --git a/docs/.vuepress/public/assets/img/logo.svg b/docs/.vuepress/public/assets/img/logo.svg
deleted file mode 100644
index 4984d640a..000000000
--- a/docs/.vuepress/public/assets/img/logo.svg
+++ /dev/null
@@ -1,181 +0,0 @@
-
diff --git a/docs/.vuepress/public/icon.png b/docs/.vuepress/public/icon.png
index ea7e43f3f..aa17157ad 100644
Binary files a/docs/.vuepress/public/icon.png and b/docs/.vuepress/public/icon.png differ
diff --git a/docs/.vuepress/public/manifest.json b/docs/.vuepress/public/manifest.json
index 3b5e6e41e..284b07869 100644
--- a/docs/.vuepress/public/manifest.json
+++ b/docs/.vuepress/public/manifest.json
@@ -13,7 +13,7 @@
"type": "image/png"
}
],
- "start_url": "/docs/installation.html",
+ "start_url": "/docs/quickstart.html",
"display": "standalone",
"background_color": "#ddd",
"theme_color": "#7e8ea1"
diff --git a/docs/README.md b/docs/README.md
index 956b4990d..a4d131d58 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -1,10 +1,10 @@
---
home: true
-heroImage: /assets/img/logo.svg
-heroText: Laravel Restify
-tagline: A package to start a Rest API
+heroImage: /assets/img/logo.png
+heroText:
+tagline: A package to implement a JSON:API with Laravel
actionText: Docs →
-actionLink: /docs/
+actionLink: /docs/2.0/
features:
- title: Magic CRUD over entities
details: Enjoy powerful "CRUD" over your entities in seconds
diff --git a/docs/docs/1.0/README.md b/docs/docs/1.0/README.md
new file mode 100644
index 000000000..e69de29bb
diff --git a/docs/docs/auth/auth.md b/docs/docs/1.0/auth/auth.md
similarity index 100%
rename from docs/docs/auth/auth.md
rename to docs/docs/1.0/auth/auth.md
diff --git a/docs/docs/exception-handler/exception-handler.md b/docs/docs/1.0/exception-handler/exception-handler.md
similarity index 100%
rename from docs/docs/exception-handler/exception-handler.md
rename to docs/docs/1.0/exception-handler/exception-handler.md
diff --git a/docs/docs/1.0/quickstart.md b/docs/docs/1.0/quickstart.md
new file mode 100644
index 000000000..4fa613df8
--- /dev/null
+++ b/docs/docs/1.0/quickstart.md
@@ -0,0 +1,64 @@
+# Installation 1.0
+
+[[toc]]
+
+## Requirements
+
+Laravel Restify has a few requirements you should be aware of before installing:
+
+- Composer
+- Laravel Framework 5.5+
+
+## Installing Laravel Restify
+
+```bash
+composer require binaryk/laravel-restify
+```
+
+## Setup Laravel Restify
+After the instalation, the package requires a setup process, this will publish configuration, provider and will create the
+`app/Restify` directory with an abstract `Repository` and scaffolding a `User` repository you can play with:
+
+```shell script
+php artisan restify:setup
+```
+
+:::tip Package Stability
+
+If you are not able to install Restify into your application because of your `minimum-stability` setting,
+ consider setting your `minimum-stability` option to `dev` and your `prefer-stable` option to `true`.
+ This will allow you to install Laravel Restify while still preferring stable package
+ releases for your application.
+:::
+
+## Quick start
+
+Having the package setup and users table migrated, you should be good to perform the first API request:
+
+```http request
+GET: /restify-api/users?perPage=10
+```
+
+This should return the users list paginated and formatted according to [JSON:API](https://jsonapi.org/format/) standard.
+
+## Generate repository
+
+Creating a new repository can be done via restify command:
+
+```shell script
+php artisan restify:repository Post
+```
+
+If you want to generate the Policy, Model and migration as well, then you can use the `--all` option:
+
+```shell script
+php artisan restify:repository Post --all
+```
+## Generate policy
+
+Since the authorization is done through the Laravel Policies, a good way of generating a complete policy for an entity
+is by using the restify command:
+
+```shell script
+php artisan restify:policy Post
+```
diff --git a/docs/docs/repository-pattern/field.md b/docs/docs/1.0/repository-pattern/field.md
similarity index 100%
rename from docs/docs/repository-pattern/field.md
rename to docs/docs/1.0/repository-pattern/field.md
diff --git a/docs/docs/repository-pattern/repository-pattern.md b/docs/docs/1.0/repository-pattern/repository-pattern.md
similarity index 100%
rename from docs/docs/repository-pattern/repository-pattern.md
rename to docs/docs/1.0/repository-pattern/repository-pattern.md
diff --git a/docs/docs/rest-methods/rest-methods.md b/docs/docs/1.0/rest-methods/rest-methods.md
similarity index 100%
rename from docs/docs/rest-methods/rest-methods.md
rename to docs/docs/1.0/rest-methods/rest-methods.md
diff --git a/docs/docs/search/search.md b/docs/docs/1.0/search/search.md
similarity index 100%
rename from docs/docs/search/search.md
rename to docs/docs/1.0/search/search.md
diff --git a/docs/docs/2.0/README.md b/docs/docs/2.0/README.md
new file mode 100644
index 000000000..465a744e1
--- /dev/null
+++ b/docs/docs/2.0/README.md
@@ -0,0 +1,64 @@
+# Installation
+
+[[toc]]
+
+## Requirements
+
+Laravel Restify has a few requirements you should be aware of before installing:
+
+- Composer
+- Laravel Framework 5.5+
+
+## Installing Laravel Restify
+
+```bash
+composer require binaryk/laravel-restify
+```
+
+## Setup Laravel Restify
+After the instalation, the package requires a setup process, this will publish configuration, provider and will create the
+`app/Restify` directory with an abstract `Repository` and scaffolding a `User` repository you can play with:
+
+```shell script
+php artisan restify:setup
+```
+
+:::tip Package Stability
+
+If you are not able to install Restify into your application because of your `minimum-stability` setting,
+ consider setting your `minimum-stability` option to `dev` and your `prefer-stable` option to `true`.
+ This will allow you to install Laravel Restify while still preferring stable package
+ releases for your application.
+:::
+
+## Quick start
+
+Having the package setup and users table migrated, you should be good to perform the first API request:
+
+```http request
+GET: /restify-api/users?perPage=10
+```
+
+This should return the users list paginated and formatted according to [JSON:API](https://jsonapi.org/format/) standard.
+
+## Generate repository
+
+Creating a new repository can be done via restify command:
+
+```shell script
+php artisan restify:repository Post
+```
+
+If you want to generate the Policy, Model and migration as well, then you can use the `--all` option:
+
+```shell script
+php artisan restify:repository Post --all
+```
+## Generate policy
+
+Since the authorization is done through the Laravel Policies, a good way of generating a complete policy for an entity
+is by using the restify command:
+
+```shell script
+php artisan restify:policy Post
+```
diff --git a/docs/docs/2.0/auth/auth.md b/docs/docs/2.0/auth/auth.md
new file mode 100644
index 000000000..f47525f82
--- /dev/null
+++ b/docs/docs/2.0/auth/auth.md
@@ -0,0 +1,498 @@
+# Authentication setup
+
+Laravel Restify has the authentication implemented with Passport, so you can use it out of the box.
+You'll finally enjoy the auth setup (`register`, `login`, `forgot` and `reset password`).
+
+:::tip
+
+First make sure you have installed and configured the Laravel Passport properly.
+This can be done easily by using the follow Restify command:
+
+`php artisan restify:check-passport`
+
+This command will become with suggestions if anything is setup wrong.
+:::
+
+## Prerequisites
+- When using the Restify authentication service, you will need to migrate the `users` and `password_resets` table (these 2 migrations are by default in a fresh laravel app, however you may modify the users table as you prefer)
+- Make sure your authenticatable entity (usually `User`) implements: `Illuminate\Contracts\Auth\Authenticatable`
+- Make sure your authenticatable implements the `Binaryk\LaravelRestify\Contracts\Passportable` interface.
+- Check if `restify:check-passport` passes with success.
+
+## Register users
+
+- Define a register route to an action controller:
+
+```php
+Route::post('register', 'AuthController@register');
+```
+
+- Inject the AuthService into your controller and call the register method:
+
+```php
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use Binaryk\LaravelRestify\Services\AuthService;
+
+class AuthController
+{
+ /**
+ * @var AuthService
+ */
+ protected $authService;
+
+ public function __construct(AuthService $authService)
+ {
+ $this->authService = $authService;
+ }
+ /**
+ * This will validate the input,
+ * will register the user and return it back to the api
+ *
+ * @param Request $request
+ * @return JsonResponse
+ */
+ public function register(Request $request)
+ {
+ $user = $this->authService->register($request->all());
+
+ return Response::make(['data' => $user], 201);
+ }
+```
+
+- Validating input
+
+Restify will automatically validate the data send from the request to the `register` method.
+
+Used validation FormRequest is: `Binaryk\LaravelRestify\Http\Requests\RestifyRegisterRequest`
+
+However if you want to validate the registration payload yourself you can disable the built in validation by nullifying `$registerFormRequest`:
+
+```php
+public function register(UserRegisterRequest $request)
+{
+ AuthService::$registerFormRequest = null;
+
+ $user = $this->authService->register($request->only(array_keys($request->rules())));
+
+ return Response::make(['data' => $user], 201);
+}
+```
+
+Or you could simply override the `$registerFormRequest` with custom FormRequest:
+
+```php
+public function register(Request $request)
+{
+ AuthService::$registerFormRequest = UserRegisterRequest::class;
+
+ $user = $this->authService->register($request->all());
+
+ return Response::make(['data' => $user], 201);
+}
+```
+
+- Exceptions
+
+If something went wrong inside the register method, the AuthService will thrown few suggestive exceptions you can handle in the controller:
+
+ > `Binaryk\LaravelRestify\Exceptions\PassportUserException` - Make sure your authenticatable entity (usually `User`) is implementing the `Binaryk\LaravelRestify\Contracts\Passportable` interface.
+
+ > `\Binaryk\LaravelRestify\Exceptions\AuthenticatableUserException` - Make sure your authenticatable entity (usually `User`) is implementing the `Illuminate\Contracts\Auth\Authenticatable` interface.
+
+ > `\Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException` - Class (usually `App\User`) defined in the configuration `auth.providers.users.model` could not been instantiated (may be abstract or missing at all)
+
+- After successfully registering user, an `Illuminate\Auth\Events\Registered` event will be dispatched.
+
+## Verifying users (optional)
+
+This is an optional feature, but sometimes we may want users to validation the registered email.
+
+- Prerequisites
+
+Make sure `User` model implementing the `Illuminate\Contracts\Auth\MustVerifyEmail` contract.
+
+The `MustVerifyEmail` contract will wait for a `sendEmailVerificationNotification` method definition.
+
+This method could look like this:
+
+```php
+// app/User.php
+
+public function sendEmailVerificationNotification()
+{
+ $this->notify(new VerifyEmail);
+}
+```
+
+The `VerifyEmail` should send the notification email to the user. This email should include two required data:
+> the sha1 hash of the user email
+
+> user id
+
+so your frontend application could easily make a verify call to the API with this data.
+
+Example of notification:
+
+```php
+use Illuminate\Notifications\Messages\MailMessage;
+use Illuminate\Notifications\Notification;
+use Illuminate\Support\Carbon;
+use Illuminate\Support\Facades\Config;
+use Illuminate\Support\Facades\Lang;
+use Illuminate\Support\Facades\URL;
+
+class VerifyEmail extends Notification
+{
+ /**
+ * Get the notification's channels.
+ *
+ * @param mixed $notifiable
+ * @return array|string
+ */
+ public function via($notifiable)
+ {
+ return ['mail'];
+ }
+
+ /**
+ * Build the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ public function toMail($notifiable)
+ {
+ $verificationUrl = $this->verificationUrl($notifiable);
+
+ return (new MailMessage)
+ ->subject(Lang::get('Verify Email Address'))
+ ->line(Lang::get('Please click the button below to verify your email address.'))
+ ->action(Lang::get('Verify Email Address'), $verificationUrl)
+ ->line(Lang::get('If you did not create an account, no further action is required.'));
+ }
+
+ /**
+ * Get the verification URL for the given notifiable.
+ *
+ * @param mixed $notifiable // the User entity in our case
+ * @return string
+ */
+ protected function verificationUrl($notifiable)
+ {
+ return URL::temporarySignedRoute(
+ 'register.verify',
+ Carbon::now()->addMinutes(Config::get('auth.verification.expire', 60)),
+ [
+ 'id' => $notifiable->getKey(),
+ 'hash' => sha1($notifiable->getEmailForVerification()),
+ ]
+ );
+ }
+}
+```
+
+As you may noticed it uses a route, let's scaffolding an verify route example as well:
+
+```php
+Route::get('email/verify/{id}/{hash}', 'AuthController@verify')
+ ->name('register.verify')
+ ->middleware([ 'signed', 'throttle:6,1' ]);
+```
+
+In a real life use case, the email content will look a bit different, because the `action` URL you want to send to the user
+should match your frontend domain, not the API domain, and request to the API should be done from the frontend application.
+
+- Next let's define the controller action:
+
+```php
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use Binaryk\LaravelRestify\Services\AuthService;
+
+class AuthController
+{
+ /**
+ * @var AuthService
+ */
+ protected $authService;
+
+ public function __construct(AuthService $authService)
+ {
+ $this->authService = $authService;
+ }
+
+ /**
+ * This will mark the email verified if the email sha1 hash and user id matches
+ *
+ * @param Request $request
+ * @return JsonResponse
+ * @throws AuthorizationException - thrown if hash or id doesn't match
+ * @throws PassportUserException - thrown if the User don't implements Passportable
+ * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException - thrown if the user not found
+ */
+ public function verify(Request $request)
+ {
+ $user = $this->authService->verify($request->route('id'), $request->route('hash'));
+
+ return Response::make(['data' => $user]);
+ }
+}
+```
+
+- After verifying with success an `Illuminate\Auth\Events\Verified` event is dispatched.
+
+## Login users (issue token)
+After having user registered and verified (if the case) the API should be able to issue personal authorization tokens.
+
+- Define a login route to an action controller:
+
+```php
+Route::post('login', 'AuthController@login');
+```
+
+- Inject the AuthService into your controller and call the login method:
+
+```php
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use Binaryk\LaravelRestify\Services\AuthService;
+use Binaryk\LaravelRestify\Exceptions\UnverifiedUser;
+use Binaryk\LaravelRestify\Exceptions\CredentialsDoesntMatch;
+use Binaryk\LaravelRestify\Exceptions\PassportUserException;
+
+class AuthController
+{
+ /**
+ * @var AuthService
+ */
+ protected $authService;
+
+ public function __construct(AuthService $authService)
+ {
+ $this->authService = $authService;
+ }
+ /**
+ * This will validate the input,
+ * will register the user and return it back to the api
+ *
+ * @param Request $request
+ * @return JsonResponse
+ */
+ public function login(Request $request)
+ {
+ try {
+ $token = $this->authService->login($request->only('email', 'password'));
+
+ return Response::make(compact('token'));
+ } catch (CredentialsDoesntMatch | UnverifiedUser | PassportUserException $e) {
+ return Response::make('Something went wrong.', 401);
+ }
+ }
+}
+```
+
+The login method will thrown few exceptions:
+> `Binaryk\LaravelRestify\Exceptions\CredentialsDoesntMatch` - when email or password doesn't match
+
+> `Binaryk\LaravelRestify\Exceptions\UnverifiedUser` - when `User` model implements `Illuminate\Contracts\Auth\MustVerifyEmail`
+and he did not verified the email
+
+> `Binaryk\LaravelRestify\Exceptions\PassportUserException` - when `User` didn't implement `Binaryk\LaravelRestify\Contracts\Passportable`, the
+authenticatable entity should implement this contract, this way Restify will take the control over generating tokens.
+
+- After login with success a personal token is issued and an `Binaryk\LaravelRestify\Events\UserLoggedIn` event is dispatched.
+
+## Forgot password
+
+Forgot password is the action performing by user in terms of recovering his lost password. Usually the API should send an email
+with a unique URL that allow users to reset password.
+
+- Prerequisites:
+
+If you want your users to be able to reset their passwords, make sure your `User` model implements the
+`Illuminate\Contracts\Auth\CanResetPassword` contract.
+
+This contract requires the `sendPasswordResetNotification` to be implemented. It could looks like this:
+
+```php
+/**
+ * Send the password reset notification.
+ *
+ * @param string $token
+ * @return void
+ */
+public function sendPasswordResetNotification($token)
+{
+ $this->notify(new ResetPassword($token));
+}
+```
+
+Next let's define the `ResetPassword` notification. It should include a unique token, and should provide some information about the
+user email. The token will be resolved by the Laravel Restify and injected into your notification, so you don't have to worry about it:
+
+```php
+use Illuminate\Notifications\Messages\MailMessage;
+use Illuminate\Notifications\Notification;
+use Illuminate\Support\Facades\Lang;
+
+class ResetPassword extends Notification
+{
+ /**
+ * The password reset token.
+ *
+ * @var string
+ */
+ public $token;
+
+ /**
+ * The token is generated by the Restify through the Broker class
+ *
+ * @param string $token
+ * @return void
+ */
+ public function __construct($token)
+ {
+ $this->token = $token;
+ }
+
+ /**
+ * Get the notification's channels.
+ *
+ * @param mixed $notifiable
+ * @return array|string
+ */
+ public function via($notifiable)
+ {
+ return ['mail'];
+ }
+
+ /**
+ * Build the mail representation of the notification.
+ *
+ * @param mixed $notifiable
+ * @return \Illuminate\Notifications\Messages\MailMessage
+ */
+ public function toMail($notifiable)
+ {
+
+ $frontendUrl = url(config('app.url') . '/reset-password?';
+
+ return (new MailMessage)
+ ->subject(Lang::get('Reset Password Notification'))
+ ->line(Lang::get('You are receiving this email because we received a password reset request for your account.'))
+ ->action(Lang::get('Reset Password'), $frontendUrl . http_build_query(['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()])))
+ ->line(Lang::get('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.' . config('auth.defaults.passwords') . '.expire')]))
+ ->line(Lang::get('If you did not request a password reset, no further action is required.'));
+ }
+```
+
+The `getEmailForPasswordReset` method simply returns the user email:
+
+```php
+/**
+ * @inheritDoc
+ */
+public function getEmailForPasswordReset()
+{
+ return $this->email;
+}
+```
+
+- Define a forgot password route to an action controller:
+
+```php
+Route::post('password/email', 'AuthController@sendResetLinkEmail');
+```
+
+- Inject the AuthService into your controller and call the `sendResetPasswordLinkEmail` method:
+
+```php
+use Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException;use Binaryk\LaravelRestify\Exceptions\PasswordResetException;use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use Binaryk\LaravelRestify\Services\AuthService;use Illuminate\Validation\ValidationException;
+
+class AuthController
+{
+ /**
+ * @var AuthService
+ */
+ protected $authService;
+
+ public function __construct(AuthService $authService)
+ {
+ $this->authService = $authService;
+ }
+
+ /**
+ * This will validate the request input (email exists for example) and will send an reset passw
+ *
+ * @param Request $request
+ * @return JsonResponse
+ */
+ public function sendResetLinkEmail(Request $request)
+ {
+ try {
+ $this->authService->sendResetPasswordLinkEmail($request->get('email'));
+
+ return Response::make('', 204);
+ } catch (EntityNotFoundException $e) {
+ // Defined in the configuration auth.providers.users.model could not been instantiated (may be abstract or missing at all)
+ } catch (PasswordResetException $e) {
+ // Something unexpected from the Broker class
+ } catch (ValidationException $e) {
+ // The email is not valid
+ }
+ }
+```
+
+## Reset password
+
+Finally we have to reset the users passwords. This can easily be done by using Restify AuthService as well.
+
+- Define a reset password route to an action controller:
+
+```php
+Route::post('password/reset', 'AuthController@resetPassword')->name('password.reset');
+```
+
+- Inject the AuthService into your controller and call the resetPassword method:
+
+```php
+use Illuminate\Http\Request;
+use Illuminate\Http\Response;
+use Binaryk\LaravelRestify\Services\AuthService;
+
+class AuthController
+{
+ /**
+ * @var AuthService
+ */
+ protected $authService;
+
+ public function __construct(AuthService $authService)
+ {
+ $this->authService = $authService;
+ }
+
+ /**
+ * @param Request $request
+ * @return JsonResponse
+ * @throws \Binaryk\LaravelRestify\Exceptions\Eloquent\EntityNotFoundException
+ * @throws \Illuminate\Contracts\Container\BindingResolutionException
+ * @throws \Illuminate\Validation\ValidationException
+ */
+ public function resetPassword(Request $request)
+ {
+ try {
+ $this->authService->resetPassword($request);
+
+ return Response::make(__('Password reset'));
+ } catch (PasswordResetException|PasswordResetInvalidTokenException $e) {
+ return Response::make('Something went wrong', 401);
+ }
+ }
+
+```
+
+After successfully password reset an `Illuminate\Auth\Events\PasswordReset` event is dispatched.
diff --git a/docs/docs/2.0/exception-handler/exception-handler.md b/docs/docs/2.0/exception-handler/exception-handler.md
new file mode 100644
index 000000000..c371daff5
--- /dev/null
+++ b/docs/docs/2.0/exception-handler/exception-handler.md
@@ -0,0 +1,56 @@
+# Restify Exception Handler
+When creating an API the exceptions usually should be handled and resolved before sending to the client.
+This is usually done in the Laravel ExceptionHandler which transform the exception in a RestResponse and debug it for you into:
+- line
+- code
+- file
+- stack trace
+
+
+## Disable Restify exception handler
+However Restify gives you a handy exception handler which is configured in the
+`restify.exception_handler` you may want to delegate the exception handling to your application ExceptionHandler for more control.
+
+You can do that changing config by nullifying it or replace with another handler class:
+
+```php
+[
+ // config/restify.php
+ ...
+
+ 'exception_handler' => null
+]
+```
+
+## Intercept exceptions
+
+Intercepting exceptions for a specific request is breeze to do with Restify.
+Let's assume we have the store users controller action:
+
+```php
+use App\User;
+use Binaryk\LaravelRestify\Controllers\RestController;
+use Binaryk\LaravelRestify\Restify;use Illuminate\Http\Request;use Illuminate\Http\Response;use Illuminate\Support\Facades\Log;
+
+class UserController extends RestController
+{
+ /**
+ * Store a newly created resource in storage.
+ *
+ * @param Request $request
+ * @return Response
+ */
+ public function store(Request $request)
+ {
+ // Intercept the exception handler and log the exception message
+ Restify::exceptionHandler(function ($request, Exception $exception) {
+ Log::alert($exception->getMessage());
+ return Response::make('Something went wrong', $exception->getCode());
+ });
+
+ return $this->response(User::create($request->all()));
+ }
+}
+```
+
+As we can see the `exceptionHandler` callback receive the `$request` and thrown `$exception`.
diff --git a/docs/docs/2.0/quickstart.md b/docs/docs/2.0/quickstart.md
new file mode 100644
index 000000000..e3b7e3e90
--- /dev/null
+++ b/docs/docs/2.0/quickstart.md
@@ -0,0 +1,64 @@
+# Installation 2.0
+
+[[toc]]
+
+## Requirements
+
+Laravel Restify has a few requirements you should be aware of before installing:
+
+- Composer
+- Laravel Framework 5.5+
+
+## Installing Laravel Restify
+
+```bash
+composer require binaryk/laravel-restify
+```
+
+## Setup Laravel Restify
+After the instalation, the package requires a setup process, this will publish configuration, provider and will create the
+`app/Restify` directory with an abstract `Repository` and scaffolding a `User` repository you can play with:
+
+```shell script
+php artisan restify:setup
+```
+
+:::tip Package Stability
+
+If you are not able to install Restify into your application because of your `minimum-stability` setting,
+ consider setting your `minimum-stability` option to `dev` and your `prefer-stable` option to `true`.
+ This will allow you to install Laravel Restify while still preferring stable package
+ releases for your application.
+:::
+
+## Quick start
+
+Having the package setup and users table migrated, you should be good to perform the first API request:
+
+```http request
+GET: /restify-api/users?perPage=10
+```
+
+This should return the users list paginated and formatted according to [JSON:API](https://jsonapi.org/format/) standard.
+
+## Generate repository
+
+Creating a new repository can be done via restify command:
+
+```shell script
+php artisan restify:repository Post
+```
+
+If you want to generate the Policy, Model and migration as well, then you can use the `--all` option:
+
+```shell script
+php artisan restify:repository Post --all
+```
+## Generate policy
+
+Since the authorization is done through the Laravel Policies, a good way of generating a complete policy for an entity
+is by using the restify command:
+
+```shell script
+php artisan restify:policy Post
+```
diff --git a/docs/docs/2.0/repository-pattern/field.md b/docs/docs/2.0/repository-pattern/field.md
new file mode 100644
index 000000000..0dcdc1a6f
--- /dev/null
+++ b/docs/docs/2.0/repository-pattern/field.md
@@ -0,0 +1,118 @@
+# Field
+
+Field is basically the model attribute representation. Each Field generally extend the Field class from the Restify.
+This class ships a variety of mutators, interceptors, validators chaining methods you can use for defining your attribute
+according with your needed.
+
+To add a field to a repository, we can simply add it to the repository's fields method.
+Typically, fields may be created using their static make method. This method accepts the underlying database column as
+argument:
+
+```php
+
+use Illuminate\Support\Facades\Hash;
+use Binaryk\LaravelRestify\Fields\Field;
+use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
+
+/**
+ * @param RestifyRequest $request
+ * @return array
+ */
+public function fields(RestifyRequest $request)
+{
+ return [
+ Field::make('email')->rules('required')->storingRules('unique:users')->messages([
+ 'required' => 'This field is required.',
+ ]),
+ Field::make('password')->storeCallback(function ($value) {
+ return Hash::make($value);
+ })->rules('required')->storingRules('confirmed'),
+ ];
+}
+```
+
+# Validation
+
+There is a gold rule saying - catch the exception as soon as possible on it's request way.
+Validations are the first bridge of your request information, it would be a good start to validate
+your input so you don't have to worry about payload anymore.
+
+## Attaching rules
+
+Validation rules could be add by chaining the `rules` method to attach [validation rules](https://laravel.com/docs/validation#available-validation-rules)
+to the field:
+
+```php
+Field::make('email')->rules('required'),
+```
+
+Of course, if you are leveraging Laravel's support for [validation rule objects](https://laravel.com/docs/validation#using-rule-objects),
+you may attach those to resources as well:
+
+```php
+Field::make('email')->rules('required', new CustomRule),
+```
+
+Additionally, you may use [custom Closure rules](https://laravel.com/docs/validation#using-closures)
+to validate your resource fields:
+
+```php
+Field::make('email')->rules('required', function($attribute, $value, $fail) {
+ if (strtolower($value) !== $value) {
+ return $fail('The '.$attribute.' field must be lowercase.');
+ }
+}),
+```
+
+## Storing Rules
+
+If you would like to define rules that only apply when a resource is being storing, you may use the `storingRules` method:
+
+```php
+Field::make('email')
+ ->rules('required', 'email', 'max:255')
+ ->storingRules('unique:users,email');
+```
+
+## Update Rules
+
+Likewise, if you would like to define rules that only apply when a resource is being updated, you may use the `updatingRules` method.
+
+```php
+Field::make('email')->updatingRules('required', 'email');
+```
+
+
+# Interceptors
+However the default storing process is done automatically, sometimes you may want to take the control over it.
+That's a breeze with Restify, since Field expose few useful chained helpers for that.
+
+## Fill callback
+
+There are two steps before the value from the request is attached to model attribute.
+Firstly it is get from the application request, and go to the `fillCallback` and secondly,
+the value is transforming by the `storeCallback`.
+
+You may intercept each of those with closures.
+
+```php
+Field::make('title')
+ ->fillCallback(function (RestifyRequest $request, $model, $attribute) {
+ $model->{$attribute} = strtoupper($request->get('title_from_the_request'));
+})
+```
+
+This way you can get anything from the `$request` and perform any transformations with the value before storing.
+
+
+## Store callback
+
+Another handy interceptor is the `storeCallback`, this is the step immediately before attaching the value from the request to the model attribute:
+
+This interceptor may be useful for modifying the value passed through the `$request`.
+
+```php
+Field::make('password')->storeCallback(function ($value) {
+ return Hash::make($value);
+ });
+```
diff --git a/docs/docs/2.0/repository-pattern/repository-pattern.md b/docs/docs/2.0/repository-pattern/repository-pattern.md
new file mode 100644
index 000000000..b5b901263
--- /dev/null
+++ b/docs/docs/2.0/repository-pattern/repository-pattern.md
@@ -0,0 +1,413 @@
+# Repository
+
+[[toc]]
+
+## Introduction
+
+The Repository is the main core of the Laravel Restify, included with Laravel provides the an easy way of
+managing (usually called "CRUD"). It works along with
+[Laravel API Resource](https://laravel.com/docs/6.x/eloquent-resources),
+that 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 model - Restify will get from the request all of the attributes matching by key
+with those from the `fillable` array of the model definition.
+Restify will fill these fields with the value from the request.
+However if you want to transform some attributes before they are filled into the model
+you can do that by defining 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)
+ {
+ // Silence is golden
+ }
+```
+
+### show
+
+```php
+ public function show(RestifyRequest $request, $repositoryId)
+ {
+ // Silence is golden
+ }
+```
+
+### store
+
+```php
+ /**
+ * @param RestifyRequest $request
+ * @return \Illuminate\Http\JsonResponse|void
+ */
+ public function store(Binaryk\LaravelRestify\Http\Requests\RestifyRequest $request)
+ {
+ // Silence is golden
+ }
+```
+
+### update
+
+```php
+ public function update(RestifyRequest $request, $repositoryId)
+ {
+ // Silence is golden
+ }
+```
+
+### destroy
+
+```php
+ public function destroy(RestifyRequest $request, $repositoryId)
+ {
+ // Silence is golden
+ }
+```
+
+## 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, or for a specific
+repository by overriding it:
+
+```php
+/**
+ * Resolve the response for the details
+ *
+ * @param $request
+ * @param $serialized
+ * @return array
+ */
+public function serializeDetails($request, $serialized)
+{
+ return $serialized;
+}
+```
+
+You can change the index response by modifying the `resolveIndex` method:
+
+```php
+/**
+ * Resolve the response for the details
+ *
+ * @param $request
+ * @param $serialized
+ * @return array
+ */
+public function serializeIndex($request, $serialized)
+{
+ return $serialized;
+}
+```
+
+## Custom routes
+
+Laravel Restify has its own "CRUD" routes, however you're able to define your own routes right from your Repository class:
+
+```php
+/**
+ * Defining custom routes
+ *
+ * The default prefix of this route is the uriKey (e.g. 'restify-api/posts'),
+ *
+ * The default namespace is AppNamespace/Http/Controllers
+ *
+ * The default middlewares are the same from config('restify.middleware')
+ *
+ * However all options could be overrided by passing an $options argument
+ *
+ * @param \Illuminate\Routing\Router $router
+ * @param $options
+ */
+public static function routes(\Illuminate\Routing\Router $router, $options = [])
+{
+ $router->get('hello-world', function () {
+ return 'Hello World';
+ });
+}
+```
+
+Let's diving into a more "real life" example. Let's take the Post repository we had above:
+
+```php
+use Illuminate\Routing\Router;
+use Binaryk\LaravelRestify\Repositories\Repository;
+
+class Post extends Repository
+{
+ /*
+ * @param \Illuminate\Routing\Router $router
+ * @param $options
+ */
+ public static function routes(Router $router, $options = [])
+ {
+ $router->get('/{id}/kpi', 'PostController@kpi');
+ }
+
+ public static function uriKey()
+ {
+ return 'posts';
+ }
+}
+```
+
+At this moment Restify built the new route as a child of the `posts`, so it has the route:
+
+```http request
+GET: /restify-api/posts/{id}/kpi
+```
+
+This route is pointing to the `PostsController`, let's define it:
+
+```php
+response();
+ }
+}
+```
+
+### Route prefix
+
+As we noticed in the example above, the route is generated as a child of the current repository `uriKey` route,
+however sometimes you may want to have a separate prefix, which doesn't depends of the URI of the current repository.
+Restify provide you an easy of doing that, by adding default value `prefix` for the second `$options` argument:
+
+```php
+/**
+ * @param \Illuminate\Routing\Router $router
+ * @param $options
+ */
+public static function routes(Router $router, $options = ['prefix' => 'api',])
+{
+ $router->get('hello-world', function () {
+ return 'Hello World';
+ });
+}
+````
+
+Now the generated route will look like this:
+
+```http request
+GET: '/api/hello-world
+```
+
+With `api` as a custom prefix.
+
+
+### Route middleware
+
+All routes declared in the `routes` method, will have the same middelwares defined in your `restify.middleware` configuration file.
+Overriding default middlewares is a breeze with Restify:
+
+```php
+/**
+ * @param \Illuminate\Routing\Router $router
+ * @param $options
+ */
+public static function routes(Router $router, $options = ['middleware' => [CustomMiddleware::class],])
+{
+ $router->get('hello-world', function () {
+ return 'Hello World';
+ });
+}
+````
+
+In that case, the single middleware of the route will be defined by the `CustomMiddleware` class.
+
+### Route Namespace
+
+By default each route defined in the `routes` method, will have the namespace `AppRootNamespace\Http\Controllers`.
+You can override it easily by using `namespace` configuration key:
+
+```php
+/**
+ * @param \Illuminate\Routing\Router $router
+ * @param $options
+ */
+public static function routes(Router $router, $options = ['namespace' => 'App\Services',])
+{
+ $router->get('hello-world', 'WorldController@hello');
+}
+````
diff --git a/docs/docs/2.0/rest-methods/rest-methods.md b/docs/docs/2.0/rest-methods/rest-methods.md
new file mode 100644
index 000000000..fbeb93e37
--- /dev/null
+++ b/docs/docs/2.0/rest-methods/rest-methods.md
@@ -0,0 +1,354 @@
+[[toc]]
+
+## Introduction
+The API response format must stay consistent along the application. Ideally it would be good to follow a standard
+as the [JSON:API](https://jsonapi.org/format/) so your frontend app could align with the API.
+
+Restify provides several different approaches to respond consistent to the application's incoming request.
+By default, Restify's base rest controller class uses a `RestResponse` structure which provides a convenient
+method to respond to the HTTP request with a variety of handy magic methods.
+
+## Restify Response Quickstart
+To learn about Restify's handy response, let's look at a complete example of responding a
+request and returning the data back to the client.
+
+### Defining The Route
+First, let's assume we have the following routes defined in our `routes/api.php` file:
+
+```php
+Route::post('users', 'UserController@store');
+
+Route::get('users/{id}', 'UserController@show');
+```
+
+The `GET` route will return back a user for the given `id`.
+
+### Creating The Controller
+
+Next, let's take a look at a simple `API` controller that handles this route. We'll leave the `show` and `store` methods empty for now:
+
+```php
+response(User::find($id));
+}
+```
+
+As you can see, we pass the desired data into the `respond` method. This method will wrap the passed data into a
+JSON object and attach it to the `data` response property.
+
+### Receiving API Response
+
+Once the `respond` method wrapping the data, the HTTP request will receive back a response having always the
+structure:
+
+```json
+{
+ "data": {
+ "id": 1,
+ "name": "User name",
+ "email": "kshlerin.hertha@example.com",
+ "email_verified_at": "2019-12-20 09:48:54",
+ "created_at": "2019-12-20 09:48:54",
+ "updated_at": "2020-01-10 12:01:17"
+ }
+}
+
+```
+
+or:
+
+```json
+{
+ "errors": [...]
+}
+```
+
+## Response factory
+In addition the parent `RestController` provides a powerful `response` factory method.
+To understand this let's return back to our `store` method from the `UserController`:
+
+```php
+/**
+ * Store a newly created resource in storage.
+ *
+ * @param Request $request
+ * @return Response
+ */
+public function store(Request $request)
+{
+ return $this->response();
+}
+```
+
+The `response()` method will be an instance of `Binaryk\LaravelRestify\Controllers\RestResponse`. For more information on working with this object instance,
+[check out its documentation](#rest-response-methods).
+
+```php
+$this->response()
+->data($user)
+->message('This is the first user');
+```
+
+The response will look like:
+
+```json
+{
+ "data": {
+ "id": 1,
+ "name": "User name",
+ "email": "kshlerin.hertha@example.com",
+ "email_verified_at": "2019-12-20 09:48:54",
+ "created_at": "2019-12-20 09:48:54",
+ "updated_at": "2020-01-10 12:01:17"
+ },
+ "meta": {
+ "message": "This is the first user"
+ }
+}
+```
+
+### Displaying Response Errors
+
+As we saw above, the response always contains an `errors` property. This can be either an empty array, or a list with errors.
+For example, what if the incoming request parameters do not pass the given validation rules? This can be handled by the `errors` proxy
+method:
+
+```php
+/**
+ * Store a newly created resource in storage.
+ *
+ * @param Request $request
+ * @return Response
+ */
+public function store(Request $request)
+{
+ try {
+ $this->validate($request, [
+ 'title' => 'required|unique:users|max:255',
+ ]);
+
+ // The user is valid
+ } catch (ValidationException $exception) {
+ // The user is not valid
+ return $this->errors($exception->errors());
+ }
+}
+```
+
+And returned `API` response will have the `400` HTTP code and the following format:
+
+```json
+{
+ "errors": {
+ "title": [
+ "The title field is required."
+ ]
+ }
+}
+```
+
+## Custom Header
+
+Sometimes you may need to respond with a custom header, for example according with [JSON:API](https://jsonapi.org/format/#crud-creating-responses-201)
+after storing an entity, we should respond with a `Location` header which has the value endpoint to the resource:
+
+```php
+return $this->response()
+ ->header('Location', 'api/users/1')
+ ->data($user);
+```
+
+## Optional Attributes
+
+By default Restify returns `data` and `errors` attributes in the API response. It also wrap the message into a `meta` object.
+But what if we have to send some custom attributes. In addition to generating default fields, you may add extra fields to the
+response by using `setMeta` method from the `RestResponse` object:
+
+```php
+return $this->response()
+ ->data($user)
+ ->setMeta('related', [ 'William Shakespeare', 'Agatha Christie', 'Leo Tolstoy' ]);
+```
+
+## Hiding Default Attribute
+
+Restify has a list of predefined attributes: `'line', 'file', 'stack', 'data', 'errors', 'meta'`.
+
+Some of those are hidden in production: `'line', 'file', 'stack'`, since they are only used for tracking exceptions.
+
+If you would like the API response to not contain any of these fields (or hiding a specific one, `errors` for example),
+this can be done by setting in the application provider the:
+
+```php
+RestResponse::$RESPONSE_DEFAULT_ATTRIBUTES = ['data', 'meta'];
+```
+
+## Rest Response Methods
+
+The `$this->response()` returns an instance of `Binaryk\LaravelRestify\Controllers\RestResponse`. This expose multiple
+magic methods for your consistent API response.
+
+### Data attaching
+As we already have seen, attaching data to the response can be done by using:
+
+```php
+->data($info)
+```
+
+### Headers setup
+Header could be set by using `header` method, it accept two arguments, the header name and header value:
+
+```php
+->header('Location', 'api/users/1')
+```
+
+### Meta information
+In addition with `data` you may want to send some extra attributes to the client, a message for example, or anything else:
+
+```php
+->setMeta('name', 'Eduard Lupacescu')
+```
+
+```php
+->message(__('Silence is golden.'))
+```
+
+## Response code modifiers
+Very often we have to send an informative response code. The follow methods are used for setting the response code:
+
+### Auth 401
+```php
+->auth()
+``````
+### Refresh 103
+```php
+->refresh()
+````
+### Created 201
+
+```php
+->created()
+````
+### Deleted (No Content) 204
+```php
+->deleted()
+````
+```php
+->blank()
+````
+### Invalid 400
+```php
+->invalid()
+````
+### Unauthorized 401
+```php
+->unauthorized()
+````
+### Forbidden 403
+```php
+->forbidden()
+````
+### Missing 404
+```php
+->missing()
+````
+### success
+```php
+->success()
+````
+### Unavailable 503
+```php
+->unavailable()
+````
+
+### Throttle 429
+```php
+->throttle()
+```
+
+### Debug methods
+The follow methods could be used to debug some information in the dev mode:
+
+### line
+```php
+$lineNumber = 201;
+$this->line($lineNumber)
+```
+
+### file
+This could be used for debugging the file name
+
+```php
+$this->file($exception->getFile())
+```
+### stack
+With this you may log the exception stach trace
+
+```php
+$this->stack($exception->getTraceAsString())
+```
+
+### Errors methods
+The follow methods could be used for adding errors to the response:
+
+### errors
+Adding a set of errors at once:
+
+```php
+$this->errors([ 'Something went wrong' ])
+```
+
+### addError
+Adding error by error in a response instance:
+
+```php
+$this->addError('Something went wrong')
+```
diff --git a/docs/docs/2.0/search/search.md b/docs/docs/2.0/search/search.md
new file mode 100644
index 000000000..f18513340
--- /dev/null
+++ b/docs/docs/2.0/search/search.md
@@ -0,0 +1,135 @@
+# Filtering entities
+
+Laravel Restify provides configurable and powerful way of filtering over entities.
+
+- Prerequisites
+
+In order to make a model searchable, it should implement the `Binaryk\LaravelRestify\Contracts\RestifySearchable` contract.
+After running this command, add the `Binaryk\LaravelRestify\Traits\InteractWithSearch` trait to your model.
+This trait will provide a few helper methods to your model which allow you to filter.
+
+:::tip
+The searchable feature is available as for the Restify generated endpoints as well as from a custom Controller searching,
+`$this->search(Model::class)`
+:::
+
+## Search
+
+If you want search for some specific fields from a model, you have to define these fields in the `$search` static
+property:
+
+```php
+use Illuminate\Database\Eloquent\Model;
+use Binaryk\LaravelRestify\Traits\InteractWithSearch;
+use Binaryk\LaravelRestify\Contracts\RestifySearchable;
+
+class Post extends Model implements RestifySearchable
+{
+ use InteractWithSearch;
+
+ public static $search = ['id', 'title'];
+```
+
+Now the `Post` entity is searchable by `id` and `title`, so you could use `search` query param for filtering the index
+request:
+
+```http request
+GET: /restify-api/posts?search="Test title"
+```
+
+## Match
+
+Matching by specific attributes may be useful if you want an exact matching. Model
+configuration:
+
+```php
+use Illuminate\Database\Eloquent\Model;
+use Binaryk\LaravelRestify\Traits\InteractWithSearch;
+use Binaryk\LaravelRestify\Contracts\RestifySearchable;
+
+class Post extends Model implements RestifySearchable
+{
+ use InteractWithSearch;
+
+ public static $search = ['id', 'title'];
+
+ public static $match = ['id' => 'int', 'title' => 'string'];
+
+```
+
+As we may notice the match configuration is an associative array, defining the attribute name and type mapping.
+
+Available types:
+
+- text (or `string`)
+- bool
+- int (or `integer`)
+
+When performing the request you may pass the match field and value as query params:
+
+```http request
+GET: /restify-api/posts?id=1
+```
+
+or by title:
+
+```http request
+GET: /restify-api/posts?title="Some title"
+```
+
+
+## Sort
+When index query entities, usually we have to sort by specific attributes.
+
+This requires the `$sort` configuration:
+
+```php
+use Illuminate\Database\Eloquent\Model;
+use Binaryk\LaravelRestify\Traits\InteractWithSearch;
+use Binaryk\LaravelRestify\Contracts\RestifySearchable;
+
+class Post extends Model implements RestifySearchable
+{
+ use InteractWithSearch;
+
+ public static $search = ['id', 'title'];
+
+ public static $match = ['id' => 'int', 'title' => 'string'];
+
+ public static $sort = ['id'];
+```
+
+ Performing request requires the sort query param:
+
+ Sorting DESC requires a minus sign before the attribute name:
+
+ ```http request
+GET: /restify-api/posts?sort=-id
+```
+
+ Sorting ASC:
+
+ ```http request
+GET: /restify-api/posts?sort=id
+```
+
+or with plus sign before the field:
+
+ ```http request
+GET: /restify-api/posts?sort=+id
+```
+
+## Eager loading - aka withs
+
+When get a repository index or details about a single entity, often we have to get the related entities (we have access to).
+This eager loading is configurable by Restify as follow:
+
+```php
+public static $withs = ['posts'];
+```
+
+This means that we could use `posts` query for eager loading posts:
+
+```http request
+GET: /restify-api/users?with=posts
+```
diff --git a/src/Commands/stubs/policy.stub b/src/Commands/stubs/policy.stub
index 2e090991f..1908cf2bb 100644
--- a/src/Commands/stubs/policy.stub
+++ b/src/Commands/stubs/policy.stub
@@ -16,19 +16,19 @@ class DummyClass
* @param \App\User $user
* @return mixed
*/
- public function viewAny(User $user = null)
+ public function showAny(User $user = null)
{
//
}
/**
- * Determine whether the user can view the model.
+ * Determine whether the user can get the model.
*
* @param \App\User $user
* @param DummyModel $model
* @return mixed
*/
- public function view(User $user, DummyModel $model)
+ public function show(User $user, DummyModel $model)
{
//
}
diff --git a/src/Controllers/RestController.php b/src/Controllers/RestController.php
index 3488e34c7..6db68502e 100644
--- a/src/Controllers/RestController.php
+++ b/src/Controllers/RestController.php
@@ -23,6 +23,7 @@
use Illuminate\Http\JsonResponse;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Support\Facades\Password;
+use Illuminate\Support\Traits\ForwardsCalls;
use Throwable;
/**
@@ -36,7 +37,7 @@
*/
abstract class RestController extends BaseController
{
- use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
+ use AuthorizesRequests, DispatchesJobs, ValidatesRequests, ForwardsCalls;
/**
* @var RestResponse
@@ -93,23 +94,6 @@ public function config()
return $this->config;
}
- /**
- * Returns a generic response to the client.
- *
- * @param mixed $data
- * @param int $httpCode
- *
- * @return JsonResponse
- * @throws BindingResolutionException
- */
- protected function respond($data = null, $httpCode = 200)
- {
- $response = new \stdClass();
- $response->data = $data;
-
- return $this->response()->data($data)->code($httpCode)->respond();
- }
-
/**
* Get Response object.
*
@@ -230,14 +214,13 @@ public function broker()
/**
* Returns with a message.
* @param $msg
- * @return JsonResponse
+ * @return RestResponse
* @throws BindingResolutionException
*/
public function message($msg)
{
return $this->response()
- ->message($msg)
- ->respond();
+ ->message($msg);
}
/**
@@ -251,7 +234,13 @@ protected function errors(array $errors)
{
return $this->response()
->invalid()
- ->errors($errors)
- ->respond();
+ ->errors($errors);
+ }
+
+ public function __call($method, $parameters)
+ {
+ $this->response();
+
+ $this->forwardCallTo($this->response, $method, $parameters);
}
}
diff --git a/src/Controllers/RestResponse.php b/src/Controllers/RestResponse.php
index 7af6770e9..be073c2e5 100644
--- a/src/Controllers/RestResponse.php
+++ b/src/Controllers/RestResponse.php
@@ -9,6 +9,7 @@
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\JsonResponse;
+use Illuminate\Support\Arr;
/**
* Class RestResponse.
@@ -18,6 +19,7 @@
* @method RestResponse created()
* @method RestResponse deleted()
* @method RestResponse blank()
+ * @method RestResponse notFound()
* @method RestResponse error() 500
* @method RestResponse invalid() 400
* @method RestResponse unauthorized() 401 - don't have correct password/email
@@ -29,9 +31,10 @@
*
* @author lupacescueduard