该包已被废弃,不再维护。未建议替代包。

为 csrdelft.nl 定制的创新命名对象关系映射库

v1.9.0 2020-03-05 16:18 UTC

README

Maintainability Rating Coverage Build Status

C.S.R. Delft ORM

一个简单的 PHP 对象关系映射器。我们目前在 csrdelft.nl 上使用此库进行生产。

安装

使用 composer 安装

composer require csrdelft/orm

在可以使用 ORM 之前,必须初始化缓存和数据库。memcache 需要一个 UNIX 套接字或主机和端口,数据库和数据库管理员需要主机、数据库、用户名和密码。之后,任何模型都可以访问数据库。

CsrDelft\Orm\Configuration::load([
  'cache_path' => '/path/to/data/dir/cache.socket', // Host or unix socket
  'cache_port' => 11211, // Optional if cache_path is a host
  'db' => [
    'host' => 'localhost',
    'db' => 'myDatabase',
    'user' => 'myUser',
    'pass' => 'myPass'
  ]
]);

使用

ORM 依赖于模型和实体。模型是与数据库交互的类。实体是包含数据库表定义的数据类。本文档将简要概述您开始之前需要了解的基本内容。

实体

实体是一个包含数据的对象,例如汽车、人等。

当您想要将实体保存到数据库中时,您必须扩展 PersistentEntity 类。实体必须包含一些变量,如下所述。实体必须只包含关于自身的逻辑,而不是关于其他类或同一实体的其他实例的逻辑。这应该在模型(或不是此库部分的控制器)中完成。

实体放在 model/entity/ 文件夹中,并命名为 EntityName.php

实体中的变量

对于实体的每个属性,都必须有一个公共变量。这些将在从数据库加载时由模型使用。

public $id;
public $num_wheels;
public $color;
$table_name

数据库中的表名。

protected static $table_name = 'cars';
$persistent_attributes

一个包含实体属性的数组,映射到类型。

类型是一个数组,具有以下值。

  • 0:来自 TPersistentAttributeType.enum)的类型
  • 1:此变量是否可为空?
  • 如果 0 是 T::Enumeration,则枚举类(扩展 PersistentEnum)。否则 'extra',例如 auto_increment 或注释。
protected static $persistent_attributes = [
  'id' => [T::Integer, false, 'auto_increment'],
  'num_wheels' => [T::Integer],
  'color' => [T::Enumeration, false, 'ColorEnum']
];
$primary_key

包含完整主键的数组。

protected static $primary_key = ['id'];
$computed_attributes

一个包含计算属性的数组。这将这些属性映射到函数并添加到 jsonSerialize

protected static $computed_attributes = [
  'my_val' => [T::Integer],
];

protected function getMyVal() {
  return 42;
}

示例

model/entities/Car.php

class Car extends PersistentEntity {
  public $id;
  public $num_wheels;
  public $color;

  public function carType() {
    if ($this->num_wheels == 4) {
      return "Normal car";
    } else {
      return "Weird car";
    }
  }

  protected static $table_name = 'cars';
  protected static $persistent_attributes = [
    'id' => [T::Integer, false, 'auto_increment'],
    'num_wheels' => [T::Integer],
    'color' => [T::Enumeration, false, 'ColorEnum']
  ];
  protected static $primary_key = ['id'];
}

模型

模型必须扩展 PersistenceModel 类。模型是特定实体的所有者。模型可以通过公共静态 instance() 方法在任何地方访问。然而,应尽可能避免这样做。

模型应放置在 model/ 目录中。

模型中的变量

模型有一些静态变量必须定义。

ORM

常量 ORM 定义了这个模型是哪个实体的所有者。这是一个字符串或类。

const ORM = 'Car';
const ORM = Car::class;
$default_order

这是选择数据库时使用的默认排序值。

protected static $default_order = 'num_wheels DESC';

模型中的函数

以下函数可以在模型中使用

find($criteria, $criteria_params, ...) : PersistentEntity[]

根据条件在数据库中查找实体。如果您以前使用过PHP中的PDO,那么这个语法的用法应该很熟悉。这里的 $criteria 是基础选择语句的 WHERE 子句,您可以在变量所在的位置放置 ?。条件参数是填充这些变量的地方。条件参数会自动过滤并安全用于用户输入。

CarModel::instance()->find('num_wheels = ? AND color = ?', [$normal_car_wheels, $car_color]);

