graze/dal

数据访问层

v1.0.0-beta5 2017-03-25 23:18 UTC

README

Graze.com

Travis branch Scrutinizer Scrutinizer Coverage PHP Version Packagist Packagist

DAL 是一个 PHP 数据访问层,旨在在您的应用程序和持久层之间添加一个额外的抽象层。这个库的主要目标是允许使用多个持久层,每个持久层可能采用不同的抽象模式,但都通过一个 单一 接口使用。

数据映射模式 在将数据持久化的细节隐藏在其接口后面方面非常出色。DAL 通过包含多个持久层并支持除数据映射以外的其他模式(例如 活动记录)来改进这种抽象。

我们正在内部使用它来将我们的应用程序转向一个更可管理的数据映射层,而不是我们当前的 active record 实现。这将是我们在可预见的未来跨 PHP 应用程序访问持久层的接口。我们将继续在 DAL 下方使用我们的 active record 实现,直到我们决定完全移除它,那时 DAL 将保持原位。

DAL 的主要接口模仿 Doctrine ORM 的 API,具有类似命名的 getRepository()persist()flush() 方法。通过 getRepository() 方法公开的仓库甚至实现了 Doctrine 的 ObjectRepository,所有这些都旨在与 PHP 中功能最完整的数据映射库之一具有共同基础。

您可以根据自己的喜好以任何方式进行安装,但我们推荐 Composer

{
    "require": {
        "graze/dal": "^1.0"
    }
}
$ composer require graze/dal

文档

开始使用 Eloquent

使用 Eloquent 与 DAL 需要安装 illuminate/eloquent 包。如果您在 Laravel 之外使用 Eloquent,以下资源可能会有所帮助: 如何在 Laravel 之外使用 Eloquent在 Laravel 之外使用 Eloquent

配置 DalManager

use Graze\Dal\Adapter\Orm\EloquentOrmAdapter;

$eloquentAdapter = EloquentOrmAdapter::createFromYaml(/*Eloquent DB Connection*/, ['path/to/config/eloquent.yml']);
$dal = new DalManager([$eloquentAdapter]);

在我们的应用程序中设置了一个 DalManager 对象,现在我们需要为我们的实体编写一些配置

编写配置

适配器提供两种配置方法 createFromYamlcreateFromArray,在本文档中所有示例都使用 YAML,但您也可以使用数组或任何可以转换为数组的任何内容。

App\Entity\Product:
    record: App\Eloquent\Product
    adapter: Graze\Dal\Adapter\Orm\EloquentOrmAdapter
    repository: App\Repository\ProductRepository
    table: products
    fields:
        id:
            mapsTo: id
            type: int
        name:
            mapsTo: name
            type: string

这是我们在应用程序中基本 Product 实体的配置。它配置了以下字段

  • record - 这是您持久层提供的类,在这种情况下是一个 Eloquent 模型。
  • adapter - 这是管理此实体的适配器。
  • repository(可选)- 这是此实体的自定义仓库类,如果在此处未指定,DAL 提供了一个通用仓库。
  • table - 这是特定于适配器的字段,在这种情况下,我们需要为我们提供 Eloquent 模型此实体的表名。
  • fields - 定义实体的每个字段,mapsTo 字段指的是我们将用于此字段的底层 Eloquent 模型的属性。

生成类

使用此配置,我们实际上不需要为我们的实体编写任何PHP代码,DAL可以为我们生成所有代码。通过运行此命令,DAL将生成一个实体类、一个存储库以及我们开始所需的底层Eloquent模型。

$ bin/dal generate path/to/config/eloquent.yml App src

使用DalManager

我们现在已经拥有了开始使用我们的实体所需的所有内容,操作方式如下

$product = $dalManager->getRepository('App\Entity\Product')->find(1);
echo $product->getName();

开始使用 Doctrine

开始使用Doctrine与开始使用Eloquent非常相似。关键区别在于DAL目前不支持为您生成底层的Doctrine类和配置,因此您需要自己编写这些代码。您还需要doctrine/orm包。

配置 DalManager

use Graze\Dal\Adapter\Orm\DoctrineOrmAdapter;
use Doctrine\ORM\EntityManager;

