PHPackages                             verclam/smart-fetch-bundle - 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. verclam/smart-fetch-bundle

ActiveSymfony-bundle[Database &amp; ORM](/categories/database)

verclam/smart-fetch-bundle
==========================

This bundle will help avoiding make n+1 in symfony doctrine entities

0.2(2y ago)216[7 issues](https://github.com/Medwas/smart-fetch-bundle/issues)[1 PRs](https://github.com/Medwas/smart-fetch-bundle/pulls)MITPHPPHP &gt;=8.1

Since Oct 21Pushed 2y ago1 watchersCompare

[ Source](https://github.com/Medwas/smart-fetch-bundle)[ Packagist](https://packagist.org/packages/verclam/smart-fetch-bundle)[ RSS](/packages/verclam-smart-fetch-bundle/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (3)Dependencies (6)Versions (7)Used By (0)

Intro to Verclam/Smart-Fetch-Bundle
===================================

[](#intro-to-verclamsmart-fetch-bundle)

Enhance your project performance by avoiding N+1 queries with Smart Fetch Bundle.

Latest updates
--------------

[](#latest-updates)

For notes about the latest changes please read [`CHANGELOG`](https://github.com/kalilovsky/smart-fetch-bundle/blob/master/CHANGELOG.md), for required changes in your code please read [`UPGRADE`](https://github.com/kalilovsky/smart-fetch-bundle/blob/master/docs/upgrade.md)chapter of the documentation.

Requirements:
-------------

[](#requirements)

- Verclam/Smart-Fetch-Bundle's master is compatible with Symfony `>=5.4` versions.
- "doctrine/persistence" and "doctrine/doctrine-bundle" `>=2.2` version is required.
- PHP `>=8.0` version is required.

Installation :
--------------

[](#installation-)

### Pretty simple with [Composer](http://packagist.org), run

[](#pretty-simple-with-composer-run)

```
composer require verclam/smart-fetch-bundle
```

Usage :
-------

[](#usage-)

### 1 - Configuration

[](#1---configuration)

Nothing to do, the bundle is ready to use, you can start using it and implement it directly to you project.

### 2 - Use in controller's method

[](#2---use-in-controllers-method)

To use the bundle in a controller's method, you have to add the `#[SmartFetch]` attribute to the parameter or the method.

```
    #[Route('{id}/user', name: 'user_get', requirements: [ 'id' => '\d+' ], methods: ['GET'])]
    #[SmartFetch(
        queryName: 'id',
        class: User::class,
        joinEntities:
        [
            'articles',
            'articles.comments',
            'articles.categories',
            'comments.likedBy'
        ],
        argumentName: 'user'
    )]
    public function index(User $user): Response
    {

        return $this->render('user/get.html.twig', [
            'user'              => $user,
        ]);
    }
```

- `queryName`: The name of the query parameter in the URL that will be used to fetch the entity 'mandatory'.
- `class`: The class of the entity that will be fetched 'mandatory'.
- `joinEntities`: The relations that will be fetched, the order of the relation is important.
- `argumentName`: The name of the variable in the method's parameter 'mandatory if you fetch more than one entity'.

### 3 - Use in controller's parameter

[](#3---use-in-controllers-parameter)

```
    #[Route('{id}/user', name: 'user_get', requirements: [ 'id' => '\d+' ], methods: ['GET'])]
    public function index(
        #[SmartFetch(
            queryName: 'id',
            joinEntities:
            [
                'articles',
                'articles.comments',
                'articles.categories',
                'comments.likedBy'
            ],
        )] User $user
    ): Response
    {

        return $this->render('user/get.html.twig', [
            'user'              => $user,
        ]);
    }
```

- `queryName`: The name of the query parameter in the URL that will be used to fetch the entity 'mandatory'.
- `joinEntities`: The relations that will be fetched, the order of the relation is important.
- `argumentName`: Not mandatory.
- `class`: Not mandatory.

### Entities

[](#entities)

Suppose that you have a project when a `User` can post `Article` and `Comment`, a `User` can also like a `Comment`, and an `Article` can belong to many `Category`. The entities for this project will be like this:

#### User

[](#user)

```
    // ... other properties
    #[ORM\Column(length: 255)]
    private ?string $firstname = null;

    #[ORM\Column(length: 255)]
    private ?string $lastname = null;

    #[ORM\Column(type: Types::DATETIME_MUTABLE)]
    private ?\DateTimeInterface $birthDate = null;

    #[ORM\OneToMany(mappedBy: 'createdBy', targetEntity: Article::class)]
    private Collection $articles;

    #[ORM\OneToMany(mappedBy: 'createdBy', targetEntity: Comment::class)]
    private Collection $comments;

    #[ORM\ManyToMany(targetEntity: Comment::class, mappedBy: 'likedBy')]
    private Collection $likedComments;
```

#### Article

[](#article)

```
    // ... other properties
    #[ORM\Column(length: 255)]
    private ?string $title = null;

    #[ORM\Column(type: Types::TEXT)]
    private ?string $content = null;

    #[ORM\ManyToOne(inversedBy: 'articles')]
    #[ORM\JoinColumn(nullable: false)]
    private ?User $createdBy = null;

    #[ORM\ManyToMany(targetEntity: Category::class, inversedBy: 'articles')]
    private Collection $categories;

    #[ORM\OneToMany(mappedBy: 'article', targetEntity: Comment::class)]
    private Collection $comments;
```

#### Comment

[](#comment)

```
    // ... other properties
    #[ORM\Column(type: Types::TEXT)]
    private ?string $content = null;

    #[ORM\ManyToOne(inversedBy: 'comments')]
    #[ORM\JoinColumn(nullable: false)]
    private ?User $createdBy = null;

    #[ORM\ManyToOne(inversedBy: 'comments')]
    #[ORM\JoinColumn(nullable: false)]
    private ?Article $article = null;

    #[ORM\ManyToMany(targetEntity: User::class, inversedBy: 'likedComments')]
    private Collection $likedBy;
```

#### Category

[](#category)

```
    // ... other properties
    #[ORM\Column(length: 255)]
    private ?string $name = null;

    #[ORM\ManyToMany(targetEntity: Article::class, mappedBy: 'categories')]
    private Collection $articles;
```

### Controller

[](#controller)

Now, suppose that you want to get a `User` with all his `Article` and `Comment` and all the `Category` of each `Article` and all the `User` who liked each `Comment` of each `Article`, and to show it in a view, the controller and view.

#### Controller

[](#controller-1)

```
    #[Route('{id}/user', name: 'user_get', requirements: [ 'id' => '\d+' ], methods: ['GET'])]
    public function show(User $user): Response
    {
        return $this->render('user/show.html.twig', [
            'user' => $user,
        ]);
    }
```

#### View

[](#view)

```
    Hello {{ user }}! ✅

    The article that you created are:

        {% for article in user.articles %}

                {{ article.title }}
                This article has {{ article.comments|length }} comments which are.

                    {% for comment in article.comments %}

                            {{ comment.content ~ ' created by:' ~ comment.createdBy ~ ' and liked by :'  }}
                            {% for liker in comment.likedBy %}
                                {{ liker }}
                            {% endfor %}

                    {% endfor %}

                Category:
                {% for category in article.categories %}
                    {{ category.name }}
                {% endfor %}

        {% endfor %}

```

### What do you think Symfony and Doctrine will do ?

[](#what-do-you-think-symfony-and-doctrine-will-do-)

[![img.png](docs/images/database_explosion.png)](docs/images/database_explosion.png)Explosion, yes because the `User` in the controller will be fetched with this simple SQL query:

```
SELECT t0.id AS id_1, t0.firstname AS firstname_2, t0.lastname AS lastname_3, t0.birth_date AS birth_date_4 FROM user t0 WHERE t0.id = '11';
```

All the relation will be a `Proxy` and every time you will access to a relation, a new SQL query will be executed to fetch only one entity. It's easy when the view in rendered, if you take a look on the Symfony profiler toolbar, in the `Doctrine` section , you will see it in `Yellow` color in other terms, `Warning` it's probably a `N+1` query.

Here is the query metrics:

[![img_1.png](docs/images/n_plus_1_query_metrics_1.png)](docs/images/n_plus_1_query_metrics_1.png)

[![img_2.png](docs/images/n_plus_1_query_metrics_2.png)](docs/images/n_plus_1_query_metrics_2.png)

### How to avoid this ?

[](#how-to-avoid-this-)

With `Smart Fetch Bundle` you can fetch all the relations that you want, and you can also fetch the relations of the relations, and the relations of the relations of the relations, and so on in only `ONE QUERY`.

#### How to do it ?

[](#how-to-do-it-)

After installing the bundle, the only thing that you have to do is to add the `#[SmartFetch]` attribute to the property or the methode and indicate what do you want to fetch.

#### Example:

[](#example)

##### Controller:

[](#controller-2)

###### On the method:

[](#on-the-method)

```
    #[Route('{id}/user', name: 'user_get', requirements: [ 'id' => '\d+' ], methods: ['GET'])]
    #[SmartFetch(
        queryName: 'id',
        class: User::class,
        joinEntities:
        [
            'articles',
            'articles.comments',
            'articles.categories',
            'comments.likedBy'
        ],
        argumentName: 'user'
    )]
    public function index(User $user): Response
    {

        return $this->render('user/get.html.twig', [
            'user'              => $user,
        ]);
    }
```

###### On the parameter:

[](#on-the-parameter)

```
    #[Route('{id}/user', name: 'user_get', requirements: [ 'id' => '\d+' ], methods: ['GET'])]
    public function index(
        #[SmartFetch(
            queryName: 'id',
            joinEntities:
            [
                'articles',
                'articles.comments',
                'articles.categories',
                'comments.likedBy'
            ],
        )] User $user
    ): Response
    {

        return $this->render('user/get.html.twig', [
            'user'              => $user,
        ]);
    }
```

### What do you think Symfony and Doctrine will do ?

[](#what-do-you-think-symfony-and-doctrine-will-do--1)

Nothing bad, the `User` and all the relations that you want will be fetched in only one query. The query metrics will be like this:

[![img.png](docs/images/smart_fetch_query_metrics_1.png)](docs/images/smart_fetch_query_metrics_1.png)[![img_1.png](docs/images/smart_fetch_query_metrics_2.png)](docs/images/smart_fetch_query_metrics_2.png)

Isn't it amazing ? 😍

Don't waste your time, install the bundle and enjoy it.

PS: If you're thinking of using this bundle to retrieve several hundred entities in a single query, take a pause and think again, because it won't be reasonable, Doctrine will lose his mind when hydrating thousands of rows to the entity, so our advice is to use this bundle to find the right balance between accepting a certain degree of n+1 and making joins.

###  Health Score

18

—

LowBetter than 8% of packages

Maintenance0

Infrequent updates — may be unmaintained

Popularity9

Limited adoption so far

Community9

Small or concentrated contributor base

Maturity46

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 87.5% 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 ~87 days

Total

3

Last Release

758d ago

PHP version history (2 changes)0.1PHP &gt;=8.0

0.2PHP &gt;=8.1

### Community

Maintainers

![](https://www.gravatar.com/avatar/d32aa8893f309d003a69d389d521536d0368ff2f3529c63636b6bc9337ec1cb2?d=identicon)[kalilov](/maintainers/kalilov)

---

Top Contributors

[![kalilovsky](https://avatars.githubusercontent.com/u/79199532?v=4)](https://github.com/kalilovsky "kalilovsky (28 commits)")[![wassayd](https://avatars.githubusercontent.com/u/45386184?v=4)](https://github.com/wassayd "wassayd (4 commits)")

### Embed Badge

![Health badge](/badges/verclam-smart-fetch-bundle/health.svg)

```
[![Health](https://phpackages.com/badges/verclam-smart-fetch-bundle/health.svg)](https://phpackages.com/packages/verclam-smart-fetch-bundle)
```

###  Alternatives

[sylius/sylius

E-Commerce platform for PHP, based on Symfony framework.

8.4k5.6M650](/packages/sylius-sylius)[sulu/sulu

Core framework that implements the functionality of the Sulu content management system

1.3k1.3M152](/packages/sulu-sulu)[sonata-project/entity-audit-bundle

Audit for Doctrine Entities

644989.8k1](/packages/sonata-project-entity-audit-bundle)[contao/core-bundle

Contao Open Source CMS

1231.6M2.3k](/packages/contao-core-bundle)[ec-cube/ec-cube

EC-CUBE EC open platform.

78527.0k1](/packages/ec-cube-ec-cube)[open-dxp/opendxp

Content &amp; Product Management Framework (CMS/PIM)

7310.3k29](/packages/open-dxp-opendxp)

PHPackages © 2026

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