taro / db-model
php-ormapper的改进版本。
Requires
- composer/composer: ^2.4
- vlucas/phpdotenv: ^5.4
Requires (Dev)
- phpunit/phpunit: ^9.5
README
PHP操作关系型数据库的 O/R 映射器。是之前创建的 php-ormapper 的改进版。使用此库可以创建、更新数据库/表,以及创建、更新和检索记录等。
用法
模型类
\Taro\DBModel\Models\Model 类继承
class Post extends Model { // フィールド名を protected で定義 protected $title; protected $body; // リレーションメソッドを定義 public function relatedComments() { return $this->hasMany(Comment::class); } }
关系定义
在模型类内部定义关系方法。可以定义以下关系。
- 1对1:hasOne(关联的模型类);
- 1对多:hasMany(关联的模型类);
- 多对1:belongsTo(关联的模型类);
- 多对多:manyToMany(关联的模型类, 中间表名);
- 通过中间模型的多对1:hasManyThrough(关联的模型类, 中间模型类);
- 通过中间模型的多对1:belongsToThrough(关联的模型类, 中间模型类);
// モデル と Post が1対1 public function post() { return $this->hasOne(Post::class); } // モデル と Post が1対多 public function posts() { return $this->hasMany(Post::class); } // モデル と User が多対1 public function user() { return $this->belongsTo(User::class); } // モデル と Post が favorites 中間テーブルを介して 多対多 public function favoritePosts() { return $this->manyToMany(Post::class , 'favorites'); } // モデル と Comment が Postモデルを介して 1対多 public function comments() { return $this->hasManyThrough(Comment::class, Post::class); } // モデル と User が Postモデルを介して 多対1 public function users() { return $this->belongsToThrough(User::class, Post::class); }
创建新记录
方法1:创建实例,将值分配给属性并保存
$post = new Post; $post->title = 'title1'; $post->body = 'test'; $post->insert();
方法2:fill 方法接收数组,批量保存
$post = new Post(); $data = [ 'title'=>'How to cook pizza2', 'content'=>'test create from array' ]; $post->fill($data)->insert();
更新记录
方法1:将更新数据分配给实例的属性。
$post = Post::query()->findById(1); $post->body = 'updated'; $post->update();
方法2:将更新数据作为数组传递给实例的 fill 方法以更新。
$post = Post::query()->findById(1); $data = [ 'body' => 'updated' ]; $post->fill($data)->update();
删除记录
$post = Post::query()->findById(1); $post->delete();
查询构建器和查询执行
从模型类的 query 开始构建查询。
构建查询构建器
通过方法链连接必要的函数。
// where(カラム名, 値) 又は、 where(カラム名, oper, 値) Post::query()->where('title', 'IN', ['test1', 'test2']) Post::query()->where('id', '1') // whereIn(カラム名, 値の配列) Post::query()->whereIn('id', [1,2,3]) // whereBetween (カラム名, min, max) Post::query()->whereBetween('id', 2, 4) // orderBy(カラム名, ['DESC' | 'ASC']) Post::query()->orderBy('id', 'DESC') // limit(取得数) Post::query()->limit(2) // groupBy(カラム名) Post::query()->groupBy('user_id') // カラムを選択 Post::query()->select('title','body')
执行查询
构建查询构建器后,可以单独执行的方法。
// 複数のモデルを取得 $posts = Post::query()->where('id', '>', '2')->getAll(); // 複数のレコードを配列で取得 $posts = Post::query()->where('id', '>', '2')->getArrayAll(); // 最初のレコードを取得 $posts = Post::query()->where('id', '1')->getFirst(); // モデルクラスから単独で実行できる $posts = Post::query()->getAll(); // 集計用のメソッド // count, average, max, min, sum Post::query()->count(); Post::query()->count('views'); // カラムを指定
创建复杂的 WHERE 子句
使用 Wh 类创建 Where 子句。创建后,使用查询构建器的 addWhClause 方法将其添加到查询中。
$query = Post::query(); // where 句の作成 // WHERE (views > 2) AND (hidden = "public") OR (title = "test3") $where = new Wh(); $where->add('views', '>', '2'); $where->addAnd('hidden','public'); $where->addOr('title', 'test3'); // where句をクエリに追加 $query->addWhClause($where); $posts = $query->getAll();
创建嵌套结构时,使用 Wh 的 static 方法。
$where = new Wh(); // WHERE (hidden = "public") AND ((views > 2) OR (title = "test3")) $where->addBlock( Wh::and( Wh::block('hidden', 'public'), Wh::or( Wh::block('views', '>', '2'), Wh::block('title', 'test3') ) ) ); $query2->addWhClause($where);
模型列表用类
在查询构建器执行 getAll・getArrayAll 后,结果作为实现了 ActiveList 接口的类的对象返回。ActiveList 是 ArrayList,ObjectList 的接口。可以作为数组处理,并提供操作列表的便利方法。
- ArrayList: 以数组数据为目标
- ObjectList: 以模型对象列表为目标
// getAllの戻り値は、モデルのリストオブジェクト(ObjectList)になります $posts = Post::query()->where('id', '>', '2')->getAll(); // getArrayAllの戻り値は、配列のリストオブジェクト(ArrayList)になります $posts = Post::query()->where('id', '>', '2')->getArrayAll();
上述接口提供的功能
merge(): ActiveList 同士の結合 orderBy(key, 'asc'): リストの並び替え orderBy(callback) pluck(key): リストの各要素の特定キーのみを取得する filter(condition): リストを条件で絞り込む shift(): 先頭を取得してリストから取り除く pop():末尾を取得してリストから取り除く first(): リストの先頭を取得 last(): リストの末尾を取得 slice(offset, length): 先頭から offset 番目から length 個取り出す map(callback): 各要素に処理を実行する ifAny(callback): 条件が true のものがひとつでもあるか ifAll(callback): すべての要素で条件が true になるか groupBy(key): key の値でリストをグループ分けする toArray(): 格納したデータを配列として返す clone(): リストのクローン(シャローコピー) // 使用例 Post の id >= 3 のレコードの title を配列で取得 $posts = Post::query()->getAll(); $titles = $posts->filter(function($item) { return $item->id >= 3; })->pluck('title');
分页
获取指定数量的模型记录,返回值是 Paginator 类。
// paginate(一度に取得するレコード数) $posts = Post::paginate(10); ?> <?php foreach ($posts as $post): ?> <ul> <li><?= $post->id ?></li> <li><?= $post->title ?></li> <li><?= $post->body ?></li> </ul> <?php endforeach; ?> <?php // リンクを表示する $posts->dispLinks(); ?>
获取链接数据
如果只需要数据而不是标签,请使用 getLinkData 方法。
$data = $posts->getLinkData(); // 以下、データのフォーマット $data =[ 'routeUrl' => 'https:///posts?key1=value1', 'links' => array ( array ( 'label' => '最初', 'href' => 'https:///posts?key1=value1?pageNo=0', ), array ( 'label' => '前', 'href' => 'https:///posts?key1=value1?pageNo=1', ), array ( 'label' => 1, 'href' => 'https:///posts?key1=value1?pageNo=0', ), array ( 'label' => 2, 'href' => 'https:///posts?key1=value1?pageNo=1', ), array ( 'label' => 3, 'href' => 'https:///posts?key1=value1?pageNo=2', 'selected' => true, 'disabled' => true, ), array ( 'label' => '次', 'href' => 'https:///posts?key1=value1?pageNo=2', 'disabled' => true, ), array ( 'label' => '最後', 'href' => 'https:///posts?key1=value1?pageNo=2', 'disabled' => true, ), ), ];
自定义链接设计
默认情况下,显示 bootstrap 风格的链接(需要 Bootstrap 库)。
如果需要自定义,请创建扩展了 BasicLinks 类的类,并将其作为 Paginator::setTemplate() 的参数传递。
$posts = Post::paginate(10); $posts->setTemplate(new CustomLinks);
不使用模型类执行查询的方法
DirectSql 类
使用 DirectSql 可以创建和执行不通过模型类的查询,或直接执行 SQL 语句。
// 直接SQL文を実行 $sql = 'SELECT * FROM posts WHERE title = :title'; $query = DirectSql::query()->prepareSql($sql); $query->bindParam(':title', 'my first post'); $results = $query->runSql(); // よりORM的な記述方法 // レコードの取得 $results = DirectSql::query() ->table('posts') ->select('title','body') ->where('id','>=', 2) ->orderBy('create_date') ->limit(10) ->getAsModels(Post::class); // 更新 DirectSql::query() ->table('posts') ->where('title', 'test') ->update([ 'title' => 'test 1', 'body' => 'test post 1' ]); // 削除 DirectSql::query() ->table('posts') ->where('id', 5) ->delete();
获取关联模型
// 関連モデルのデータを取得 $user = $post->user->getFirst(); $posts = $user->posts->getAll(); // eagerloading N+1問題の解決 // eagerLoad(['リレーション名']) User::query()->eagerLoad(['tasks'])->getAll(); $tasks = $user->tasks;
创建中间表记录
在多对多关系中,向中间表中创建记录。
-
定义多对多关系
public function favoritePosts() { return $this->manyToMany(Post::class , 'favorites'); }
-
使用 insertPivot 方法创建记录
// insertPivot(関連テーブルのid, [独自のカラムに登録するデータを配列形式で]) $user->favoritePosts()->insertPivot($postId, ['star'=>$star]);
更新记录
使用 updatePivot 方法更新记录
// updatePivot(関連テーブルのid, [独自のカラムに更新するデータを配列形式で]) $user->favoritePosts()->updatePivot($postId, ['star'=>$star]);
删除记录
使用 deletePivot 方法删除记录
// deletePivot(関連テーブルのid) $user->favoritePosts()->deletePivot($postId);
数据库连接配置文件
在 项目根目录/config/database.php 文件中注册数据库连接信息。如果使用 .env 文件中的信息,请使用 env() 方法。
// connections 以下に、接続名をキーとして、接続情報を指定する return [ 'default'=>'mysql', // デフォルト接続先 'connections' => [ 'mysql'=>[ 'driver'=>'mysql', 'host'=>'localhost', 'user'=>'root', 'password'=> env('DB_PASSWORD'), // .env ファイルのデータを指定 'dbname'=>'tasksdb', ], 'pgsql'=>[ 'driver'=>'pgsql', 'host'=>'localhost', 'user'=>'postgres', 'password'=>'password', 'dbname'=>'MyDB', 'schema'=>'public', 'port'=>5433, ], // 例では、プロジェクトルート/database 直下の database.sqlite ファイルを読み込む 'sqlite'=>[ 'driver'=>'sqlite', 'dsn'=>'sqlite:' . FileHandler::sqlitePath(), ], ] ];
连接到数据库
在使用模型类之前,使用 DB::start 方法连接到数据库。
use Taro\DBModel\DB\DB; // 引数として利用する接続名を渡す(省略すると、database.phpに記載したdefault値が使われる) $db = DB::start('mysql', true); // 接続を閉じる DB::stopGlobal();
事务控制
使用 DB 类控制事务。
// トランザクション開始 DB::beginTrans(); // ロールバック DB::rollback(); // コミット DB::commit();
DDL相关
数据库管理
执行数据库的创建或删除操作。(SQLite 不适用)
use Taro\DBModel\Schema\Database; // データベースを作成 Database::create('データベース名'); // データベースを削除 Database::dropIfExists('データベース名');
创建表
使用 Schema::createTable 创建表。根据数据库类型,在参数的回调类型提示中指定 MySqlTable/PostgreSqlTable/SqliteTable。
use Taro\DBModel\Schema\Schema; use Taro\DBModel\Schema\MySql\MySqlTable; // use Taro\DBModel\Schema\PostgreSql\PostgreSqlTable; // use Taro\DBModel\Schema\Sqlite\SqliteTable; Schema::createTable('test', function(MySqlTable $table){ $table->addColumn('id','int')->unsigned()->primary(); $table->addColumn('content','text')->nullable(); $table->addColumn('status','string')->length(5)->default('good'); $table->addColumn('user_id','int')->unsigned(); $table->addUnique('content', 'status'); $table->addForeign('user_id')->references('users', 'id')->onDelete('CASCADE'); }); // データベースによっては、一部のメソッドが利用できません。(例:postgresqlでは、unsigned() 利用不可)
更新表
要更新表,首先使用 Schema::getTable 获取目标表,描述更改内容后执行 Schema::alterTable。
// test テーブルを取得 $table = Schema::getTable('test'); // 変更内容を記述 $table->addColumn('post_id','int'); $table->changeColumn('status')->default(0); $table->addForeign('post_id')->references('posts','id')->onDelete('cascade')->name('FK1'); $table->dropForeign('fk_test2_user_id_users_id'); $table->dropIndexByColumns('content','status'); $table->addIndex('status')->name('INDEX1'); $table->dropColumn('content'); // 最後に変更クエリを実行 Schema::alterTable($table);
删除表
要删除表,请执行 Schema::dropTableIfExists。还可以通过执行 Schema::dropTable 来删除获取的表。
// 取得したテーブルを削除 $table = Schema::getTable('test'); Schema::dropTable($table); // test テーブルを削除 Schema::dropTableIfExists('test');
表的单元管理
使用 Schema::saveTable 可以自动执行上述表的创建、更新和删除。通过修改回调内的代码执行,可以创建与现有同名表的差异查询来更新表。
use Taro\DBModel\Schema\Schema; use Taro\DBModel\Schema\MySql\MySqlTable; // use Taro\DBModel\Schema\PostgreSql\PostgreSqlTable; // use Taro\DBModel\Schema\Sqlite\SqliteTable; Schema::saveTable('test', function(MySqlTable $table){ $table->addColumn('id','int')->primary(); $table->addColumn('content','text')->nullable(); $table->addColumn('status','string')->length(5)->default('good'); $table->addUnique('content', 'status'); });
许可证 (License)
DB Model 在 MIT license 下发布。
DB Model 是在 MIT license 下许可的开源软件。