2

在哪里放置不断增长的查询列表(主要是所有“SELECT”查询)以及如何在我的整个应用程序中正确访问它们?
我正在寻找重构我的应用程序,该应用程序目前在我的应用程序中的所有服务中都有查询。我开始遇到 DRY 问题,其中我有与我已经编写的其他查询类似的大小查询。我也有一些地方我在这些查询上一遍又一遍地重写相同的格式。虽然我不担心替换我选择的数据库,但我确实担心以后优化我的查询,并想知道我是否通过没有更好地组织我的所有查询并将它们保存在某个中心位置而使这几乎不可能实现。我想遵循良好的 OOP 和 SoC 实践。

我当前在我的应用程序中查询的
示例 例如,我有一项服务,它提供某个事件中所有员工的 PDF(或内容)。该服务有一个方法可以接受一些输入(如“排序”、“过滤器”、“分页限制”),并将执行一个包含 10 个表连接的查询(在大多数表连接之前,查询会很好地切分到可管理的行数) JOIN 处理它)并以 PDF 所需的方式格式化结果。

到目前为止
,我的计划是在尝试完美地考虑我的数据库对象方面的需求时遇到了麻烦。我做了很多灵活的查询,并非完全是临时的,而是具有许多 JOIN 和 WHERE 的范围广泛的 SELECT,但我正在尽我所能:

  1. 域对象:我将创建类usersEntityeventsEntity(我的数据库中的每个匹配表)并在其中设置属性,例如,,id等。匹配数据库表字段。我可以创建 getter/setter,以便我可以变异(格式化日期等内容或执行验证)。我可能创建的实体并不真正代表我的数据库中的单个表,但可能代表我发现我在整个应用程序中重复使用的常见数据集合。也许叫它或。namephoneusersCollectionusersEvents

  2. 数据映射器:所以我把所有的 CUD(创建更新删除......不是读取......除了findById)操作并将它们存储在名为usersMapper,eventsMapper等的类中。映射器只接受一种类型的域对象(参见上面的#1)并将处理持久性(可能还有一小组非常基本的读取/选择,如findById($id)or findByEvent($eventId))。

现在这给我留下了越来越多的读取列表(SELECT 查询)以及将它们放在哪里?

我知道的选项,但不足。也许我误解了他们。

假设我有这样的查询

选择 users.first_name、users.last_name、events.name、events.date、venues.name、
场地_types.name, GROUP_CONCAT(user_friends.last_name) 作为“朋友”
来自用户
加入事件(events.id = users.event_id AND events.date = :date)
加入等....

所以我在整个 PHP 社区的博客中看到的是 3 种选择:

  1. 用类似Doctrine2的调用替换我的查询调用。他们的 DQL 似乎没有使调用可重用?事物似乎以某种方式连接起来,它会返回 5 个实体(基于 JOIN),所有属性都为用户、事件、场所、场所类型、朋友填充。这是我不需要的很多字段,我只想要每个字段中的 1 列,正如您在上面的查询中看到的那样。另外,我不打算在这样的查询后保存任何实体。
  2. 将我所有的查询调用替换为具有以下方法的存储库类:$someUserRepo-> findNameAndEventNameAndEventDateAndVenueNameAndVenueTypeNameAndFriends(). 除了真正疯狂的方法名称之外,我不确定我在这里传递了什么(条件如:sortlimit、几个where's?)或我返回什么(数组?对象?)。

  3. 使用他们的查询构建器将我所有的查询调用替换为ORM调用(如 Laravel 的 Eloquent)。我没有看到通过将我的 SQL 查询替换为please->ORM->build->this->for->me->where->andWhere->andWhere->I->dont->know->SQL. 顺便说一句,我几乎确信 ORM 是一种反模式?

我怀疑这只是整个 PHP 社区中一个非常复杂的问题。或者我在这里错过了什么?

4

1 回答 1

0

使用存储库是您考虑的唯一明智的选择。

这是你可以做的。

定义存储库接口

此界面将描述您如何查询某些实体。

interface Events
{
    /**
     * @return Event[]
     */
    public function findUserEvents(User $user, $sort, $limit, array $filters = []);
}

也许:

interface Events
{
    /**
     * @return Event[]
     */
    public function findUserEvents(User $user, Query $query);
}

class Query
{
    private $sort;
    private $limit;
    private $filters = [];

    // methods
}

实现接口

使用任何你喜欢的方式来实现接口——Doctrine、Eloquent、简单的 SQL——任何你觉得舒服的东西。

use Doctrine\ORM\EntityRepository;

class DoctrineEvents implements Events
{
    private $entityRepository;

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

    /**
     * @return Event[]
     */
    public function findUserEvents(User $user, $sort, $limit, array $filters = [])
    {
        // query $this->entityRepostory and return a collection of Event objects
    }
}

或者,您可以注入EntityManager而不是EntityRepository

只是为了向您展示提供替代实现是多么容易,这里有一个 SQL 实现:

class SqlEvents implements Events
{
    private $pdo;

    public function __construct(\PDO $pdo)
    {
        $this->pdo = $pdo;
    }

    /**
     * @return Event[]
     */
    public function findUserEvents(User $user, $sort, $limit, array $filters = [])
    {
        // use $this->pdo to query the database
        // you'll need to convert results into a collection of Event objects 
    }
}

简单的。对于测试,您可以提供内存实现。

依赖接口

每当您需要闪亮的新存储库时,请始终使用接口类型提示。这将允许您在您觉得一开始选择的那个不再起作用时替换实现。

class MyController
{
    private $events;
    private $view;

    public function __construct(Events $events, View $view)
    {
        $this->events = $events;
    }

    public function listAction(Request $request)
    {
        $user = // ...

        $events = $this->events->findUserEvents($user, 'ASC', 10);

        return $this->view->render('list.html', ['events' => $events]);
    }
}

在这个例子中,MyController可以与任何事件接口的实现一起工作。不管是Doctrine、eloquent还是sql。

选择实施

我个人的偏好是使用 Doctrine。它会处理很多事情,比如水合对象或缓存。当您需要更好的性能时,您是否需要手动编写 SQL 查询,您始终可以针对此特定查询执行此操作。并非所有事情都需要以单一方式实现,并且由于您与实现分离,因此很容易更改。

于 2015-03-19T19:59:02.457 回答