{"id":20547757,"url":"https://github.com/ferpetrelli/eloquent-consumer","last_synced_at":"2025-04-14T10:37:05.504Z","repository":{"id":57038249,"uuid":"146933631","full_name":"ferpetrelli/eloquent-consumer","owner":"ferpetrelli","description":"API consumer and query builder with an Eloquent like syntax","archived":false,"fork":false,"pushed_at":"2018-10-11T15:52:59.000Z","size":35,"stargazers_count":6,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-27T23:51:04.361Z","etag":null,"topics":["api","client","consumer","eloquent","eloquent-models","laravel-package"],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ferpetrelli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-08-31T19:17:34.000Z","updated_at":"2023-12-26T20:53:41.000Z","dependencies_parsed_at":"2022-08-24T00:51:08.702Z","dependency_job_id":null,"html_url":"https://github.com/ferpetrelli/eloquent-consumer","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferpetrelli%2Feloquent-consumer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferpetrelli%2Feloquent-consumer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferpetrelli%2Feloquent-consumer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ferpetrelli%2Feloquent-consumer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ferpetrelli","download_url":"https://codeload.github.com/ferpetrelli/eloquent-consumer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248864384,"owners_count":21174139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","client","consumer","eloquent","eloquent-models","laravel-package"],"created_at":"2024-11-16T02:10:04.470Z","updated_at":"2025-04-14T10:37:05.476Z","avatar_url":"https://github.com/ferpetrelli.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# About\n\nEloquent API Consumer will allow you to solve two main problems regarding API's in Laravel:\n\n1. Generate and execute API calls in a clear and simple way\n2. Process API responses and generate Eloquent like models and collections (including paginated ones)\n\nYou can see an example application here: [Eloquent Consumer Example APP](https://github.com/ferpetrelli/eloquent-consumer-test).\n\n\n## Development Note\n\nThis package is still on alpha state, and under heavy development. Things might change in the near future.\n\n\n## Motivation to create this package\n\nOur CMS was designed to use Eloquent like models as the data source.\n\nSo we needed a way to integrate an API origin to build some listings in a seamless way, without having to modify the CMS.\n\nThis library creates models who's interface will be compatible with Eloquent, so building queries, pagination, scopes, filtering, and mostly everything related to Eloquent will be available.\n\nWith this package you will be able to manage your API endpoints, caching strategies, low level API query configuration, model attributes, scopes and functions, and basically every element involved in the process of generating a query, to getting the processed end result.\n\n# Table of contents\n\n- [Overview](#overview)\n- [Core Concepts](#core-concepts)\n\n    - [Endpoint](#endpoints)\n    - [Consumer](#consumers)\n    - [Grammar](#grammar)\n\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Usage](#usage)\n- [Extended Reference](#extended-reference)\n- [License](#license)\n\n# Overview\n\nLet's imagine we have a `Book` API model and we want to read some data from the API.\nLet's perform some calls right now:\n\n```php\n\n// Call to the collection endpoint, and return a collection of Books\n\\App\\Book::query()-\u003eget();\n\n// Call to the collection endpoint, and return a paginated collection of 10 elements\n\\App\\Book::query()-\u003epaginate(10);\n\n// Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object.\n\\App\\Book::query()-\u003efind($id);\n\n// Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object. Throw a 404 if not found.\n\\App\\Book::query()-\u003efindOrFail($id);\n\n// Let's get a collection of search results for a term. This will simply add a 'q=Julio Cortazar' parameter to the query by default.\n\\App\\Book::query()-\u003esearch('Julio Cortazar')-\u003eget()\n\n// Call to the collection endpoint, and let's just add a customized parameter to this call to search books by ISBN.\n\\App\\Book::query()-\u003erawQuery(['ISBN' =\u003e 123456])-\u003eget()\n\n// Call to the collection endpoint, and return a collection of Books with only id, and title columns\n\\App\\Book::query()-\u003eget(['id', 'title']);\n\n```\n\nAs you can see this syntax is very familiar.\n\nLet's say you want to add your own Eloquent like scope to get only published elements. Just follow the same syntax as you usually do with Laravel:\n\n\n```php\n\u003c?php\n\n// ...\nclass Book extends ApiModel\n{\n    //...\n\n    public function scopePublished($query)\n    {\n        return $query-\u003erawQuery(['published' =\u003e true]);\n    }\n\n    //...\n}\n```\n\n\n```php\n// Call to the published scope and then call to the collection endpoint\n\\App\\Book::query()-\u003epublished()-\u003eget();\n```\n\n\nThis will pass a `published=true` parameter when performing the API call (because we simply used the rawQuery function as we saw before)\n\nTo configure this model we have to create an `Endpoint` class for our specific API. You can do that manually or using the following command:\n\n```\nphp eloquent-consumer:endpoint Main\n```\n\nLet's edit some options:\n\n```php\n\u003c?php\n\nnamespace App\\ApiConsumer\\Endpoints;\n\nuse \\Petrelli\\EloquentConsumer\\Endpoints\\BaseEndpoint;\n\nclass Main extends BaseEndpoint\n{\n\n    protected $baseUri = 'https://baseapi.com';\n\n    protected $defaultTTL = 200;\n\n\n}\n```\n\nAs you can see, we only defined here a `$baseUri` and a default TTL for caching (200 seconds).\n\nNow that we have the endpoint configured, we should create a the actual Book Model. This model must inherit from our faux Eloquent like class.\n\n```php\n\u003c?php\n\nnamespace App;\n\nuse \\Petrelli\\EloquentConsumer\\Models\\ApiModel;\n\nclass Book extends ApiModel\n{\n\n    protected $endpointClass = \\App\\ApiConsumer\\Endpoints\\Main::class;\n\n    protected $endpoints = [\n        'collection' =\u003e '/books',\n        'resource'   =\u003e '/books/{id}',\n    ];\n\n}\n```\n\nHere we just have to configure two things:\n\n* Endpoint class to be used\n* Actual URL's used by this type of resource\n\nCollection and resource are the default endpoints that the package uses. Of course you can define your own and use them later, but these are enough.\n\nThis will be enough to get you started.\n\nIf you continue reading you will learn to modify how calls are performed, and how responses will be  processed.\n\n\n# Core concepts\n\nYou will have a default configuration file for you project defining everything we will show here. But of course, you can override these values configuring each class separately.\n\nIt's recommended to use the default namespace `ApiConsumer` when creating new elements.\n\nWe will mention the following entities:\n\n* Endpoint\n* Consumer\n* Grammar\n\n\n\n## Endpoint\n\nThis one will be your main entity.\n\nYou can have as many as you want per project, allowing you to use multiple API's and/or multiple configurations within the same API.\n\nHere you can define the following options:\n\n```php\n\nuse \\Petrelli\\EloquentConsumer\\Endpoints\\BaseEndpoint;\n\nclass MainEndpoint extends BaseEndpoint\n{\n\n    //Mandadory if not added at the general config file\n    //This will be your baseline URL. E.g. http://apibase.com\n    protected $baseUri;\n\n    //Mandadory if not added at the general config file. Number of seconds each call will be cached.\n    protected $defaultTTL;\n\n    //Custom Grammar or Consumer classes\n    protected $grammarClass;\n    protected $consumerClass;\n\n    //......\n}\n```\n\nKeep in mind these options:\n\n* Consumer Class\n* Grammar Class\n\n\n## Consumer\n\nConsumers are the clients that will execute the actual API calls.\nThis package includes a default client that uses [Guzzle](http://docs.guzzlephp.org/en/stable/).\n\nUsually using the default one with Guzzle will be more than enough.\n\nCreating a new Consumer class could be useful if you had to add new options like authentication headers, timeout configurations, basically, any configuration you might want supported by Guzzle or your chosen client.\n\n\n\n## Grammar\n\nA Grammar class basically a collector that transforms all the information you gave to the Query Builder into fields that will be sent in your API request.\n\nIt's a very simple class that you can extend and adjust to your API specs.\n\nLet's see this Grammar Class example function:\n\n```php\nprotected function compileSearchText($query, $text)\n{\n    if ($text)\n        return ['q' =\u003e $text];\n    else\n        return [];\n}\n```\n\nHere you see that the function `search('term')` will be transformed into a parameter named `q`.\n\nPlease refer to our default grammar class code, and you will observe there how we process all options.\n\n\n# Installation\n\n1. Install the package via composer:\n\n```\ncomposer require petrelli/eloquent-consumer\n```\n\n2. Export configuration file:\n\n```\nphp artisan vendor:publish --provider=\"Petrelli\\EloquentConsumer\\EloquentConsumerServiceProvider\"\n```\n\nIn there you can define default options and entities (endpoint, grammar and connection).\n\n# Configuration\n\nLet's generate an Endpoint:\n\n```\nphp artisan eloquent-consumer:endpoint Base\n```\n\nOr in case you want to use your own namespace:\n\n```\nphp artisan eloquent-consumer:endpoint EndpointName Namespace1/Namespace2\n```\n\nNow let's add some configuration values to the endpoint:\n\n```php\n\u003c?php\n\nnamespace App\\ApiConsumer\\Endpoints;\n\nuse \\Petrelli\\EloquentConsumer\\Endpoints\\BaseEndpoint;\n\nclass Base extends BaseEndpoint\n{\n\n    protected $baseUri = 'https://baseapi.com';\n\n    protected $defaultTTL = 200;\n\n\n}\n```\n\nHere you can see it's a very basic Endpoint. No grammar or consumer customized.\n\nYou can add a `$grammarClass` variable in case you want to extend the default one:\n\n```php\nprotected $connectionClass = \\App\\ApiConsumer\\Grammar\\MyOwnGrammar::class;\n```\n\n\nAnd we will need to create that Grammar class:\n\n```php\n\u003c?php\n\nnamespace App\\ApiConsumer\\Grammar;\n\nuse \\Petrelli\\EloquentConsumer\\Grammar\\BaseGrammar;\n\nclass MyOwnGrammar extends BaseGrammar\n{\n\n    protected function compilePage($query, $page)\n    {\n        return ['page_number' =\u003e $page];\n    }\n\n}\n```\n\nSo here, we redefine `compilePage` which will send a `page_number` parameter on the API query, instead of the `page` default.\n\n\n# Usage\n\nAfter everything is configured we can start using this model almost as if it was an eloquent entity.\n\n```php\n\n// Call to the collection endpoint, and return a collection of Books\n\\App\\Book::query()-\u003eget();\n\n// Call to the collection endpoint, and return a paginated collection of 10 elements\n\\App\\Book::query()-\u003epaginate(10);\n\n// Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object.\n\\App\\Book::query()-\u003efind($id);\n\n// Call to the resource endpoint, {id} will be replaced with the value of $id and will return a Test object. Throw a 404 if not found.\n\\App\\Book::query()-\u003efindOrFail($id);\n\n// Let's get a collection of search results for a term. This will simply add a 'q=Julio Cortazar' parameter to the query by default.\n\\App\\Book::query()-\u003esearch('Julio Cortazar')-\u003eget();\n\n// Call to the collection endpoint, and let's just add a customized parameter to this call to search books by ISBN.\n\\App\\Book::query()-\u003erawQuery(['ISBN' =\u003e 123456])-\u003eget();\n\n// Call to the collection endpoint, and return a collection of Books with only id, and title columns\n\\App\\Book::query()-\u003eget(['id', 'title']);\n\n// Call to the customized 'something' endpoint, and return a collection of Books with only id, and title columns\n\\App\\Book::query()-\u003eforceEndpoint('something')-\u003eget(['id', 'title']);\n\n```\n\nAs you can see this syntax is very familiar.\n\nLet's say you want to add your own Eloquent like scope to get only published elements. Just follow the same syntax as you usually do with Laravel:\n\n\n```php\n\u003c?php\n\n// ...\nclass Book extends ApiModel\n{\n    //...\n\n    public function scopePublished($query)\n    {\n        return $query-\u003erawQuery(['published' =\u003e true]);\n    }\n\n    //...\n}\n```\n\n\n```php\n// Call to the published scope and then call to the collection endpoint\n\\App\\Book::query()-\u003epublished()-\u003eget();\n```\n\n\nThis will pass a `published=true` parameter when performing the API call (because we simply used the rawQuery function as we saw before)\n\n\n## Relationships\n\nDocumentation to be completed.\n\n\n# Transformer classes\n\nSo we generated the query, and now we are getting a correct response.\n\nWe have now to refactor this response to fit the library standards so everything works as expected.\n\nThis will be done easily with a TransformerClass.\n\n```php\n\u003c?php\n\nnamespace App\\ApiConsumer\\Transformers;\n\nuse Petrelli\\EloquentConsumer\\Transformers\\BaseTransformer;\n\n\nclass Base extends BaseTransformer\n{\n\n    /**\n     * Transform Grants API response to a format we can read\n     */\n\n    public function transform()\n    {\n        $original = $this-\u003eresponse-\u003ebody;\n\n        $original-\u003epagination = (object) [\n         'total' =\u003e $original-\u003erecordsTotal,\n         'page'  =\u003e $original-\u003edraw,\n        ];\n\n        return $this-\u003eresponse;\n\n    }\n\n\n}\n```\n\n\nTransformer classes have only one function: `transform()`.\nThere you have available `$this-\u003eresponse` to use it and transform your responses to our proper format.\n\nThis will enable you to use any API transparently of the underline response format.\n\nThe expected format looks like the following:\n\n```json\n{\n    pagination: {\n        total: 100,\n        limit: 12,\n        offset: 0,\n        total_pages: 520,\n        current_page: 1,\n    },\n    data: [\n        {},\n        {},\n        {},\n        {},\n        ...\n    ]\n}\n```\n\n\nSo in order to be able to use full power, you will have to adapt your response to look like this one.\n\n\n# Extended Reference\nTODO\n\n# License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fferpetrelli%2Feloquent-consumer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fferpetrelli%2Feloquent-consumer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fferpetrelli%2Feloquent-consumer/lists"}