sqrt-pro / db
db 是 SQRT 框架的一部分
Requires
- php: >=5.3.0
- danielstjules/stringy: ~1.9
- doctrine/collections: ~1.3
- robmorgan/phinx: ~0.4
- sqrt-pro/exception: ~0.1
- sqrt-pro/helpers: ~0.1
- sqrt-pro/image: ~0.1
- sqrt-pro/query-builder: ~0.1
- sqrt-pro/url: ~0.1
Requires (Dev)
- phpunit/phpunit: ~4.7
- satooshi/php-coveralls: ~0.7
This package is not auto-updated.
Last update: 2024-09-28 16:31:04 UTC
README
数据库操作可以通过两种方式实现 - 直接在数据库中执行查询,或与对象交互。
术语和使用的类
Manager
- 数据库管理器。存储数据库连接、仓库,并提供执行数据库直接查询的能力。Schema
- 数据库结构的描述,根据该结构生成迁移、Repository 和 Item 对象。Repository
- 代表数据库中的表的对象,允许进行 Item 对象的选择。Collection
- Item 对象的存储库,实现访问数组的接口,并提供操作集合的方法。Item
- 代表数据库中的记录的对象,包含与该记录相关的业务逻辑。
数据库连接
连接设置存储在 Manager
对象中,并通过方法设置
$manager->addConnection($host, $user, $pass, $db_name, $db_charset = 'utf8', $connection_name = null);
如果不指定连接名,它将被分配默认名称,默认使用。如果需要,可以添加多个连接,并指定连接名来访问它们。
$manager->getConnection($name = null); // Возвращает объект PDO
为了方便在受限制的虚拟主机环境中部署项目,提供了为组件使用的所有表设置前缀的选项
$manager->setPrefix($prefix);
这样,所有创建的表及其使用的对象都将使用该前缀。
注意!在用户生成的查询中,需要手动插入前缀!
直接与数据库交互
可以从 Manager 对象中获取 PDO 对象并直接执行查询,或者使用获取数据的方法。
建议通过 PDO 占位符(例如 WHERE id = :id
)将所有值插入到查询中,并将值通过 $values 参数传递。
可以将字符串或由 QueryBuilder 创建的 Query 对象传递给查询。在这种情况下,数据将从 Query 对象中自动提取并插入到查询中。
// Выполнить запрос в БД, возвращает \PDOStatement $manager->query($sql, $values = null, $connection = null) // Получить все записи в виде списка ассоциативных массивов. Если указать $key, значения этого столбца будет ключами списка. $manager->fetchAll($sql, $key = null, $values = null, $connection = null) // Получить одну строку в виде ассоциативного массива $manager->fetchOne($sql, $values = null, $connection = null) // Получить одно значение из первой строки ответа. $col - имя столбца, или будет возвращено значение первого столбца в ответе. $manager->fetchValue($sql, $col = null, $values = null, $connection = null) // Выбрать один столбец и возвратить список значений этого столбца $manager->fetchColumn($sql, $col = null, $values = null, $connection = null) // Получить массив вида ключ => значение из запроса $manager->fetchPair($sql, $values = null, $connection = null) // Применить $callable ко всем результатам выборки по очереди. // Первым аргументом будет передан массив содержащий текущую строку $callable($row) $manager->each($sql, $callable, $values = null, $connection = null)
为了调试,可以启用所有执行查询的日志记录
$manager->setDebug($debug = true); // Включить отладку $manager->getQueries(); // Получить список всех выполненных запросов, {query:..., values:..., time:...} $manager->getQueriesCount(); // Количество запросов к БД $manager->getQueriesTime(); // Суммарное время выполнения запросов к БД
事务
Manager 对象支持以下方法以方便地处理事务
$manager->beginTransaction($connection = null); // Начать транзакцию на соединении $connection $manager->commit($connection = null); // Применить транзакцию на соединении $connection $manager->rollback($connection = null); // Откатить транзакцию на соединении $connection $manager->inTransaction($connection = null); // Проверка, активна ли транзакция на соединении $connection
在事务中执行代码
$manager->transaction(\Closure $closure, $connection = null); // Выполнить $closure внутри транзакции на соединении $connection
对象 $closure
将以单个参数 - 对象 Manager
被调用。
在执行之前,事务将被打开,执行后完成(commit
),并且方法将返回 $closure
执行的结果。
如果 $closure
抛出异常,则事务将被回滚(rollback
)并且异常将被进一步抛出。
模式
模式包含数据库字段及其类型的逻辑表示。根据模式生成模型文件,还可以与数据库的当前状态进行比较,自动创建迁移文件。
通过继承基本模式类并重写 init()
和 relations()
方法来设置模式。
可能的字段类型
$schema->addInt($col, $default = 0, $signed = true, $length = 10) $schema->addBool($col) $schema->addChar($col, $length = 255) $schema->addFloat($col, $length = 10, $decimals = 2, $signed = false) $schema->addText($col, $size = false) $schema->addTime($col, $unix = true) $schema->addTimeCreated($col = 'created_at') $schema->addTimeUpdated($col = 'updated_at')
注意!表格只能包含 addTimeCreated
或 addTimeUpdated
中的一个字段,因为 MySQL 不允许创建多个具有 CURRENT_TIME 的字段。其余日期应使用常规的 addTime
方法设置。
除了“常规”字段外,还可以包含包含生成模型时额外逻辑的字段。
// Первичный ключ таблицы $schema->addId($col = 'id') // Поле INT и набор методов для работы с битовой маской $schema->addBitmask($col, array $options, $default = 0) // Поле ENUM, содержащее выбор из нескольких вариантов $schema->addEnum($col, array $options, $default = null) // Поле TEXT, содержащее сериализованный массив данных о файле $schema->addFile($column) // Поле TEXT, содержащее сериализованный массив данных о изображении $schema->addImage($column, array $size_arr = null)
ORM 基础类的生成
根据模式生成基本模型类,在其中为所有字段创建获取器和设置器,并考虑其类型,同时创建相关字段、方法和常量。
日期
-
设置器支持以任何支持
strtotime()
函数的格式指定日期。$item->setCreatedAt('2015-01-01 12:45'); $item->setCreatedAt('-7 days');
-
获取器支持由
date()
函数接受的格式。$item->getCreatedAt(false, 'd.m.Y H:i');
Float
-
获取器支持使用
number_format()
函数的格式。$item->getPrice(); // 12345.67 $item->getPrice(false, 1, ',', ' '); // 12 345,7
ENUM
ENUM 字段包含字段的有效选项列表。例如
$schema->addEnum('status', array('new', 'progress', 'done'));
-
对于所有选项,将生成形如
[column]_[value]
的常量,例如 STATUS_NEW, STATUS_DONE。 -
将生成一个常量名称数组,可以在子类中重写并设置易理解的名称。
protected static $status_arr = array( self::STATUS_NEW => 'new', self::STATUS_PROGRESS => 'progress', self::STATUS_DONE => 'done', );
-
如果尝试传递数组中不存在的值给设置器,将会抛出异常。
-
将生成额外的方法。
- 形如
get[column]Name()
的获取器,它将返回数组中包含的值的名称。 - 静态方法
Get[column]Arr()
,返回名称数组。 - 静态方法
GetNameFor[column]($status)
,返回指定值的名称。
- 形如
位掩码 - 位运算符
位掩码允许指定列表中的多个值并保存在一个字段中。值将以2的幂的形式表示数字。与ENUM的主要区别在于可以同时保存多个值。
$schema->addBitmask('status', array('payed', 'delivered', 'happy'))
-
对于所有选项,将生成形如
[column]_[value]
的常量,常量的值将分配为键数组的2的幂(0, 1, 2, ...)。const STATUS_PAYED = 1; const STATUS_DELIVERED = 2; const STATUS_HAPPY = 4;
-
将生成一个常量名称数组,可以在子类中重写并设置易理解的名称。
protected static $status_arr = array( self::STATUS_PAYED => 'payed', self::STATUS_DELIVERED => 'delivered', self::STATUS_HAPPY => 'happy', );
-
设置器有两种类型。
$item->addStatus(Item::STATUS_PAYED); $item->setStatus(array(Item::STATUS_PAYED, Item::STATUS_HAPPY));
-
获取器返回数组。
$item->getStatus(); // [1, 4]
-
检查是否设置了相应的标志。
$item->hasStatus(Item::STATUS_PAYED);
重要!不应过度使用位掩码来存储频繁变化的数据。对于这种情况,应使用多对多关系和外部参考表。
文件
保存文件信息的典型任务需要生成多个字段,并手动处理文件的上传。通常,这不需要在数据库内部搜索或处理这些数据,因此可以采取反规范化并简化过程。
$schema->addFile('pdf')
将在表中添加一个TEXT字段,用于存储序列化的文件属性数组。
- 设置器记录文件信息,并执行通过使用
setFilesPath()
在类初始化时指定的目录复制文件。 - 生成获取器,用于所有文件属性,形如 get[column][property],例如对于
column = "pdf"
。getPdf($default = false)
- 文件的相对路径。服务器上文件夹的路径通过初始化时通过setPublicPath()
设置。getPdfPath($default = false)
- 服务器上文件的路径getPdfUrl($default = false)
- URL对象,指向文件的相对路径getPdfSize($human = true)
- 文件大小。如果 $human == true,则应用格式化。getPdfName($default = false)
- 文件名称getPdfExtension($default = false)
- 文件扩展名
图像
加载图像的典型任务需要保存图像,有时需要调整大小和/或添加水印,并保存其多个版本。
$schema->addImage('image', array('thumb', 'medium', 'orig');
将在表中添加一个TEXT字段,用于存储序列化的文件属性数组。
- 设置器记录文件信息,并执行通过使用
setFilesPath()
在类初始化时指定的目录复制文件。 - 保存照片时,对每个尺寸调用私有方法
prepareImageFor[column]($file, $size)
,其中 size 是方案中指定的每个尺寸。如果该方法返回SQRT\Image
对象,则从该对象保存图像,否则保存原始图像。 - 生成获取器,用于所有文件属性,类似于File类型,但添加了图像尺寸,形如
get[column][size][property]
。 - 此外,创建了特定于图像的方法。例如,对于
column = "image", size = "thumb"
。getImageThumbWidth($default = false)
- 图像宽度getImageThumbHeight($default = false)
- 图像高度getImageThumbImg($alt = null, $attr = null, $default = false)
- 生成带有替换文件路径、宽度和高度的Img
标签
索引
方案包括创建一个或多个列的索引。
$schema->addIndex($column, $_ = null) $schema->addUniqueIndex($column, $_ = null)
表之间的关系
在InnoDB表中创建外键可以使用以下方法
$schema->addForeignKey($col, $schema, $foreign_id = null, $on_delete = null, $on_update = null)
或者创建一个关系,将生成在模型对象中的额外函数,实现此类关系的基本逻辑。
对于每个实体都可以指定不同的关系类型,例如,对于一本书(Book),可能有一个作者(Author),但作者可能有很多书。也就是说,关系 Book -> Author
是一对一的,但 Author -> Book
是一对多的。因此,每个关系及其类型都在其自己的模式中指定。
重要!所有创建关系的表必须是 InnoDB。
一对一
addOneToOne($schema, $col = null, $foreign_id = null, $on_delete = null, $on_update = null, $name = null, $one = null)
这是最简单的关系类型,当外部对象的 ID 明确指在当前对象的某个字段中。关系会在当前表中添加一个名为 $col
的 INT UNSIGNED DEFAULT NULL 字段。
例如:Book 对象包含一个 author_id 字段,它对应一个 Author 对象。
$schema->addOneToOne('Authors', 'author_id', 'id', Schema::FK_RESTRICT, Schema::FK_RESTRICT)
如果遵守列和表的命名规则,则可以为大多数列保留默认值。
$schema->addOneToOne('Authors')
当存在多个关系或需要与模式不同的实体名称时,$name
和 $one
参数允许指定关系的任意名称。
$schema->addOneToOne('Authors', 'author_id', 'id', Schema::FK_RESTRICT, Schema::FK_RESTRICT, 'MyAuthors', 'MyAuthor')
Book 对象将包含方法
/** @return \Author */ public function getMyAuthor($reload = false) /** @return static */ public function setMyAuthor(\Author $my_author) /** @return \Author */ protected function findOneMyAuthor($id)
一对多
addOneToMany($schema, $foreign_id = null, $col = null, $name = null, $one = null)
关系允许选择与当前对象有关系的多个对象。在当前数据库表中不进行任何更改。
例如:Author 对象与多个 Book 有关系,这些 Book 在 author_id 字段中指出了这种依赖关系。
$schema->addOneToMany('Books', 'book_id', 'id')
当存在多个关系或需要与模式不同的实体名称时,$name
和 $one
参数允许指定关系的任意名称。
$schema->addOneToMany('Books', null, null, 'MyBooks', 'MyBook')
Author 对象将包含方法
/** @return Collection|\Book[] */ public function getMyBooks($reload = false) /** @return static */ public function setMyBooks($my_books_arr = null) /** @return Collection|\Book[] */ protected function findMyBooks()
多对多
addManyToMany($schema, $join_table = null, $foreign_col = null, $my_col = null, $foreign_id = null, $my_id = null, $name = null, $one = null)
通过第三个表连接两个表的关系。
例如:一本书可能有多个作者(Author),一个作者可能有几本书(Book)。为此,我们创建一个包含 book_id
和 author_id
字段的 AuthorBook 表,并通过 JOIN 到这个表来获取作者和书的关系。
// Схема AuthorBook $schema->addOneToOne('Authors') // author_id $schema->addOneToOne('Books') // book_id // В схеме Authors: addManyToMany('Books', 'author_book', 'book_id', 'author_id', 'id', 'id') // В схеме Books: addManyToMany('Authors', 'author_book', 'author_id', 'book_id', 'id', 'id')
Author 对象将包含方法
/** @return Collection|\Book[] */
public function getBooks($reload = false)
/** @return static */
public function addBook($book)
/** @return static */
public function removeBook($book)
/** @return static */
public function removeAllBooks()
/** @param $book integer|\Book */
protected function getBookPK($book)
/** @return Collection|\Book[] */
protected function findBooks()
迁移
基于模式和数据库的当前状态,可以生成用于 迁移管理器 Phinx 的迁移文件。
如果数据库中尚不存在表,则生成创建该表及其所有列和索引的迁移。
创建新表 Books 的示例
class NewBooksTable extends AbstractMigration { public function up() { $tbl = $this->table('test_books', array('id' => 'id')); $tbl->addColumn("name", "string", array ( 'length' => 255, 'null' => true,)); $tbl->addColumn("author_id", "integer", array ( 'length' => 11, 'signed' => true, 'null' => true,)); $tbl->addForeignKey("author_id", "test_authors", "id", array ( 'delete' => 'RESTRICT', 'update' => 'CASCADE',)); $tbl->save(); } public function down() { $tbl = $this->table('test_books', array('id' => 'id')); $tbl->drop(); } }
如果数据库中已存在表,则迁移中会生成添加和删除列的命令,这些列在模式和数据库中不同。对于先前创建并存在于数据库中的列,将生成 changeColumn
方法。
更改现有表 Pages 的示例
class MyMigration extends AbstractMigration { public function up() { $tbl = $this->table('test_pages', array('id' => 'id')); $tbl->addColumn("is_active", "boolean", array ( 'default' => 0,)); $tbl->addColumn("price", "float", array ( 'precision' => 10, 'scale' => 2, 'signed' => false, 'default' => 0,)); $tbl->removeColumn("name"); $tbl->changeColumn("created_at", "timestamp", array ( 'default' => 'CURRENT_TIMESTAMP',)); if (!$tbl->hasForeignKey("parent_id")) { $tbl->addForeignKey("parent_id", "test_pages", "id", array ()); } $tbl->save(); } public function down() { $tbl = $this->table('test_pages', array('id' => 'id')); $tbl->removeColumn("is_active"); $tbl->removeColumn("price"); // TODO: добавить инструкции для создания столбца name $tbl->save(); } }
重要!生成的迁移应被视为草稿,需要检查和根据需要进行调整。不要盲目应用迁移!