优雅且语义化的断言之—将模型属性断言变为模型方法断言
举个例子,断言直接裸露的写在控制器中
控制器的方法
<?php
use App\Models\Order;
class OrderController extends Controller
{
public function applyRefund(Order $order, ApplyRefundRequest $request) {
// 1.断言 - 判断是否未支付
if (! $order->paid_at) {
throw new InvalidRequestException('该订单未支付,不可退款');
}
// 2.断言 - 判断订单是否已退款
if ($order->refund_status !== Order::REFUND_STATUS_PENDING) {
throw new InvalidRequestException('该订单已经申请过退款,请勿重复申请');
}
...
}
}
这样写不是不可以,也是正确的,但是存在三面问题:
- 断言有点长,看起来不够优雅,控制器的代码应简约一点。
- 断言不具有语义化,阅读起来不能快速理解其含义。
- 后期代码需要批量修改,麻烦且容易出错。
针对以上问题我要做是以下两点:
- 长的断言修改为方法,写入到模型中,控制器直接调用模型方法即可。(代码看起来简约且后期维护只需修改模型方法)
- 方法名使用快速可读,可理解其中含义的名称。
优化后的样子
模型中的方法:
<?php
namespace App\Models;
class Order extends Model
{
// 退订状态
const REFUND_STATUS_PENDING = 'pending';
// 是否未付款
public function isUnpaid() :bool
{
return ! ($this->paid_at);
}
// 是否订单已退款
public function isRefunded() :bool
{
return $this->refund_status !== self::REFUND_STATUS_PENDING;
}
}
控制器中的调用:
<?php
use App\Models\Order;
class OrderController extends Controller
{
public function applyRefund(Order $order, ApplyRefundRequest $request) {
// 1.断言 - 判断是否未支付
if ($order->isUnpaid()) {
throw new InvalidRequestException('该订单未支付,不可退款');
}
// 2.断言 - 判断订单是否已退款
if ($order->isRefunded()) {
throw new InvalidRequestException('该订单已经申请退款,请勿重复申请');
}
...
}
}
完了么?并没有,咱们继续优化!!
模型中的方法:
<?php
namespace App\Models;
class Order extends Model
{
// 退订状态
const REFUND_STATUS_PENDING = 'pending';
// 是否未付款
public function isUnpaid() :bool
{
return ! ($this->paid_at);
}
// 直接抛出异常 - paid_at 未支付时值为null
public function isUnpaidNotSupported() :bool
{
return ! $this->isUnpaid() ?: throw new InvalidRequestException('该订单未支付,不可退款');
}
// 是否订单已退款
public function isRefunded() :bool
{
return $this->refund_status !== self::REFUND_STATUS_PENDING;
}
// 直接抛出异常
public function isRefundedNotSupported() :bool
{
return ! $this->isRefunded() ?: throw new InvalidRequestException('该订单已经申请退款,请勿重复申请');;
}
}
控制器中的调用:
<?php
use App\Models\Order;
class OrderController extends Controller
{
public function applyRefund(Order $order, ApplyRefundRequest $request) {
// 1.断言 - 判断是否未支付
$order->isUnpaidNotSupported()
// 2.断言 - 判断订单是否已退款
$order->isRefundedNotSupported()
...
}
}
如此这样,控制器的判断看起来不那么蹩脚,读起来也很通顺,后期代码维护直接修改模型方法,且属性也可调整,挺好!以后都按照这种方式写具有可读可维护性的代码!赏心也悦目,哈哈!
还能再优化么?
- 我想还能优化,直接控制器无需断言。
- 将断言放入中间件中。
- 将断言放入请求类中,都可以,就看你要优化到那种程度了。
本作品采用《CC 协议》,转载必须注明作者和本文链接
基超
感觉没什么区别
这些方法已经有了 可以再来个 调用这些方法 然后直接有问题抛出异常的方法 命名为 没用符合条件就抛出异常
你的控制器就更节省代码了
isOrFail 是不是更加优雅
empty 暴露你的基础不好或者说数据库设计有问题。。。。
boolean
控制器 是好看了 没模型不好看了 :joy: