PHPackages                             spiral/data-grid - 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. [Utility &amp; Helpers](/categories/utility)
4. /
5. spiral/data-grid

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

spiral/data-grid
================

Data Grid specification builder

v3.0.1(9mo ago)54108.2k↓11%3[13 issues](https://github.com/spiral/data-grid/issues)3MITPHPPHP &gt;=8.1CI passing

Since Nov 28Pushed 9mo ago7 watchersCompare

[ Source](https://github.com/spiral/data-grid)[ Packagist](https://packagist.org/packages/spiral/data-grid)[ Docs](https://spiral.dev)[ RSS](/packages/spiral-data-grid/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (10)Dependencies (6)Versions (46)Used By (3)

Data Grid specification builder
===============================

[](#data-grid-specification-builder)

[![PHP Version Require](https://camo.githubusercontent.com/af68c295a7fc72c9cdc9d4e009e2e74bf1ca8e76735eef67e2e8015263ca279c/68747470733a2f2f706f7365722e707567782e6f72672f73706972616c2f646174612d677269642f726571756972652f706870)](https://packagist.org/packages/spiral/data-grid)[![Latest Stable Version](https://camo.githubusercontent.com/293c5890230c6e77e561fb8448b05fe8f68db3207ba645972b4f4ce8776e8e42/68747470733a2f2f706f7365722e707567782e6f72672f73706972616c2f646174612d677269642f762f737461626c65)](https://packagist.org/packages/spiral/data-grid)[![phpunit](https://github.com/spiral/data-grid/actions/workflows/phpunit.yml/badge.svg)](https://github.com/spiral/data-grid/actions)[![psalm](https://github.com/spiral/data-grid/actions/workflows/static-analysis.yml/badge.svg)](https://github.com/spiral/data-grid/actions)[![Codecov](https://camo.githubusercontent.com/ae52744449b2b7d88de60ca80793f98373bcff24c6e94abd4fb98b654905a716/68747470733a2f2f636f6465636f762e696f2f67682f73706972616c2f646174612d677269642f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/spiral/data-grid)[![Total Downloads](https://camo.githubusercontent.com/5b67c49490fb021b128a281096b95aebebc93f2427911fd0773e669d6600f0ed/68747470733a2f2f706f7365722e707567782e6f72672f73706972616c2f646174612d677269642f646f776e6c6f616473)](https://packagist.org/packages/spiral/data-grid)[![type-coverage](https://camo.githubusercontent.com/68a628ea0b18a0f2dafef9c063cc399a6b99900d9c4e5786c3d648b055409a29/68747470733a2f2f73686570686572642e6465762f6769746875622f73706972616c2f646174612d677269642f636f7665726167652e737667)](https://shepherd.dev/github/spiral/data-grid)[![psalm-level](https://camo.githubusercontent.com/5420da8503aa905887ba26b2e4b928ea57de1f9a59dc4185366a1670cd55d041/68747470733a2f2f73686570686572642e6465762f6769746875622f73706972616c2f646174612d677269642f6c6576656c2e737667)](https://shepherd.dev/github/spiral/data-grid)

**[Documentation](https://spiral.dev/docs/component-data-grid)** | [Framework Bundle](https://github.com/spiral/framework)

What is Data Grid?
------------------

[](#what-is-data-grid)

**Data Grid** is a PHP component that acts as an intelligent data query abstraction layer. It automatically converts user interface specifications (filters, sorting, pagination) into database queries or data transformations

Think of it as a smart translator that sits between your user interface and your data sources. You define once what operations users are allowed to perform on your data (what fields they can filter, how they can sort, pagination limits), and the component handles all the complex logic of:

- Input validation and security
- Query optimization
- Multi-source compatibility - Working with databases, arrays, APIs, or custom data sources
- Result formatting - Providing consistent pagination and metadata.

Let's imagine you're building an **E-commerce website** where customers need to find products among thousands of items

**Your users want to:**

- **Filter products** by price range ($50-$200), category (Electronics), brand (Apple)
- **Sort results** by popularity, price (low to high), or newest arrivals
- **Navigate pages** - show 20 products per page instead of overwhelming them with 10,000 items at once

```
GET: /api/products?min_price=50&max_price=200&category=Electronics&sort_by=popularity&sort_direction=desc&page=2&limit=20

```

**Without Data Grid**, you'd need to write repetitive code for every single page AND manually process all user input in your controllers:

```
// Products Controller - Manual input processing nightmare
public function products(Request $request)
{
    // 1. Manual input validation and sanitization
    $filters = [];
    $sorts = [];
    $page = 1;
    $limit = 20;

    // Process price filter
    if ($request->has('min_price')) {
        $minPrice = $request->get('min_price');
        if (!is_numeric($minPrice) || $minPrice < 0) {
            throw new ValidationException('Invalid minimum price');
        }
        $filters['min_price'] = (float)$minPrice;
    }

    if ($request->has('max_price')) {
        $maxPrice = $request->get('max_price');
        if (!is_numeric($maxPrice) || $maxPrice < 0) {
            throw new ValidationException('Invalid maximum price');
        }
        $filters['max_price'] = (float)$maxPrice;
    }

    // Process category filter
    if ($request->has('category')) {
        $category = trim($request->get('category'));
        $allowedCategories = ['Electronics', 'Clothing', 'Books', 'Sports'];
        if (!in_array($category, $allowedCategories)) {
            throw new ValidationException('Invalid category');
        }
        $filters['category'] = $category;
    }

    // Process name search
    if ($request->has('search')) {
        $search = trim($request->get('search'));
        if (strlen($search) < 2) {
            throw new ValidationException('Search term too short');
        }
        $filters['search'] = $search;
    }

    // Process sorting
    if ($request->has('sort_by')) {
        $sortBy = $request->get('sort_by');
        $allowedSorts = ['price', 'name', 'created_at', 'popularity_score'];
        if (!in_array($sortBy, $allowedSorts)) {
            throw new ValidationException('Invalid sort field');
        }
        $sorts['field'] = $sortBy;

        $sortDirection = $request->get('sort_direction', 'asc');
        if (!in_array($sortDirection, ['asc', 'desc'])) {
            throw new ValidationException('Invalid sort direction');
        }
        $sorts['direction'] = $sortDirection;
    }

    // Process pagination
    if ($request->has('page')) {
        $page = (int)$request->get('page');
        if ($page < 1) {$page = 1;}
    }

    if ($request->has('limit')) {
        $limit = (int)$request->get('limit');
        $allowedLimits = [10, 20, 50, 100];
        if (!in_array($limit, $allowedLimits)) {
            $limit = 20; // default
        }
    }

    // 2. Manual query building with Laravel Query Builder
    $query = Product::query();

    // Apply price filters
    if (isset($filters['min_price'])) {
        $query->where('price', '>=', $filters['min_price']);
    }

    if (isset($filters['max_price'])) {
        $query->where('price', 'addFilter('price', new Between('price', new NumericValue()));
        //                  ↑                    ↑
        //              Input Key          Database Field
        //          (from user request)    (actual column)

        $this->addFilter('category', new Equals('category', new StringValue()));
        //                  ↑                      ↑
        //              Input Key            Database Field

        $this->addFilter('search', new Like('name', new StringValue()));
        //                  ↑                   ↑
        //             Input Key         Database Field
        //         (?filter[search]=...)   (searches in 'name' column)

        // Allow sorting by these fields
        $this->addSorter('price', new Sorter('price'));
        //                 ↑                   ↑
        //             Input Key         Database Field
        //        (?sort[price]=desc)    (sorts by 'price' column)

        $this->addSorter('popularity', new Sorter('popularity_score'));
        //                    ↑                        ↑
        //               Input Key              Database Field
        //        (?sort[popularity]=desc)    (sorts by 'popularity_score' column)

        // Set pagination rules
        $this->setPaginator(new PagePaginator(20, [10, 20, 50, 100]));
    }
}
```

### Why Separate Input Keys from Database Fields?

[](#why-separate-input-keys-from-database-fields)

Input keys provide a stable API while allowing database schema changes

```
// Input key stays the same, but you can change database structure
$this->addFilter('search', new Like('product_name', new StringValue()));

// Later change to search multiple fields:
$this->addFilter('search', new Any(
    new Like('product_name', new StringValue()),
    new Like('description', new StringValue())
));
```

Now **any interface** (web page, mobile app, API) can use this schema:

```
// Controller - same code works for web, mobile, API
public function products(ProductSchema $schema, GridFactoryInterface $factory, ProductRepository $products): array
{
    // User input: ?filter[price]=50,200&sort[popularity]=desc&paginate[page]=2
    $grid = $factory->create($products->select(), $schema);

    return [
        'products' => iterator_to_array($grid),      // [Product objects]
        'total' => $grid->getOption(GridInterface::COUNT),     // 1,247 total items
        'page' => $grid->getOption(GridInterface::PAGINATOR),  // Current page info
        'filters' => $grid->getOption(GridInterface::FILTERS), // Applied filters
    ];
}
```

How Data Grid Works
-------------------

[](#how-data-grid-works)

1. User makes a request with filters, sorting, or pagination parameters
2. Grid Schema validates the request against predefined rules
3. Input Processor sanitizes and converts user input safely
4. Compiler Engine determines the best way to fulfill the request
5. Writer generates the appropriate query (SQL, API call, array operation)
6. Data Source executes the operation and returns raw results
7. Grid View formats results with pagination metadata and applied filters

Getting Started
---------------

[](#getting-started)

1. **Install the component:**

```
composer require spiral/data-grid-bridge spiral/cycle-bridge
```

2. **Define your first schema:**

```
class UserSchema extends GridSchema {
    public function __construct() {
        $this->addFilter('name', new Like('name', new StringValue()));
        $this->addSorter('created_at', new Sorter('created_at'));
        $this->setPaginator(new PagePaginator(25));
    }
}
```

3. **Use in your controller:**

```
public function users(UserSchema $schema, GridFactoryInterface $factory, UserRepository $users) {
    $grid = $factory->create($users->select(), $schema);
    return ['users' => iterator_to_array($grid)];
}
```

4. **Frontend integration:**

```

        Newest first
        Oldest first

```

License:
--------

[](#license)

MIT License (MIT). Please see [`LICENSE`](./LICENSE) for more information. Maintained by [Spiral Scout](https://spiralscout.com).

###  Health Score

53

—

FairBetter than 97% of packages

Maintenance51

Moderate activity, may be stable

Popularity44

Moderate usage in the ecosystem

Community23

Small or concentrated contributor base

Maturity79

Established project with proven stability

 Bus Factor2

2 contributors hold 50%+ of commits

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 ~48 days

Recently: every ~262 days

Total

44

Last Release

295d ago

Major Versions

v0.2.0 → v1.0.02019-12-10

v1.2.4 → 2.7.02020-12-23

2.14.1 → 3.0.x-dev2022-09-13

PHP version history (4 changes)v0.1.0PHP ^7.2

2.7.0PHP &gt;=7.2

2.10.0PHP &gt;=7.4

3.0.x-devPHP &gt;=8.1

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/796136?v=4)[Anton Tsitou](/maintainers/wolfy-j)[@wolfy-j](https://github.com/wolfy-j)

---

Top Contributors

[![SerafimArts](https://avatars.githubusercontent.com/u/2461257?v=4)](https://github.com/SerafimArts "SerafimArts (16 commits)")[![butschster](https://avatars.githubusercontent.com/u/773481?v=4)](https://github.com/butschster "butschster (14 commits)")[![msmakouz](https://avatars.githubusercontent.com/u/67324318?v=4)](https://github.com/msmakouz "msmakouz (8 commits)")[![roxblnfk](https://avatars.githubusercontent.com/u/4152481?v=4)](https://github.com/roxblnfk "roxblnfk (4 commits)")[![spiralbot](https://avatars.githubusercontent.com/u/100867204?v=4)](https://github.com/spiralbot "spiralbot (4 commits)")[![vvval](https://avatars.githubusercontent.com/u/11367763?v=4)](https://github.com/vvval "vvval (3 commits)")[![samsonasik](https://avatars.githubusercontent.com/u/459648?v=4)](https://github.com/samsonasik "samsonasik (1 commits)")

---

Tags

data-gridphpquery-builderspecification

###  Code Quality

TestsPHPUnit

Static AnalysisPsalm, Rector

Type Coverage Yes

### Embed Badge

![Health badge](/badges/spiral-data-grid/health.svg)

```
[![Health](https://phpackages.com/badges/spiral-data-grid/health.svg)](https://phpackages.com/packages/spiral-data-grid)
```

###  Alternatives

[ipub/gravatar

Gravatar creator for Nette Framework

122.0k1](/packages/ipub-gravatar)

PHPackages © 2026

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