visavi / motor-orm
Requires
- php: ^8.0
- ext-mbstring: *
Requires (Dev)
- phpunit/phpunit: ^9.6
This package is auto-updated.
Last update: 2024-08-28 10:40:02 UTC
README
此脚本提供面向对象的方法来处理存储在文件系统中的文本数据
数据结构CSV兼容,但为了更快的运行做了一些修改
功能
构建器
- 通过唯一键搜索
- 通过任何指定条件搜索
- 返回文件结构
- 返回文件中的记录数量
- 返回记录是否存在的信息
- 排序行
- 将行写入文件,并生成自增键
- 根据任何条件更新记录
- 根据任何条件删除记录
- 类型转换(Casts)
- 作用域
- 清理文件
- 贪婪加载
- 一对一关系
- 一对多关系
- 多对多关系
集合
- 将集合转换为数组
- 获取第一个记录
- 获取最后一个记录
- 获取集合中的记录数量
- 向集合中添加记录
- 从集合中删除记录
- 在集合中设置值
- 检查集合是否为空
- 清理集合
- 切片集合
- 遍历集合,获取键和值
集合分页
- 扩展Collection类
- 获取当前页
- 获取页数数量
- 获取页面数组
迁移
处理文件中的更改,包括插入操作,使用文件锁定来防止多个用户同时写入文件时意外删除数据
文件中的第一列被视为唯一键
可以是字符串或数字
如果列是字符串,则所有插入操作都必须具有已定义的唯一键
如果列是数字,则唯一键将自动生成
查询
所有查询都通过模型进行,其中必须指定数据文件的路径。模型本身可以实现额外的方法。
示例
# Create class use MotorORM\Builder; class TestModel extends Builder { public string $table = __DIR__ . '/test.csv'; } # Find by primary key TestModel::query()->find(1); # Find by name limit 1 TestModel::query()->where('name', 'Миша')->limit(1)->get(); # Find by name and first 1 TestModel::query()->where('name', 'Миша')->first(); # Find by name and title TestModel::query()->where('name', 'Миша')->where('title', 'Заголовок10')->get(); # Get from condition TestModel::query()->where('time', '>=', 1231231235)->get(); # Get by condition in TestModel::query()->whereIn('id', [1, 3, 4, 7])->get(); # Get by condition not in TestModel::query()->whereNotIn('id', [1, 2, 3, 4, 5, 6, 7, 8, 9, 10])->get(); # Get records by multiple conditions and pagination TestModel::query() ->where(function(Builder $builder) { $builder->where('name', 'Миша'); $builder->orWhere(function(Builder $builder) { $builder->where('name', 'Петя'); $builder->where('title', '<>', ''); }); }) ->paginate(10); # Get count TestModel::query()->where('time', '>', 1231231234)->count(); # Get lines 1 - 10 $lines = TestModel::query()->offset(0)->limit(10)->get(); # Get last 10 records $lines = TestModel::query()->orderByDesc('created_at')->offset(0)->limit(10)->get(); # Get headers TestModel::query()->headers(); # Get first line TestModel::query()->first(); # Get first 3 lines TestModel::query()->limit(3)->get(); # Get last 3 lines TestModel::query()->orderByDesc('created_at')->limit(3)->get(); # Find by name and double sort (time desc, id asc) Test::query() ->where('name', 'Миша') ->orderByDesc('time') ->orderBy('id') ->limit(3) ->get(); # Create string TestModel::query()->create(['name' => 'Миша']); # Update strings TestModel::query()->where('name', 'Миша')->update(['text' => 'Новый текст']); # Update string $test = TestModel::query()->where('name', 'Миша')->first(); $test->text = 'Новый текст'; $test->save(); # Update strings $testModel = TestModel::query()->find(17); $affectedLines = $testModel->update(['text' => 'Новый текст']); # Delete records TestModel::query()->where('name', 'Миша')->delete(); # Delete records $records = TestModel::query()->get(); foreach($records as $record) { $record->delete(); } # Truncate file TestModel::query()->truncate();
部分搜索(Like)
部分匹配搜索
// Строки начинающиеся на hi $test = TestModel::query()->where('tag', 'like', 'hi%')->get(); // Строки заканчивающиеся на hi $test = TestModel::query()->where('tag', 'like', '%hi')->get(); // Строки содержащие hi $test = TestModel::query()->where('tag', 'like', '%hi%')->get(); // Этот запрос эквивалентен запросу выше $test = TestModel::query()->where('tag', 'like', 'hi')->get();
宽松搜索(Lax)
非严格匹配搜索
在搜索中,ORM使用严格比较来激活非严格模式,可以使用lax
// Будут найдено первое совпадение NAME, name, namE, Name итд $user = User::query()->where('login', 'lax', 'name')->first();
类型转换(Casts)
默认情况下,从文件获取的所有字段都是字符串类型
但有例外
- 主键字段 - int
- 以_id和_at结尾的字段 - int
- 空字段 - null
要重写,请使用casts属性
class Story extends Model { protected array $casts = [ 'rating' => 'int', 'reads' => 'int', 'locked' => 'bool', ]; }
支持以下类型
- 'int', 'integer' => int
- 'real', 'float', 'double' => float
- 'string' => string
- 'bool', 'boolean' => bool
- 'object' => json_decode($value, false),
- 'array' => json_decode($value, true),
查询条件(Scope)
每个scope都是一个普通方法,它以scope前缀开始。ORM通过前缀理解这是scope。在scope内部传递查询,可以在其上附加额外的条件。
class Story extends Model { public function scopeActive(Builder $query): Builder { return $query->where('active', true); } }
使用
Story::query() ->active() ->paginate($perPage);
动态条件
某些scope依赖于在查询构建过程中传递的参数。为此,只需在scope内部描述这些参数即可,在$query参数之后
class Story extends Model { public function scopeOfType(Builder $query, string $type): Builder { return $query->where('type', $type); } }
使用
Story::query() ->ofType('new') ->paginate($perPage);
条件表达式(Conditional clauses)
有时您可能需要根据其他条件执行特定查询。例如,您可能只想在输入值存在于输入HTTP请求的情况下应用WHERE运算符。您可以使用when方法来实现这一点。
$active = true; $stories = Story::query() ->when($active, function (Story $query, $active) { $query->where('active', $active); }) ->get();
when方法仅在第一个参数等于true时执行该闭包。如果第一个参数等于false,则不会执行闭包。
您可以将另一个闭包作为when方法的三级参数传递。当第一个参数评估为false时,将执行此闭包。为了说明如何使用此功能,我们将使用它来设置查询的默认顺序。
$sortByVotes = 'sort_by_votes'; $users = Story::query() ->when($sortByVotes, function ($query, $sortByVotes) { $query->orderBy('votes'); }, function ($query) { $query->orderBy('name'); }) ->get();
关系(Relations)
目前支持3种关系类型
- hasOne - 一对一
- hasMany - 一对多
- hasManyThrough - 多对多
一对一(hasOne)
3个参数,类名,外键和内键
外键和内键自动定义,除非字段名称与类名不匹配或反向关系belongs_to(可能在未来实现)
// Прямая связь class User extends Model { public function story(): Builder { return $this->hasOne(Story::class); } } // Обратная связь class Story extends Model { public function user(): Builder { return $this->hasOne(User::class, 'id', 'user_id'); } }
一对多(hasMany)
3个参数,类名,外键和内键
外键和内键自动定义,除非字段名称与类名不匹配
class Story extends Model { public function comments(): Builder { return $this->hasMany(Comment::class); } }
多对多(hasManyThrough)
5个参数,最终类名,中间类名,外键和内键
外键和内键自动定义,除非字段名称与类名不匹配
class Story extends Model { public function tags(): Builder { return $this->hasManyThrough(Tag::class, TagStory::class); } }
贪婪加载(Eager load)
默认情况下,所有关系都使用懒加载
关系只有在显式调用时才会加载
为了贪婪加载数据,需要调用with方法并传递要贪婪加载的关联名称
class StoryRepository implements RepositoryInterface public function getStories(int $perPage): CollectionPaginate { return Story::query() ->orderByDesc('locked') ->orderByDesc('created_at') ->with(['user', 'comments']) ->paginate($perPage); } }
贪婪加载通过仅使用少量查询来提取数据。这有助于避免N + 1问题。
假设您有以下代码,该代码找到10条消息,然后显示每条消息的作者姓名。
foreach ($storyRepository->getStories(10) as $story) { echo $story->user->login; }
在没有懒加载的情况下,每次循环迭代都会调用文件系统以获取数据,即获取帖子列表的1个查询和10个获取用户的查询
贪婪加载消除了这个问题,1个获取帖子列表的查询和1个获取这些帖子用户的查询
迁移
要调用迁移类,需要将所需模型传递给构造函数
$migration = new Migration(new Test());
创建表
创建表,例如创建具有五个字段的test.csv文件
$migration->createTable(function (Migration $table) { $table->create('id'); $table->create('title'); $table->create('text'); $table->create('user_id'); $table->create('created_at'); });
删除表
$migration->deleteTable();
创建列
$migration->changeTable(function (Migration $table) { // Создаст колонку text c текстом по умолчанию "Текст" после колонки title $table->create('text')->default('Текст')->after('title'); // Создаст колонку test перед колонкой id $table->create('test')->before('id'); });
重命名列
$migration->changeTable(function (Migration $table) { // Переименует user_id в author_id $table->rename('user_id', 'author_id'); });
删除列
$migration->changeTable(function (Migration $table) { // Удалит колонку title $table->delete('title'); });
检查表/列是否存在
// Проверит существование таблицы $migration->hasTable(); // Проверит существование колонки $migration->hasColumn('field');