potfur/stash

Stash: Mongo ODM

1.0.0-alpha 2015-08-23 11:43 UTC

This package is not auto-updated.

Last update: 2024-09-14 17:32:03 UTC


README

Scrutinizer Code Quality Code Coverage Build Status License

Stash 是一个用 PHP 编写的 MongoDB 对象-文档映射器。它在保留 MongoDB 易用性和数据处理方式的同时,添加了一个完全透明的持久化层。

这意味着 MongoDB 可以几乎以完全相同的方式使用,就像使用数组一样。这里的细微但重要的区别是,而不是返回普通数组,Stash 将返回对象(实体)。当然,Stash 不仅返回实体,还存储它们。

示例

模型定义

$models = new \Stash\ModelCollection();
$models->register(
    new \Stash\Model\Model(
        '\Order',
        [
            new \Stash\Converter\Type\Id(),
            new \Stash\Converter\Type\Document('customer'),
            new \Stash\Converter\Type\ArrayOf('items', Fields::TYPE_DOCUMENT)
        ]
    ),
    'order'
);

$models->register(
    new \Stash\Model\
        '\OrderItem',
        [
            new \Stash\Converter\Type\Scalar('name', Fields::TYPE_STRING),
            new \Stash\Converter\Type\Scalar('amount', Fields::TYPE_INTEGER),
            new \Stash\Converter\Type\Scalar('cost', Fields::TYPE_INTEGER)
        ]
    )
);

$models->register(
    new \Stash\Model\
        '\Voucher',
        [
            new \Stash\Converter\Type\Scalar('name', Fields::TYPE_STRING),
            new \Stash\Converter\Type\Scalar('cost', Fields::TYPE_INTEGER)
        ]
    )
);

$models->register(
    new \Stash\Model\
        '\Customer',
        [
            new \Stash\Converter\Type\Scalar('name', Fields::TYPE_STRING),
            new \Stash\Converter\Type\Document('address')
        ]
    )
);

$models->register(
    new \Stash\Model\
        '\CustomerAddress',
        [
            new \Stash\Converter\Type\Scalar('address', Fields::TYPE_STRING),
            new \Stash\Converter\Type\Scalar('city', Fields::TYPE_STRING),
            new \Stash\Converter\Type\Scalar('zip', Fields::TYPE_STRING)
        ]
    )
);

数据库连接

$types = [
    new \Stash\Converter\Type\IdType(),
    new \Stash\Converter\Type\BooleanType(),
    new \Stash\Converter\Type\IntegerType(),
    new \Stash\Converter\Type\DecimalType(),
    new \Stash\Converter\Type\StringType(),
    new \Stash\Converter\Type\DateType(),
    new \Stash\Converter\Type\ArrayType(),
    new \Stash\Converter\Type\DocumentType()
];

$proxyAdapter = new \Stash\ProxyAdapter(new \ProxyManager\Factory\LazyLoadingValueHolderFactory());
$converter = new \Stash\Converter\Converter($types);
$referencer = new \Stash\ReferenceResolver($models);
$documentConverter = new \Stash\DocumentConverter($converter, $referencer, $models, $proxyAdapter);
$eventDispatcher = new \Symfony\Component\EventDispatcher\EventDispatcher();

$connection = new \Stash\Connection(new \MongoClient(), $documentConverter, $eventDispatcher);
$connection->selectDB('test');

实体创建和存储

class Order
{
    private $id;
    private $customer;
    private $items;

    public function __construct($customer, $items)
    {
        $this->customer = $customer;
        $this->items = $items;
    }
}

class OrderItem
{
    private $name;
    private $amount;
    private $cost;

    public function __construct($name, $amount, $cost)
    {
        $this->name = $name;
        $this->amount = $amount;
        $this->cost = $cost;
    }
}

class Voucher
{
    private $name;
    private $discount;

    public function __construct($name, $discount)
    {
        $this->name = $name;
        $this->discount = $discount;
    }
}

class Customer
{
    private $name;
    private $address;

    public function __construct($name, CustomerAddress $address)
    {
        $this->$name = $name;
        $this->address = $address;
    }
}

class CustomerAddress
{
    private $address;
    private $city;
    private $zip;

    public function __construct($address, $city, $zip)
    {
        $this->address = $address;
        $this->city = $city;
        $this->zip = $zip;
    }
}

$order = new Order(
    new Customer('Joe Doe', new CustomerAddress('Mongo alley', 'Somewhere', '12345')),
    [
        new OrderItem('Foos', 10, 1000),
        new Voucher('Voucher', 250)
    ]
);

$connection->getCollection('order')->save($order);

这是存储的 MongoDB 的半 JSON 表示形式。在保存对象(实体)时,Stash 添加 _class 字段,其中存储类名

{
  "_id" : ObjectId("55746f4f87dee7bc0b000033"),
  "_class" : "Order",
  "customer" : {
    "_class" : "Customer",
    "address" : {
      "_class" : "CustomerAddress",
      "address" : "Mongo alley",
      "city" : "Somewhere",
      "zip" : "12345"
    }
  },
  "items" : [
    {
      "_class" : "OrderItem",
      "name" : "Foos",
      "amount" : 10,
      "cost" : 1000
    },
    {
      "_class" : "Voucher",
      "name" : "Voucher",
      "discount" : 250
    }
  ]
}                                                     

事件订阅

Stash 使用 Symfonys Event Dispatcher 来派发事件。

  • find.after 在从数据库读取文档并将其转换为实体实例后触发
  • persist.before 在将实体转换为数组文档之前触发
  • persist.after 在文档保存后(如果需要,更新新 _id
  • remove.before 在实体从数据库中删除之前触发

每个事件都由 Event 实体表示,类似于 Symfony 的 Event,但有两个方法 getPayloadsetPayload 来管理主题实体。

$subscriber = new \Fake\EventSubscriber(
    [
        \Stash\Events::FIND_AFTER,
        \Stash\Events::PERSIST_BEFORE,
        \Stash\Events::PERSIST_AFTER,
        \Stash\Events::REMOVE_BEFORE
    ]
);
$eventDispatcher->addSubscriber($subscriber);

配置代理

默认情况下,所有必需的代理类在运行时生成。生成使用大量的反射,可能会导致性能较差。为了防止这种情况,代理生成器需要配置为重用生成的代理。

$config = new \ProxyManager\Configuration();
$config->setProxiesTargetDir(__DIR__ . '/generated/proxy/);
spl_autoload_register($config->getProxyAutoloader());

$proxyAdapter = new \Stash\ProxyAdapter($config);