简化嵌套的if语句

时间:2020-10-07 15:17:02

标签: php laravel

我正在实现搜索功能,并根据查询参数使用其他类进行搜索。

class Search { 

    public function getResults()
    {
        if (request('type') == 'thread') {
                $results = app(SearchThreads::class)->query();
        } elseif (request('type') == 'profile_post') {
                $results = app(SearchProfilePosts::class)->query();
        } elseif (request()->missing('type')) {
                $results = app(SearchAllPosts::class)->query();
     }

}

现在,当我想搜索线程时,我有以下代码。

class SearchThreads{

        public function query()
        {
            $searchQuery = request('q');
            $onlyTitle = request()->boolean('only_title');

            if (isset($searchQuery)) {
                if ($onlyTitle) {
                    $query = Thread::search($searchQuery);
                } else {
                    $query = Threads::search($searchQuery);
                }
            } else {
                if ($onlyTitle) {
                    $query = Activity::ofThreads();
                } else {
                    $query = Activity::ofThreadsAndReplies();
                }
            }
        }

}

解释代码。

如果用户输入搜索词( $ searchQuery ),则使用 Algolia 进行搜索,否则直接进行数据库查询。

  • 如果用户输入搜索词

    1. 如果用户已经选中了 onlyTitle 复选框
    2. ,则使用 Thread 索引。
    3. 如果用户没有选中 onlyTitle 复选框
    4. ,则使用线程索引
  • 如果用户未输入搜索词

    1. 如果用户选中 onlyTitle 复选框,则获取所有线程
    2. 如果用户没有选中 onlyTitle 复选框
    3. ,则获取所有线程和答复

是否有一种模式可以简化嵌套的if语句,还是应该为其中的情况创建一个单独的类

  1. 用户输入了搜索词
  2. 用户未输入搜索词

在每个类中检查用户是否已选中 onlyTitle 复选框

4 个答案:

答案 0 :(得分:1)

我会将这段代码重构为:

保留请求参数以统一界面中的搜索方法。

interface SearchInterface
{
    public function search(\Illuminate\Http\Request $request);
}

class Search {

    protected $strategy;

    public function __construct($search)
    {
        $this->strategy = $search;
    }

    public function getResults(\Illuminate\Http\Request $request)
    {
        return $this->strategy->search($request);
    }
}

class SearchFactory
{
    private \Illuminate\Contracts\Container\Container $container;

    public function __construct(\Illuminate\Contracts\Container\Container $container)
    {
        $this->container = $container;
    }

    public function algoliaFromRequest(\Illuminate\Http\Request  $request): Search
    {
        $type = $request['type'];
        $onlyTitle = $request->boolean('only_title');
        if ($type === 'thread' && !$onlyTitle) {
            return $this->container->get(Threads::class);
        }

        if ($type === 'profile_post' && !$onlyTitle) {
            return $this->container->get(ProfilePosts::class);
        }

        if (empty($type) && !$onlyTitle) {
            return $this->container->get(AllPosts::class);
        }

        if ($onlyTitle) {
            return $this->container->get(Thread::class);
        }

        throw new UnexpectedValueException();
    }

    public function fromRequest(\Illuminate\Http\Request $request): Search
    {
        if ($request->missing('q')) {
            return $this->databaseFromRequest($request);
        }
        return $this->algoliaFromRequest($request);
    }

    public function databaseFromRequest(\Illuminate\Http\Request $request): Search
    {
        $type = $request['type'];
        $onlyTitle = $request->boolean('only_title');
        if ($type === 'thread' && !$onlyTitle) {
            return $this->container->get(DatabaseSearchThreads::class);
        }

        if ($type === 'profile_post' && !$onlyTitle) {
            return $this->container->get(DatabaseSearchProfilePosts::class);
        }

        if ($type === 'thread' && $onlyTitle) {
            return $this->container->get(DatabaseSearchThread::class);
        }

        if ($request->missing('type')) {
            return $this->container->get(DatabaseSearchAllPosts::class);
        }

        throw new InvalidArgumentException();
    }
}


final class SearchController
{
    private SearchFactory $factory;

    public function __construct(SearchFactory $factory)
    {
        $this->factory = $factory;
    }

    public function listResults(\Illuminate\Http\Request $request)
    {
        return $this->factory->fromRequest($request)->getResults($request);
    }
}

由此得出的结论是,不要将请求包含在构造函数中非常重要。这样,您可以在应用程序生命周期中创建实例而无需请求。这有利于缓存,可测试性和模块化。我也不喜欢应用程序和请求方法,因为它们会无聊地拉出变量,从而降低了可测试性和性能。

答案 1 :(得分:0)

class Search 
{
     public function __construct(){
             $this->strategy = app(SearchFactory::class)->create();
         }

        public function getResults()
        {
             return $this->strategy->search();
         }
}
class SearchFactory
{
    public function create()
    {
        if (request()->missing('q')) {
            return app(DatabaseSearch::class);
        } else {
            return app(AlgoliaSearch::class);
        }

    }
}
class AlgoliaSearch implements SearchInterface
{

    
    public function __construct()
    {
        $this->strategy = app(AlgoliaSearchFactory::class)->create();
    }
    public function search()
    {
        $this->strategy->search();
    }
}
class AlgoliaSearchFactory
{

    public function create()
    {
        if (request('type') == 'thread') {
            return app(Threads::class);
        } elseif (request('type') == 'profile_post') {
            return app(ProfilePosts::class);
        } elseif (request()->missing('type')) {
            return app(AllPosts::class);
        } elseif (request()->boolean('only_title')) {
            return app(Thread::class);
        }
    }
}

AlgoliaSearchFactory 中创建的类是algolia聚合器,因此可以在任何这些类上调用 search 方法。

这样的东西会使它更干净或更差吗?

现在我有一些策略,这些策略对我来说听起来太多了。

答案 2 :(得分:0)

我试图为您实现一个好的解决方案,但是我不得不对代码进行一些假设。

我将请求与构造函数逻辑分离,并为搜索接口提供了一个请求参数。这不仅使意图更加清晰,而不仅仅是具有请求功能的请求。

final class SearchFactory
{
    private ContainerInterface $container;

