PHP/Laravel API class 实现的接口

PHP/Laravel Interface for API class realization

我有一个带有搜索功能的界面:

interface Searcher
{
    public function search($text,$limit)
}

我有一些实现是基于API

class APISeach implements Searcher
{
   public function search($text,$limit)
   {
        $params = [
            'name' => $sName,
            'maxRows' => $iLimit,
        ];
        $response = Http::get('http://some_api_service/search', $params)->json();
   }
}

我有一些使用此搜索的代码:

class MyController extends Controller
{
    private $seacher;
    
    public function __construct(Searcher $mySeacher)
    {
         $this->seacher = $mySeacher;
    }

    public function search($text,$limit=10)
    {
         $this->seacher->search($text,$limit)
    }
}

一切看起来都很好(可能是)。但是,如果我需要更改 API 实现并且需要另一个参数怎么办。例如:

class AnotherAPISeach implements Searcher
{
   public function search($text,$language,$fuzzy,$limit)
   {
       $ApiObjectFromLib = new ApiObjectFromLib();
       $response = $ApiObjectFromLib->search($text,$language,$fuzzy,$limit)->json();
   }
}

所以它不能再实现 Searcher 接口了。 是否存在使用 API 函数接口的任何方式?所有 API 都可能需要各种参数,如果我需要为每个 API 更改接口或控制器代码,那就不好了。

您可以使用variadic arguments

interface Searcher
{
    public function search($text, $limit, ...$additional);
}

如果这违背了 interface 的目的,则由您决定

这样的事情怎么样 (demo)?

public function search(string $name, int $limit = \Search\Limit::DEFAULT)
{
    return $this->api->search(
        new \Search\Name($name),
        new \Search\Limit($limit)
    );
}

public function lookup(
    string $name,
    string $language = \Search\Language::DEFAULT,
    bool $fuzzy = \Search\Fuzzy::DEFAULT,
    int $limit = \Search\Limit::DEFAULT
) {
    return $this->api->search(
        new \Search\Name($name),
        new \Search\Language($language),
        new \Search\Fuzzy($fuzzy),
        new \Search\Limit($limit)
    );
}

这些“规范”看起来像这样:

namespace Search
{
    interface Specification
    {
        public function __invoke(array $params): array;
    }
    
    class Name implements Specification
    {
        private $name = null;
        
        public function __construct(string $name)
        {
            $this->name = $name;
        }
        
        public function __invoke(array $params): array
        {
            return [
                'name' => $this->name,
            ];
        }
    }
    
    class Language implements Specification
    {
        const DEFAULT = 'en';
        
        private $language = null;
        
        public function __construct(string $language)
        {
            $this->language = $language;
        }
        
        public function __invoke(array $params): array
        {
            return [
                'language' => $this->language ?? 'en',
            ];
        }
    }
    
    class Fuzzy implements Specification
    {
        const DEFAULT = true;
        
        private $fuzzy = null;
        
        public function __construct(bool $fuzzy)
        {
            $this->fuzzy = $fuzzy;
        }
        
        public function __invoke(array $params): array
        {
            return [
                'fuzzy' => $this->fuzzy,
            ];
        }
    }
    
    class Limit implements Specification
    {
        const DEFAULT = 10;
        
        private $max = null;
        
        public function __construct(int $limit)
        {
            $this->limit = $limit;
        }
        
        public function __invoke(array $params): array
        {
            return [
                'maxRows' => $this->limit ?: self::DEFAULT,
            ];
        }
    }
}

可搜索的API是这样组成的:

interface Searchable
{
    public function search(\Search\Specification... $criteria);
}

class Search implements Searchable
{
    private $url = '/search';
    
    private $defaults = [
        'maxRows' => \Search\Limit::DEFAULT,
    ];
    
    public function search(\Search\Specification ...$criteria)
    {
        return \Http::get($this->url, array_reduce(
            $criteria,
            fn($params, $criteria) => $criteria($params) + $params,
            $this->defaults
        ))->json();
    }
}

Specification Pattern 很有趣,因为它暗示对域的请求实际上只是一系列决策,这些决策会导致可在其他地方应用的配置。

例如,请注意上面的 $criteria($params) 个对象是如何为每个请求提供当前 $params 的,它可能会覆盖参数、读取和修改参数,或者可能包含一个规范检查以验证参数。

关于 array + array 语法的注意事项,这是一种合并数组的方法:

['foo' => 'bar'] + ['foo' => 'baz'] // left takes precedence: ['foo' => 'bar']

Filter/Criteria 非常相似;我倾向于认为那些对其应用的对象(存储库、查询或集合)比规范更严格 link,在我看来,规范更适用于要返回的内容。