如何重构Symfony 5控制器以符合SOLID设计原则

问题描述

我正在尝试创建此API端点,该端点将接受JSON有效负载并根据提供的因素及其等级计算报价。

我的实体中的评分约为

  • “年龄”
  • “邮政编码”
  • “ ABI代码

这些Agerating,Postcoderating和Abirating实体实现ratingFactorInterface来强制实施getratingFactor()方法

QuoteController似乎违反了“单一职责”和“开放/关闭”设计原则,因为诸如“年龄”,“邮政编码”之类的因素可以改变-可以添加额外的因素,也可以不使用其中之一。 / p>

我当时想也许可以在依赖项注入容器中指定评级因子,但似乎找不到一个很好的例子,特别是在依赖于其他服务(例如依赖于AbiCoderating的依赖项)的情况下,如何工作使用接受汽车注册号的第三方API返回的ABI代码

如何重构控制器和服务,以免违反单一职责和开放/封闭设计原则?

POST JSON有效负载示例

{
    "age": 20,"postcode": "PE3 8AF","regNo": "PJ63 LXR"
}

QuoteController

<?PHP

namespace App\Controller;

use App\Repository\AbiCoderatingRepository;
use App\Repository\AgeratingRepository;
use App\Repository\PostcoderatingRepository;
use App\Service\AbiCodeLookup;
use App\Service\QuoteCalculator;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

/**
 * Class QuoteController
 * @package App\Controller
 */
class QuoteController extends AbstractController
{
    /**
     * @Route("/",name="quote")
     *
     * @param Request $request
     * @param AbiCoderatingRepository $abiCoderatingRepository
     * @param AgeratingRepository $ageratingRepository
     * @param PostcoderatingRepository $postcoderatingRepository
     * @return JsonResponse
     */
    public function index(Request $request,AbiCoderatingRepository $abiCoderatingRepository,AgeratingRepository $ageratingRepository,PostcoderatingRepository $postcoderatingRepository)
    {
        try{

            $request = $this->transformJsonBody($request);

            /**
             * Quoting engine Could be used with a different set of rating factors!
             * Meaning age,postcode and regNo maybe not be required,some other rating factors might be introduced
             * How to make controller to accept rating factors dynamically?
             */
            if (!$request || !$request->get('age') || !$request->request->get('postcode') || !$request->get('regNo')){
                throw new \Exception();
            }

            /**
             * call to a third party API to look up the vehicle registration number and return an ABI code
             * this is only required if Abirating is used with the quoting engine
             */
            $abiCode          = AbiCodeLookup::getAbiCode($request->get('regNo'));
            /**
             * $abiCode is only required if postcoderating is used by quoting engine
             */
            $ratingFactors[]  = $abiCoderatingRepository->findOneBy(["abiCode"=>$abiCode]);
            $ratingFactors[]  = $ageratingRepository->findOneBy(["age"=>$request->get("age")]);
            /**
             * $area is only required if postcoderating is used by quoting engine
             */
            $area             = substr($request->get("postcode"),3);
            $ratingFactors[]  = $postcoderatingRepository->findByPostcodeArea($area);
            $premiumTotal     = QuoteCalculator::calculate($ratingFactors);

            $data = [
                'status' => 200,'success' => "Quote created successfully",'quote' => $premiumTotal
            ];

            return new JsonResponse($data,200);

        }catch (\Exception $e){
            $data = [
                'status' => 422,'errors' => "Data is not valid",];
            return new JsonResponse($data,422);
        }

    }

    /**
     * @param Request $request
     * @return Request
     */
    protected function transformJsonBody(Request $request)
    {
        $data = json_decode($request->getContent(),true);

        if ($data === null) {
            return $request;
        }

        $request->request->replace($data);

        return $request;
    }
}

AbiCoderating

<?PHP

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\AbiCoderatingRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ApiResource(
 *     collectionoperations={"get","post"},*     itemOperations={"get"}
 * )
 * @ORM\Entity(repositoryClass=AbiCoderatingRepository::class)
 */
class AbiCoderating implements ratingFactorInterface
{
    /**
     * @ORM\Id
     * @ORM\Column(type="string",length=10)
     */
    private $abiCode;

    /**
     * @ORM\Column(type="decimal",precision=10,scale=2,nullable=true)
     */
    private $ratingFactor;


    public function getAbiCode(): ?string
    {
        return $this->abiCode;
    }

    public function setAbiCode(string $abiCode): self
    {
        $this->abiCode = $abiCode;

        return $this;
    }

    public function getratingFactor(): ?float
    {
        return $this->ratingFactor;
    }