    /**
     * I am not a big fan of using the container to locate the dependencies.
     * If possible I would implement the construction logic inside the methods.
     * The only object you would then pass into the constructor are basic building blocks,
     * independent from the HTTP request (e.g. PDO, AlgoliaClient etc.)
     */
    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;
    }

    private function databaseSearch(): DatabaseSearch
    {
        return // databaseSearch construction logic
    }

    public function thread(): AlgoliaSearch
    {
        return // thread construction logic
    }

    public function threads(): AlgoliaSearch
    {
        return // threads construction logic
    }

    public function profilePost(): AlgoliaSearch
    {
        return // thread construction logic
    }

    public function onlyTitle(): AlgoliaSearch
    {
        return // thread construction logic
    }

    public function fromRequest(Request $request): SearchInterface
    {
        if ($request->missing('q')) {
            return $this->databaseSearch();
        }

        // Fancy solution to reduce if statements in exchange for legibility :)
        // Note: this is only a viable solution if you have done correct http validation IMO
        $camelCaseType = Str::camel($request->get('type'));
        if (!method_exists($this, $camelCaseType)) {
            // Throw a relevent error here
        }

        return $this->$camelCaseType();
    }
}

// According to the code you provided, algoliasearch seems an unnecessary wrapper class, which receives a search interface, just to call another search interface. If this is the only reason for its existence, I would remove it
final class AlgoliaSearch implements SearchInterface {
    private SearchInterface $search;

    public function __construct(SearchInterface $search) {
        $this->search = $search;
    }

    public function search(Request $request): SearchInterface {
        return $this->search->search($request);
    }
}

我也不知道Search类的意义。如果它仅有效地将搜索方法重命名为getResults,则我不知道这是什么意思。这就是为什么我省略了它。

答案 3 :(得分:0)

我必须写下所有这些内容才能使问题易于理解。

SearchFactory 接受所有必需的参数,并基于这些参数调用 AlgoliaSearchFactory DatabaseSearchFactory 来生成最终对象,该对象将返回。

class SearchFactory
{
    protected $type;
    protected $searchQuery;
    protected $onlyTitle;
    protected $algoliaSearchFactory;
    protected $databaseSearchFactory;

    public function __construct(
        $type,
        $searchQuery,
        $onlyTitle,
        DatabaseSearchFactory $databaseSearchFactory,
        AlgoliaSearchFactory $algoliaSearchFactory
    ) {
        $this->type = $type;
        $this->searchQuery = $searchQuery;
        $this->onlyTitle = $onlyTitle;
        $this->databaseSearchFactory = $databaseSearchFactory;
        $this->algoliaSearchFactory = $algoliaSearchFactory;
    }

    public function create()
    {
        if (isset($this->searchQuery)) {
            return $this->algoliaSearchFactory->create($this->type, $this->onlyTitle);
        } else {
            return $this->databaseSearchFactory->create($this->type, $this->onlyTitle);
        }
    }
}
SearchFactory 传递的

基于 $ type DatabaseSearchFactory onlyTitle 参数返回对象,这是最终要获得结果所需要使用的对象。

class DatabaseSearchFactory
{
    public function create($type, $onlyTitle)
    {
        if ($type == 'thread' && !$onlyTitle) {
            return app(DatabaseSearchThreads::class);
        } elseif ($type == 'profile_post' && !$onlyTitle) {
            return app(DatabaseSearchProfilePosts::class);
        } elseif ($type == 'thread' && $onlyTitle) {
            return app(DatabaseSearchThread::class);
        } elseif (is_null($type)) {
            return app(DatabaseSearchAllPosts::class);
        }
    }
}

DatabaseSearchFactory

相同的逻辑
class AlgoliaSearchFactory
{
    public function create($type, $onlyTitle)
    {
        if ($type == 'thread' && !$onlyTitle) {
            return app(Threads::class);
        } elseif ($type == 'profile_post' && !$onlyTitle) {
            return app(ProfilePosts::class);
        } elseif (empty($type) && !$onlyTitle) {
            return app(AllPosts::class);
        } elseif ($onlyTitle) {
            return app(Thread::class);
        }
    }
}

AlgoliaSearchFactory 创建的对象具有方法 search ,该方法需要 $ searchQuery

interface AlgoliaSearchInterface
{
    public function search($searchQuery);
}

DatabaseSearchFactory 创建的对象具有不需要任何参数的 search 方法。

interface DatabaseSearchInterface
{
    public function search();
}

Search 现在将由 SearchFactory 生成的最终对象作为参数,该对象可以实现 AlgoliaSearchInterface DatabaseSearchInterface 这就是为什么我没有输入提示

getResults 方法现在必须找出 search 变量的类型(它实现的接口),以便传递 $ searchQuery 是否作为参数。

这就是控制器可以使用 Search 类获取结果的方式。 类搜索 { 受保护的$ strategy;

    public function __construct($search)
    {
        $this->strategy = $search;
    }

    public function getResults()
    {
        if(isset(request('q')))
        {
            $results = $this->strategy->search(request('q'));
        }
        else
        {
            $results = $this->strategy->search();
        }
    }
}


class SearchController(Search $search)
{
    $results = $search->getResults();
}

根据所有@Transitive建议,这就是我想到的。我唯一无法解决的问题是如何在没有语句的情况下在 getResults 方法中调用 search