php强类型编码下的db操作类设计思考
我对php代码的编写,有一个要求是编写出编辑器可读的代码,换句话说就是编辑器能够点击跳转的代码。
如今的db操作库在执行查询方法后,返回的数据要么是collection集合,要么是mixed类型,编辑器无法提示返回值里面具体有什么。
所以我写了一些伪代码,大伙看看这个设计有没有搞头,或者有没有类似的解决方案。
定义PrimaryKey
注解
<?php
namespace App\Models\Test;
use Attribute;
/**
* 标记主键
*/
#[Attribute] class PrimaryKey
{
}
定义DB
类
<?php
namespace App\Models\Test;
use ReflectionClass;
use ReflectionProperty;
class DB
{
protected string $connection = '';
protected string $table = '';
/**
* 查询条件
* @param string $column 查询列
* @param string|null $operator 查询操作符
* @param mixed|null $value 查询值
* @param string $boolean 查询条件连接符
* @return $this
*/
public function where(string $column, string|null $operator = null, mixed $value = null, string $boolean = 'and'): static
{
return $this;
}
/**
* 获取所有
* @return array|static[]
*/
public function get(): array
{
return [
new static(),
new static()
];
}
/**
* 获取第一个
* @return $this|null
*/
public function find(): static|null
{
return new static;
}
/**
* 转换为数组
* @return array
*/
public function toArray(): array
{
//反射子类,并遍历子类的属性
$reflection = new ReflectionClass($this);
$properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
$data = [];
foreach ($properties as $property) {
$propertyName = $property->getName();
$data[$propertyName] = $this->$propertyName;
}
return $data;
}
/**
* 删除
* @return int 删除的行数
*/
public function delete(): int
{
return 1;
}
/**
* 创建
* @return bool 创建成功或失败
*/
public function create(): bool
{
return 1;
}
/**
* 更新
* @return int 更新的行数
*/
public function update(): int
{
return 1;
}
}
定义表user的模型
<?php
namespace App\Models\Test;
class User extends DB
{
protected string $connection = 'test_db';
protected string $table = 'user';
#[PrimaryKey]
public int $userId = 0;
public string $username = '';
public string $password = '';
public string $nickname = '';
public string $avatar = '';
}
定义表order的模型
<?php
namespace App\Models\Test;
class Order extends DB
{
protected string $connection = 'test_db';
protected string $table = 'order';
#[PrimaryKey]
public int $orderNo = 0;
public int $userId = 0;
public int $amount = 0;
/**
* @var int 订单状态【0:待支付 1:支付成功 2:支付失败】
*/
public int $status = 0;
const STATUS_WAIT = 0;
const STATUS_SUCCESS = 1;
const STATUS_FAIL = 2;
}
增删改查示例
<?php
namespace App\Http\Controllers;
use App\Models\Test\Order;
use App\Models\Test\User;
use Symfony\Component\HttpFoundation\Response;
class TestController extends Controller
{
/**
* 查询示例
* @return Response
*/
public function index(): Response
{
$admin = (new User)->where('username', '=', 'admin')->find();
$users = (new User)->where('nickname', 'like', "%$admin->nickname%")->get();
$ret = [];
foreach ($users as $user) {
$tmp = [
'username' => $user->username,
'nickname' => $user->nickname,
'avatar' => $user->avatar
];
$order = (new Order)->where('user_id', '=', $user->userId)->find();
if ($order && $order->status = Order::STATUS_SUCCESS) {
$tmp['order'] = $order->toArray();
} else {
$tmp['order'] = null;
}
$ret[] = $tmp;
}
return $this->success($ret);
}
/**
* 新增示例
* @return Response
*/
public function create(): Response
{
$user = new User();
$user->username = 'test';
$user->nickname = 'test';
$user->avatar = 'test';
return $user->create() ? $this->success() : $this->error('新增失败', 1);
}
/**
* 删除示例
* @return Response
*/
public function delete(): Response
{
$ok = (new Order)->where('user_id', '=', 1)->delete();
return $ok ? $this->success() : $this->error('删除失败', 1);
}
/**
* 更新示例
* @return Response
*/
public function update(): Response
{
$user = new User();
$user->username = 'test';
$ok = $user->where('userId', '=', 1)->update();
return $ok ? $this->success() : $this->error('更新失败', 1);
}
}
优缺点分析:
- 优点:
get
或find
返回的数据,编辑器是可是识别的。 - 缺点:使用返回的数据时,编辑器既提示了表模型本身的属性字段,也提示了表模型父类的方法,这些方法在操作返回值的时候也会提示,属于提示噪音。
“提示”噪音的解决思路
首先是表模型不再继承DB
类,然后是改造DB
类的find
和get
方法。find
方法返回单条数据时,可以改为传递model
对象,并将查询的数据填写到对象身上,类似go
中的设计:
order := TestDbEntity.Order{}
TestDbDao.Order.Scan(&order)
但是get
方法返回的是多条数据,此时由于php没有强类型的数组,并不能像go
一样,实现如下的写法:
var orderList []TestDbEntity.Order
TestDbDao.Order.Scan(&orderList)
折中的思路是让表模型继承一个基础模型,然后基础模型提供public static function makes(): array
方法。
整个实现如下:
定义ModelInterface接口
<?php
namespace App\Models\Test;
interface ModelInterface
{
public static function getConnection(): string;
public static function getTable(): string;
public static function makes(): array;
public static function make(): static;
public function toArray(): array;
}
实现ModelInterface接口
<?php
namespace App\Models\Test;
use ReflectionClass;
use ReflectionProperty;
class Model implements ModelInterface
{
protected static string $connection = '';
protected static string $table = '';
/**
* @return array | static[]
*/
public static function makes(): array
{
return [];
}
/**
* @return static
*/
public static function make(): static
{
return new static();
}
public static function getConnection(): string
{
return static::$connection;
}
public static function getTable(): string
{
return static::$table;
}
/**
* 转换为数组
* @return array
*/
public function toArray(): array
{
//反射子类,并遍历子类的属性
$reflection = new ReflectionClass($this);
$properties = $reflection->getProperties(ReflectionProperty::IS_PUBLIC);
$data = [];
foreach ($properties as $property) {
$propertyName = $property->getName();
$data[$propertyName] = $this->$propertyName;
}
return $data;
}
}
表模型继承基础Model类
<?php
namespace App\Models\Test;
class Order extends Model
{
protected static string $connection = 'test_db';
protected static string $table = 'order';
#[PrimaryKey]
public int $orderNo = 0;
public int $userId = 0;
public int $amount = 0;
/**
* @var int 订单状态【0:待支付 1:支付成功 2:支付失败】
*/
public int $status = 0;
const STATUS_WAIT = 0;
const STATUS_SUCCESS = 1;
const STATUS_FAIL = 2;
}
<?php
namespace App\Models\Test;
class User extends Model
{
protected static string $connection = 'test_db';
protected static string $table = 'user';
#[PrimaryKey]
public int $userId = 0;
public string $username = '';
public string $password = '';
public string $nickname = '';
public string $avatar = '';
}
改写DB
类
<?php
namespace App\Models\Test;
class DB
{
protected string $connection = '';
protected string $table = '';
/**
* @var string |ModelInterface
*/
protected string|ModelInterface $model = '';
public static function make(string $model): static
{
$ret = new static;
$ret->model = $model;
return $ret;
}
/**
* 查询条件
* @param string $column 查询列
* @param string|null $operator 查询操作符
* @param mixed|null $value 查询值
* @param string $boolean 查询条件连接符
* @return $this
*/
public function where(string $column, string|null $operator = null, mixed $value = null, string $boolean = 'and'): static
{
return $this;
}
/**
* 获取列表
* @param array|ModelInterface[] $models
*/
public function get(array &$models): void
{
$models[] = $this->model::make();
$models[] = $this->model::make();
}
/**
* 获取第一个
* @param ModelInterface $model
* @return bool
*/
public function find(ModelInterface $model): bool
{
return true;
}
/**
* 删除
* @return int 删除的行数
*/
public function delete(): int
{
return 1;
}
/**
* 创建
* @return bool 创建成功或失败
*/
public function create(ModelInterface $model): bool
{
return 1;
}
/**
* 更新
* @return int 更新的行数
*/
public function update(ModelInterface $model): int
{
return 1;
}
}
改写增删改查示例
<?php
namespace App\Http\Controllers;
use App\Models\Test\DB;
use App\Models\Test\Order;
use App\Models\Test\User;
use Symfony\Component\HttpFoundation\Response;
class TestController extends Controller
{
/**
* 查询示例
* @return Response
*/
public function index(): Response
{
$admin = User::make();
DB::make(User::class)->where('username', '=', 'admin')->find($admin);
$users = User::makes();
DB::make(User::class)->where('nickname', 'like', "%$admin->nickname%")->get($users);
$ret = [];
foreach ($users as $user) {
$tmp = [
'username' => $user->username,
'nickname' => $user->nickname,
'avatar' => $user->avatar
];
$order = new Order;
$ok = DB::make(Order::class)->where('user_id', '=', $user->userId)->find($order);
if ($ok && $order->status = Order::STATUS_SUCCESS) {
$tmp['order'] = $order->toArray();
} else {
$tmp['order'] = null;
}
$ret[] = $tmp;
}
return $this->success($ret);
}
/**
* 新增示例
* @return Response
*/
public function create(): Response
{
$user = new User();
$user->username = 'test';
$user->nickname = 'test';
$user->avatar = 'test';
return DB::make(User::class)->create($user) ? $this->success() : $this->error('新增失败', 1);
}
/**
* 删除示例
* @return Response
*/
public function delete(): Response
{
$ok = DB::make(Order::class)->where('user_id', '=', 1)->delete();
return $ok ? $this->success() : $this->error('删除失败', 1);
}
/**
* 更新示例
* @return Response
*/
public function update(): Response
{
$user = new User();
$user->username = 'test';
$ok = DB::make(User::class)->where('userId', '=', 1)->update($user);
return $ok ? $this->success() : $this->error('更新失败', 1);
}
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: