那么……我做错了吗?
是的。如果您真的不想使用 ORM,那么使用 ORM 接口是没有意义的。
我认为最好的方法是根本不考虑实现细节。为存储库引入您自己的接口:
interface Products
{
/**
* @param string $slug
*
* @return Product[]
*/
public function findBySlug($slug);
}
interface Orders
{
/**
* @param Product $product
*
* @return Order[]
*/
public function findProductOrders(Product $product);
}
并使用 ORM 实现它们:
class DoctrineProducts implements Products
{
private $em;
public function __construct(EntityManager $em)
{
$this->em = $em;
}
public function findBySlug($slug)
{
return $this->em->createQueryBuilder()
->select()
// ...
}
}
或 Rest 客户端:
class RestOrders implements Orders
{
private $httpClient;
public function __construct(HttpClient $httpClient)
{
$this->httpClient = $httpClient;
}
public function findProductOrders(Product $product)
{
$orders = $this->httpClient->get(sprintf('/product/%d/orders', $product->getId()));
$orders = $this->hydrateResponseToOrdersInSomeWay($orders);
return $orders;
}
}
您甚至可以让一些方法使用 http 客户端,而另一些则使用单个存储库中的数据库。
将您的存储库注册为服务并使用它们,而不是直接调用Doctrine::getRepository():
services:
repository.orders:
class: DoctrineOrders
arguments:
- @doctrine.orm.entity_manager
始终依赖于您的存储库接口,而不是特定的实现。换句话说,始终使用存储库接口类型提示:
class DefaultController
{
private $orders;
public function __construct(Orders $orders)
{
$this->orders = $orders;
}
public function indexAction(Product $product)
{
$orders = $this->orders->findProductOrders($product);
// ...
}
}
如果您不将控制器注册为服务:
class DefaultController extends Controller
{
public function indexAction(Product $product)
{
$orders = $this->get('repository.orders')->findProductOrders($product);
// ...
}
}
这种方法的一个巨大优势是您可以随时更改实现细节。 mysql 搜索不够好?让我们使用弹性搜索,它只是一个存储库!
如果您需要调用 $product->getOrders() 并在后台从 API 获取订单,在学说的延迟加载和事件侦听器的帮助下应该仍然可以。