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

时间:2010-12-23 22:13:27

标签: php sql oop

我正在使用members.php中的以下代码获取要在页面上显示的用户列表:

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

以下是我想知道的:有没有办法可以缩短if语句的数量或提高效率?我做得对吗?因为我不喜欢我有这么多参数的事实,特别是当我想要扩展时。

**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);
    }

感谢。

3 个答案:

答案 0 :(得分:2)

这里实际上有两个问题。首先,您的界面设计太具体了。另一个是你将数据模型与数据库访问层混合在一起,这使你的代码变得更加复杂。

为简洁起见,我在示例中略过了一些细节(代码未经测试),但我认为你可以填补空白。

通用界面

第一项业务是使您的界面更加通用。不是使用一种方法来完成所有事情,而是将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';
    }
}

使用上面的接口,您可以选择添加任意数量的参数,每个参数(可能)都有自己的逻辑与验证输入,编写SQL条件等相关联。

数据库访问层

此解决方案的难点在于编写实际的SQL。为此,您需要了解一些事项:

  1. 您想要哪些列(SELECT子句)?
  2. 您想要哪些行(WHERE子句)?
  3. 您的架构是什么样的,或者更具体地说,哪些列名对应于哪些参数?
  4. 您想多次选择一个参数吗? (我假设有多个)
  5. 解决此问题的简单方法是使用数组,因为您可以轻松地foreach包含数组,并且可以使用其key =>值配对来维护关联您的数据库架构。

    现在,当您开始编写fetchUsers()的查询时,您可以使用array_keys($this->params)作为列名称并使用$this->params作为数据来迭代$this->params。这可能看起来像这样:

    // 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);
    

    对于需要连接的更复杂的架构,您可能需要一个开关来处理每个连接和连接条件,或者您可能会找到一种方法将其塞入阵列。你还需要额外的逻辑来确保你不添加一个空的WHERE条款或其他愚蠢的东西,但它的内容就在那里。

    使用界面

    当上述代码在类中自包含时,获取数据非常简单。

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

    如果您想有条件地使用某些方法:

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

    附加

答案 1 :(得分:1)

我经常最终得到一个像这样(大约)的结构:

$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

答案 2 :(得分:0)

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