$em = new EntityManager(); // see Doctrine's documentation for setting this up
$doctrineAdapter = DoctrineOrmAdapter::createFromYaml($em, ['path/to/config/doctrine.yml']);
$dal = new DalManager([$doctrineAdapter]);

编写配置

App\Entity\Product:
    record: App\Doctrine\Product
    repository: App\Repository\ProductRepository
    fields:
        id:
            mapsTo: id
            type: int
        name:
            mapsTo: name
            type: string

您还需要编写底层Doctrine实体的Doctrine配置。

生成DAL实体和存储库

$ bin/dal generate path/to/config/doctrine.yml App src

注意:当前不支持生成Doctrine实体。您必须自己编写这些代码。

使用DalManager

$product = $dalManager->getRepository('App\Entity\Product')->find(1);
echo $product->getName();

开始使用 PDO

DAL还提供了一个非常简单的PdoAdapter,如果您不需要ORM提供的特性,但仍然想要一组合理的数据管理接口,那么您可以使用它。不过,这依赖于aura/sql包,所以请确保您已将其配置为应用的依赖项。

配置 DalManager

use Graze\Dal\Adapter\Pdo\PdoAdapter;
use Aura\Sql\ExtendedPdo;

$pdo = new ExtendedPdo(); // @see https://github.com/auraphp/Aura.Sql#lazy-connection-instance for setting this up
$pdoAdapter = PdoAdapter::createFromYaml($pdo, ['path/to/config/pdo.yml']);
$dal = new DalManager([$pdoAdapter]);

编写配置

App\Entity\Product:
    table: products
    repository: App\Repository\ProductRepository
    fields:
        id:
            mapsTo: id
            type: int
        name:
            mapsTo: name
            type: string

注意,这里没有record字段,因为没有底层的模型或记录类与PDO相关联,这是不必要的。

生成DAL实体和存储库

没有记录/模型需要生成,所以我们只需要通过运行以下命令来生成DAL实体和存储库

$ bin/dal generate path/to/config/pdo.yml App src

使用DalManager

$product = $dalManager->getRepository('App\Entity\Product')->find(1);
echo $product->getName();

关系

实体之间的关系应在DAL级别定义,而不是在底层持久化层中定义。这是为了方便不同适配器管理实体。

App\Entity\Customer:
    record: App\Eloquent\Customer
    repository: App\Repository\CustomerRepository
    table: customers
    fields:
        id:
            mapsTo: id
            type: int
        firstName:
            mapsTo: first_name
            type: string
        lastName:
            mapsTo: last_name
            type: string
    related:
        orders:
            type: oneToMany
            entity: App\Entity\Order
            foreignKey: customer_id
            collection: true

App\Entity\Order:
    record: App\Eloquent\Order
    table: orders
    fields:
        id:
            mapsTo: id
            type: int
        price:
            mapsTo: price
            type: float
    related:
        customer:
            type: manyToOne
            entity: App\Entity\Customer
            localKey: customer_id

此示例显示了客户和订单之间的简单关系。客户拥有许多订单,因此与订单实体有一个oneToMany类型,使用订单表上的customer_id字段作为外键,并表明这将是一个实体集合。

在订单实体配置中,我们定义了一个单独的客户字段,类型为manyToOne,因为订单属于客户,因此使用订单表上的customer_id字段作为本地键来确定哪个客户拥有此订单。

localKeyforeignKey指的是数据库表上的字段,而不是DAL实体或底层持久化层实体。因此,当前仅在关系的一侧至少有一个使用SQL作为其底层存储机制实体的情况下,才支持关系。

上面的示例显示了manyToOneoneToMany关系类型,还有manyToMany

多对多关系

多对多关系需要两个实体和一个存储它们之间关系的枢纽表

App\Entity\Order:
    record: App\Eloquent\Order
    table: orders
    fields:
        id:
            mapsTo: id
            type: int
        price:
            mapsTo: price
            type: float
    related:
        customer:
            type: manyToOne
            entity: App\Entity\Customer
            localKey: customer_id
        products:
            type: manyToMany
            entity: App\Entity\Product
            pivot: order_item
            localKey: order_id
            foreignKey: product_id
            collection: true

