open-tiny-orm_初探Atlas-提供的ORM

open-tiny-orm

This article was peer reviewed by Paul M. Jones and Scott Molinari. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

本文由Paul M. JonesScott Molinari进行同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!

AtlasORM is a data mapper implementation for your persistence model (not your domain model)”

AtlasORM是针对您的持久性模型(而不是您的域模型)的数据映射器实现”

By definition, a Data Mapper moves data between objects and a database and isolates them from one another. With a Data Mapper, the in memory objects don’t even need to know that a database exists. It does not need to know the SQL interface or database schema; it doesn’t even need the domain layer to know it exists!

根据定义,数据映射器在对象和数据库之间移动数据,并将它们彼此隔离。 使用数据映射器,内存对象甚至不需要知道数据库的存在。 它不需要知道SQL接口或数据库模式; 它甚至不需要域层就知道它的存在!

This might lead us to thinking that, in Atlas, the persistence layer is totally disconnected from the database, but that is not quite what happens. Atlas uses the term Record to indicate that its objects are not domain entities. An Atlas Record is passive; not an active record. Unlike most ORMs, its objects represent the persistence model, not the domain model. Think of it as representing how the data is stored and not as real world representations.

这可能使我们想到,在Atlas中 ,持久层与数据库完全断开连接,但是事实并非如此。 Atlas使用术语“ Record来表示其对象不是域实体。 Atlas记录是被动的; 不是活动记录。 与大多数ORM不同,其对象代表持久性模型,而不是域模型。 可以将其视为代表如何存储数据,而不是作为现实世界的代表。

Illustration of the god Atlas carrying a big globe

What’s the reasoning behind it?

它背后的原因是什么?

The creator of Atlas, Paul Jones, based it on this article from Mehdi Khalili. The idea was to create an alternative to the Active Record pattern (an object that carries both data and behavior, and is intrinsically connected to the database).

Atlas的创建者Paul Jones基于Mehdi Khalili的这篇文章 。 这个想法是创建Active Record模式(一种既包含数据又包含行为,并固有地连接到数据库的对象)的替代方法。

Citing the author:

引用作者:

“I wanted an alternative to Active Record that would allow you to get started about as easily as [with] Active Record for your persistence model, and then refactor more easily towards a richer domain model as needed.”

“我想要Active Record的替代品,它可以让您像持久性模型一样简单地开始使用Active Record,然后根据需要更轻松地重构为更丰富的域模型。”

Altas has its own set of characteristics that differentiates it from others.

Altas具有自己的一套特色,这使其与众不同。

  • No annotations – Under the premise that the code should be in the code and not in the comments.

    无注释 –在代码应位于代码中而不是注释中的前提下。

  • No migrations or database modification logic – Since Atlas is decoupled from the database (not entirely, the package, as a whole, needs to interact with the database to be able to move data back and forth), it makes sense that it only acts as a model of the schema and not has a creator and manager of it.

    无需迁移或数据库修改逻辑 –由于Atlas与数据库解耦(整体而言,该软件包需要与数据库进行交互才能来回移动数据)(并非完全如此),因此,它仅起到以下作用:模式的模型,没有模型的创建者和管理者。

  • No lazy-loading – The creator thought of lazy loading as being useful, but ultimately too much trouble for what it is worth.

    没有延迟加载 –创作者认为延迟加载是有用的,但最终却为它带来了太多麻烦。

  • No data type abstractions – The database types are exposed and available where possible.

    没有数据类型抽象 -数据库类型公开并且在可能的情况下可用。

  • Composite key support – Atlas supports both composite primary keys and composite foreign keys.

    复合键支持 – Atlas支持复合主键和复合外键。

Now that we have a general idea of Atlas and what it stands for, let’s have a look at it in use.

现在,我们对Atlas及其含义有了一个大致的了解,让我们来看看它的使用情况。

安装 (Installation)

Because Atlas is still in development at the time of this writing, we will install the latest, cutting edge version. Also, we will install the CLI package for ease of development. That leaves us with the following composer.json file:

由于撰写本文时Atlas仍在开发中,因此我们将安装最新的最新版本。 另外,我们将安装CLI软件包以简化开发。 剩下下面的composer.json文件:

{
    "require": {
        "atlas/orm": "@dev"
    },
    "require-dev": {
        "atlas/cli": "@dev"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/App/"
        }
    }
}

We also have some PSR-4 there for autoloading, which we will use further down the line.

我们也有一些PSR-4用于自动加载,我们将在以后的代码中使用它们。

设定 (Setting it up)

Earlier on, we said that Atlas doesn’t act like a schema creator and manager. That means that we don’t have the tools for database creation. In this example, we are creating a new and simple database from scratch, but Atlas will work with any database you already have in place.

