yunusbek / adaptive-api
Adaptive API package for Yii2 projects
Installs: 24
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/yunusbek/adaptive-api
Requires
- greenlion/php-sql-parser: ^4.7
- vlucas/phpdotenv: ^5.6
- yiisoft/yii2: ^2.0
README
Adaptive API package features
✅ Dynamically generate custom API structures based on client requirements
✅ Allow clients to fetch data with dynamic filtering options
✅ Retrieve only the required tables and columns from the database
✅ Access all database tables through a single API endpoint (one unified URL).
– Clients send the desired table structures in the request body, and the API returns exactly what is requested
Installation
Either run
composer require yunusbek/adaptive-api
or add
"yunusbek/adaptive-api": "^1.0",
to the require
section of your composer.json.
Installation
- Create
.env
file in the root of your application:
API_DB_ROLE=api_reader API_DB_USER=api_user_api API_DB_PASSWORD=_@p!_reaDer_ #API_DB_HOST=localhost #API_DB_NAME=your-dbname #API_DB_PORT=5432 UPLOAD_FOLDER_PATH=/uploads/
- Load
.env
insideweb/index.php
and rootyii
file:
#... $dotenv = Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__.'/../.env')); $dotenv->load(); #...
- Run migration to create the
api_user
database user and apply permissions:
php yii migrate --migrationPath=@vendor/yunusbek/adaptive-api/src/migrations
# Add the following code to controllerMap [ #... 'controllerMap' => [ 'api-schema-migration' => 'Yunusbek\AdaptiveApi\commands\Migrations', ], #... ]
The next thing you need to do is updating your database schema by applying the migration of table client_api_schema_permissions
:
php yii api-schema-migration
Example to permission schema
table - client_api_schema_permissions
column - schema:jsonb
{ "{user}": { "username": "", "email": "", "phone": "", "address": "" }, "{company}": { "company_name": "", "email": "", "phone": "", "address": "" } }
Usage
Example to api request structure
{ "customer": { "personal_info": { "person": "{user}.username", "email": "{user}.email", "gender": "{user}.gender", "age": "TO_CHAR(TO_TIMESTAMP({user}.age), 'DD.MM.YYYY')", "personal_phone": "{user}.phone", "personal_address": "{user}.address" }, "company_info": { "company": "{company}.company_name", "company_email": "{company}.email", "company_phone": "{company}.phone", "company_address": "{company}.address" }, "position_info": { "user_department": "{position}.department", "user_position": "{position}.name", "product_info": { "product_name": "{product}.name", "product_type": "{product}.type", "product_cost": "{product}.cost" } } } }
result
{ "customer": { "personal_info": { "person": "John Doe", "email": "johndoe@example.com", "gender": "male", "personal_phone": "+9989********", "personal_address": "Some address data" }, "company_info": { "company": "Example LLC", "company_email": "example@example.com", "company_phone": "+9989********", "company_address": "Some address data" }, "position_info": { "user_department": "Sales department", "user_position": "seller", "product_info": { "product_name": "Bicycle", "product_type": "Sport", "product_cost": "$33.5" } } } }
$root = [ 'unique_number' => 'user_id', 'select' => [ "user_id", "position_id", "company_id", "product_id" => "product.id", "started_date", "status", ], 'class' => UserRelCompany::class, // or by table name 'table' => 'user_rel_company' 'join' => [ ['JOIN', "products AS product", 'on' => ["user_id" => "user_id"], 'condition' => ['status' => 'ACTIVE']] ], 'where' => [ 'current_company' => true, 'product.type' => ['building', 'food', 'sport'] ], 'filter' => [ 'product_type' => "product.type", ] ]; $relations = [ 'user' => [ 'on' => ["id" => "user_id"], 'class' => Users::class // or users_table_name, 'select' => [ "username", "email", "gender", "phone", "address", ], 'where' => [ 'status' => 'ACTIVE' ] ], 'company' => [ 'on' => ["id" => "company_id"], 'class' => Companies::class // or companies_table_name, 'select' => [ "company_name" => 'name', "email", "phone", "address", ], 'where' => [ 'status' => 'ACTIVE' ] ], 'position' => [ 'on' => ["id" => "position_id"], 'class' => UserStaffPosition::class, 'select' => [ "name", "department", ], 'where' => [ 'status' => 'ACTIVE' ] ], 'product' => [ 'on' => ["id" => "product_id"], 'class' => Products::class // or products_table_name, 'select' => [ "name", "type", "cost", ], 'where' => [ 'status' => 'ACTIVE' ] ], #... other relations tables ]; $response = CteBuilder::root($root) // root table ->relation($relations) // relation tables ->with($with) // if you need with cte ->queryParams($params) // GET query string params (client request) ->template($sendTemplate) // body params (client request structure) // ->setCallback(function (string $text, string $lang) use ($params) { // this is optional if you want to add some callback function // if ($lang === 'en') { $lang = 'ru'; } // return KirillToLatin::widget(['text' => $text, 'lang' => $lang]); // }, '/^\{(uz|ru|en)\}(.*)/') ->getApi(); // finish
📘 /reference
API
Method: POST
Description:
This endpoint returns reference data from related tables using dynamic field selection and alias-based filtering.
It supports both pagination (limit
) and relation field filters (e.g. <department>code=av
).
🧩 Request Parameters
🔹 Query Parameters
Name | Type | Required | Description |
---|---|---|---|
limit |
integer |
❌ | The number of records to return. Example: 4 |
<department>code |
string |
❌ | Filter applied to the related table department.code . Example: av |
📝 Note:
The<department>
prefix indicates that the field belongs to a related (joined) table or alias.
🔹 Request Body (JSON)
Field | Type | Required | Description |
---|---|---|---|
users |
string |
✅ | Pattern for selecting fields from the users CTE/table. {users}.* means “select all columns.” |
department |
string |
✅ | Pattern for including all fields from the related department table or CTE. <{department}>.* means “include all columns.” |
Example:
{ "users": "{users}.*", "department": "<{department}>.*" }
🧾 Example Request
GET /reference?limit=4&<department>code=av