count($criteria, $criteria_params) : int

计算满足条件的实体数量,与 find(..) 相同。创建类似于 SELECT COUNT(*) ... 的语句,这比在PHP中计数更快。

exists($entity) : boolean

检查实体是否存在于数据库中。

create($entity) : string

将新实体保存到数据库中。返回插入实体的ID。

update($entity) : int

将实体存储在数据库中,替换具有相同主键的实体。

delete($entity) : int

从数据库中删除实体。

示例

model/CarModel.php

require_once 'model/entity/Car.php';

class CarModel extends PersistenceModel {
  const ORM = 'Car';

  public function findByColor($color) {
    return $this->find('color = ?', [$color]);
  }
}

index.php

require_once 'model/CarModel.php';

$model = CarModel::instance();
$cars = $model->find();
$actual_cars = $model->find('num_wheels = 4');
$yellow_cars = $model->findByColor('yellow');

数据库更新

此ORM可以为您检查模型。要启用此功能,您需要将全局常量 DB_CHECK 定义为 true。之后,您可以检查 DatabaseAdmin 中是否有任何更新的表。这仅检查正在使用的表,必须在所有模型使用后执行。还有可能通过将全局常量 DB_MODIFY 定义为 true 来自动更新数据库。这将根据您的模型更新表。这不会尝试迁移数据,所以请小心。 DB_MODIFY 不会删除表。为此,您需要将 DB_DROP 定义为 true

if (DB_CHECK) {
    $queries = DatabaseAdmin::instance()->getQueries();
    if (!empty($queries)) {
        if (DB_MODIFY) {
            header('Content-Type: text/x-sql');
            header('Content-Disposition: attachment;filename=DB_modify_' . time() . '.sql');
            foreach ($queries as $query) {
                echo $query . ";\n";
            }
            exit;
        } else {
            var_dump($queries);
        }
    }
}

JSON字段

通过使用T::JSON,可以将数据库字段映射为数组或对象。在保存时,数据会被序列化为JSON格式,之后可以进行反序列化。在示例中,$reviews属性是一个JSON字段。

$model = CarModel::instance();
$car = $model->find()[0];

$review = new CarReview();
$review->userId = '1801';
$review->reviewText = 'Very good car!';

$car->reviews = [$review];
$model->update($car);

为了防止远程代码执行,只有允许的类才能进行序列化和反序列化。在属性定义的第三个元素中应指定允许的类列表。也可以传递null以允许所有类。

数据库事务

要在一个事务中包装多个数据库调用,可以使用Database::transaction(Closure)。这个函数将另一个函数包装在数据库事务中。任何抛出的异常都会导致事务回滚。如果数据库已经在一个事务中,这个函数将只会调用Closure而不会尝试创建新的事务。

$car = new Car();
Database::transaction(function () use ($car) {
    CarModel::instance()->create($car);
    CarWheelModel::instance()->create($car->getWheels());
});

依赖注入

您可以为DependencyManager提供自己的ContainerInterface,这个容器将用于一切。您仍然需要向容器提供DatabaseDatabaseAdminOrmMemcache的实例。

$container = $kernel->getContainer();

DependencyManager::setContainer($container);

$container->set(OrmMemcache::class, new OrmMemcache($cachePath));
$container->set(Database::class, new Database($pdo));
$container->set(DatabaseAdmin::class, new DatabaseAdmin($pdo));

还可以利用orm提供的依赖注入功能。orm使用一种非常简单的方式进行依赖注入。当创建模型实例时,它试图查找任何扩展DependencyManager的依赖项。如果找到,它们将被连接到模型中并可供使用。模型只能有一个版本,这由DependencyManager跟踪。

示例

class OwnerModel extends PersistenceModel {
  const ORM = 'Owner';

  /** @var CarModel */
  protected $carModel;

  public function __construct(CarModel $carModel) {
    $this->carModel = $carModel;
  }

  public function getCarsForOwner(Owner $owner) {
    return $this->carModel->find('owner = ?', [$owner->id]);
  }
}

class CarModel extends PersistenceModel {
  const ORM = 'Car';

  public function findByColor($color) {
    return $this->find('color = ?', [$color]);
  }
}
$owner = OwnerModel::instance()->find('id = 1');

$cars = OwnerModel::instance()->getCarsForOwner($owner);