之前,我们说过Atlas不像架构创建者和管理者那样工作。 这意味着我们没有用于数据库创建的工具。 在此示例中,我们将从头开始创建一个新的简单数据库,但是Atlas可以与您已有的任何数据库一起使用。

We will use a very simple database design. We will have products and categories in separate database tables. Using MySQL, let’s create our database:

我们将使用非常简单的数据库设计。 我们将在单独的数据库表中提供产品和类别。 使用MySQL ,创建数据库:

CREATE DATABASE atlasorm;
USE DATABASE atlasORM;
CREATE TABLE `category` (`category_id` int(11) NOT NULL, `name` varchar(255) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `products` (`product_id` int(11) NOT NULL, `name` varchar(255) NOT NULL, `category_id` int(11) NOT NULL) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE `category` ADD PRIMARY KEY (`category_id`);
ALTER TABLE `products` ADD PRIMARY KEY (`product_id`);
ALTER TABLE `category` MODIFY `category_id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `products` MODIFY `product_id` int(11) NOT NULL AUTO_INCREMENT;

After this, we add some values to both tables:

之后,我们向两个表添加一些值:

INSERT INTO `category` (`category_id`, `name`) VALUES (1, 'Steel'), (2, 'Wood'), (3, 'Leather');
INSERT INTO `products` (`product_id`, `name`, `category_id`) VALUES (1, 'Nails', 1), (2, 'Pipe', 1), (3, 'Chair', 2), (4, 'Screwdriver', 1);

With this information laid out, we can now set up our ORM. Atlas has very little code generation. To generate the skeleton classes (both the Table class and the Mapper class), we will first have to create a .php file where we will have our database connection information. This way, the generator will know what properties it should generate.

有了这些信息,我们现在可以设置我们的ORM。 Atlas几乎没有代码生成。 为了生成框架类(Table类和Mapper类),我们首先必须创建一个.php文件,在其中我们将拥有数据库连接信息。 这样,生成器将知道应该生成什么属性。

<?php
// connection.php
return ['mysql:dbname=testdb;host=localhost', 'username', 'password'];

Then, lets run the atlas-skeleton generator. The atlas-skeleton generator is a CLI tool that will use the database connection information on the connection.php file, the class name, and the given namespace to generate the skeleton classes. Please note that we have to run the generator for each individual table in the database.

然后,运行atlas-skeleton生成器。 atlas-skeleton生成器是一个CLI工具,它将使用connection.php文件,类名和给定名称空间上的数据库连接信息来生成骨架类。 请注意,我们必须为数据库中的每个表运行生成器。

./vendor/bin/atlas-skeleton.php --dir=./src/App/DataSource --conn=/path/to/connection.php --table=products App\\DataSource\\Products
./vendor/bin/atlas-skeleton.php --dir=./src/App/DataSource --conn=/path/to/connection.php --table=category App\\DataSource\\Category

After running the atlas-skeleton command, we can check the /src/App/Datasource folder and see that skeletons for both products and category were created. Both of these skeletons contain two files. A Mapper file, that’s almost empty and a Table class, which contains a description of the database table. This is all the setup we need.

运行atlas-skeleton命令后,我们可以检查/src/App/Datasource文件夹,并查看是否已创建productscategory框架。 这两个框架都包含两个文件。 一个Mapper文件(几乎为空)和一个Table类,其中包含对数据库表的描述。 这就是我们需要的所有设置。

Now, let’s finally see it in action!

现在,让我们终于看到它的实际效果!

CRUD操作 (CRUD Operations)

With everything set up, lets focus on using our ORM. For this, we need an Atlas instance, and we create one using the AtlasContainer class.

完成所有设置后,让我们专注于使用我们的ORM。 为此,我们需要一个Atlas实例,并使用AtlasContainer类创建一个实例。

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Atlas\Orm\Mapper\Mapper;
use Atlas\Orm\AtlasContainer;
use App\DataSource\Category\CategoryMapper;
use App\DataSource\Products\ProductsMapper;

$atlasContainer = new AtlasContainer('mysql:host=host;dbname=atlasorm','username', 'password' );
$atlasContainer->setMappers([ProductsMapper::CLASS, CategoryMapper::CLASS]);

$atlas = $atlasContainer->getAtlas();

Nothing too fancy here, we autoload our vendor classes, include our mappers, and create an Atlas instance with both our Products and Category Mapper information.

这里没什么花哨的,我们会自动加载供应商类,包括映射器,并使用ProductsCategory映射器信息创建Atlas实例。

(Reading)

To read a Record or a RecordSet from our database we can use the following:

要从数据库中读取RecordRecordSet ,我们可以使用以下方法:

$categoryRecord = $atlas->fetchRecord(CategoryMapper::CLASS, '2');

This will return a single Record, the one with ID 2. For a RecordSet:

这将返回一个记录,即ID为2的记录。对于RecordSet:

$categoryRecordSet = $atlas
    ->select(CategoryMapper::CLASS)
    ->orderBy(['category_id DESC'])
    ->limit(10)
    ->fetchRecordSet();

This will fetch a RecordSet of the last 10 categories ordered by category_id.

这将获取由category_id排序的最后10个类别的RecordSet。

创建,更新和删除 (Creating, updating and deleting)

For creating, updating and deleting, Atlas uses the same principle. Obtaining a Record and manipulating it:

对于创建,更新和删除,Atlas使用相同的原理。 获取记录并进行操作:

// start insert
$newCategory = $atlas->newRecord(CategoryMapper::CLASS);
$newCategory->name = "Bone";

// create a transaction
$transaction = $atlas->newTransaction();
$transaction->insert($newCategory);

// execute the transaction
$ok = $transaction->exec();
if ($ok) {
    echo "Transaction success.";
} else {
    echo "Transaction failure. ";
}

As we can see, creating is pretty straightforward. For updating and deleting we can use the same principle:

如我们所见,创建非常简单。 对于更新和删除,我们可以使用相同的原理:

//Start updating
$newCategory = $atlas->fetchRecord(CategoryMapper::CLASS, '2');
$newCategory->name = "Wood";

// create a transaction
$transaction = $atlas->newTransaction();

// plan work for the transaction
$transaction->update($newCategory);
//$transaction->delete($newCategory);

// execute the transaction plan
$ok = $transaction->exec();
if ($ok) {
    echo "Transaction success.";
} else {
    echo "Transaction failure. ";
}
//End updating

In this case, we can use either update together with new table values to update a Record, or delete to delete this same Record.

在这种情况下,我们既可以将update与新表值一起使用来更新Record,也可以使用delete删除相同的Record。

关于关系的简要说明 (A quick note on Relationships)

Atlas supports all four kinds of relationships: OneToMany, ManyToOne, OneToOne and MantToMany. To add relationships, we have to add them in the Mapper classes.

Atlas支持所有四种关系: OneToManyManyToOneOneToOneMantToMany 。 要添加关系,我们必须将它们添加到Mapper类中。

In our particular case, let’s imagine that each category can have many products. A one to many relationship. Our CategoryMapper would look like this:

在我们的特定情况下,让我们想象每个类别可以有许多产品。 一对多的关系。 我们的CategoryMapper如下所示:

<?php

namespace App\DataSource\Category;

use Atlas\Orm\Mapper\AbstractMapper;
use App\DataSource\Products\ProductsMapper;

/**
 * @inheritdoc
 */
class CategoryMapper extends AbstractMapper
{
    /**
     * @inheritdoc
     */
    protected function setRelated()
    {
        $this->oneToMany('products', ProductsMapper::CLASS);
    }
}

By default, the relationship will take the primary key on the table of origin and map it to the corresponding column in the destination table. Of course, in the case where we have a many to one relationship this is not possible. In that case the reverse will be done. The primary key on the destination table will be mapped to the corresponding column on the table of origin.

默认情况下,该关系将采用原始表上的主键,并将其映射到目标表中的相应列。 当然,在我们有多对一关系的情况下,这是不可能的。 在这种情况下,将相反。 目标表上的主键将映射到源表上的相应列。

So, if we want to fetch a RecordSet of our categories with their related product records, we can use the with() method, just like this:

因此,如果我们想获取带有相关产品记录的类别的RecordSet,则可以使用with()方法,如下所示:

$newRecord = $atlas
    ->select(CategoryMapper::CLASS)
    ->with([
        'products'
    ])
    ->fetchRecordSet();

With the knowledge of how the basic CRUD operations work, we can now take a look at some more practical examples of code using Atlas.

了解了基本CRUD操作的工作原理之后,我们现在来看一下使用Atlas的一些更实际的代码示例。

一些实际的例子 (Some practical examples)

It’s great to have a tool like Atlas and to see how it works. But ultimately, we want to see it being used in more practical scenarios. While we will not build a full fledged application, we will look at some possible uses.

拥有Atlas之类的工具并查看其工作原理非常棒。 但最终,我们希望看到它在更实际的场景中使用。 虽然我们不会构建完整的应用程序,但我们将研究一些可能的用途。

There are some operations that we use every day without even noticing, like fetching information, inserting and deleting database records. How would those look using Atlas?

我们每天都会使用一些操作,甚至没有引起注意,例如获取信息,插入和删除数据库记录。 使用Atlas的外观如何?

<?php

require_once __DIR__ . '/vendor/autoload.php';

use Atlas\Orm\Mapper\Mapper;
use Atlas\Orm\AtlasContainer;
use App\DataSource\Category\CategoryMapper;
use App\DataSource\Products\ProductsMapper;

/**
* This function will create and return our Atlas instance set up
* with both our Mappers
*
* @return $atlasContainer
*/
function getAtlasContainer(){

    $atlasContainer = new AtlasContainer('mysql:host=localhost;dbname=atlasorm','root', '' );
    $atlasContainer->setMappers([ProductsMapper::CLASS, CategoryMapper::CLASS]);

    return $atlasContainer->getAtlas();
}

/**
* This function will return a RecordSet of all our products
*
* @return RecordSet
*/
function getAllProducts(){

    $atlas = getAtlasContainer();

    $productsRecordSet = $atlas
            ->select(ProductsMapper::CLASS)
            ->fetchRecordSet();

    return $productsRecordSet;
}

/**
* This function will return a Record of the Product with the specified id
*
* @param int 
* @return Record 
*/
function getProductById( $id ){

    $atlas = getAtlasContainer();

    $productRecord = $atlas->fetchRecord(ProductsMapper::CLASS, $id);

    return $productRecord;
}

/**
* This function will insert a new product Record
*
* @param string $product_name
* @param int $category_name 
*/
function addProduct( $product_name, $category_id ){

    $atlas = getAtlasContainer();

    //First we check if the category exists
    $categoryRecord = $atlas->fetchRecord(CategoryMapper::CLASS, $category_id);

    //if our category exists we will insert our product
    if( $categoryRecord ){
        //Start insert
        $newProduct = $atlas->newRecord(ProductsMapper::CLASS);
        $newProduct->name = $product_name;
        $newProduct->category_id = $category_id;

        // create a transaction
        $transaction = $atlas->newTransaction();
        $transaction->insert($newProduct);

        // execute the transaction
        $ok = $transaction->exec();
        if ($ok) {
            echo "Transaction success.";
        } else {
            // get the exception that was thrown in the transaction
            $exception = $transaction->getException();
            $work = $transaction->getFailure();
            echo "Transaction failure. ";
            echo $work->getLabel() . ' threw ' . $exception->getMessage();
        }
        //End insert
    }else{
        echo 'The category is not valid.';
    }
}

/**
* This function will delete a product Record
*
* @param id $product_id
*/
function deleteProduct( $product_id ){

    $atlas = getAtlasContainer();

    //First, lets check if the product with $product_id exists
    $productRecord = $atlas->fetchRecord(ProductsMapper::CLASS, $product_id);

    if( $productRecord ){
        //Delete the product
        $transaction = $atlas->newTransaction();
        $transaction->delete($productRecord);

        // execute the transaction
        $ok = $transaction->exec();
        if ($ok) {
            echo "Transaction success.";
        } else {
            // get the exception that was thrown in the transaction
            $exception = $transaction->getException();
            $work = $transaction->getFailure();
            echo "Transaction failure. ";
            echo $work->getLabel() . ' threw ' . $exception->getMessage();
        }
    }else{
        echo 'The product you are trying to delete does not exist.';
    }
}

A very basic skeleton of operations using Atlas (void of OOP and modern programming concepts due this being just a demo).

使用Atlas的基本操作框架(由于这只是一个演示,因此没有OOP和现代编程概念)。

注意事项 (Caveats)

Finally, let’s look at two caveats.

最后,让我们看一下两个警告。

  • Atlas uses code generation. If you change a database table that already had its code generated and you re-generate it, the mapper class will not be overridden. This is specially helpful if you wrote custom code in the mapper class.

    Atlas使用代码生成 。 如果更改已经生成了其代码的数据库表,然后重新生成它,则映射器类将不会被覆盖。 如果您在mapper类中编写了自定义代码,这将特别有用。

  • Atlas is a work in progress, 1.0.0-alpha1 was very recently released. Be very careful when using it, especially if you try to use it in a production environment, as breaking changes may affect your projects.

    Atlas是一项正在进行工作 ,1.0.0-α1是最近发布的。 在使用它时要特别小心,尤其是如果您尝试在生产环境中使用它,因为破坏更改可能会影响您的项目。

结论 (Conclusion)

The concept behind Atlas is original, and it’s easy to understand and use. The no annotations and very little code generation aspects are really strong selling points, especially if you, just like me, like to keep your code as decoupled and independent as possible.

Atlas背后的概念是原始的,易于理解和使用。 没有注释和很少的代码生成方面是真正的卖点,尤其是如果您像我一样喜欢保持代码尽可能地分离和独立。

What do you think of Atlas? Will you give it a spin?

您如何看待Atlas? 你会旋转吗?

翻译自: https://www.sitepoint.com/a-first-look-at-atlas-the-orm-that-delivers/

open-tiny-orm

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值