简体   繁体   English

我可以使此搜索功能更有效吗?

[英]Can I make this search function more efficient?

I am getting a list of users to display on a page using the following code in members.php : 我正在使用members.php的以下代码获取要显示在页面上的用户列表:

$users = $user->getUsers($_GET['skill'], $_GET['hometowm'], $_GET['county']);
  • members.php displays all members members.php显示所有成员
  • members.php?skill=Foo displays users with that skill members.php?skill=Foo用户显示该技能
  • members.php?hometown=Foo displays users of hometown "Foo" members.php?hometown=Foo显示家乡“ Foo”的用户
  • members.php?county=Foo display users of county (AKA state in US) members.php?county=Foo显示县(美国的AKA州)的用户
  • members.php?skill=Foo&hometown=Foo displays users of skill and hometown members.php?skill=Foo&hometown=Foo显示技能和家乡的用户
  • members.php?skill=Foo&county=Foo displays users of skill and county members.php?skill=Foo&county=Foo显示技能和县的用户

Here is what I'd like to know: Is there a way I can shorten the amount of if statements or make them more efficient? 我想知道的是:有没有一种方法可以缩短if语句的数量或使它们更有效? Am I doing it correctly? 我做得对吗? Because I don't like the fact I have so many parameters especially when I want to expand. 因为我不喜欢这样的事实,我有很多参数,尤其是当我要扩展时。

