├── LICENSE
├── README.md
├── composer.json
├── docs
├── .nojekyll
├── 1.x
│ ├── .nojekyll
│ ├── README.md
│ ├── _coverpage.md
│ ├── _sidebar.md
│ ├── advanced_usage.md
│ ├── appends.md
│ ├── configuration.md
│ ├── exception_handler.md
│ ├── filtering.md
│ ├── filters_old.md
│ ├── including_relationships.md
│ ├── index.html
│ ├── installation.md
│ ├── pagination.md
│ ├── repository.md
│ ├── scopes.md
│ ├── selecting_fields.md
│ └── sorting.md
├── README.md
├── _coverpage.md
├── _sidebar.md
├── advanced_usage.md
├── api_consumer.md
├── appends.md
├── configuration.md
├── exception_handler.md
├── filtering.md
├── filters_old.md
├── including_relationships.md
├── index.html
├── installation.md
├── pagination.md
├── repository.md
├── scopes.md
├── selecting_fields.md
└── sorting.md
└── src
├── Config
└── larapi.php
├── Console
├── ComponentMakeCommand.php
└── Stubs
│ ├── controllers
│ └── controller.stub
│ ├── events
│ ├── wascreated.stub
│ ├── wasdeleted.stub
│ └── wasupdated.stub
│ ├── exceptions
│ └── notfoundexception.stub
│ ├── models
│ └── model.stub
│ ├── repositories
│ └── repository.stub
│ ├── requests
│ ├── createrequest.stub
│ └── updaterequest.stub
│ └── services
│ └── service.stub
├── Controllers
└── LaravelController.php
├── Database
├── EloquentBuilderTrait.php
└── Repository.php
├── Exceptions
├── ApiException.php
└── LarapiException.php
├── ExceptionsFormatters
├── ExceptionFormatter.php
└── UnprocessableEntityHttpExceptionFormatter.php
├── Facades
└── ApiConsumer.php
├── Providers
└── LarapiServiceProvider.php
└── Routes
└── ApiConsumerRouter.php
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 one2tek
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ### Introduction
2 |
3 | **Larapi** is a package thats offers you to do modern API development in Laravel with support for new versions of Laravel.
4 |
5 | **Larapi** comes included with...
6 | * Exception handler for APIs.
7 | * A Controller class that gives response, parse data for your endpoints.
8 | * A Repository class for requesting entities from your database.
9 | * Sorting.
10 | * Filtering.
11 | * Eager loading.
12 | * Pagination.
13 | * Selecting columns dynamically.
14 | * Selecting scopes dynamically.
15 | * Appends.
16 | * A class for making internal API requests.
17 |
18 | ### Documentation
19 | https://one2tek.github.io/larapi/
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "one2tek/larapi",
3 | "description": "Modern API development in Laravel.",
4 | "homepage": "https://github.com/one2tek/larapi",
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Gentrit Abazi",
9 | "email": "gentritabazi01@gmail.com"
10 | }
11 | ],
12 | "autoload": {
13 | "psr-4": {
14 | "one2tek\\larapi\\": "src/"
15 | }
16 | },
17 | "require": {
18 | "illuminate/support": "~5.6.0|~5.7.0|~5.8.0|^6.0|^7.0|^8.0|^9.0|^10.0"
19 | },
20 | "config": {
21 | "sort-packages": true
22 | },
23 | "extra": {
24 | "laravel": {
25 | "providers": [
26 | "one2tek\\larapi\\Providers\\LarapiServiceProvider"
27 | ]
28 | }
29 | },
30 | "minimum-stability": "dev",
31 | "prefer-stable": true
32 | }
33 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/one2tek/larapi/23780ac23136b771368b62ffb0ca4d5a4daa1ed6/docs/.nojekyll
--------------------------------------------------------------------------------
/docs/1.x/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/one2tek/larapi/23780ac23136b771368b62ffb0ca4d5a4daa1ed6/docs/1.x/.nojekyll
--------------------------------------------------------------------------------
/docs/1.x/README.md:
--------------------------------------------------------------------------------
1 | ## Larapi
2 |
3 | > Build fast API-s in Laravel.
4 |
5 | ## What it is
6 |
7 | Larapi is a package thats offers you to do modern API development in Laravel with support for new versions of Laravel.
8 |
9 | ## Features
10 |
11 | - Exception handler for APIs.
12 | - A Controller class that gives response, parse data for your endpoints.
13 | - A Repository class for requesting entities from your database.
14 | - Sorting.
15 | - Filtering.
16 | - Eager loading.
17 | - Pagination.
18 | - Selecting columns dynamically.
19 | - Selecting scopes dynamically.
20 | - Appends.
21 | - A class for making internal API requests.
22 |
23 | ## Authors
24 |
25 | - Gentrit Abazi (https://github.com/gentritabazi01)
--------------------------------------------------------------------------------
/docs/1.x/_coverpage.md:
--------------------------------------------------------------------------------
1 |
2 | # Larapi
3 |
4 | > Build fast API-s in Laravel.
5 |
6 | [GitHub](https://github.com/one2tek/larapi)
7 | [Get Started](README.md)
--------------------------------------------------------------------------------
/docs/1.x/_sidebar.md:
--------------------------------------------------------------------------------
1 | - Getting started
2 | - [Quick start](README.md)
3 | - [Installation](installation.md)
4 | - [Configuration](configuration.md)
5 |
6 | - Selecting fields
7 | - [Quick start](selecting_fields.md?id=selecting-fields)
8 | - [Basic usage](selecting_fields.md?id=basic-usage)
9 | - [Selecting fields for included relations](selecting_fields.md?id=selecting-fields-for-included-relations)
10 |
11 | - Scopes
12 | - [Quick start](scopes.md?id=scopes)
13 | - [Usage](scopes.md?id=usage)
14 | - [Remove Global Scopes](scopes.md?id=remove-global-scopes)
15 |
16 | - Appends
17 | - [Quick start](appends.md?id=appends)
18 | - [Usage](appends.md?id=usage)
19 |
20 | - Including relationships
21 | - [Quick start](including_relationships.md?id=including-relationships)
22 | - [Basic usage](including_relationships.md?id=basic-usage)
23 | - [Load multiple](including_relationships.md?id=load-multiple)
24 | - [Load nested](including_relationships.md?id=load-nested)
25 | - [Counting related relations](including_relationships.md?id=counting-related-relations)
26 | - [Querying Relationship Existence](including_relationships.md?id=querying-relationship-existence)
27 | - [Querying Relationship Absence](including_relationships.md?id=querying-relationship-absence)
28 | - [Modes](including_relationships.md?id=modes)
29 | - [IDs mode](including_relationships.md?id=ids-mode)
30 | - [Sideload mode](including_relationships.md?id=sideload-mode)
31 |
32 | - Pagination
33 | - [Quick start](pagination.md?id=pagination)
34 | - [Usage](pagination.md?id=usage)
35 |
36 | - Sorting
37 | - [Quick start](sorting.md?id=sorting)
38 | - [Usage](sorting.md?id=usage)
39 | - [Sort multiple columns](sorting.md?id=sort-multiple-columns)
40 |
41 | - Filtering
42 | - [Quick start](filtering.md?id=filtering)
43 | - [Operators](filtering.md?id=operators)
44 | - [Build filter](filtering.md?id=build-filter)
45 | - [Example filters](filtering.md?id=example-filters)
46 |
47 | - Repository
48 | - [Quick start](repository.md?id=repository)
49 | - [Create repository](repository.md?id=create-repository)
50 | - [Default sort](repository.md?id=default-sort)
51 | - [Functions](repository.md?id=functions)
52 |
53 | - Exception Handler
54 | - [Quick start](exception_handler.md?id=exception-handler)
55 | - [Configure](exception_handler.md?id=configure)
56 | - [Formatters](exception_handler.md?id=formatters)
57 |
58 | - Advanced Usage
59 | - [Quick start](advanced_usage.md?id=avanced_usage)
60 | - [Custom Sort](advanced_usage.md?id=custom-sort)
61 | - [Custom Filter](advanced_usage.md?id=custom-filter)
--------------------------------------------------------------------------------
/docs/1.x/advanced_usage.md:
--------------------------------------------------------------------------------
1 | # Advanced Usage
2 |
3 | Learn how to use `Larapi` in Advanced usage.
4 |
5 | # Custom Sort
6 |
7 | You can create custom sort in your repository like this:
8 |
9 | ```php
10 | public function sortMyName($queryBuilder, $direction)
11 | {
12 | //
13 | }
14 | ```
15 |
16 | # Custom Filter
17 |
18 | You can create custom filter in your repository like this:
19 |
20 | ```php
21 | public function filterName($queryBuilder, $method, $operator, $value, $clauseOperator, $or)
22 | {
23 | //
24 | }
25 | ```
--------------------------------------------------------------------------------
/docs/1.x/appends.md:
--------------------------------------------------------------------------------
1 | # Appends
2 |
3 | With Laravel you can add attributes that do not have a corresponding column in your database.
4 |
5 | # Usage
6 |
7 | The following example appends `isAdmin` attribute:
8 |
9 | ```console
10 | {base_url}/users?append[]=isAdmin
11 | ```
12 |
13 | # Append multiple
14 |
15 | You can append multiple attributes separating them with a comma:
16 |
17 | ```console
18 | {base_url}/users?append[]=isAdmin,isDriver
19 | ```
--------------------------------------------------------------------------------
/docs/1.x/configuration.md:
--------------------------------------------------------------------------------
1 | ## Configuration
2 |
3 | > [Check how to use in real application.](https://github.com/gentritabazi01/Clean-Laravel-Api)
4 |
5 | 1. Extends `one2tek\larapi\Controllers\LaravelController` in your base controller.
6 |
7 | ```php
8 | userRepository = $userRepository;
40 | }
41 |
42 | public function getAll()
43 | {
44 | $resourceOptions = $this—>parseResourceOptions();
45 |
46 | $users = $this—>userRepository—>get($resourceOptions);
47 |
48 | return $this—>response($users);
49 | }
50 | }
51 | ```
52 |
53 | 4. Example Repository for Users.
54 |
55 | ```php
56 | generateExceptionResponse();
18 | }
19 | ```
20 |
21 | # Formatters
22 |
23 | `Larapi` already comes with sensible formatters out of the box. In `config/larapi.php` is a section where the formatter priority is defined.
24 |
25 | ```php
26 | 'exceptions_formatters' => [
27 | Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException::class => one2tek\larapi\ExceptionsFormatters\UnprocessableEntityHttpExceptionFormatter::class,
28 | Throwable::class => one2tek\larapi\ExceptionsFormatters\ExceptionFormatter::class
29 | ]
30 | ```
31 |
32 | You can write custom formatters easily:
33 |
34 | ```php
35 | false,
48 | 'status' => self::STATUS_CODE,
49 | 'message' => self::MESSAGE
50 | ];
51 |
52 | return response()->json($data, self::STATUS_CODE);
53 | }
54 | }
55 | ```
56 |
57 | Now you just need to add it to `config/larapi.php` and all `NotFoundHttpExceptions` will be formatted using our custom formatter.
--------------------------------------------------------------------------------
/docs/1.x/filtering.md:
--------------------------------------------------------------------------------
1 | # Filtering
2 |
3 | Data filtering is very easy with at `Larapi` see the examples below.
4 |
5 | By default, all filters have to be explicitly allowed using `$whiteListFilter` property in specified Model.
6 |
7 | For more advanced use cases, [custom filter](advanced_usage?id=custom-filter) can be used.
8 |
9 | #### Operators
10 |
11 | Type | Description
12 | ---- | -----------
13 | ct | String contains
14 | sw | Starts with
15 | ew | Ends with
16 | eq | Equals
17 | gt | Greater than
18 | gte| Greater than or equalTo
19 | lt | Lesser than
20 | lte | Lesser than or equalTo
21 | in | In array
22 | bt | Between
23 |
24 | #### Build filter
25 |
26 | The way a filter should be formed is:
27 |
28 | ```console
29 | {base_url}/users?filter[columnName][operator][not]=value
30 | ```
31 |
32 | Another available parameter is `filterByOr`, `search` and `searchByOr`.
33 |
34 | * **columnName** - (Required) - Name of column you want to filter, for relationships use `dots`.
35 | * **operator** - (Optional | Default: `eq`) Type of operator you want to use.
36 | * **not** - (Optional | Default: `false`) Negate the filter (Accepted values: yes|true|1).
37 |
38 | #### Example filters
39 |
40 | Filter all users whose id start with `1000`.
41 |
42 | ```console
43 | {base_url}/users?filter[name][sw]=1000
44 | ```
45 |
46 | Filter all books whose author is `Gentrit`.
47 |
48 | ```console
49 | {base_url}/users?filter[name]=author.name
50 | ```
51 |
52 | Filter all users whose name start with `Gentrit` or ends with `Abazi`.
53 |
54 | ```console
55 | {base_url}/users?filterByOr[name][sw]=Gentrit&filterByOr[name][ew]=Abazi
56 | ```
57 |
58 | [See other ways for filtering](filters_old.md)
--------------------------------------------------------------------------------
/docs/1.x/filters_old.md:
--------------------------------------------------------------------------------
1 | # Filtering
2 |
3 | Filters should be defined as an array of filter groups.
4 |
5 | **Filter groups**
6 |
7 | Property | Value type | Description
8 | -------- | ---------- | -----------
9 | or | boolean | Should the filters in this group be grouped by logical OR or AND operator
10 | filters | array | Array of filters (see syntax below)
11 |
12 | **Filters**
13 |
14 | Property | Value type | Description
15 | -------- | ---------- | -----------
16 | column | string | The property of the model to filter by (can also be custom filter)
17 | value | mixed | The value to search for
18 | operator | string | The filter operator to use (see different types below)
19 | not | boolean | Negate the filter
20 |
21 | **Operators**
22 |
23 | Type | Description
24 | ---- | -----------
25 | ct | String contains
26 | sw | Starts with
27 | ew | Ends with
28 | eq | Equals
29 | gt | Greater than
30 | gte| Greater than or equalTo
31 | lt | Lesser than
32 | lte | Lesser than or equalTo
33 | in | In array
34 | bt | Between
35 |
36 | #### Example filters
37 |
38 | Filter all users whose name start with “Gentrit” or ends with “Abazi”.
39 |
40 | ```SELECT * FROM `users` WHERE name LIKE "Gentrit%" OR name LIKE "%Abazi"```
41 |
42 | ```json
43 | {
44 | "filter_groups": [
45 | {
46 | "or": true,
47 | "filters": [
48 | {
49 | "column": "name",
50 | "operator": "sw",
51 | "value": "Gentrit"
52 | },
53 | {
54 | "column": "name",
55 | "operator": "ew",
56 | "value": "Abazi"
57 | }
58 | ]
59 | }
60 | ]
61 | }
62 | ```
63 |
64 | Filter all users whose name start with “A” and which were born between years 1990 and 2000.
65 |
66 | ```SELECT * FROM `users` WHERE (name LIKE "A%") AND (`birth_year` >= 1990 and `birth_year` <= 2000)```
67 |
68 | ```json
69 | {
70 | "filter_groups": [
71 | {
72 | "filters": [
73 | {
74 | "column": "name",
75 | "operator": "sw",
76 | "value": "A"
77 | }
78 | ]
79 | },
80 | {
81 | "filters": [
82 | {
83 | "column": "birth_year",
84 | "value": 1990,
85 | "operator": "gte"
86 | },
87 | {
88 | "column": "birth_year",
89 | "value": 2000,
90 | "operator": "lte"
91 | }
92 | ]
93 | }
94 | ]
95 | }
96 | ```
97 |
98 | Filter all books whose author is Gentrit.
99 | ```json
100 | {
101 | "filter_groups": [
102 | {
103 | "filters": [
104 | {
105 | "column": "author.name",
106 | "operator": "eq",
107 | "value": "Gentrit"
108 | }
109 | ]
110 | }
111 | ]
112 | }
113 | ```
--------------------------------------------------------------------------------
/docs/1.x/including_relationships.md:
--------------------------------------------------------------------------------
1 | # Including relationships
2 |
3 | The `include` query parameter will load any Eloquent relation on the resulting models.
4 |
5 | # Basic usage
6 |
7 | The following query parameter will include the `logs` relation:
8 |
9 | ```console
10 | {base_url}/users?include=logs
11 | ```
12 |
13 | Users will have all their their `logs` related models loaded.
14 |
15 | # Load multiple
16 |
17 | You can load multiple relationships by separating them with a semicolon:
18 |
19 | ```console
20 | {base_url}/users?include=logs;tasks
21 | ```
22 |
23 | # Load nested
24 |
25 | You can load nested relationships using the dot `.` notation:
26 |
27 | ```console
28 | {base_url}/users?include=logs.causer
29 | ```
30 |
31 | # Counting related relations
32 |
33 | If you want to count the number of results from a relationship without actually loading them you may use the `withCount` query parameter, which will place a {relation}_count column on your resulting models.
34 |
35 | ```console
36 | {base_url}/users?withCount[]=comments
37 | ```
38 |
39 | # Querying Relationship Existence
40 |
41 | Imagine you want to retrieve all blog posts that have at least one comment.
42 | You can do this by passing `has` paramter in query:
43 |
44 | ```console
45 | {base_url}/posts?has[]=comments
46 | ```
47 |
48 | Nested `has` statements may also be constructed using "dot" notation. For example, you may retrieve all posts that have at least one `comment` and `vote`:
49 |
50 | ```console
51 | {base_url}/posts?has[]=comments.votes
52 | ```
53 |
54 | # Querying Relationship Absence
55 |
56 | When accessing the records for a model, you may wish to limit your results based on the absence of a relationship. For example, imagine you want to retrieve all blog posts that don't have any `comments`. To do so, you may pass `doesntHave` paramter in query:
57 |
58 | ```console
59 | {base_url}/posts?doesntHave[]=comments
60 | ```
61 |
62 | # Modes
63 |
64 | With the `Larapi` package you have the opportunity to return the relations in `IDs` mode and `Sideload` mode.
65 |
66 | # IDs mode
67 |
68 | ```console
69 | {base_url}/books?modeIds[]=author
70 | ```
71 |
72 | Will return a collection of Books eager loaded with the ID of their Author.
73 |
74 | # Sideload mode
75 |
76 | ```console
77 | {base_url}/books?modeSideload[]=author
78 | ```
79 |
80 | Will return a collection of Books and a eager loaded collection of their Authors in the root scope.
--------------------------------------------------------------------------------
/docs/1.x/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Larapi
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/docs/1.x/installation.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | You can install the package via composer:
4 |
5 | ```bash
6 | composer require one2tek/larapi
7 | ```
--------------------------------------------------------------------------------
/docs/1.x/pagination.md:
--------------------------------------------------------------------------------
1 | # Pagination
2 |
3 | Two parameters are available: `limit` and `page`. limit will determine the number of records per page and page will determine the current page.
4 |
5 | # Usage
6 |
7 | ```console
8 | {base_url}/books?limit=10&page=3
9 | ```
10 |
11 | Will return books number 30-40.
--------------------------------------------------------------------------------
/docs/1.x/repository.md:
--------------------------------------------------------------------------------
1 | # Repository
2 |
3 | `Larapi` included Repository class for requesting entities from your database.
4 |
5 | # Create repository
6 |
7 | To create repository is easy you need just to define own model:
8 |
9 | ```php
10 | $value1, $column2 => $value2]`)
70 |
71 | ### getWhereIn (string $column, array $values, array $options = [])
72 |
73 | Get `User` rows where `$column` can be any of the values given by `$values`
74 |
75 | ### getLatest (array $options = [])
76 |
77 | Get the most recent `User`
78 |
79 | ### getLatestWhere (string $column, mixed $value, array $options = [])
80 |
81 | Get the most recent `User` where `$column=$value`
82 |
83 | ### delete ($id)
84 |
85 | Delete `User` rows by primary key
86 |
87 | ### deleteWhere ($column, $value)
88 |
89 | Delete `User` rows where `$column=$value`
90 |
91 | ### deleteWhereArray (array $clauses)
92 |
93 | Delete `User` rows by multiple where clauses (`[$column1 => $value1, $column2 => $value2]`)
--------------------------------------------------------------------------------
/docs/1.x/scopes.md:
--------------------------------------------------------------------------------
1 | # Scopes
2 |
3 | Sometimes more advanced filtering options are necessary. This is where scope filters, callback filters and custom filters come in handy.
4 |
5 | # Usage
6 |
7 | The following query parameter will add the `popular` scope:
8 |
9 | ```console
10 | {base_url}/users?scope[]=popular
11 | ```
12 |
13 | # Remove Global Scopes
14 |
15 | Global scopes allow you to add constraints to all queries for a given model.
16 | What if we want to remove it ?
17 |
18 | The following query parameter will remove the `delivered` global scope:
19 |
20 | ```console
21 | {base_url}/books?excludeGlobalScopes[]=delivered
22 | ```
23 |
24 | # Select multiple
25 |
26 | You can select multiple scopes separating them with a comma:
27 |
28 | ```console
29 | {base_url}/users?scope[]=popular,famous
30 | ```
--------------------------------------------------------------------------------
/docs/1.x/selecting_fields.md:
--------------------------------------------------------------------------------
1 | # Selecting fields
2 |
3 | Sometimes you'll want to fetch only a couple fields to reduce the overall size of your SQL query. This can be done by specifying some fields in request by query parameter.
4 |
5 | # Basic usage
6 |
7 | The following example fetches only the users' id and name:
8 |
9 | ```console
10 | {base_url}/users?select=id,name
11 | ```
12 |
13 | The SQL query will look like this:
14 |
15 | ```sql
16 | `SELECT "id", "name" FROM "users"`
17 | ```
18 |
19 | # Selecting fields for included relations
20 |
21 | The following example fetches only the authors' id and name:
22 |
23 | ```console
24 | {base_url}/books?include=author:id,name.
25 | ```
--------------------------------------------------------------------------------
/docs/1.x/sorting.md:
--------------------------------------------------------------------------------
1 | # Sorting
2 |
3 | The `sortByAsc` and `sortByDesc` query parameters are used to determine by which property the results collection will be ordered.
4 |
5 | For more advanced use cases, [custom sorts](advanced_usage?id=custom-sort) can be used.
6 |
7 | # Usage
8 |
9 | The following query parameter `sortByAsc` will sort results by from the lowest value to the highest value:
10 |
11 | ```console
12 | {base_url}/books?sortByAsc=id
13 | ```
14 |
15 | The following query parameter `sortByDesc` will sort results by from the highest value to the lowest value:
16 |
17 | ```console
18 | {base_url}/books?sortByDesc=id
19 | ```
20 |
21 | # Sort multiple columns
22 |
23 | You can sort multiple columns separating them with a comma:
24 |
25 | ```console
26 | {base_url}/books?sortByDesc=id,name
27 | ```
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | ## Larapi
2 |
3 | > Build fast API-s in Laravel.
4 |
5 | ## What it is
6 |
7 | Larapi is a package thats offers you to do modern API development in Laravel with support for new versions of Laravel.
8 |
9 | ## Features
10 |
11 | - Exception handler for APIs.
12 | - A Controller class that gives response, parse data for your endpoints.
13 | - A Repository class for requesting entities from your database.
14 | - Sorting.
15 | - Filtering.
16 | - Eager loading.
17 | - Pagination.
18 | - Selecting columns dynamically.
19 | - Selecting scopes dynamically.
20 | - Appends.
21 | - A class for making internal API requests.
22 |
23 | ## Authors
24 |
25 | - Gentrit Abazi (https://github.com/gentritabazi)
26 |
27 | ## Contributors
28 |
29 | - https://github.com/one2tek/larapi/graphs/contributors
30 |
--------------------------------------------------------------------------------
/docs/_coverpage.md:
--------------------------------------------------------------------------------
1 |
2 | # Larapi
3 |
4 | > Build fast API-s in Laravel.
5 |
6 | [GitHub](https://github.com/one2tek/larapi)
7 | [Get Started](README.md)
--------------------------------------------------------------------------------
/docs/_sidebar.md:
--------------------------------------------------------------------------------
1 | - Getting started
2 | - [Quick start](README.md)
3 | - [Installation](installation.md)
4 | - [Configuration](configuration.md)
5 |
6 | - Selecting fields
7 | - [Quick start](selecting_fields.md?id=selecting-fields)
8 | - [Basic usage](selecting_fields.md?id=basic-usage)
9 | - [Selecting fields for included relations](selecting_fields.md?id=selecting-fields-for-included-relations)
10 |
11 | - Scopes
12 | - [Quick start](scopes.md?id=scopes)
13 | - [Usage](scopes.md?id=usage)
14 | - [Remove Global Scopes](scopes.md?id=remove-global-scopes)
15 |
16 | - Appends
17 | - [Quick start](appends.md?id=appends)
18 | - [Usage](appends.md?id=usage)
19 |
20 | - Including relationships
21 | - [Quick start](including_relationships.md?id=including-relationships)
22 | - [Basic usage](including_relationships.md?id=basic-usage)
23 | - [Load multiple](including_relationships.md?id=load-multiple)
24 | - [Load nested](including_relationships.md?id=load-nested)
25 | - [Counting related relations](including_relationships.md?id=counting-related-relations)
26 | - [Querying Relationship Existence](including_relationships.md?id=querying-relationship-existence)
27 | - [Querying Relationship Absence](including_relationships.md?id=querying-relationship-absence)
28 |
29 | - Pagination
30 | - [Quick start](pagination.md?id=pagination)
31 | - [Usage](pagination.md?id=usage)
32 |
33 | - Sorting
34 | - [Quick start](sorting.md?id=sorting)
35 | - [Usage](sorting.md?id=usage)
36 | - [Sort multiple columns](sorting.md?id=sort-multiple-columns)
37 |
38 | - Filtering
39 | - [Quick start](filtering.md?id=filtering)
40 | - [Operators](filtering.md?id=operators)
41 | - [Build filter](filtering.md?id=build-filter)
42 | - [Example filters](filtering.md?id=example-filters)
43 |
44 | - Repository
45 | - [Quick start](repository.md?id=repository)
46 | - [Create repository](repository.md?id=create-repository)
47 | - [Default sort](repository.md?id=default-sort)
48 | - [Functions](repository.md?id=functions)
49 |
50 | - Exception Handler
51 | - [Quick start](exception_handler.md?id=exception-handler)
52 | - [Configure](exception_handler.md?id=configure)
53 | - [Formatters](exception_handler.md?id=formatters)
54 |
55 | - Api Consumer
56 | - [Quick start](api_consumer.md?api-consumer)
57 | - [Usage Example](api_consumer.md?usage-example)
58 |
59 | - Advanced Usage
60 | - [Quick start](advanced_usage.md?id=avanced_usage)
61 | - [Custom Sort](advanced_usage.md?id=custom-sort)
62 | - [Custom Filter](advanced_usage.md?id=custom-filter)
63 | - [Include Soft Deleted](advanced_usage.md?id=include-soft-deleted)
--------------------------------------------------------------------------------
/docs/advanced_usage.md:
--------------------------------------------------------------------------------
1 | # Advanced Usage
2 |
3 | Learn how to use `Larapi` in Advanced usage.
4 |
5 | # Custom Sort
6 |
7 | You can create custom sort in your repository like this:
8 |
9 | ```php
10 | public function sortMyName($queryBuilder, $direction)
11 | {
12 | //
13 | }
14 | ```
15 |
16 | # Custom Filter
17 |
18 | You can create custom filter in your repository like this:
19 |
20 | ```php
21 | public function filterName($queryBuilder, $method, $operator, $value, $clauseOperator, $or)
22 | {
23 | //
24 | }
25 | ```
26 |
27 | # Include Soft Deleted
28 |
29 | By laravel soft deleted models will automatically be excluded from query results. However, you may force soft deleted models to be included in a query's results by calling the `withTrashed` parameter on the url:
30 |
31 | ```console
32 | {base_url}/users?withTrashed=1
33 | ```
--------------------------------------------------------------------------------
/docs/api_consumer.md:
--------------------------------------------------------------------------------
1 | # Api Consumer
2 |
3 | `Api Consumer` is a small class for making internal API requests.
4 |
5 | # Usage Example
6 |
7 | ```php
8 | private $apiConsumer;
9 |
10 | public function __construct()
11 | {
12 | $this->apiConsumer = app()->make('apiconsumer');
13 | }
14 |
15 | public function index()
16 | {
17 | return $this->apiConsumer->post('/oauth/token', []);
18 | }
19 | ```
--------------------------------------------------------------------------------
/docs/appends.md:
--------------------------------------------------------------------------------
1 | # Appends
2 |
3 | With Laravel you can add attributes that do not have a corresponding column in your database.
4 |
5 | # Usage
6 |
7 | The following example appends `isAdmin` attribute:
8 |
9 | ```console
10 | {base_url}/users?append=isAdmin
11 | ```
12 |
13 | # Append multiple
14 |
15 | You can append multiple attributes separating them with a comma:
16 |
17 | ```console
18 | {base_url}/users?append=isAdmin,isDriver
19 | ```
--------------------------------------------------------------------------------
/docs/configuration.md:
--------------------------------------------------------------------------------
1 | ## Configuration
2 |
3 | > [Check how to use in real application.](https://github.com/gentritabazi01/Clean-Laravel-Api)
4 |
5 | 1. Extends `one2tek\larapi\Controllers\LaravelController` in your base controller.
6 |
7 | ```php
8 | userRepository = $userRepository;
40 | }
41 |
42 | public function getAll()
43 | {
44 | $resourceOptions = $this—>parseResourceOptions();
45 |
46 | $users = $this—>userRepository—>get($resourceOptions);
47 |
48 | return $this—>response($users);
49 | }
50 | }
51 | ```
52 |
53 | 4. Example Repository for Users.
54 |
55 | ```php
56 | generateExceptionResponse();
18 | }
19 | ```
20 |
21 | # Formatters
22 |
23 | `Larapi` already comes with sensible formatters out of the box. In `config/larapi.php` is a section where the formatter priority is defined.
24 |
25 | ```php
26 | 'exceptions_formatters' => [
27 | Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException::class => one2tek\larapi\ExceptionsFormatters\UnprocessableEntityHttpExceptionFormatter::class,
28 | Throwable::class => one2tek\larapi\ExceptionsFormatters\ExceptionFormatter::class
29 | ]
30 | ```
31 |
32 | You can write custom formatters easily:
33 |
34 | ```php
35 | false,
48 | 'status' => self::STATUS_CODE,
49 | 'message' => self::MESSAGE
50 | ];
51 |
52 | return response()->json($data, self::STATUS_CODE);
53 | }
54 | }
55 | ```
56 |
57 | Now you just need to add it to `config/larapi.php` and all `NotFoundHttpExceptions` will be formatted using our custom formatter.
--------------------------------------------------------------------------------
/docs/filtering.md:
--------------------------------------------------------------------------------
1 | # Filtering
2 |
3 | Data filtering is very easy with at `Larapi` see the examples below.
4 |
5 | By default, all filters have to be explicitly allowed using `$whiteListFilter` property in specified Model.
6 |
7 | List of all valid syntax for $whiteListFilter:
8 |
9 | ```php
10 | public static $whiteListFilter = ['*'];
11 | public static $whiteListFilter = ['id', 'title', 'author'];
12 | public static $whiteListFilter = ['id', 'title', 'author.*'];
13 | ```
14 |
15 | If the filter is `['*']` then all properties and sub-properties can be used for filtering.
16 |
17 | If the filter is a list of model properties then only the selected properties can be filtered.
18 |
19 | If some of the filter are a relationship then only the `$whiteListFilter` properties of the sub-property's model can be filtered.
20 |
21 | If some of the filter contains a `.*` the all sub-properties of the relationship model can be filtered.
22 |
23 | For more advanced use cases, [custom filter](advanced_usage?id=custom-filter) can be used.
24 |
25 | #### Operators
26 |
27 | Type | Description
28 | ---- | -----------
29 | ct | String contains
30 | sw | Starts with
31 | ew | Ends with
32 | eq | Equals
33 | gt | Greater than
34 | gte| Greater than or equalTo
35 | lt | Lesser than
36 | lte | Lesser than or equalTo
37 | in | In array
38 | bt | Between
39 |
40 | #### Build filter
41 |
42 | The way a filter should be formed is:
43 |
44 | ```console
45 | {base_url}/users?filter[columnName][operator][not]=value
46 | ```
47 |
48 | Another available parameter is `filterByOr`, `search` and `searchByOr`.
49 |
50 | * **columnName** - (Required) - Name of column you want to filter, for relationships use `dots`.
51 | * **operator** - (Optional | Default: `eq`) Type of operator you want to use.
52 | * **not** - (Optional | Default: `false`) Negate the filter (Accepted values: not|yes|true|1).
53 |
54 | #### Example filters
55 |
56 | Filter all users whose id start with `1000`.
57 |
58 | ```console
59 | {base_url}/users?filter[name][sw]=1000
60 | ```
61 |
62 | Filter all books whose author is `Gentrit`.
63 |
64 | ```console
65 | {base_url}/users?filter[author.name]=Gentrit
66 | ```
67 |
68 | Filter all users whose name start with `Gentrit` or ends with `Abazi`.
69 |
70 | ```console
71 | {base_url}/users?filterByOr[name][sw]=Gentrit&filterByOr[name][ew]=Abazi
72 | ```
73 |
74 | [See other ways for filtering](filters_old.md)
--------------------------------------------------------------------------------
/docs/filters_old.md:
--------------------------------------------------------------------------------
1 | # Filtering
2 |
3 | Filters should be defined as an array of filter groups.
4 |
5 | **Filter groups**
6 |
7 | Property | Value type | Description
8 | -------- | ---------- | -----------
9 | or | boolean | Should the filters in this group be grouped by logical OR or AND operator
10 | filters | array | Array of filters (see syntax below)
11 |
12 | **Filters**
13 |
14 | Property | Value type | Description
15 | -------- | ---------- | -----------
16 | column | string | The property of the model to filter by (can also be custom filter)
17 | value | mixed | The value to search for
18 | operator | string | The filter operator to use (see different types below)
19 | not | boolean | Negate the filter
20 |
21 | **Operators**
22 |
23 | Type | Description
24 | ---- | -----------
25 | ct | String contains
26 | sw | Starts with
27 | ew | Ends with
28 | eq | Equals
29 | gt | Greater than
30 | gte| Greater than or equalTo
31 | lt | Lesser than
32 | lte | Lesser than or equalTo
33 | in | In array
34 | bt | Between
35 |
36 | #### Example filters
37 |
38 | Filter all users whose name start with “Gentrit” or ends with “Abazi”.
39 |
40 | ```SELECT * FROM `users` WHERE name LIKE "Gentrit%" OR name LIKE "%Abazi"```
41 |
42 | ```json
43 | {
44 | "filter_groups": [
45 | {
46 | "or": true,
47 | "filters": [
48 | {
49 | "column": "name",
50 | "operator": "sw",
51 | "value": "Gentrit"
52 | },
53 | {
54 | "column": "name",
55 | "operator": "ew",
56 | "value": "Abazi"
57 | }
58 | ]
59 | }
60 | ]
61 | }
62 | ```
63 |
64 | Filter all users whose name start with “A” and which were born between years 1990 and 2000.
65 |
66 | ```SELECT * FROM `users` WHERE (name LIKE "A%") AND (`birth_year` >= 1990 and `birth_year` <= 2000)```
67 |
68 | ```json
69 | {
70 | "filter_groups": [
71 | {
72 | "filters": [
73 | {
74 | "column": "name",
75 | "operator": "sw",
76 | "value": "A"
77 | }
78 | ]
79 | },
80 | {
81 | "filters": [
82 | {
83 | "column": "birth_year",
84 | "value": 1990,
85 | "operator": "gte"
86 | },
87 | {
88 | "column": "birth_year",
89 | "value": 2000,
90 | "operator": "lte"
91 | }
92 | ]
93 | }
94 | ]
95 | }
96 | ```
97 |
98 | Filter all books whose author is Gentrit.
99 | ```json
100 | {
101 | "filter_groups": [
102 | {
103 | "filters": [
104 | {
105 | "column": "author.name",
106 | "operator": "eq",
107 | "value": "Gentrit"
108 | }
109 | ]
110 | }
111 | ]
112 | }
113 | ```
--------------------------------------------------------------------------------
/docs/including_relationships.md:
--------------------------------------------------------------------------------
1 | # Including relationships
2 |
3 | The `include` query parameter will load any Eloquent relation on the resulting models.
4 |
5 | # Basic usage
6 |
7 | The following query parameter will include the `logs` relation:
8 |
9 | ```console
10 | {base_url}/users?include=logs
11 | ```
12 |
13 | Users will have all their their `logs` related models loaded.
14 |
15 | # Load multiple
16 |
17 | You can load multiple relationships by separating them with a semicolon:
18 |
19 | ```console
20 | {base_url}/users?include=logs;tasks
21 | ```
22 |
23 | # Load nested
24 |
25 | You can load nested relationships using the dot `.` notation:
26 |
27 | ```console
28 | {base_url}/users?include=logs.causer
29 | ```
30 |
31 | # Counting related relations
32 |
33 | If you want to count the number of results from a relationship without actually loading them you may use the `withCount` query parameter, which will place a {relation}_count column on your resulting models.
34 |
35 | ```console
36 | {base_url}/users?withCount=comments
37 | ```
38 |
39 | # Querying Relationship Existence
40 |
41 | Imagine you want to retrieve all blog posts that have at least one comment.
42 | You can do this by passing `has` paramter in query:
43 |
44 | ```console
45 | {base_url}/posts?has=comments
46 | ```
47 |
48 | Nested `has` statements may also be constructed using "dot" notation. For example, you may retrieve all posts that have at least one `comment` and `vote`:
49 |
50 | ```console
51 | {base_url}/posts?has=comments.votes
52 | ```
53 |
54 | # Querying Relationship Absence
55 |
56 | When accessing the records for a model, you may wish to limit your results based on the absence of a relationship. For example, imagine you want to retrieve all blog posts that don't have any `comments`. To do so, you may pass `doesntHave` paramter in query:
57 |
58 | ```console
59 | {base_url}/posts?doesntHave=comments
60 | ```
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Larapi
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/docs/installation.md:
--------------------------------------------------------------------------------
1 | ## Installation
2 |
3 | You can install the package via composer:
4 |
5 | ```bash
6 | composer require one2tek/larapi
7 | ```
--------------------------------------------------------------------------------
/docs/pagination.md:
--------------------------------------------------------------------------------
1 | # Pagination
2 |
3 | Two parameters are available: `limit` and `page`. limit will determine the number of records per page and page will determine the current page.
4 |
5 | # Usage
6 |
7 | ```console
8 | {base_url}/books?limit=10&page=3
9 | ```
10 |
11 | Will return books number 30-40.
--------------------------------------------------------------------------------
/docs/repository.md:
--------------------------------------------------------------------------------
1 | # Repository
2 |
3 | `Larapi` included Repository class for requesting entities from your database.
4 |
5 | # Create repository
6 |
7 | To create repository is easy you need just to define own model:
8 |
9 | ```php
10 | $value1, $column2 => $value2]`)
69 |
70 | ### getWhereIn (string $column, array $values, array $options = [])
71 |
72 | Get `User` rows where `$column` can be any of the values given by `$values`
73 |
74 | ### getLatest (array $options = [])
75 |
76 | Get the most recent `User`
77 |
78 | ### getLatestWhere (string $column, mixed $value, array $options = [])
79 |
80 | Get the most recent `User` where `$column=$value`
81 |
82 | ### delete ($id)
83 |
84 | Delete `User` rows by primary key
85 |
86 | ### deleteWhere ($column, $value)
87 |
88 | Delete `User` rows where `$column=$value`
89 |
90 | ### deleteWhereArray (array $clauses)
91 |
92 | Delete `User` rows by multiple where clauses (`[$column1 => $value1, $column2 => $value2]`)
--------------------------------------------------------------------------------
/docs/scopes.md:
--------------------------------------------------------------------------------
1 | # Scopes
2 |
3 | Sometimes more advanced filtering options are necessary. This is where scope filters, callback filters and custom filters come in handy.
4 |
5 | # Usage
6 |
7 | The following query parameter will add the `popular` scope:
8 |
9 | ```console
10 | {base_url}/users?scope=popular
11 | ```
12 |
13 | # Remove Global Scopes
14 |
15 | Global scopes allow you to add constraints to all queries for a given model.
16 | What if we want to remove it ?
17 |
18 | The following query parameter will remove the `delivered` global scope:
19 |
20 | ```console
21 | {base_url}/books?excludeGlobalScopes=delivered
22 | ```
23 |
24 | # Select multiple
25 |
26 | You can select multiple scopes separating them with a comma:
27 |
28 | ```console
29 | {base_url}/users?scope=popular,famous
30 | ```
--------------------------------------------------------------------------------
/docs/selecting_fields.md:
--------------------------------------------------------------------------------
1 | # Selecting fields
2 |
3 | Sometimes you'll want to fetch only a couple fields to reduce the overall size of your SQL query. This can be done by specifying some fields in request by query parameter.
4 |
5 | # Basic usage
6 |
7 | The following example fetches only the users' id and name:
8 |
9 | ```console
10 | {base_url}/users?select=id,name
11 | ```
12 |
13 | The SQL query will look like this:
14 |
15 | ```sql
16 | `SELECT "id", "name" FROM "users"`
17 | ```
18 |
19 | # Selecting fields for included relations
20 |
21 | The following example fetches only the authors' id and name:
22 |
23 | ```console
24 | {base_url}/books?include=author:id,name.
25 | ```
--------------------------------------------------------------------------------
/docs/sorting.md:
--------------------------------------------------------------------------------
1 | # Sorting
2 |
3 | The `sortByAsc` and `sortByDesc` query parameters are used to determine by which property the results collection will be ordered.
4 |
5 | For more advanced use cases, [custom sorts](advanced_usage?id=custom-sort) can be used.
6 |
7 | # Usage
8 |
9 | The following query parameter `sortByAsc` will sort results by from the lowest value to the highest value:
10 |
11 | ```console
12 | {base_url}/books?sortByAsc=id
13 | ```
14 |
15 | The following query parameter `sortByDesc` will sort results by from the highest value to the lowest value:
16 |
17 | ```console
18 | {base_url}/books?sortByDesc=id
19 | ```
20 |
21 | # Sort multiple columns
22 |
23 | You can sort multiple columns separating them with a comma:
24 |
25 | ```console
26 | {base_url}/books?sortByDesc=id,name
27 | ```
28 |
29 | # Order by random
30 |
31 | You can also sort data randomly:
32 |
33 | ```console
34 | {base_url}/books?orderByRandom=1
35 | ```
--------------------------------------------------------------------------------
/src/Config/larapi.php:
--------------------------------------------------------------------------------
1 | 'api',
6 |
7 | 'exceptions_formatters' => [
8 | Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException::class => one2tek\larapi\ExceptionsFormatters\UnprocessableEntityHttpExceptionFormatter::class,
9 | Throwable::class => one2tek\larapi\ExceptionsFormatters\ExceptionFormatter::class
10 | ]
11 |
12 | ];
13 |
--------------------------------------------------------------------------------
/src/Console/ComponentMakeCommand.php:
--------------------------------------------------------------------------------
1 | [
39 | 'model'
40 | ],
41 | 'Controllers' => [
42 | 'controller'
43 | ],
44 | 'Services' => [
45 | 'service'
46 | ],
47 | 'Repositories' => [
48 | 'repository'
49 | ],
50 | 'Events' => [
51 | 'WasCreated',
52 | 'WasUpdated',
53 | 'WasDeleted'
54 | ],
55 | 'Exceptions' => [
56 | 'NotFoundException'
57 | ],
58 | 'Requests' => [
59 | 'CreateRequest',
60 | 'UpdateRequest'
61 | ]
62 | ];
63 |
64 | /**
65 | * Create a new command instance.
66 | *
67 | * @param Filesystem $files
68 | * @param Composer $composer
69 | */
70 | public function __construct(Filesystem $files)
71 | {
72 | parent::__construct();
73 |
74 | $this->files = $files;
75 | }
76 |
77 | /**
78 | * Execute the console command.
79 | *
80 | * @return mixed
81 | */
82 | public function fire()
83 | {
84 | $this->makeDirectory(base_path(). '/'. $this->getModulesFolder(). '/'. $this->argument('parent'));
85 |
86 | foreach ($this->fileTypes as $key => $fileType) {
87 | $this->makeSubDirectories(base_path(). '/'. $this->getModulesFolder(). '/'. $this->argument('parent'), $key);
88 |
89 | foreach ($fileType as $file) {
90 | $this->makeFile($key, $file);
91 | }
92 | }
93 | }
94 |
95 | /**
96 | * Make file.
97 | */
98 | protected function makeFile($dir, $fileName)
99 | {
100 | $stubFile = strtolower(rtrim($dir. '/'. $fileName, 's'));
101 | $name = $this->argument('name');
102 | $myFileName = $fileName;
103 | if ($myFileName == 'model') {
104 | $myFileName = '';
105 | }
106 | $filePath = base_path(). '/'. $this->getModulesFolder(). '/'. $this->argument('parent'). '/' . $dir. '/'. $name. ucfirst($myFileName). '.php';
107 |
108 | if (!$this->files->exists($filePath)) {
109 | $class = $this->buildFile($name, $stubFile, $dir. '/'. $fileName, $dir, $fileName);
110 | $this->files->put($filePath, $class);
111 | }
112 | }
113 |
114 | /**
115 | * Build the directory for the class if necessary.
116 | *
117 | * @param string $path
118 | *
119 | * @return string
120 | */
121 | protected function makeDirectory($path)
122 | {
123 | if (!$this->files->isDirectory($path)) {
124 | $this->files->makeDirectory($path, 0777, true, true);
125 | }
126 | }
127 |
128 | /**
129 | * Build the directory for the class if necessary.
130 | *
131 | * @param string $path
132 | *
133 | * @return string
134 | */
135 | protected function makeSubDirectories($path, $fileType)
136 | {
137 | if ($this->files->isDirectory($path)) {
138 | $this->makeDirectory($path.'/'.$fileType.'/');
139 | }
140 | }
141 |
142 | /**
143 | * Build file.
144 | */
145 | protected function buildFile($name, $type, $fileName, $dir, $shortFileName)
146 | {
147 | $stub = $this->files->get($this->getStub($type));
148 | return $this->replaceNamespace($stub, $fileName, $dir)->replaceClass($stub, $name, $fileName, $shortFileName);
149 | }
150 |
151 | /**
152 | * Replace the class name for the given stub.
153 | *
154 | * @param string $stub
155 | * @param string $name
156 | *
157 | * @return string
158 | */
159 | protected function replaceClass($stub, $name, $fileName, $shortFileName)
160 | {
161 | $class = str_replace($this->getNamespace($name). '\\', '', $name);
162 |
163 | $stub = str_replace('DummyVariable', $class, $stub);
164 | $stub = str_replace('dummyVariable', lcfirst($class), $stub);
165 | $stub = str_replace('dummyvariable', strtolower($class), $stub);
166 | $stub = str_replace('Singular_Dummy_Variable', Str::singular(strtolower($class)), $stub);
167 | $stub = str_replace('Plural_Dummy_Variable', Str::plural(strtolower($class)), $stub);
168 | $stub = str_replace('DummyName', ucfirst($name), $stub);
169 |
170 | return str_replace('DummyClass', $this->argument('name'). ucfirst($shortFileName), $stub);
171 | }
172 |
173 | /**
174 | * Get the stub file for the generator.
175 | *
176 | * @return string
177 | */
178 | protected function getStub($type)
179 | {
180 | return __DIR__. '/Stubs/'. $type. '.stub';
181 | }
182 |
183 | /**
184 | * Replace the namespace for the given stub.
185 | *
186 | * @param string $stub
187 | * @param string $name
188 | *
189 | * @return $this
190 | */
191 | protected function replaceNamespace(&$stub, $fileName, $dir)
192 | {
193 | $stub = str_replace('DummyNamespace', 'Api\\'. $this->argument('parent'). '\\'. $dir, $stub);
194 | $stub = str_replace('DummyPath', 'Api\\'.$this->argument('parent'), $stub);
195 |
196 | return $this;
197 | }
198 |
199 | /**
200 | * Get the full namespace for a given class, without the class name.
201 | *
202 | * @param string $name
203 | *
204 | * @return string
205 | */
206 | protected function getNamespace($name)
207 | {
208 | return trim(implode('\\', array_slice(explode('\\', $name), 0, -1)), '\\');
209 | }
210 |
211 | /**
212 | * Execute the console command.
213 | *
214 | * @return mixed
215 | */
216 | public function handle()
217 | {
218 | $this->fire();
219 |
220 | $routePath = $this->argument('name');
221 | $routePath = strtolower($routePath);
222 | $routePath = Str::plural($routePath);
223 | $controllerName = $this->argument('name');
224 | $controllerName = ucfirst($controllerName);
225 |
226 | $this->info(
227 | "Routes: ". PHP_EOL.
228 | "$". "router->get('/". $routePath. "', '". $controllerName. "Controller@getAll');". PHP_EOL.
229 | "$". "router->get('/". $routePath. "/{id}', '". $controllerName. "Controller@getById');". PHP_EOL.
230 | "$". "router->post('/". $routePath. "', '". $controllerName. "Controller@create');". PHP_EOL.
231 | "$". "router->put('/". $routePath. "/{id}', '". $controllerName. "Controller@update');". PHP_EOL.
232 | "$". "router->delete('/". $routePath. "/{id}', '". $controllerName. "Controller@delete');". PHP_EOL
233 | );
234 | }
235 |
236 | private function getModulesFolder()
237 | {
238 | return config('larapi.modules_folder');
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/Console/Stubs/controllers/controller.stub:
--------------------------------------------------------------------------------
1 | dummyVariableService = $dummyVariableService;
18 | }
19 |
20 | public function getAll()
21 | {
22 | $resourceOptions = $this->parseResourceOptions();
23 |
24 | $sendData = $this->dummyVariableService->getAll($resourceOptions);
25 |
26 | return $this->response($sendData);
27 | }
28 |
29 | public function getById($dummyVariableId)
30 | {
31 | $resourceOptions = $this->parseResourceOptions();
32 |
33 | $sendData['dummyvariable'] = $this->dummyVariableService->getById($dummyVariableId, $resourceOptions);
34 |
35 | return $this->response($sendData);
36 | }
37 |
38 | public function create(DummyVariableCreateRequest $request)
39 | {
40 | $data = $request->validated();
41 |
42 | $sendData['dummyvariable'] = $this->dummyVariableService->create($data);
43 |
44 | return $this->response($sendData, 201);
45 | }
46 |
47 | public function update($dummyVariableId, DummyVariableUpdateRequest $request)
48 | {
49 | $data = $request->validated();
50 |
51 | $sendData['dummyvariable'] = $this->dummyVariableService->update($dummyVariableId, $data);
52 |
53 | return $this->response($sendData);
54 | }
55 |
56 | public function delete($dummyVariableId)
57 | {
58 | $this->dummyVariableService->delete($dummyVariableId);
59 |
60 | return $this->response(null, 204);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Console/Stubs/events/wascreated.stub:
--------------------------------------------------------------------------------
1 | dummyVariable = $dummyVariable;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Console/Stubs/events/wasdeleted.stub:
--------------------------------------------------------------------------------
1 | dummyVariable = $dummyVariable;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Console/Stubs/events/wasupdated.stub:
--------------------------------------------------------------------------------
1 | dummyVariable = $dummyVariable;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Console/Stubs/exceptions/notfoundexception.stub:
--------------------------------------------------------------------------------
1 | getModel();
18 |
19 | $dummyVariable->fill($data);
20 | $dummyVariable->save();
21 |
22 | return $dummyVariable;
23 | }
24 |
25 | public function update(DummyVariable $dummyVariable, array $data)
26 | {
27 | $dummyVariable->fill($data);
28 |
29 | $dummyVariable->save();
30 |
31 | return $dummyVariable;
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Console/Stubs/requests/createrequest.stub:
--------------------------------------------------------------------------------
1 | 'required',
13 | 'field2' => 'required'
14 | ];
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Console/Stubs/requests/updaterequest.stub:
--------------------------------------------------------------------------------
1 | 'nullable',
13 | 'field2' => 'nullable'
14 | ];
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Console/Stubs/services/service.stub:
--------------------------------------------------------------------------------
1 | dummyVariableRepository = $dummyVariableRepository;
22 | $this->dispatcher = $dispatcher;
23 | }
24 |
25 | public function getAll($options = [])
26 | {
27 | return $this->dummyVariableRepository->getWithCount($options);
28 | }
29 |
30 | public function getById($dummyVariableId, array $options = [])
31 | {
32 | $dummyVariable = $this->getRequestedDummyVariable($dummyVariableId, $options);
33 |
34 | return $dummyVariable;
35 | }
36 |
37 | public function create($data)
38 | {
39 | $dummyVariable = $this->dummyVariableRepository->create($data);
40 |
41 | $this->dispatcher->dispatch(new DummyVariableWasCreated($dummyVariable));
42 |
43 | return $dummyVariable;
44 | }
45 |
46 | public function update($dummyVariableId, array $data)
47 | {
48 | $dummyVariable = $this->getRequestedDummyVariable($dummyVariableId);
49 |
50 | $this->dummyVariableRepository->update($dummyVariable, $data);
51 |
52 | $this->dispatcher->dispatch(new DummyVariableWasUpdated($dummyVariable));
53 |
54 | return $dummyVariable;
55 | }
56 |
57 | public function delete($dummyVariableId)
58 | {
59 | $dummyVariable = $this->getRequestedDummyVariable($dummyVariableId);
60 |
61 | $this->dummyVariableRepository->delete($dummyVariableId);
62 |
63 | $this->dispatcher->dispatch(new DummyVariableWasDeleted($dummyVariable));
64 | }
65 |
66 | public function getRequestedDummyVariable($dummyVariableId, $options = [])
67 | {
68 | $dummyVariable = $this->dummyVariableRepository->getById($dummyVariableId, $options);
69 |
70 | if (is_null($dummyVariable)) {
71 | throw new DummyVariableNotFoundException;
72 | }
73 |
74 | return $dummyVariable;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Controllers/LaravelController.php:
--------------------------------------------------------------------------------
1 | toArray();
33 | }
34 |
35 | return new JsonResponse($data, $statusCode, $headers);
36 | }
37 |
38 | /**
39 | * Parse sort by asc.
40 | *
41 | * @param string $sortByAsc
42 | * @return array
43 | */
44 | protected function parseSortByAsc($sortByAsc)
45 | {
46 | if (is_null($sortByAsc)) {
47 | return [];
48 | }
49 |
50 | return explode(',', $sortByAsc);
51 | }
52 |
53 | /**
54 | * Parse sort by desc.
55 | *
56 | * @param string $sortByDesc
57 | * @return array
58 | */
59 | protected function parseSortByDesc($sortByDesc)
60 | {
61 | if (is_null($sortByDesc)) {
62 | return [];
63 | }
64 |
65 | return explode(',', $sortByDesc);
66 | }
67 |
68 | /**
69 | * Parse selects.
70 | *
71 | * @param string $selects
72 | * @return array
73 | */
74 | protected function parseSelects($selects)
75 | {
76 | if (is_null($selects)) {
77 | return [];
78 | }
79 |
80 | return explode(',', $selects);
81 | }
82 |
83 | /**
84 | * Parse appends.
85 | *
86 | * @param string $appends
87 | * @return array
88 | */
89 | protected function parseAppends($appends)
90 | {
91 | if (is_null($appends)) {
92 | return [];
93 | }
94 |
95 | return explode(',', $appends);
96 | }
97 |
98 | /**
99 | * Parse includes.
100 | *
101 | * @param string $includes
102 | * @return array
103 | */
104 | protected function parseIncludes($includes)
105 | {
106 | if (is_null($includes)) {
107 | return [];
108 | }
109 |
110 | return explode(';', $includes);
111 | }
112 |
113 | /**
114 | * Parse with counts into resource.
115 | *
116 | * @param string $withCounts
117 | * @return array
118 | */
119 | protected function parseWithCounts($withCounts)
120 | {
121 | if (is_null($withCounts)) {
122 | return [];
123 | }
124 |
125 | return explode(',', $withCounts);
126 | }
127 |
128 | /**
129 | * Parse has into resource.
130 | *
131 | * @param string $has
132 | * @return array
133 | */
134 | protected function parseHas($has)
135 | {
136 | if (is_null($has)) {
137 | return [];
138 | }
139 |
140 | return explode(',', $has);
141 | }
142 |
143 | /**
144 | * Parse doesnt have into resource.
145 | *
146 | * @param string $has
147 | * @return array
148 | */
149 | protected function parseDoesntHave($doesntHave)
150 | {
151 | if (is_null($doesntHave)) {
152 | return [];
153 | }
154 |
155 | return explode(',', $doesntHave);
156 | }
157 |
158 | /**
159 | * Parse exclude global scopes into resource.
160 | *
161 | * @param string $excludeGlobalScopes
162 | * @return array
163 | */
164 | protected function parseExcludeGlobalScopes($excludeGlobalScopes)
165 | {
166 | if (is_null($excludeGlobalScopes)) {
167 | return [];
168 | }
169 |
170 | return explode(',', $excludeGlobalScopes);
171 | }
172 |
173 | /**
174 | * Parse scopes into resource.
175 | *
176 | * @param string $scopes
177 | * @return array
178 | */
179 | protected function parseScopes($scopes)
180 | {
181 | if (is_null($scopes)) {
182 | return [];
183 | }
184 |
185 | return explode(',', $scopes);
186 | }
187 |
188 | /**
189 | * Parse order by random.
190 | *
191 | * @param string $value
192 | * @return bool
193 | */
194 | protected function parseOrderByRandom($value)
195 | {
196 | $value = $value ? filter_var($value, FILTER_VALIDATE_BOOLEAN) : false;
197 |
198 | return $value;
199 | }
200 |
201 | /**
202 | * Parse with trashed.
203 | *
204 | * @param string $value
205 | * @return bool
206 | */
207 | protected function parseWithTrashed($value)
208 | {
209 | $value = $value ? filter_var($value, FILTER_VALIDATE_BOOLEAN) : false;
210 |
211 | return $value;
212 | }
213 |
214 | /**
215 | * Parse filters.
216 | *
217 | * @param array $filter
218 | * @param bool $or
219 | * @return array
220 | */
221 | protected function parseFilters(array $filters, $or = false, $defaultOperator = 'eq')
222 | {
223 | if (!count($filters)) {
224 | return [];
225 | }
226 |
227 | $parsedFilters = [];
228 | $allowedOperators = [
229 | 'ct',
230 | 'sw',
231 | 'ew',
232 | 'eq',
233 | 'gt',
234 | 'gte',
235 | 'lte',
236 | 'lt',
237 | 'in',
238 | 'bt',
239 | ];
240 |
241 | foreach ($filters as $column => $part) {
242 | $arrayCountValues = is_array($part) ? count($part, COUNT_RECURSIVE) : 0;
243 | $operator = $defaultOperator;
244 | $not = false;
245 | $value = (is_array($part)) ? null : $part;
246 | if (is_array($part)) {
247 | $operator = key($part) ?? $defaultOperator;
248 | }
249 |
250 | if ($arrayCountValues > 2) {
251 | throw new LarapiException('Filter is not well formed.');
252 | }
253 |
254 | if (!in_array($operator, $allowedOperators)) {
255 | throw new LarapiException('Operator '. $operator. ' is not supported.');
256 | }
257 |
258 | if (is_array($part)) {
259 | $not = ($arrayCountValues == 2) ? key($part[$operator]) : false;
260 | $not = $not === 'not' ? true : $not;
261 | $not = $not ? filter_var($not, FILTER_VALIDATE_BOOLEAN) : false;
262 |
263 | $value = $part[(string)key($part)];
264 | $value = is_array($value) ? array_shift($value) : $value;
265 | $value = (Str::contains($value, ',')) ? explode(',', $value) : $value;
266 | }
267 |
268 | $parsedFilters[] = [
269 | 'column' => $column,
270 | 'operator' => $operator,
271 | 'not' => $not,
272 | 'value' => $value
273 | ];
274 | }
275 |
276 | return [
277 | [
278 | 'filters' => $parsedFilters,
279 | 'or' => $or
280 | ]
281 | ];
282 | }
283 |
284 | /**
285 | * Parse filter group strings into filters.
286 | *
287 | * @param array $filter_groups
288 | * @return array
289 | */
290 | protected function parseFilterGroups(array $filter_groups)
291 | {
292 | $return = [];
293 |
294 | $keysNeeded = ['column', 'operator', 'value'];
295 | foreach ($filter_groups as $group) {
296 | if (!array_key_exists('filters', $group)) {
297 | throw new LarapiException('Filter group does not have the \'filters\' key.');
298 | }
299 | $filters = array_map(function ($filter) use ($keysNeeded) {
300 | if (count(array_intersect_key(array_flip($keysNeeded), $filter)) != count($keysNeeded)) {
301 | throw new LarapiException('You need to pass column, operator and value in filters.');
302 | }
303 |
304 | if (($filter['operator'] == 'in') && (!is_array($filter['value']))) {
305 | throw new LarapiException('You need to make value as array because you are using \'in\' operator.');
306 | }
307 |
308 | if (($filter['operator'] == 'bt') && (!is_array($filter['value']))) {
309 | throw new LarapiException('You need to make value as array because you are using \'bt\' operator.');
310 | }
311 |
312 | if (!isset($filter['not'])) {
313 | $filter['not'] = false;
314 | }
315 | return $filter;
316 | }, $group['filters']);
317 |
318 | $return[] = [
319 | 'filters' => $filters,
320 | 'or' => isset($group['or']) ? $group['or'] : false
321 | ];
322 | }
323 |
324 | return $return;
325 | }
326 |
327 | /**
328 | * Parse GET parameters into resource options.
329 | *
330 | * @return array
331 | */
332 | protected function parseResourceOptions($request = null)
333 | {
334 | if ($request === null) {
335 | $request = request();
336 | }
337 |
338 | $this->defaults = array_merge([
339 | 'selects' => null,
340 | 'select' => null,
341 | 'includes' => null,
342 | 'include' => null,
343 | 'withCount' => null,
344 | 'has' => null,
345 | 'doesntHave' => null,
346 | 'excludeGlobalScopes' => null,
347 | 'scope' => null,
348 | 'limit' => null,
349 | 'page' => null,
350 | 'filter_groups' => [],
351 | 'filterByAnd' => [],
352 | 'filterByOr' => [],
353 | 'searchByAnd' => [],
354 | 'searchByOr' => [],
355 | 'append' => null,
356 | 'sortByDesc' => null,
357 | 'sortByAsc' => null,
358 | 'orderByRandom' => false,
359 | 'withTrashed' => false
360 | ], $this->defaults);
361 |
362 | $selects = $this->parseSelects($request->get('selects', $this->defaults['selects']));
363 | $select = $this->parseSelects($request->get('select', $this->defaults['select']));
364 | $includes = $this->parseIncludes($request->get('includes', $this->defaults['includes']));
365 | $include = $this->parseIncludes($request->get('include', $this->defaults['include']));
366 | $withCount = $this->parseWithCounts($request->get('withCount', $this->defaults['withCount']));
367 | $has = $this->parseHas($request->get('has', $this->defaults['has']));
368 | $doesntHave = $this->parseDoesntHave($request->get('doesntHave', $this->defaults['doesntHave']));
369 | $excludeGlobalScopes = $this->parseExcludeGlobalScopes($request->get('excludeGlobalScopes', $this->defaults['excludeGlobalScopes']));
370 | $scope = $this->parseScopes($request->get('scope', $this->defaults['scope']));
371 | $limit = $request->get('limit', $this->defaults['limit']);
372 | $page = $request->get('page', $this->defaults['page']);
373 | $filter_groups = $this->parseFilterGroups($request->get('filter_groups', $this->defaults['filter_groups']));
374 | $filterByAnd = $this->parseFilters($request->get('filter', $this->defaults['filterByAnd']));
375 | $filterByOr = $this->parseFilters($request->get('filterByOr', $this->defaults['filterByOr']), true);
376 | $searchByAnd = $this->parseFilters($request->get('search', $this->defaults['searchByAnd']), false, 'ct');
377 | $searchByOr = $this->parseFilters($request->get('searchByOr', $this->defaults['searchByOr']), true, 'ct');
378 | $append = $this->parseAppends($request->get('append', $this->defaults['append']));
379 | $sortByDesc = $this->parseSortByDesc($request->get('sortByDesc', $this->defaults['sortByDesc']));
380 | $sortByAsc = $this->parseSortByAsc($request->get('sortByAsc', $this->defaults['sortByAsc']));
381 | $orderByRandom = $this->parseOrderByRandom($request->get('orderByRandom', $this->defaults['orderByRandom']));
382 | $withTrashed = $this->parseWithTrashed($request->get('withTrashed', $this->defaults['withTrashed']));
383 |
384 | $data = [
385 | 'select' => $select,
386 | 'selects' => $selects,
387 | 'includes' => $includes,
388 | 'include' => $include,
389 | 'withCount' => $withCount,
390 | 'has' => $has,
391 | 'doesntHave' => $doesntHave,
392 | 'excludeGlobalScopes' => $excludeGlobalScopes,
393 | 'scope' => $scope,
394 | 'limit' => $limit,
395 | 'page' => $page,
396 | 'filter_groups' => $filter_groups,
397 | 'filterByAnd' => $filterByAnd,
398 | 'filterByOr' => $filterByOr,
399 | 'searchByAnd' => $searchByAnd,
400 | 'searchByOr' => $searchByOr,
401 | 'append' => $append,
402 | 'sortByDesc' => $sortByDesc,
403 | 'sortByAsc' => $sortByAsc,
404 | 'orderByRandom' => $orderByRandom,
405 | 'withTrashed' => $withTrashed
406 | ];
407 |
408 | $this->validateResourceOptions($data);
409 |
410 | return $data;
411 | }
412 |
413 | /**
414 | * Validate resource options.
415 | */
416 | private function validateResourceOptions(array $data)
417 | {
418 | if ($data['page'] !== null && $data['limit'] === null) {
419 | throw new LarapiException('Cannot use page option without limit option.');
420 | }
421 |
422 | if (!is_null($data['page'])) {
423 | if (!is_int((int)$data['page'])) {
424 | throw new LarapiException('Page need to be int.');
425 | }
426 |
427 | if ($data['page'] == 0) {
428 | throw new LarapiException('Page need to start from 1.');
429 | }
430 | }
431 |
432 | if (!is_null($data['limit'])) {
433 | if (!is_int((int)$data['limit'])) {
434 | throw new LarapiException('Limit need to be int.');
435 | }
436 | }
437 | }
438 | }
439 |
--------------------------------------------------------------------------------
/src/Database/EloquentBuilderTrait.php:
--------------------------------------------------------------------------------
1 | applySelects($queryBuilder, $selects);
21 | }
22 |
23 | if (isset($select) && $select) {
24 | $this->applySelects($queryBuilder, $select);
25 | }
26 |
27 | if (isset($includes)) {
28 | $this->applyWith($queryBuilder, $includes);
29 | }
30 |
31 | if (isset($include)) {
32 | $this->applyWith($queryBuilder, $include);
33 | }
34 |
35 | if (isset($withCount)) {
36 | $this->applyWithCount($queryBuilder, $withCount);
37 | }
38 |
39 | if (isset($has)) {
40 | $this->applyHas($queryBuilder, $has);
41 | }
42 |
43 | if (isset($doesntHave)) {
44 | $this->applyDoesntHave($queryBuilder, $doesntHave);
45 | }
46 |
47 | if (isset($excludeGlobalScopes)) {
48 | $this->applyWithouGlobalScopes($queryBuilder, $excludeGlobalScopes);
49 | }
50 |
51 | if (isset($filter_groups)) {
52 | $this->applyFilterGroups($queryBuilder, $filter_groups);
53 | }
54 |
55 | if (isset($filterByAnd)) {
56 | $this->applyFilterGroups($queryBuilder, $filterByAnd);
57 | }
58 |
59 | if (isset($filterByOr)) {
60 | $this->applyFilterGroups($queryBuilder, $filterByOr);
61 | }
62 |
63 | if (isset($searchByAnd)) {
64 | $this->applyFilterGroups($queryBuilder, $searchByAnd);
65 | }
66 |
67 | if (isset($searchByOr)) {
68 | $this->applyFilterGroups($queryBuilder, $searchByOr);
69 | }
70 |
71 | if (isset($limit)) {
72 | $queryBuilder->limit($limit);
73 | }
74 |
75 | if (isset($page)) {
76 | $queryBuilder->offset(($page > 0 ? $page - 1 : 0) * $limit);
77 | }
78 |
79 | if (isset($sortByAsc)) {
80 | $this->applySortByAsc($queryBuilder, $sortByAsc);
81 | }
82 |
83 | if (isset($sortByDesc)) {
84 | $this->applySortByDesc($queryBuilder, $sortByDesc);
85 | }
86 |
87 | if (isset($orderByRandom) && $orderByRandom) {
88 | $this->applyOrderByRandom($queryBuilder, $orderByRandom);
89 | }
90 |
91 | if( (isset($withTrashed) && $withTrashed)) {
92 | $this->applyWithTrashed($queryBuilder);
93 | }
94 |
95 | return $queryBuilder;
96 | }
97 |
98 | protected function applyFilterGroups(Builder $queryBuilder, array $filterGroups = [])
99 | {
100 | foreach ($filterGroups as $groups) {
101 | $or = $groups['or'];
102 | $filters = $groups['filters'];
103 |
104 | $queryBuilder->where(function (Builder $query) use ($filters, $or) {
105 | foreach ($filters as $filter) {
106 | $this->applyFilter($query, $filter, $or);
107 | }
108 | });
109 | }
110 | }
111 |
112 | protected function applySortByAsc(Builder $queryBuilder, array $sortByAsc = [])
113 | {
114 | foreach ($sortByAsc as $sortByAscKey) {
115 | $customSortMethod = $this->hasCustomMethod('sort', $sortByAscKey);
116 | if ($customSortMethod) {
117 | call_user_func([$this, $customSortMethod], $queryBuilder, 'ASC');
118 | } else {
119 | $queryBuilder->orderBy($sortByAscKey);
120 | }
121 | }
122 | }
123 |
124 | protected function applySortByDesc(Builder $queryBuilder, array $sortByDesc = [])
125 | {
126 | foreach ($sortByDesc as $sortByDescKey) {
127 | $customSortMethod = $this->hasCustomMethod('sort', $sortByDescKey);
128 | if ($customSortMethod) {
129 | call_user_func([$this, $customSortMethod], $queryBuilder, 'DESC');
130 | } else {
131 | $queryBuilder->orderByDesc($sortByDescKey);
132 | }
133 | }
134 | }
135 |
136 | protected function applySelects(Builder $queryBuilder, array $fields = [])
137 | {
138 | $queryBuilder->select($fields);
139 | }
140 |
141 | protected function applyWith(Builder $queryBuilder, array $withs = [])
142 | {
143 | $queryBuilder->with($withs);
144 | }
145 |
146 | protected function applyWithCount(Builder $queryBuilder, array $withCount = [])
147 | {
148 | $queryBuilder->withCount($withCount);
149 | }
150 |
151 | protected function applyOrderByRandom(Builder $queryBuilder, bool $orderByRaw)
152 | {
153 | $queryBuilder->orderByRaw('RAND()');
154 | }
155 |
156 | protected function applyWithTrashed(Builder $queryBuilder)
157 | {
158 | $queryBuilder->withTrashed();
159 | }
160 |
161 | protected function applyHas(Builder $queryBuilder, array $relations = [])
162 | {
163 | foreach ($relations as $relation) {
164 | $queryBuilder->has($relation);
165 | }
166 | }
167 |
168 | protected function applyDoesntHave(Builder $queryBuilder, array $relations = [])
169 | {
170 | foreach ($relations as $relation) {
171 | $queryBuilder->doesntHave($relation);
172 | }
173 | }
174 |
175 | protected function applyWithouGlobalScopes(Builder $queryBuilder, array $excludeGlobalScopes = [])
176 | {
177 | $queryBuilder->withoutGlobalScopes($excludeGlobalScopes);
178 | }
179 |
180 | protected function applyFilter(Builder $queryBuilder, array $filter, $or = false)
181 | {
182 | $column = $filter['column'];
183 | $method = 'where';
184 | $operator = $filter['operator'] ?? 'eq';
185 | $value = $filter['value'];
186 | $not = $filter['not'] ?? false;
187 | $wantsRelationship = stripos($column, '.');
188 | $clauseOperator = true;
189 | $lastColumn = explode('.', $column);
190 | $lastColumn = end($lastColumn);
191 | $relationName = str_replace('.'. $lastColumn, '', $column);
192 | $filterRawJoinColumns = isset($this->filterRawJoinColumns) ? $this->filterRawJoinColumns : [];
193 |
194 | $this->checkFilterColumn($column, get_class($queryBuilder->getModel()));
195 |
196 | // Check operator.
197 | switch ($operator) {
198 | // String contains
199 | case 'ct':
200 | $operator = $not ? 'NOT LIKE' : 'LIKE';
201 | $value = "%$value%";
202 | break;
203 |
204 | // Starts with
205 | case 'sw':
206 | $operator = $not ? 'NOT LIKE' : 'LIKE';
207 | $value = "$value%";
208 | break;
209 |
210 | // Ends with
211 | case 'ew':
212 | $operator = $not ? 'NOT LIKE' : 'LIKE';
213 | $value = "%$value";
214 | break;
215 |
216 | // Equals
217 | case 'eq':
218 | $operator = $not ? '!=' : '=';
219 | break;
220 |
221 | // Greater than
222 | case 'gt':
223 | $operator = $not ? '<' : '>';
224 | break;
225 |
226 | // Greater than or equalTo
227 | case 'gte':
228 | $operator = $not ? '<' : '>=';
229 | break;
230 |
231 | // Lesser than or equalTo
232 | case 'lte':
233 | $operator = $not ? '>' : '<=';
234 | break;
235 |
236 | // Lesser than
237 | case 'lt':
238 | $operator = $not ? '>' : '<';
239 | break;
240 |
241 | // In array
242 | case 'in':
243 | $method = $not ? 'whereNotIn' : 'whereIn';
244 | $clauseOperator = false;
245 | break;
246 |
247 | // Between
248 | case 'bt':
249 | $method = $not ? 'whereNotBetween' : 'whereBetween';
250 | $clauseOperator = false;
251 | break;
252 | }
253 |
254 | // Support or operator.
255 | if ($or == true) {
256 | $method = 'or'. $method;
257 | }
258 |
259 | // Custom filter.
260 | $customFilterMethod = $this->hasCustomMethod('filter', $column);
261 | if ($customFilterMethod) {
262 | return call_user_func_array([$this, 'filter'. $column], array($queryBuilder, $method, $operator, $value, $clauseOperator, $or));
263 | }
264 |
265 | // Finally apply filter.
266 | if ($wantsRelationship && !in_array($column, $filterRawJoinColumns)) {
267 | // Remove or operator support.
268 | $method = str_replace('or', '', $method);
269 |
270 | $queryFunction = function ($q) use ($lastColumn, $operator, $value, $method, $clauseOperator) {
271 | if ($clauseOperator == false) {
272 | $q->$method($lastColumn, $value);
273 | } else {
274 | $q->$method($lastColumn, $operator, $value);
275 | }
276 | };
277 |
278 | if ($or == true) {
279 | $queryBuilder->orWhereHas($relationName, $queryFunction);
280 | } else {
281 | $queryBuilder->whereHas($relationName, $queryFunction);
282 | };
283 | } else {
284 | if ($clauseOperator == false) {
285 | $queryBuilder->$method($column, $value);
286 | } else {
287 | $queryBuilder->$method($column, $operator, $value);
288 | }
289 | }
290 | }
291 |
292 | private function checkFilterColumn(String $column, String $baseClassName, array $overrideWhiteListFilter = null)
293 | {
294 | if (empty($column) || empty($baseClassName)) {
295 | return;
296 | }
297 |
298 | // Retrieve the whiteListFilter
299 | $whiteListFilter = $overrideWhiteListFilter ?? ((array)(get_class_vars($baseClassName)['whiteListFilter']) ?? []);
300 |
301 | // Check if the whitelist filter is a star
302 | if (in_array('*', $whiteListFilter)) {
303 | if (count($whiteListFilter) > 1) {
304 | throw new LarapiException('Oops! If you use "*" for the whiteListFilter, you cannot specify another column on ' . $baseClassName . ' class.');
305 | }
306 | return;
307 | }
308 |
309 | // Check if full column can filered.
310 | if (in_array($column, $whiteListFilter)) {
311 | return;
312 | }
313 |
314 | $parts = explode('.', $column);
315 | $firstPart = $parts[0];
316 |
317 | $simpleColumnCheckInListFilter = in_array($firstPart, $whiteListFilter);
318 | $complexColumnCheckInListFilter = in_array($firstPart . '.*', $whiteListFilter);
319 |
320 | // Check if splitted column can filered.
321 | if (!$simpleColumnCheckInListFilter && !$complexColumnCheckInListFilter) {
322 | throw new LarapiException('Oops! You cannot filter column ' . $column . ' on ' . $baseClassName . ' class.');
323 | }
324 |
325 | // Get next part and next class
326 | $nextColums = join('.', array_slice($parts, 1));
327 | $baseClass = new $baseClassName();
328 | $nextClass = method_exists($baseClass, $firstPart) ? get_class($baseClass->$firstPart()->getRelated()) : '';
329 |
330 | // If the whiteListFilter contains a column with a star we want to bypass the check for the next part
331 | $nextOverrideWhiteListFilter = $complexColumnCheckInListFilter ? ['*'] : null;
332 |
333 | // Recursive call to check sub parts
334 | $this->checkFilterColumn($nextColums, $nextClass, $nextOverrideWhiteListFilter);
335 | }
336 |
337 | private function hasCustomMethod($type, $key)
338 | {
339 | $methodName = sprintf('%s%s', $type, Str::studly($key));
340 | if (method_exists($this, $methodName)) {
341 | return $methodName;
342 | }
343 |
344 | return false;
345 | }
346 | }
347 |
--------------------------------------------------------------------------------
/src/Database/Repository.php:
--------------------------------------------------------------------------------
1 | model = $this->getModel();
25 | }
26 |
27 | /**
28 | * Get all resources.
29 | *
30 | * @param array $options
31 | *
32 | * @return Collection
33 | */
34 | public function get(array $options = [])
35 | {
36 | $query = $this->createBaseBuilder($options);
37 |
38 | $data = $query->get();
39 |
40 | $this->appendAttributes($data, $options);
41 |
42 | return $data;
43 | }
44 |
45 | /**
46 | * Get all resources with count.
47 | *
48 | * @param array $options
49 | *
50 | * @return array
51 | */
52 | public function getWithCount(array $options = [])
53 | {
54 | $query = $this->createBaseBuilder($options);
55 |
56 | $totalData = $this->countRows($query);
57 | $allRows = $query->get();
58 |
59 | $this->appendAttributes($allRows, $options);
60 |
61 | return ['total_data' => $totalData, 'rows' => $allRows];
62 | }
63 |
64 | /**
65 | * Get a resource by its primary key.
66 | *
67 | * @param mixed $id
68 | * @param array $options
69 | *
70 | * @return Collection
71 | */
72 | public function getById($id, array $options = [])
73 | {
74 | $query = $this->createBaseBuilder($options);
75 |
76 | $query = $query->find($id);
77 |
78 | $this->appendAttributes($query, $options);
79 |
80 | return $query;
81 | }
82 |
83 | /**
84 | * Append attributes.
85 | *
86 | * @param \Illuminate\Database\Eloquent\Model|\Illuminate\Database\Eloquent\Collection $query
87 | */
88 | public function appendAttributes($query, $options = [])
89 | {
90 | if (is_null($query)) {
91 | return;
92 | }
93 |
94 | if (!isset($options['append'])) {
95 | return;
96 | }
97 |
98 | foreach ($options['append'] as $append) {
99 | $appends = explode('.', $append);
100 | $lastAppend = count($appends) - 1;
101 | $appends[$lastAppend] = Str::snake($appends[$lastAppend]);
102 |
103 | if (count($appends) == 1) {
104 | $query = $query->append($appends[0]);
105 | continue;
106 | }
107 |
108 | if (!is_a($query, 'Illuminate\Database\Eloquent\Collection')) {
109 | if (count($appends) == 2) {
110 | $relation1 = $appends[0];
111 | $relation1InstanceOf = get_class($query->$relation1());
112 | $attributeName = $appends[1];
113 |
114 | if ($relation1InstanceOf == 'Illuminate\Database\Eloquent\Relations\HasOne') {
115 | $query->$relation1->setAppends([$attributeName]);
116 | }
117 |
118 | if ($relation1InstanceOf == 'Illuminate\Database\Eloquent\Relations\HasMany') {
119 | $query->$relation1->each->setAppends([$attributeName]);
120 | }
121 | }
122 |
123 | if (count($appends) == 3) {
124 | $relation1 = $appends[0];
125 | $relation1InstanceOf = get_class($query->$relation1());
126 | $relation1Exists = $query->$relation1()->exists();
127 | $relation2 = $appends[1];
128 | if ($relation1Exists) {
129 | $relation2InstanceOf = get_class($query->$relation1->$relation2());
130 | $attributeName = $appends[2];
131 |
132 | if ($relation1InstanceOf == 'Illuminate\Database\Eloquent\Relations\HasOne') {
133 | if ($relation2InstanceOf == 'Illuminate\Database\Eloquent\Relations\BelongsTo') {
134 | $query->$relation1->$relation2->setAppends([$attributeName]);
135 | }
136 | }
137 | }
138 | }
139 | }
140 | }
141 | }
142 |
143 | /**
144 | * Get all resources ordered by recentness.
145 | *
146 | * @param array $options
147 | *
148 | * @return Collection
149 | */
150 | public function getRecent(array $options = [])
151 | {
152 | $query = $this->createBaseBuilder($options);
153 |
154 | $query->orderBy($this->getCreatedAtColumn(), 'DESC');
155 |
156 | $data = $query->get();
157 |
158 | $this->appendAttributes($data, $options);
159 |
160 | return $data;
161 | }
162 |
163 | /**
164 | * Get all resources by a where clause ordered by recentness.
165 | *
166 | * @param string $column
167 | * @param mixed $value
168 | * @param array $options
169 | *
170 | * @return Collection
171 | */
172 | public function getRecentWhere($column, $value, array $options = [])
173 | {
174 | $query = $this->createBaseBuilder($options);
175 |
176 | $query->where($column, $value);
177 |
178 | $query->orderBy($this->getCreatedAtColumn(), 'DESC');
179 |
180 | $data = $query->get();
181 |
182 | $this->appendAttributes($data, $options);
183 |
184 | return $data;
185 | }
186 |
187 | /**
188 | * Get latest resource.
189 | *
190 | * @param array $options
191 | *
192 | * @return Collection
193 | */
194 | public function getLatest(array $options = [])
195 | {
196 | $query = $this->createBaseBuilder($options);
197 |
198 | $query->orderBy($this->getCreatedAtColumn(), 'DESC');
199 |
200 | $data = $query->first();
201 |
202 | $this->appendAttributes($data, $options);
203 |
204 | return $data;
205 | }
206 |
207 | /**
208 | * Get latest resource by a where clause.
209 | *
210 | * @param string $column
211 | * @param mixed $value
212 | * @param array $options
213 | *
214 | * @return Collection
215 | */
216 | public function getLatestWhere($column, $value, array $options = [])
217 | {
218 | $query = $this->createBaseBuilder($options);
219 |
220 | $query->where($column, $value);
221 |
222 | $query->orderBy($this->getCreatedAtColumn(), 'DESC');
223 |
224 | $data = $query->get();
225 |
226 | $this->appendAttributes($data, $options);
227 |
228 | return $data;
229 | }
230 |
231 | /**
232 | * Get resources by a where clause.
233 | *
234 | * @param string $column
235 | * @param mixed $value
236 | * @param array $options
237 | *
238 | * @return Collection
239 | */
240 | public function getWhere($column, $value, array $options = [])
241 | {
242 | $query = $this->createBaseBuilder($options);
243 |
244 | $query->where($column, $value);
245 |
246 | $data = $query->get();
247 |
248 | $this->appendAttributes($data, $options);
249 |
250 | return $data;
251 | }
252 |
253 | /**
254 | * Get resources by multiple where clauses.
255 | *
256 | * @param array $clauses
257 | * @param array $options
258 | *
259 | * @return Collection
260 | */
261 | public function getWhereArray(array $clauses, array $options = [])
262 | {
263 | $query = $this->createBaseBuilder($options);
264 |
265 | $this->applyWhereArray($query, $clauses);
266 |
267 | $data = $query->get();
268 |
269 | $this->appendAttributes($data, $options);
270 |
271 | return $data;
272 | }
273 |
274 | /**
275 | * Get resources where a column value exists in array.
276 | *
277 | * @param string $column
278 | * @param array $values
279 | * @param array $options
280 | *
281 | * @return Collection
282 | */
283 | public function getWhereIn($column, array $values, array $options = [])
284 | {
285 | $query = $this->createBaseBuilder($options);
286 |
287 | $query->whereIn($column, $values);
288 |
289 | $data = $query->get();
290 |
291 | $this->appendAttributes($data, $options);
292 |
293 | return $data;
294 | }
295 |
296 | /**
297 | * Delete a resource by its primary key.
298 | *
299 | * @param mixed $id
300 | *
301 | * @return void
302 | */
303 | public function delete($id)
304 | {
305 | $query = $this->createQueryBuilder();
306 |
307 | $query->where($this->getPrimaryKey($query), $id);
308 | $query->delete();
309 | }
310 |
311 | /**
312 | * Delete resources by a where clause.
313 | *
314 | * @param string $column
315 | * @param mixed $value
316 | *
317 | * @return void
318 | */
319 | public function deleteWhere($column, $value)
320 | {
321 | $query = $this->createQueryBuilder();
322 |
323 | $query->where($column, $value);
324 | $query->delete();
325 | }
326 |
327 | /**
328 | * Delete resources by multiple where clauses.
329 | *
330 | * @param array $clauses
331 | *
332 | * @return void
333 | */
334 | public function deleteWhereArray(array $clauses)
335 | {
336 | $query = $this->createQueryBuilder();
337 |
338 | $this->applyWhereArray($query, $clauses);
339 | $query->delete();
340 | }
341 |
342 | /**
343 | * Creates a new query builder with options set.
344 | *
345 | * @param array $options
346 | *
347 | * @return Builder
348 | */
349 | protected function createBaseBuilder(array $options = [])
350 | {
351 | $query = $this->createQueryBuilder();
352 |
353 | if (!empty($options['scope'])) {
354 | foreach ($options['scope'] as $scope) {
355 | $query = $query->$scope();
356 | }
357 | }
358 |
359 | $this->applyResourceOptions($query, $options);
360 |
361 | if (empty($options['sortByDesc']) && empty($options['sortByAsc'])) {
362 | $this->defaultSort($query, $options);
363 | }
364 |
365 | return $query;
366 | }
367 |
368 | /**
369 | * Creates a new query builder.
370 | *
371 | * @return Builder
372 | */
373 | protected function createQueryBuilder()
374 | {
375 | return $this->model->newQuery();
376 | }
377 |
378 | /**
379 | * Get primary key name of the underlying model.
380 | *
381 | * @param Builder $query
382 | *
383 | * @return string
384 | */
385 | protected function getPrimaryKey($query)
386 | {
387 | return $query->getModel()->getKeyName();
388 | }
389 |
390 | /**
391 | * Order query by the specified sorting property.
392 | *
393 | * @param Builder $query
394 | * @param array $options
395 | *
396 | * @return void
397 | */
398 | protected function defaultSort($query, array $options = [])
399 | {
400 | if (isset($this->sortProperty)) {
401 | $query->orderBy($this->sortProperty, $this->sortDirection);
402 | }
403 | }
404 |
405 | /**
406 | * Get the name of the "created at" column.
407 | *
408 | * @return string
409 | */
410 | protected function getCreatedAtColumn()
411 | {
412 | $model = $this->model;
413 | return ($model::CREATED_AT) ? $model::CREATED_AT : 'created_at';
414 | }
415 |
416 | protected function applyWhereArray($query, array $clauses)
417 | {
418 | foreach ($clauses as $key => $value) {
419 | preg_match('/NOT\:(.+)/', $key, $matches);
420 |
421 | $not = false;
422 | if (isset($matches[1])) {
423 | $not = true;
424 | $key = $matches[1];
425 | }
426 |
427 | if (is_array($value)) {
428 | if (!$not) {
429 | $query->whereIn($key, $value);
430 | } else {
431 | $query->whereNotIn($key, $value);
432 | }
433 | } elseif (is_null($value)) {
434 | if (!$not) {
435 | $query->whereNull($key);
436 | } else {
437 | $query->whereNotNull($key);
438 | }
439 | } else {
440 | if (!$not) {
441 | $query->where($key, $value);
442 | } else {
443 | $query->where($key, '!=', $value);
444 | }
445 | }
446 | }
447 | }
448 |
449 | protected function countRows($query)
450 | {
451 | $totalQuery = clone $query;
452 |
453 | return $totalQuery->offset(0)->limit(PHP_INT_MAX)->count();
454 | }
455 | }
456 |
--------------------------------------------------------------------------------
/src/Exceptions/ApiException.php:
--------------------------------------------------------------------------------
1 | request = $request;
13 | $this->e = $e;
14 | }
15 |
16 | public function generateExceptionResponse()
17 | {
18 | $formatters = config('larapi.exceptions_formatters');
19 |
20 | foreach ($formatters as $exceptionType => $formatter) {
21 | if (!($this->e instanceof $exceptionType)) {
22 | continue;
23 | }
24 |
25 | if (!class_exists($formatter)) {
26 | continue;
27 | }
28 |
29 | $formatterInstance = new $formatter();
30 | return $formatterInstance->format($this->request, $this->e);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Exceptions/LarapiException.php:
--------------------------------------------------------------------------------
1 | getStatusCode();
22 | }
23 |
24 | $message = $this->getMessage($statusCode, $e);
25 |
26 | $data = [
27 | 'success' => false,
28 | 'status' => $statusCode,
29 | 'message' => $message
30 | ];
31 |
32 | if (config('app.debug')) {
33 | $data['exception'] = (string) $e;
34 | $data['line'] = $e->getLine();
35 | $data['file'] = $e->getFile();
36 | }
37 |
38 | return response()->json($data, $statusCode);
39 | }
40 |
41 | public function getMessage($statusCode, $e)
42 | {
43 | switch ($statusCode) {
44 | case 401:
45 | $message = strlen($e->getMessage()) ? $e->getMessage() : self::DEFAULT_401_MESSAGE;
46 | break;
47 | case 403:
48 | $message = strlen($e->getMessage()) ? $e->getMessage() : self::DEFAULT_403_MESSAGE;
49 | break;
50 | case 404:
51 | $message = strlen($e->getMessage()) ? $e->getMessage() : self::DEFAULT_404_MESSAGE;
52 | break;
53 | case 405:
54 | $message = strlen($e->getMessage()) ? $e->getMessage() : self::DEFAULT_405_MESSAGE;
55 | break;
56 | case 500:
57 | $message = (app()->environment('production')) ? self::DEFAULT_500_MESSAGE : $e->getMessage();
58 | break;
59 | case 503:
60 | $message = self::DEFAULT_503_MESSAGE;
61 | break;
62 | default:
63 | $message = $e->getMessage();
64 | break;
65 | }
66 |
67 | return $message;
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/ExceptionsFormatters/UnprocessableEntityHttpExceptionFormatter.php:
--------------------------------------------------------------------------------
1 | false,
14 | 'status' => self::STATUS_CODE,
15 | 'message' => self::MESSAGE
16 | ];
17 |
18 | // Laravel validation errors will return JSON string
19 | $decoded = json_decode($e->getMessage(), true);
20 |
21 | // Message was not valid JSON
22 | // This occurs when we throw UnprocessableEntityHttpExceptions
23 | if (json_last_error() !== JSON_ERROR_NONE) {
24 | // Mimick the structure of Laravel validation errors
25 | $decoded = [[$e->getMessage()]];
26 | }
27 |
28 | // Laravel errors are formatted as {"field": [/*errors as strings*/]}
29 | $data['errors'] = array_reduce($decoded, function ($carry, $item) use ($e) {
30 | return array_merge($carry, array_map(function ($current) use ($e) {
31 | return [
32 | 'title' => 'Validation error.',
33 | 'detail' => $current
34 | ];
35 | }, $item));
36 | }, []);
37 |
38 | return response()->json($data, self::STATUS_CODE);
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/Facades/ApiConsumer.php:
--------------------------------------------------------------------------------
1 | mergeConfigFrom(
19 | __DIR__. '../../Config/larapi.php',
20 | 'larapi'
21 | );
22 | }
23 |
24 | /**
25 | * Bootstrap the application services.
26 | *
27 | * @return void
28 | */
29 | public function boot()
30 | {
31 | $this->publishes([
32 | __DIR__. '/../Config/larapi.php' => config_path('larapi.php'),
33 | ]);
34 |
35 | if ($this->app->runningInConsole()) {
36 | $this->commands([
37 | ComponentMakeCommand::class
38 | ]);
39 | }
40 |
41 | $this->app->singleton('apiconsumer', function () {
42 | $app = app();
43 |
44 | return new ApiConsumerRouter($app, $app['request'], $app['router']);
45 | });
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Routes/ApiConsumerRouter.php:
--------------------------------------------------------------------------------
1 | app = $app;
22 | $this->request = $request;
23 | $this->router = $router;
24 | }
25 |
26 | public function get()
27 | {
28 | return $this->quickCall('GET', func_get_args());
29 | }
30 |
31 | public function post()
32 | {
33 | return $this->quickCall('POST', func_get_args());
34 | }
35 |
36 | public function put()
37 | {
38 | return $this->quickCall('PUT', func_get_args());
39 | }
40 |
41 | public function delete()
42 | {
43 | return $this->quickCall('DELETE', func_get_args());
44 | }
45 |
46 | public function batchRequest(array $requests)
47 | {
48 | foreach ($requests as $i => $request) {
49 | $requests[$i] = call_user_func_array([$this, 'singleRequest'], $request);
50 | }
51 |
52 | return $requests;
53 | }
54 |
55 | public function quickCall($method, array $args)
56 | {
57 | array_unshift($args, $method);
58 | return call_user_func_array([$this, "singleRequest"], $args);
59 | }
60 |
61 | public function singleRequest($method, $uri, array $data = [], array $headers = [], $content = null)
62 | {
63 | // Save the current request so we can reset the router back to it
64 | // after we've completed our internal request.
65 | $currentRequest = $this->request->instance()->duplicate();
66 |
67 | $headers = $this->overrideHeaders($currentRequest->server->getHeaders(), $headers);
68 |
69 | if ($this->disableMiddleware) {
70 | $this->app->instance('middleware.disable', true);
71 | }
72 |
73 | $response = $this->request($method, $uri, $data, $headers, $content);
74 |
75 | if ($this->disableMiddleware) {
76 | $this->app->instance('middleware.disable', false);
77 | }
78 |
79 | // Once the request has completed we reset the currentRequest of the router
80 | // to match the original request.
81 | $this->request->instance()->initialize(
82 | $currentRequest->query->all(),
83 | $currentRequest->request->all(),
84 | $currentRequest->attributes->all(),
85 | $currentRequest->cookies->all(),
86 | $currentRequest->files->all(),
87 | $currentRequest->server->all(),
88 | $currentRequest->content
89 | );
90 |
91 | return $response;
92 | }
93 |
94 | private function overrideHeaders(array $default, array $headers)
95 | {
96 | $headers = $this->transformHeadersToUppercaseUnderscoreType($headers);
97 | return array_merge($default, $headers);
98 | }
99 |
100 | public function enableMiddleware()
101 | {
102 | $this->disableMiddleware = false;
103 | }
104 |
105 | public function disableMiddleware()
106 | {
107 | $this->disableMiddleware = true;
108 | }
109 |
110 | private function request($method, $uri, array $data = [], array $headers = [], $content = null)
111 | {
112 | // Create a new request object for the internal request
113 | $request = $this->createRequest($method, $uri, $data, $headers, $content);
114 |
115 | // Handle the request in the kernel and prepare a response
116 | $response = $this->router->prepareResponse($request, $this->app->handle($request));
117 |
118 | return $response;
119 | }
120 |
121 | private function createRequest($method, $uri, array $data = [], array $headers = [], $content = null)
122 | {
123 | $server = $this->transformHeadersToServerVariables($headers);
124 |
125 | return $this->request->create($uri, $method, $data, [], [], $server, $content);
126 | }
127 |
128 | private function transformHeadersToUppercaseUnderscoreType($headers)
129 | {
130 | $transformed = [];
131 |
132 | foreach ($headers as $headerType => $headerValue) {
133 | $headerType = strtoupper(str_replace('-', '_', $headerType));
134 |
135 | $transformed[$headerType] = $headerValue;
136 | }
137 |
138 | return $transformed;
139 | }
140 |
141 | private function transformHeadersToServerVariables($headers)
142 | {
143 | $server = [];
144 |
145 | foreach ($headers as $headerType => $headerValue) {
146 | $headerType = 'HTTP_' . $headerType;
147 |
148 | $server[$headerType] = $headerValue;
149 | }
150 |
151 | return $server;
152 | }
153 | }
154 |
--------------------------------------------------------------------------------