PHPackages                             liquiddesign/lqgraphi - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. liquiddesign/lqgraphi

ActiveLibrary[API Development](/categories/api)

liquiddesign/lqgraphi
=====================

GraphQL API library for Liquid Design ecosystem

v2.0.1(1y ago)0116[1 issues](https://github.com/liquiddesign/lqgraphi/issues)3MITPHPPHP &gt;=8.2

Since Sep 28Pushed 1y ago1 watchersCompare

[ Source](https://github.com/liquiddesign/lqgraphi)[ Packagist](https://packagist.org/packages/liquiddesign/lqgraphi)[ RSS](/packages/liquiddesign-lqgraphi/feed)WikiDiscussions main Synced today

READMEChangelogDependencies (12)Versions (47)Used By (3)

💫 LqGrAphi
==========

[](#-lqgraphi)

GraphQL API library for Liquid Design ecosystem.

Functions
---------

[](#functions)

- Auto types creation from PHP classes to TypeRegister (Storm entities)
- Autoload of Queries, Mutations and Types from namespaces
- Caching of schema, persisted queries and mutations
- Universal CRUD query, mutations and resolvers for generic generation and resolving
- Recursive data fetcher of Storm entities from database based on requested query (highly optimized - makes only one query per entity class - simulating dataloader)

Recommendations
---------------

[](#recommendations)

This package works great with extended packages with types for LQD packages:

-
- -
- -

Installation
------------

[](#installation)

This package requires PHP 8.2 or higher.

`composer require liquiddesign/lqgraphi`

Configuration
-------------

[](#configuration)

```
extensions:
    typeRegister: LqGrAphi\LqGrAphiDI

typeRegister:
    resolvers:
        - EshopApi\Resolvers
    queriesAndMutations:
        - EshopApi\Schema\Types
    types:
        output:
            - EshopApi\Schema\Outputs
        input:
            - EshopApi\Schema\Inputs
```

Entry point
-----------

[](#entry-point)

In you entry point (probably `index.php`) you need to call handler. You just need to create container and pass it to `\LqGrAphi\Handlers\IndexHandler::handle`.

Minimal example of `index.php`:

```
require __DIR__ . '/vendor/autoload.php';

\LqGrAphi\Handlers\IndexHandler::handle(\EshopApi\Bootstrap::boot()->createContainer());
```

### Sandbox

[](#sandbox)

Apollo Sandbox is enabled by default for debug connections based on environment file.

You can permanently disable it:

```
require __DIR__ . '/vendor/autoload.php';

\LqGrAphi\Handlers\IndexHandler::handle(\EshopApi\Bootstrap::boot()->createContainer(), false);
```

Queries and Mutations
---------------------

[](#queries-and-mutations)

Location of queries and mutations is set via config `queryAndMutationsNamespace`. Query needs to extend `\LqGrAphi\Schema\BaseQuery` and mutation `\LqGrAphi\Schema\BaseMutation`.

These types are automatically loaded only first time when schema is created and cached. All other requests uses cached schema, due to that script don't need to create schema for every request and performance is not decreased. This approach has some limitations: Queries and mutations are not registered in container, so you cant use DI. All these classes will receive container as first argument.

Types
-----

[](#types)

Location of types is set via config `types`. You need to specify all used inputs and outputs here.

```
typeRegister:
    types:
        input:
            product: EshopApi\Schema\Inputs\ProductInput
        output:
            product: EshopApi\Schema\Outputs\ProductOutput
```

For more info visit documentation of [webonyx/graphql-php](https://webonyx.github.io/graphql-php/) library.

### ClassOutput

[](#classoutput)

There is interface `\LqGrAphi\Schema\ClassOutput` with method `getClass`. If you use it, TypeRegister will save this mapping ang when you call `getOutputType` you can simply pass class-string instead of name.

### ClassInput

[](#classinput)

Same as *ClassOutput* for inputs.

### Relations

[](#relations)

In outputs, relations are object or list of objects with up to 10 levels of depth. Inputs, on the other end, have always two fields for relation.

One with suffix `ID` for single relations which takes string (or null if possible). For many relations there is field with suffix `IDs` which is object with `add`, `remove` and `replace` fields. These fields takes list of strings.

Second, there is always fields with suffix `OBJ`, which takes directly input object and also updates it. It can also have 10 levels of depth. For many relations there is field with suffix `OBJs` which takes list of input objects.

Due to limitations of GraphQL where you can´t have union input type, these inputs are always in UpdateInput variant which has all fields optional. Based on ID field, object is created or only updated. This approach loses type safety for required fields when creating object, in this case error will only be caused in runtime.

```
input Object {
    fullName: String
    accountsIDs: SubObjectIDs
    accountsOBJs: [SubObjectUpdateInput]
}
```

There is interface `\LqGrAphi\Schema\ClassInput` with method `getClass`. If you use it, TypeRegister will save this mapping ang when you call `getInputType` you can simply pass class-string instead of name from config. Also, TypeRegister will map this input in input objects to relation fields.

Resolvers
---------

[](#resolvers)

Due to caching of whole schema creation, resolvers are isolated from schema and have to resolve request on their own.
There is simple routing mechanism. GraphQL's queries and mutations names uses lower-camelCase.
Name is parsed as first word in resolver class name and rest is function name.
Example: `productGetMany` is parsed as `ProductResolver` and function `getMany`.

Router that parses name to resolver and function uses on-demand cache.

Signature of every resolver function must be:

```
/**
 * @param array $rootValue
 * @param array $args
 * @param \LqGrAphi\GraphQLContext $context
 * @param \GraphQL\Type\Definition\ResolveInfo|array $resolveInfo
 * @return array|null
 * @throws \LqGrAphi\Resolvers\Exceptions\BadRequestException
 * @throws \ReflectionException
 * @throws \StORM\Exception\GeneralException
 */
public function getMany(array $rootValue, array $args, GraphQLContext $context, ResolveInfo|array $resolveInfo): ?array
{
    ...
}
```

Recommended way of configuration:

```
services:
	graphql_resolvers:
		in: %appDir%
		files: [Resolvers/*Resolver.php, Resolvers/*/*Resolver.php]
		implements:
		    - LqGrAphi\Resolvers\BaseResolver
```

### Cache

[](#cache)

Handler is using cache to remember queries and mutations. If you send same query twice, it will be resolved from cache and directly passed to resolver. This approach significantly increases performance. Data returned from resolver are validated only with first request. This approach is not safe, when resolvers are not fully tested.

You can also pass *queryId* directly instead of query. Queries are hashed by md5, so you can just hash you query with md5 and send it as queryId. But remember, that you still need to send at least one request with query to validate it.

### CRUD

[](#crud)

You can write your types and queries by yourself, but most of the time you just want to take existing entity and make crud operations for it.

At that point comes `\LqGrAphi\Schema\CrudQuery` and `\LqGrAphi\Schema\CrudMutation`.

First, create query class

```
class CustomerQuery extends CrudQuery
{
	public function getClass(): string
	{
		return Customer::class;
	}
}
```

then create output, create and update types with use of helpers:

```
class CustomerOutput extends BaseOutput
{
	public function __construct(TypeRegister $typeRegister)
	{
		$config = [
			'fields' => $typeRegister->createOutputFieldsFromClass(Customer::class, exclude: ['account']),
		];

		parent::__construct($config);
	}
}
```

```
class CustomerCreateInput extends BaseInput
{
	public function __construct(TypeRegister $typeRegister)
	{
		$config = [
			'fields' => $typeRegister->createInputFieldsFromClass(Customer::class, includeId: false),
		];

		parent::__construct($config);
	}
}
```

```
class CustomerUpdateInput extends BaseInput
{
	public function __construct(TypeRegister $typeRegister)
	{
		$config = [
			'fields' => $typeRegister->createInputFieldsFromClass(Customer::class, forceAllOptional: true),
		];

		parent::__construct($config);
	}
}
```

register your types namespace in config and lastly create resolver

```
class CustomerResolver extends CrudResolver
{
	public function getClass(): string
	{
		return Customer::class;
	}
}
```

That's all, you can query one, many or collection and mutate create, update and delete operations.

#### Helpers for CRUD

[](#helpers-for-crud)

To help working with API there is some automatic improvements

- All array outputs are encapsulated in object with key *data* and *onPageCount*
- There is universal input objects for sorting, paging and filtering

#### Filtering

[](#filtering)

Filters are input fields of type JSON, which are parsed to repository allowed *filter* functions. Due to this, filters are dynamically typed.

#### Fetch result

[](#fetch-result)

There is universal fetch function. You just need to pass collection with ResolveInfo. It will take care of retrieving only data you asked in most efficient way.

### Language mutations

[](#language-mutations)

If you need to use different language mutation, you can use HTTP header "Accept-Language". System will set detected language (if supported) as primary language for all SQL queries. If no language in "Accept-Language" header is supported, then primary language from settings is used.

Roadmap
-------

[](#roadmap)

### 2023

[](#2023)

- ✅ Persisted queries with cache
- ❗New type loading system to bring better DX
    - No need to register types
    - Load types from namespace
    - Refactor TypeRegister
- Security - guards, login
- Automatic testing

Developers info
---------------

[](#developers-info)

Project uses PHPStan (level 8) and PHP-CS-Fixer for code quality.

###  Health Score

35

—

LowBetter than 79% of packages

Maintenance36

Infrequent updates — may be unmaintained

Popularity10

Limited adoption so far

Community14

Small or concentrated contributor base

Maturity71

Established project with proven stability

 Bus Factor1

Top contributor holds 86.4% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~16 days

Recently: every ~131 days

Total

46

Last Release

610d ago

Major Versions

v0.3.0 → v1.0.0-beta.12023-04-01

v1.0.1 → v2.0.02024-09-06

PHP version history (2 changes)v0.0.1-alphaPHP &gt;=8.1

v0.3.0PHP &gt;=8.2

### Community

Maintainers

![](https://www.gravatar.com/avatar/c2a45fb465d05cafa46a49d77995ae65b2d7eef3c9c94f91367d56442173236a?d=identicon)[info@lqd.cz](/maintainers/info@lqd.cz)

---

Top Contributors

[![petr6](https://avatars.githubusercontent.com/u/73128826?v=4)](https://github.com/petr6 "petr6 (19 commits)")[![MasterPK](https://avatars.githubusercontent.com/u/16056535?v=4)](https://github.com/MasterPK "MasterPK (3 commits)")

###  Code Quality

Code StylePHP\_CodeSniffer

### Embed Badge

![Health badge](/badges/liquiddesign-lqgraphi/health.svg)

```
[![Health](https://phpackages.com/badges/liquiddesign-lqgraphi/health.svg)](https://phpackages.com/packages/liquiddesign-lqgraphi)
```

###  Alternatives

[nuwave/lighthouse

A framework for serving GraphQL from Laravel

3.5k10.7M93](/packages/nuwave-lighthouse)[thecodingmachine/graphqlite

Write your GraphQL queries in simple to write controllers (using webonyx/graphql-php).

5723.1M30](/packages/thecodingmachine-graphqlite)[overblog/graphql-bundle

This bundle provides tools to build a GraphQL server in your Symfony App.

8027.9M28](/packages/overblog-graphql-bundle)[wheelpros/fitment-platform-api

Magento 2 (Open Source)

12.1k1.2k](/packages/wheelpros-fitment-platform-api)[aimeos/ai-admin-graphql

Aimeos Admin GraphQL API extension

944100.0k4](/packages/aimeos-ai-admin-graphql)[mll-lab/graphql-php-scalars

A collection of custom scalar types for usage with https://github.com/webonyx/graphql-php

1394.2M28](/packages/mll-lab-graphql-php-scalars)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
