dpklt4291
2016-09-06 13:00
浏览 55
已采纳

Doctrine Query Builder和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.

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • douyo770657 2016-09-06 13:14
    已采纳

    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

    打赏 评论
  • dongzhuan1185 2016-09-06 13:27

    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

    打赏 评论

相关推荐 更多相似问题