peak / database
数据库通用工具 + Laravel 数据库包装 + 使用 Phinx 的数据库迁移
Requires
- php: >=7.2
Requires (Dev)
- illuminate/database: ^5.8
- illuminate/events: ^5.8
- phpstan/phpstan: ^0.11
- phpunit/phpunit: ^8.2
- robmorgan/phinx: ^0.10
Suggests
- illuminate/database: Needed for using Peak\Database\Laravel
- illuminate/events: Needed for using Peak\Database\Laravel
- robmorgan/phinx: Needed for using database migration Peak\Database\Phinx
This package is auto-updated.
Last update: 2024-09-28 05:49:22 UTC
README
此包的目的包括
- 为 DDD 和 Clean 架构提供通用的数据库工具
- 提供带有 Phinx 迁移的数据库迁移
- 便于在非 Laravel 项目中集成 Laravel 数据库
安装
composer require peak/database
Laravel 数据库使用
use Peak\Database\Laravel\DatabaseService; $config = [ 'driver' => 'mysql', 'host' => 'localhost', 'port' => '3306', 'database' => 'database', 'username' => 'root', 'password' => 'root', 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', ]; $db = (new DatabaseService())->createConnection($config, 'connectionName');
就是这样!查看 Laravel 查询构建器 了解如何进行查询的更多信息。
数据库迁移使用
对于数据库迁移,在你的项目根目录创建一个名为 phinx.php 的文件。该文件应返回一个 Phinx 配置数组。
<?php namespace { use Peak\Database\Common\LaravelPhinxMigration; use Peak\Database\Laravel\LaravelDatabaseService; use Peak\Database\Laravel\LaravelConnectionManager; use Peak\Database\Phinx\PhinxConfigService; use Peak\Database\Phinx\PhinxEnvConfig; require __DIR__.'/vendor/autoload.php'; try { $env = getenv(); $db = (new LaravelDatabaseService())->createConnection([ 'driver' => $env['DB_DRIVER'], 'host' => $env['DB_HOST'], 'port' => $env['DB_PORT'], 'database' => $env['DB_DATABASE'], 'username' => $env['DB_USERNAME'], 'password' => $env['DB_PASSWORD'], 'charset' => $env['DB_CHARSET'], 'collation' => $env['DB_COLLATION'], 'prefix' => $env['DB_PREFIX'], ], 'connectionName'); LaravelConnectionManager::setConnection($db, 'prod'); return (new PhinxConfigService()) ->create( 'migrations', LaravelPhinxMigration::class, 'migrations', 'prod', [ new PhinxEnvConfig('prod', [ 'name' => $db->getDatabaseName(), 'connection' => $db->getPdo(), ]) ] ); } catch(\Exception $e) { die($e->getMessage()); } }
上面的 phinx.php 允许在迁移中使用 Laravel 数据库,借助 LaravelPhinxMigration。
<?php use Peak\Database\Laravel\LaravelPhinxMigration; use Illuminate\Database\Schema\Blueprint; class Users extends LaravelPhinxMigration { public function up() { $this->db->getSchemaBuilder()->create('users', function(Blueprint $table){ $table->increments('id'); $table->string('username')->unique(); $table->string('email')->unique(); $table->string('password'); $this->tsColumns($table); $table->timestamp('lastSeen')->nullable()->default(null); }); } public function down() { $this->db->getSchemaBuilder()->drop('users'); } }
通用工具
通用工具的目的是帮助您表达查询,而不依赖于特定的数据库框架或任何数据库。
- 使用
QueryFilters来表达 "where" 语句。 - 使用
QueryPagination来表达分页,如排序和限制/偏移量语句。
需要注意的是,这些通用工具本身并不做任何事情。它们主要作为领域或用例与实际数据库或存储实现之间的 "边界" 接口。在您的最终实现中,您将需要一个构建器/辅助工具将通用表达式转换为实际的数据库框架/orm。
创建通用查询 "where" 过滤器和通用查询 "分页" 的示例
$queryFilters = new QueryFilters(); $queryFilters ->setColumns(['id', 'title']) ->where('level', '6', '>') ->orWhere('level', '2', '<') ->orWhereArray((new QueryFilters()) ->where('status', 'online') ->where('type', '2') ->whereNull('ban') ->whereNotNull('deletedAt') ); $queryPagination = new QueryPagination( $column, $direction, $pageNumber, $itemsPerPages );
将 $queryFilters 和 $queryPagination 传递给一个用例。这将帮助在用例和存储库之间创建边界,因为用例不必了解您实现(数据库框架/orm 等)的详细信息。
<?php namespace Domain\UseCase; use Peak\Database\Generic\QueryFiltersInterface; use Peak\Database\Generic\QueryPaginationInterface; class MyUseCase { // ... public function execute( QueryFiltersInterface $queryFilters, QueryPaginationInterface $queryPagination ) { // do things // ... return $this->repository->getMany($queryFilters, $queryPagination); } }
最后,我们在存储库实现中使用 LaravelGenericHelper 将通用 QueryFiltersInterface 转换为实际的 Laravel 查询构建器 "where" 表达式;
<?php use Domain\Repository\MyRepositoryInterface; use Peak\Database\Generic\QueryFiltersInterface; use Peak\Database\Generic\QueryPaginationInterface; use Peak\Database\Common\LaravelGenericHelper; class MyRepository implements MyRepositoryInterface { // ... public function getMany( QueryFiltersInterface $queryFilters, QueryPaginationInterface $queryPagination ) { $qb = $this->table('tusers'); $qb = LaravelGenericHelper::filterQuery($qb, $queryFilters); $qb = LaravelGenericHelper::paginateQuery($qb, $queryPagination); return $qb->get(); } }
我们可以在用例中直接使用 Laravel 查询构建器,但这可能会使代码过于依赖特定的数据库库(此处为 Laravel 数据库)。通过使用通用查询过滤器和分页,可以非常容易地测试存储库和用例,而无需真实数据库连接。
有关 Laravel 数据库分页和过滤器的安全重要信息
来自 Laravel 数据库文档
Laravel 查询构建器使用 PDO 参数绑定来保护您的应用程序免受 SQL 注入攻击。不需要清理作为绑定传递的字符串。但是
"PDO 不支持绑定列名。因此,您绝对不应该允许用户输入决定查询中引用的列名,包括 "order by" 列等。如果您必须允许用户选择要查询的列,请始终将列名与允许的列的白名单进行验证。"
如果您允许用户选择列名,应创建一个扩展 AbstractRestrictedQueryPagination 的类来防止不想要的列名。
class UserPagination extends AbstractRestrictedQueryPagination { protected $allowedColumns = [ 'username', 'email', 'createdAt', 'updatedAt', 'deletedAt' ]; protected $allowedDirections = [ 'asc', 'desc' ]; } // and use it like this: $queryPagination = new UserPagination( $column, $direction, $pageNumber, $itemsPerPages );
相同的做法可以应用于查询过滤器的列和运算符,使用 AbstractRestrictedQueryFilters
class UserFilters extends AbstractRestrictedQueryFilters { protected $allowedColumns = [ 'username', 'email', 'createdAt', 'updatedAt', 'deletedAt' ]; protected $allowedOperators = [ '=', '>', '<', 'like' ]; } // and use it like this: $queryFilters = new UserFilters(); $queryFilters ->where('username', 'bob', '=') //...