在这里,我们扩展了订单实体,使其与许多产品相关联,当然,单个产品也可以属于许多订单。

与其他关系一样,我们定义了typeentity,然后需要定义枢纽表以及两个键,localKeyforeignKey,这些键位于枢纽表上。其中,localKey是存储为此配置的实体ID的字段,在本例中为订单,而foreignKey是存储与我们相关联的实体ID的字段,在本例中为产品。

$ bin/dal generate path/to/config/eloquent.yml App src
$customer = $dalManager->getRepository('App\Entity\Customer')->findBy(['email' => 'customer@example.com']);
$orders = $customer->getOrders();

foreach ($orders as $order) {
    echo $order->getPrice();
}

使用多个适配器

use Graze\Dal\Adapter\Pdo\PdoAdapter;
use Graze\Dal\Adapter\Orm\EloquentOrmAdapter;
use Aura\Sql\ExtendedPdo;

$pdoAdapter = PdoAdapter::createFromYaml(new ExtendedPdo(/*see Aura docs*/), ['path/to/config/pdo.yml']);
$eloquentAdapter = EloquentOrmAdapter::createFromYaml(/* see eloquent docs */, ['path/to/config/eloquent.yml']);
$dalManager = new DalManager([$pdoAdapter, $eloquentAdapter]);

现在,我们已经设置了一个带有两个配置适配器的DalManager,我们可以编写配置来设置两个不同适配器的实体,并在它们之间创建关系

# eloquent.yml
App\Entity\Customer:
    record: App\Eloquent\Customer
    repository: App\Repository\CustomerRepository
    table: customers
    fields:
        id:
            mapsTo: id
            type: int
        firstName:
            mapsTo: first_name
            type: string
        lastName:
            mapsTo: last_name
            type: string
    related:
        orders:
            type: oneToMany
            entity: App\Entity\Order
            foreignKey: customer_id
            collection: true

# pdo.yml
App\Entity\Order:
    table: orders
    fields:
        id:
            mapsTo: id
            type: int
        price:
            mapsTo: price
            type: float
    related:
        customer:
            type: manyToOne
            entity: App\Entity\Customer
            localKey: customer_id

现在我们可以生成所需的全部实体、存储库和记录

$ bin/dal generate path/to/config/eloquent.yml App src

就像在所有之前的示例中使用的那样

$customer = $dalManager->getRepository('App\Entity\Customer')->findBy(['email' => 'customer@example.com']);
$orders = $customer->getOrders();

foreach ($orders as $order) {
    echo $order->getPrice();
}

这使我们能够通过不同的适配器和存储机制处理不同的数据,但通过单个接口创建它们之间的关系。每个实体在幕后如何管理都被抽象化,从我们的高级代码中分离出来。

这开辟了以下可能性:

use Graze\Dal\Adapter\Orm\EloquentOrmAdapter;
use App\Dal\Adapter\HttpAdapter;

$httpAdapter = HttpAdapter::createFromYaml(new GuzzleHttp\Client(), ['path/to/config/http.yml']);
$eloquentAdapter = EloquentOrmAdapter::createFromYaml(/* see eloquent docs */, ['path/to/config/eloquent.yml']);
$dalManager = new DalManager([$httpAdapter, $eloquentAdapter]);

数据库支持

理论上,DAL支持底层持久层(如Doctrine、Eloquent等)所支持的所有数据库。然而,DAL仅官方支持MySQL。这是因为处理关系类型的代码目前依赖于DAL中编写的SQL查询,这些查询仅在MySQL上进行过测试。

如果你正在使用持久层支持的数据库,不妨试试,在90%的情况下它可能表现得很出色。我们将在未来努力正式支持其他数据库。

贡献 & 支持

如果你有支持疑问,请创建一个issue并标记为“support”。如果你想贡献,请先创建一个PR或issue进行讨论。文档贡献非常受欢迎。

开发

有一个docker-compose文件,你可以使用它来启动开发和测试环境。

$ make install
$ make test

许可证

本库的内容由 Nature Delivered Ltd 根据 MIT许可证 发布。
你可以在 LICENSEhttps://open-source.org.cn/licenses/mit 找到此许可证的副本。