**User.class.php**
    public function getUsers($skill = null, $hometown = null, $county = null) {
        $user_table = $this->cfg['users_table']['table'];

        # Search Skill
        if ($skill != null && $hometown == null && $county == null) {
            $sql = 'SELECT skills.Title, users_skills.SkillId, users.*
FROM users INNER JOIN (skills INNER JOIN users_skills ON skills.SkillId = users_skills.SkillId) ON users.UserId = users_skills.UserId
WHERE (((skills.Title)="' . $skill . '"))';
        # Search Hometown
        } else if ($hometown != null && $skill == null && $county == null) {
            $sql = 'SELECT * FROM users WHERE HomeTown = "' . $hometown . '"';
        # Search County
        } else if ($county != null && $skill == null && $hometown == null) {
            $sql = 'SELECT * FROM users WHERE county = "' . $county . '"';
        # Search Skill and Hometown
        } else if ($skill != null && $hometown != null && $county == null) {
            //sql
        # Search Skill and County
        } else if($skill != null && $county != null && $hometown == null){
        } else {
            $sql = "SELECT * FROM " . $user_table;
        }
        $stmt = $this->db->prepare($sql);
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

Thanks. 谢谢。

There are actually two issues here. 实际上,这里有两个问题。 The first is that your interface design is too specific. 首先是您的界面设计过于具体。 The other is that you're mixing the data model with the database access layer, and that's making your code more complicated. 另一个是您将数据模型与数据库访问层混合在一起,这使您的代码更加复杂。

I've skipped some details in my examples for brevity (and the code is untested), but I presume you can fill in the gaps. 为了简洁起见,我在示例中跳过了一些细节(并且代码未经测试),但是我想您可以填补空白。

Generalize the Interface 通用化接口

The first order of business is to make your interface more general. 首先要做的是使您的界面更通用。 Instead of using one method that does everything, split county , skill , etc. into different methods: 与其使用一种无​​所不能的方法,而是将countyskill等拆分为不同的方法:

class userFilter
{
    protected $db;
    protected $params;

    protected function __construct()
    {
        $this->db = new database_connection();
    }

    public static function create()
    {
        return new userFilter();
    }

    public function addCounty($county)
    {
        // For simplicity, 'county' is the column name
        $this->params['county'][] = $county;
        // Return $this to enable method chaining, used below
        return $this;
    }

    public function addSkill($skill)
    {
        $this->params['skill'][] = $skill;
        return $this;
    }

    public function addHometown($hometown)
    {
        return $this;
    }

    public function fetchUsers()
    {
        return '$data';
    }
}

With the above interface, you can optionally add any number of parameters, and each (potentially) has its own logic associated with validating input, writing SQL conditions, etc. 使用上面的接口,您可以选择添加任意数量的参数,并且每个参数(潜在地)都有自己的逻辑,这些逻辑与验证输入,编写SQL条件等相关。

Database Access Layer 数据库访问层

The hard part of this solution is writing the actual SQL. 该解决方案的难点是编写实际的SQL。 For that, you need to know a few things: 为此,您需要了解以下几点:

  1. Which columns do you want (the SELECT clause)? 您想要哪些列( SELECT子句)?
  2. Which rows do you want (the WHERE clause)? 您想要哪些行( WHERE子句)?
  3. What does your schema look like, or more specifically, which column names correspond to which parameters? 您的架构是什么样的,或更具体地说,哪些列名与哪些参数相对应?
  4. Do you want to select a single parameter multiple times? 是否要多次选择一个参数? (I've assumed multiple) (我假设有多个)

The easy way to solve this problems is by using arrays, because you can easily foreach over them to include whichever may be specified, and you can use their key=>value pairing to maintain associations with respect to your database schema. 解决此问题的简单方法是使用数组,因为您可以轻松地foreach它们以包括可能指定的任何一个,并且可以使用它们的key => value配对来维护与数据库模式的关联。

Now when you go to write the query for fetchUsers() you can iterate over $this->params using array_keys($this->params) as the column names and $this->params as the data. 现在,当您编写对fetchUsers()的查询时,可以使用array_keys($this->params)作为列名和$this->params作为数据遍历$this->params That might look something like this: 可能看起来像这样:

// Select all columns present in $this->params
$sql = 'SELECT id, '.implode(', ', array_keys($this->params));
$sql .= 'FROM table_name WHERE ';

$where = array()
foreach($this->params as $column => $ids){
    $where[] = $column . ' IN ('.implode(', ', $ids).')';
}
$sql .= implode(' AND ', $where);

With a more complicated schema that requires joins, you may need a switch to handle each join and join condition, or you may find a way to cram that into an array. 对于需要联接的更复杂的架构,您可能需要一个开关来处理每个联接和联接条件,或者您可以找到一种将其塞入数组的方法。 You'll also need extra logic to make sure you don't add an empty WHERE clause or other silly things, but the meat of it is there. 您还需要额外的逻辑以确保您没有添加空的WHERE子句或其他愚蠢的东西,但是它的实质是存在的。

Using the Interface 使用介面

When the above code is self-contained in a class, fetching data is very simple. 当上述代码独立包含在类中时,获取数据非常简单。

$results = userFilter::create()
    ->addCounty($county)
    ->addHometown($hometown)
    ->addSkill($skill)
    ->addSkill($skill)
    ->addSkill($skill)
    ->fetchUsers();

If you want to conditionally use certain methods: 如果要有条件地使用某些方法:

$userFilter = userFilter::create();

if(isset($_GET['hometown'])){
    $userFilter->addHometown($_GET['hometown']);
}
if(isset($_GET['skill'])){
    $userFilter->addSkill($_GET['skill']);
}

$userFilter->fetchUsers();

Extra 额外

I often end up with a construction about like this (approximately): 我经常最终得到一个大约这样的构造:

$select = array("users.*"); // columns
$where = array("1=1"); // WHERE clauses
$values = array(); // values to bind
$join = array(); // additional joins
if ($skill) {
    $join[] = " JOIN users_skills USING (userid) JOIN skills USING (skillid)";
    $where[] = "skills.title = ?";
    $values[] = $skill;
}
if ($hometown) {
    $where[] = "hometown = ?";
    $values[] = $hometown;
}
if ($county) {
    $where[] = "county = ?";
    $values[] = $county;
}
$parenthesise = function($value) { return "($value)"; };
$sql = "SELECT ".implode(",", $select)
         ." FROM users ".implode("", $join)
         ." WHERE ".implode(" AND ", array_map($parenthesise, $where));
// now prepare $sql and execute with $values

在实际的查询或存储过程中执行逻辑将为您提供RDBMS提供的优化,并且返回的记录更少,消耗的资源更少,并使您的工作更少。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM