# Repository

在上一章，所有的查询是直接写在你的测试用例的方法中的。但对于程序的组织来说，为了隔离，复用和测试这些查询 Doctrine 提供了一个专门的 repository 类，它允许你保存所有查询逻辑到一个中心位置。

所有的 repository 类，都应该放到 Bundle 的 Repositories 文件夹中。

要定义repository类，有两种方法。

## 一、在实体类中添加 repository 类的声明：

```php
// src/DemoBundle/Entities/Product.php
<?php
namespace DemoBundle\Entities;

use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Entity
 * @ORM\Table(name="products")
 * @ORM\Entity(repositoryClass="DemoBundle\Repositories\ProductRepository")
 */
class Product
{
    //...
}
```

新建 Repositories 文件夹，在 Repositories 下新建 `ProductRepository` 类：

```php
//src/DemoBundle/Repositories/ProductRepository.php
<?php
namespace DemoBundle\Repositories;

use Doctrine\ORM\EntityRepository;


class ProductRepository extends EntityRepository
{
    public function findAllOrderedByName()
    {
        return $this->getEntityManager()
            ->createQuery(
                'SELECT p FROM DemoBundle\Entities\Product p ORDER BY p.name ASC'
            )
            ->getResult();
    }
}
```

> 在 Repository 类中可以通过 $this->getEntityManager() 方法类获取entity管理。

你就可以像使用默认的方法一样使用这个新定义的方法了：

```php
//src/DemoBundle/Tests/ProductTest.php
/**
 * A basic test example.
 *
 * @return void
 */
public function testProductRepository()
{
    //这一行取出了Doctrine的 entity manager 对象，它负责处理数据库的持久化（写入）和取出对象的过程。
    $em = app('registry')->getManager('default');

    $productRepository = $em->getRepository(Product::class);

    $products = $productRepository->findAllOrderedByName();

    $this->assertEquals(count($productRepository->findAll()), count($products));
}
```

> 当使用一个自定义的 repository 类时，你依然可以访问原有的默认查找方法，比如find() 和findAll()等。

使用注解的方式 Doctrine 会在调用 `$em->getRepository(Product::class);` 时，通过实体的注解找到实体对应的 repository 类。这种方式是 Doctrine 默认提供的，其有一个确定就是：这样定义一个实体只能有一个 repository ，将程序所有自定义查询都放到一个类里面，随着项目的发展，势必会导致 repository 类的肥大。这显然违反 SOLID 的单一职责原则。

## 二、通过在 repository 类中声明关联的实体

与第一种方法相反，这种方式是通过在 repository 类中声明所关联的实体，来实现 repository 类的声明。由于 repository 类初始化时，需要传入Entity Manager 和实体类的 ClassMate，为了方便初始化 repository 类，需要引入一个 Traits 。

在 Repositories 文件夹下新建一个 ProductPriceRepository 类，用来处理价格相关的查询 ：

```php
//src/DemoBundle/Repositories/ProductPriceRepository.php
<?php
namespace DemoBundle\Repositories;

use Doctrine\ORM\EntityRepository;
use EspierBundle\Traits\RepositoryFactory;
use DemoBundle\Entities\Product;

class ProductPriceRepository extends EntityRepository
{
    public static $entityClass = Product::class;

    use RepositoryFactory;

    public function findAllOrderedByName()
    {
        return $this->getEntityManager()
            ->createQuery(
                'SELECT p FROM DemoBundle\Entities\Product p ORDER BY p.name ASC'
            )
            ->getResult();
    }
}
```

使用方式如下：

```php
//src/DemoBundle/Tests/ProductTest.php

use DemoBundle\Repositories\ProductPriceRepository;

    //...

    public function testProductPriceRepository()
    {
        $productRepository = ProductPriceRepository::instance();

        $products = $productRepository->findAllOrderedByName();

        $this->assertEquals(count($productRepository->findAll()), count($products));
    }
```