    public function setratingFactor(?float $ratingFactor): self
    {
        $this->ratingFactor = $ratingFactor;

        return $this;
    }
}

Agerating

<?PHP

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;
use App\Repository\AgeratingRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ApiResource(
 *     collectionoperations={"get",*     itemOperations={"get"}
 * )
 * @ORM\Entity(repositoryClass=AgeratingRepository::class)
 */
class Agerating implements ratingFactorInterface
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     */
    private $age;

    /**
     * @ORM\Column(type="decimal",nullable=true)
     */
    private $ratingFactor;


    public function getAge(): ?int
    {
        return $this->age;
    }

    public function setAge(int $age): self
    {
        $this->age = $age;

        return $this;
    }

    public function getratingFactor(): ?float
    {
        return $this->ratingFactor;
    }

    public function setratingFactor(?float $ratingFactor): self
    {
        $this->ratingFactor = $ratingFactor;

        return $this;
    }
}

Postcoderating

<?PHP

namespace App\Entity;

use App\Repository\PostcoderatingRepository;
use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity(repositoryClass=PostcoderatingRepository::class)
 */
class Postcoderating implements ratingFactorInterface
{
    /**
     * @ORM\Id
     * @ORM\Column(type="string",length=4)
     */
    private $postcodeArea;

    /**
     * @ORM\Column(type="decimal",nullable=true)
     */
    private $ratingFactor;

    public function getPostcodeArea(): ?string
    {
        return $this->postcodeArea;
    }

    public function setPostcodeArea(string $postcodeArea): self
    {
        $this->postcodeArea = $postcodeArea;

        return $this;
    }

    public function getratingFactor(): ?float
    {
        return $this->ratingFactor;
    }

    public function setratingFactor(?float $ratingFactor): self
    {
        $this->ratingFactor = $ratingFactor;

        return $this;
    }
}

ratingFactorInterface

<?PHP


namespace App\Entity;


interface ratingFactorInterface
{
    /**
     * @return float|null
     */
    public function getratingFactor(): ?float;
}

AbiCoderatingRepository

<?PHP

namespace App\Repository;

use App\Entity\AbiCoderating;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @method AbiCoderating|null find($id,$lockMode = null,$lockVersion = null)
 * @method AbiCoderating|null findOneBy(array $criteria,array $orderBy = null)
 * @method AbiCoderating[]    findAll()
 * @method AbiCoderating[]    findBy(array $criteria,array $orderBy = null,$limit = null,$offset = null)
 */
class AbiCoderatingRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry,AbiCoderating::class);
    }

   
}

AgeratingRepository

<?PHP

namespace App\Repository;

use App\Entity\Agerating;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @method Agerating|null find($id,$lockVersion = null)
 * @method Agerating|null findOneBy(array $criteria,array $orderBy = null)
 * @method Agerating[]    findAll()
 * @method Agerating[]    findBy(array $criteria,$offset = null)
 */
class AgeratingRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry,Agerating::class);
    }
}

PostcoderatingRepository

<?PHP

namespace App\Repository;

use App\Entity\Postcoderating;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;

/**
 * @method Postcoderating|null find($id,$lockVersion = null)
 * @method Postcoderating|null findOneBy(array $criteria,array $orderBy = null)
 * @method Postcoderating[]    findAll()
 * @method Postcoderating[]    findBy(array $criteria,$offset = null)
 */
class PostcoderatingRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry,Postcoderating::class);
    }

    /**
     * @return Postcoderating Returns Postcoderating objects based on area
     */

    public function findByPostcodeArea($area): ?Postcoderating
    {
        return $this->findOneBy(["postcodeArea"=>$area]);
    }
}

AbiCodeLookup

<?PHP
namespace App\Service;

class AbiCodeLookup
{
    public static function getAbiCode(string $regNumber){

        /**
         * create a request to third party api which would return abi code
         * How to configure this service to be used only with regNo factor
         */
        return "22529902";
    }
}

QuoteCalculator

<?PHP
    namespace App\Service;
    use App\Entity\ratingFactorInterface;
    
    /**
     * Class QuoteCalculator
     */
    class QuoteCalculator
    {
        /**
         * @param array $ratingFactors
         * @return float
         */
        public static function calculate(array $ratingFactors): float
        {
            $premiumTotal = 500;
            foreach ($ratingFactors as $ratingFactor){
                $premiumTotal = $premiumTotal * ($ratingFactor instanceof ratingFactorInterface ? $ratingFactor->getratingFactor() : 1);
            }
            return $premiumTotal ;
        }
    
    }

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)