优雅且语义化的断言之—将模型属性断言变为模型方法断言

114 阅读2分钟

举个例子,断言直接裸露的写在控制器中

控制器的方法

<?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('该订单已经申请过退款,请勿重复申请');
		}
		...
	}
}

这样写不是不可以,也是正确的,但是存在三面问题

  1. 断言有点长,看起来不够优雅,控制器的代码应简约一点。
  2. 断言不具有语义化,阅读起来不能快速理解其含义。
  3. 后期代码需要批量修改,麻烦且容易出错。

针对以上问题我要做以下两点

  1. 长的断言修改为方法,写入到模型中,控制器直接调用模型方法即可。(代码看起来简约且后期维护只需修改模型方法)
  2. 方法名使用快速可读,可理解其中含义的名称。

优化后的样子

模型中的方法:

<?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()
		...
	}
}

如此这样,控制器的判断看起来不那么蹩脚,读起来也很通顺,后期代码维护直接修改模型方法,且属性也可调整,挺好!以后都按照这种方式写具有可读可维护性的代码!赏心也悦目,哈哈!

还能再优化么?

  1. 我想还能优化,直接控制器无需断言。
  2. 将断言放入中间件中。
  3. 将断言放入请求类中,都可以,就看你要优化到哪种程度了。