PHPackages                             shredio/doctrine-queries - 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. [Database &amp; ORM](/categories/database)
4. /
5. shredio/doctrine-queries

ActiveLibrary[Database &amp; ORM](/categories/database)

shredio/doctrine-queries
========================

v0.11.0(1y ago)06601MITPHPPHP &gt;=8.3CI passing

Since Jun 16Pushed 8mo agoCompare

[ Source](https://github.com/Shredio/doctrine-queries)[ Packagist](https://packagist.org/packages/shredio/doctrine-queries)[ RSS](/packages/shredio-doctrine-queries/feed)WikiDiscussions master Synced 1w ago

READMEChangelog (6)Dependencies (7)Versions (8)Used By (1)

Doctrine Queries
================

[](#doctrine-queries)

A simplified and type-safe PHP library for Doctrine ORM that provides an intuitive interface for database queries with automatic query building, result handling, and PHPStan integration.

Features
--------

[](#features)

- **Simple and intuitive API** for common database operations
- **Type-safe queries** with full PHPStan support
- **Multiple result formats** - objects, arrays, or scalar values
- **Flexible criteria system** with support for operators, null values, and arrays
- **Automatic query optimization** and result hydration
- **Memory-efficient iteration** for large datasets
- **Built-in PHPStan extensions** for static analysis

Requirements
------------

[](#requirements)

- PHP 8.3 or higher
- Doctrine ORM 3.2 or higher

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

[](#installation)

```
composer require shredio/doctrine-queries
```

Quick Start
-----------

[](#quick-start)

```
use Shredio\DoctrineQueries\DoctrineQueries;

// Initialize with Doctrine's ManagerRegistry
$queries = new DoctrineQueries($managerRegistry);

// Query entities as objects
$users = $queries->objects->findBy(User::class, ['status' => 'active'])->asArray();

// Query as associative arrays (faster hydration)
$userData = $queries->arrays->findBy(User::class, ['age >' => 18])->asArray();

// Query scalar values only
$userNames = $queries->scalars->findColumnValuesBy(User::class, 'name', ['status' => 'active'])->asArray();

// Use subqueries for complex filtering
$activeUsersWithPosts = $queries->objects->findBy(
    User::class,
    [
        'id' => $queries->subQuery(
            Post::class,
            ['status' => 'published'],
            select: ['author.id']
        )
    ]
)->asArray();

// Check if entities exist
$hasActiveUsers = $queries->existsBy(User::class, ['status' => 'active']);

// Count entities
$activeUserCount = $queries->countBy(User::class, ['status' => 'active']);

// Delete entities
$deletedCount = $queries->deleteBy(User::class, ['status' => 'inactive']);
```

Query Types
-----------

[](#query-types)

### Object Queries

[](#object-queries)

Returns full entity objects with all relationships loaded:

```
$users = $queries->objects->findBy(User::class, ['status' => 'active'])->asArray();
// Returns: User[]
```

### Array Queries

[](#array-queries)

Returns associative arrays (faster hydration, less memory usage):

```
$users = $queries->arrays->findBy(User::class, ['status' => 'active'])->asArray();
// Returns: array[]

// With field selection
$users = $queries->arrays->findBy(User::class, ['status' => 'active'], select: ['id', 'name'])->asArray();
// Returns: [['id' => 1, 'name' => 'John'], ...]

// With join configuration for optimal performance
$posts = $queries->arrays->findBy(
    Post::class,
    ['status' => 'published'],
    select: ['title', 'author.name'],
    joinConfig: ['author' => 'inner'] // Use inner join for better performance
)->asArray();
```

### Scalar Queries

[](#scalar-queries)

Returns primitive values only:

```
$userNames = $queries->scalars->findColumnValuesBy(User::class, 'name')->asArray();
// Returns: string[]

$userName = $queries->scalars->findSingleColumnValueBy(User::class, 'name', ['id' => 1]);
// Returns: string|null
```

Criteria System
---------------

[](#criteria-system)

The library supports a flexible criteria system with various operators:

### Basic Equality

[](#basic-equality)

```
// Equals
['name' => 'John']
['id' => 42]

// Explicit equals
['name =' => 'John']
```

### Comparison Operators

[](#comparison-operators)

```
// Not equals
['status !=' => 'inactive']

// Greater than
['age >' => 18]

// Greater than or equal
['age >=' => 18]

// Less than
['score ' => 18,
    'status' => 'active',
    'city' => ['Prague', 'Brno'],
    'deleted_at' => null
];
```

Sorting
-------

[](#sorting)

```
$users = $queries->arrays->findBy(
    User::class,
    ['status' => 'active'],
    orderBy: ['created_at' => 'DESC', 'name' => 'ASC']
)->asArray();
```

Field Selection
---------------

[](#field-selection)

```
// Select specific fields
$users = $queries->arrays->findBy(
    User::class,
    ['status' => 'active'],
    select: ['id', 'name', 'email']
)->asArray();

// Field aliasing
$users = $queries->arrays->findBy(
    User::class,
    ['status' => 'active'],
    select: ['id' => 'userId', 'name' => 'fullName']
)->asArray();

// Select related fields with automatic joins
$posts = $queries->arrays->findBy(
    Post::class,
    ['status' => 'published'],
    select: ['title', 'author.name', 'category.name']
)->asArray();

// Select all fields (without relations)
$posts = $queries->arrays->findBy(
    Post::class,
    ['status' => 'published'],
    select: ['*'] // Selects all fields
)->asArray();

// Select all fields including relations
$posts = $queries->arrays->findBy(
    Post::class,
    ['status' => 'published'],
    select: ['**'] // Selects all fields including ManyToOne relations
)->asArray();
```

Working with Results
--------------------

[](#working-with-results)

### Arrays

[](#arrays)

```
$users = $queries->arrays->findBy(User::class)->asArray();
foreach ($users as $user) {
    echo $user['name'];
}
```

### Memory-Efficient Iteration

[](#memory-efficient-iteration)

For large datasets, use yielding to avoid loading everything into memory:

```
foreach ($queries->arrays->findBy(User::class)->yield() as $user) {
    // Process one user at a time
    processUser($user);
}
```

### Key-Value Pairs

[](#key-value-pairs)

```
// Get id => name pairs
$userOptions = $queries->arrays->findPairsBy(User::class, 'id', 'name')->asArray();
// Returns: [1 => 'John', 2 => 'Jane', ...]
```

### Column Values

[](#column-values)

```
// Get all unique email domains
$domains = $queries->scalars->findColumnValuesBy(
    User::class,
    'email_domain',
    distinct: true
)->asArray();
```

Advanced Features
-----------------

[](#advanced-features)

### Subqueries

[](#subqueries)

Use subqueries for complex filtering and data retrieval:

```
// Find users who have published posts
$usersWithPosts = $queries->objects->findBy(
    User::class,
    [
        'id' => $queries->subQuery(
            Post::class,
            ['status' => 'published'],
            select: ['author.id']
        )
    ]
)->asArray();

// Find posts by users from specific cities
$posts = $queries->arrays->findBy(
    Post::class,
    [
        'author.id' => $queries->subQuery(
            User::class,
            ['city' => ['Prague', 'Brno']],
            select: ['id']
        )
    ]
)->asArray();
```

### Join Configuration

[](#join-configuration)

Control join types for optimal query performance:

```
// Use inner joins for better performance when you know relations exist
$posts = $queries->arrays->findBy(
    Post::class,
    ['author.status' => 'active'],
    select: ['title', 'author.name'],
    joinConfig: [
        'author' => 'inner' // Inner join for author relation
    ]
)->asArray();

// Mix of inner and left joins
$comments = $queries->arrays->findBy(
    Comment::class,
    [],
    select: ['content', 'author.name', 'post.title'],
    joinConfig: [
        'author' => 'left',  // Left join (author might be null)
        'post' => 'inner'    // Inner join (comment must have a post)
    ]
)->asArray();
```

Counting and Existence
----------------------

[](#counting-and-existence)

```
// Count entities
$activeUsers = $queries->countBy(User::class, ['status' => 'active']);

// Check existence
$hasAdmins = $queries->existsBy(User::class, ['role' => 'admin']);

// Count with complex criteria
$recentActiveUsers = $queries->countBy(User::class, [
    'status' => 'active',
    'last_login >' => new DateTime('-30 days')
]);

// Count specific field values
$uniqueEmails = $queries->countBy(User::class, [], field: 'email', distinct: true);

// Count with join configuration
$postsWithActiveAuthors = $queries->countBy(
    Post::class,
    ['author.status' => 'active'],
    joinConfig: ['author' => 'left']
);
```

Deletion Operations
-------------------

[](#deletion-operations)

```
// Delete entities by criteria
$deletedCount = $queries->deleteBy(User::class, ['status' => 'inactive']);

// Delete with complex criteria
$deletedOldUsers = $queries->deleteBy(User::class, [
    'last_login arrays->findBy(
    User::class,
    ['status' => 'active'],
    orderBy: ['created_at' => 'DESC'],
    select: ['id', 'name', 'email', 'created_at']
)->asArray();

// Get user count by status
$statusCounts = [];
foreach (['active', 'inactive', 'banned'] as $status) {
    $statusCounts[$status] = $queries->countBy(User::class, ['status' => $status]);
}

// Get recent user emails for newsletter
$recentEmails = $queries->scalars->findColumnValuesBy(
    User::class,
    'email',
    ['created_at >' => new DateTime('-7 days')]
)->asArray();
```

### Content Management

[](#content-management)

```
// Get published articles with author info using modern field path selection
$articles = $queries->arrays->findBy(
    Article::class,
    [
        'status' => 'published',
        'published_at ' => 0,
        'status' => 'active',
        'category.name' => ['Electronics', 'Books']
    ],
    orderBy: ['price' => 'ASC'],
    select: ['id', 'name', 'price', 'stock_quantity', 'category.name'],
    joinConfig: ['category' => 'inner']
)->asArray();

// Get products with recent orders using subqueries
$popularProducts = $queries->arrays->findBy(
    Product::class,
    [
        'id' => $queries->subQuery(
            OrderItem::class,
            ['order.created_at >' => new DateTime('-30 days')],
            select: ['product.id']
        )
    ],
    select: ['name', 'price']
)->asArray();

// Get order statistics
$orderStats = [
    'total' => $queries->countBy(Order::class),
    'pending' => $queries->countBy(Order::class, ['status' => 'pending']),
    'completed' => $queries->countBy(Order::class, ['status' => 'completed'])
];
```

Testing
-------

[](#testing)

```
composer test
```

Static Analysis
---------------

[](#static-analysis)

```
composer phpstan
```

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) file for details.

Contributing
------------

[](#contributing)

1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests for new functionality
5. Ensure all tests pass
6. Submit a pull request

Changelog
---------

[](#changelog)

See [releases](https://github.com/shredio/doctrine-queries/releases) for version history and changes.

###  Health Score

34

—

LowBetter than 74% of packages

Maintenance54

Moderate activity, may be stable

Popularity18

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% 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 ~3 days

Total

6

Last Release

406d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/312e788a47a251e05734378921d4596a91819b7de416fa18e77aa69e08798ea8?d=identicon)[Antik](/maintainers/Antik)

---

Top Contributors

[![MartkCz](https://avatars.githubusercontent.com/u/10145362?v=4)](https://github.com/MartkCz "MartkCz (50 commits)")

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

Type Coverage Yes

### Embed Badge

![Health badge](/badges/shredio-doctrine-queries/health.svg)

```
[![Health](https://phpackages.com/badges/shredio-doctrine-queries/health.svg)](https://phpackages.com/packages/shredio-doctrine-queries)
```

###  Alternatives

[easycorp/easyadmin-bundle

Admin generator for Symfony applications

4.3k18.3M417](/packages/easycorp-easyadmin-bundle)[rcsofttech/audit-trail-bundle

Enterprise-grade, high-performance Symfony audit trail bundle. Automatically track Doctrine entity changes with split-phase architecture, multiple transports (HTTP, Queue, Doctrine), and sensitive data masking.

12017.1k](/packages/rcsofttech-audit-trail-bundle)[kimai/kimai

Kimai - Time Tracking

4.8k9.4k1](/packages/kimai-kimai)[api-platform/doctrine-orm

Doctrine ORM bridge

314.8M106](/packages/api-platform-doctrine-orm)[2lenet/crudit-bundle

The easy like Crud'it Bundle.

1617.3k16](/packages/2lenet-crudit-bundle)

PHPackages © 2026

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