简体   繁体   中英

Doctrine Query Builder and Silex

In my Silex project I use the Doctrine Query Builder from the Database Abstraction Layer package.

"doctrine/dbal": "^2.5"

I register it in my application container like this.

/**
 * Make a connection to the database.
 */
$app['db'] = function() use($app) {
    $connectionParams = [
        'dbname' => $_ENV['DBNAME'],
        'user' => $_ENV['DBUSER'],
        'password' => $_ENV['DBPASS'],
        'host' => $_ENV['DBHOST'],
        'driver' => $_ENV['DBDRIVER'],
    ];

    return \Doctrine\DBAL\DriverManager::getConnection($connectionParams);
};

/**
 * Instantiate the query builder
 */
$app['db.builder'] = function() use($app) {
    return new \Doctrine\DBAL\Query\QueryBuilder($app['db']);
};

When I want to query for database records I am using a repository pattern and then inject the query builder instance into the repository and then I use the query builder in the repository my repositories are created like this.

$app['repository.user'] = function() use($app) {
    return new App\Repositories\UserRepository($app['db.builder']);
};

$app['repository.book'] = function() use($app) {
    return new App\Repositories\BookRepository($app['db.builder']);
};

In the user repository I query for an user to fetch the id and after that I query the book repository for the books that belong to the user, however the problem is that when I use the query builder in the book repository it is already filled with the previous user table. Do I need to reset the query builder instance or am I doing something wrong with registering the query builder into the container.

QueryBuilder is registered as a usual service and as such only one instance of it will be created. If you happen to populate any user data to it before you query some books, that's the way it is.

To avoid this problem, you might define db.builder as a factory service*. That way, every time you request it, a new instance will be created:

$app['db.builder'] = $app->factory(function () use ($app) {
    return new \Doctrine\DBAL\Query\QueryBuilder($app['db']);
});

Read more about service definitions in Silex at http://silex.sensiolabs.org/doc/master/services.html .

* http://silex.sensiolabs.org/doc/master/services.html#factory-services

By default services are shared, so when you ask for some service $app['xxx'] same copy is returned. You should say to container that function that returns service is a factory to get new instance on each call.

$app['db.builder'] = $app->factory(function() use($app) {
    return new \Doctrine\DBAL\Query\QueryBuilder($app['db']);
});

In any case query builder will be filled with something after second call to repository, in your case repository keeps and uses one copy of query builder. Better keep db in your repository, add method to get new query builder and use it in methods that select data from DB.

$app['repository.book'] = function() use($app) {
    return new App\Repositories\BookRepository($app['db']);
};

class \App\Repositories\BookRepository
{
    protected $db;
    protected $table = 'book';

    public function __construct($db)
    {
        $this->db = $db;
    }

    public function getQueryBuilder()
    {
        $qb = new \Doctrine\DBAL\Query\QueryBuilder($this->db);
        $qb->from($this->table);
        return $qb;
    }

    public function find1()
    {
        $qb = $this->getQueryBuilder();
        $qb...;
        return...;
    }

    public function find2()
    {
        $qb = $this->getQueryBuilder();
        $qb...;
        return...;
    }
}

Or move to Doctrine ORM

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM