从数据库中选择数据
自从 Joomla 在 Joomla 1.6 中引入了对各种数据库类型的支持以来,推荐的构建数据库查询的方法是通过“查询链接”(虽然字符串查询将始终受支持)。
查询链接是指将多个方法连接起来的一种方法,每个方法返回一个可以支持下一个方法的对象,从而提高可读性和简化代码。
要获得\Joomla\Database\DatabaseQuery
类的实例,我们使用 \Joomla\Database\DatabaseDriver
getQuery
方法
use Joomla\CMS\Factory;
// When used in the component's Model
$db = $this->getDatabase();
// When used in other places
$db = Factory::getContainer()->get('DatabaseDriver');
$query = $db->getQuery(true);
不要 再使用以下 Joomla 3 方法,因为它已被弃用
$db = Factory::getDbo();
\Joomla\Database\DatabaseDriver::getQuery 方法有一个可选参数,$new,该参数可以为 true 或 false(默认为 false)。
为了查询我们的数据源,我们可以调用多个 \Joomla\Database\DatabaseQuery 方法;这些方法封装了数据源的查询语言(大多数情况下为 SQL),隐藏了开发人员的查询特定语法,并提高了开发人员源代码的可移植性。
一些使用最频繁的方法包括;select,from,join,where 和 order。还有一些方法,比如 insert,update,和 delete,用于修改数据存储中的记录。通过链接这些和其他方法调用,你可以在不损害你的代码可移植性的情况下针对你的数据存储创建几乎任何查询。
从单个表中选择记录
下面是一个使用 \Joomla\Database\DatabaseQuery 类创建数据库查询的示例。使用 select,from,where,和 order 方法,我们可以创建灵活、易于阅读且可移植的查询
use Joomla\CMS\Factory;
// Get a db connection.
$db = Factory::getContainer()->get('DatabaseDriver');
// Create a new query object.
$query = $db->getQuery(true);
// Select all records from the user profile table where key begins with "custom.".
// Order it by the ordering field.
$query->select($db->quoteName(['user_id', 'profile_key', 'profile_value', 'ordering']));
$query->from($db->quoteName('#__user_profiles'));
$query->where($db->quoteName('profile_key') . ' LIKE :profile_key');
$query->order($db->quoteName('ordering') . ' ASC');
// bind value for prepared statements
$query->bind(':profile_key', 'custom.%');
// Reset the query using our newly populated query object.
$db->setQuery($query);
// Load the results as a list of stdClass objects (see later for more options on retrieving data).
$results = $db->loadObjectList();
还可以链接查询,以便进一步简化
$query
->select($db->quoteName(['user_id', 'profile_key', 'profile_value', 'ordering']))
->from($db->quoteName('#__user_profiles'))
->where($db->quoteName('profile_key') . ' LIKE :profile_key')
->order($db->quoteName('ordering') . ' ASC')
->bind(':profile_key', 'custom.%');
当查询变得更长更复杂时,链接很有用。
分组也可以很简单地实现。下面的查询将计算每个类别中的文章数。
$query
->select( ['catid', 'COUNT(*)'] )
->from($db->quoteName('#__content'))
->group($db->quoteName('catid'));
可以使用“setLimit”对查询设置限制。例如在下面的查询中,它将返回最多 10 条记录。
$query
->select($db->quoteName(['user_id', 'profile_key', 'profile_value', 'ordering']))
->from($db->quoteName('#__user_profiles'))
->setLimit('10');
从多张表中选择记录
使用 \Joomla\Database\DatabaseQuery 类中的 join 方法,我们可以从多个相关表中选择记录。通用的“join”方法有两个参数:join “type”(inner、outer、left、right)和 join 条件。在下面的示例中,你会注意到我们可以使用所有我们在编写原生 SQL 查询时通常使用的关键字,包括用于别名表的 AS 关键字和用于创建表之间关系的 ON 关键字。还要注意,表别名用于引用表列的所有方法(即 select,where,order)。
use Joomla\CMS\Factory;
// Get a db connection.
$db = Factory::getContainer()->get('DatabaseDriver');
// Create a new query object.
$query = $db->getQuery(true);
// Select all articles for users who have a username which starts with 'a'.
// Order it by the created date.
// Note by putting 'a' as a second parameter will generate `#__content` AS `a`
$query
->select(['a.*', 'b.username', 'b.name'])
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b') . ' ON (' . $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id') . ')')
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
// Reset the query using our newly populated query object.
$db->setQuery($query);
// Load the results as a list of stdClass objects (see later for more options on retrieving data).
$results = $db->loadObjectList();
上面的 join 方法使我们能够查询内容和用户表,检索带有作者详细信息的文章。还有用于 join 的便捷方法
- innerJoin()
- leftJoin()
- rightJoin()
- outerJoin()
我们可以使用多个联接以查询超过两张表的内容
$query
->select(array('a.*', 'b.username', 'b.name', 'c.*', 'd.*'))
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b') . ' ON (' . $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id') . ')')
->join('LEFT', $db->quoteName('#__user_profiles', 'c') . ' ON (' . $db->quoteName('b.id') . ' = ' . $db->quoteName('c.user_id') . ')')
->join('RIGHT', $db->quoteName('#__categories', 'd') . ' ON (' . $db->quoteName('a.catid') . ' = ' . $db->quoteName('d.id') . ')')
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
注意链接是如何使源代码为这些较长的查询变得更加清晰易读的。
在某些情况下,您还需要在选择项目时使用 AS 子句以避免列名称冲突。在这种情况下,可以通过结合使用 $db->quoteName
的第二个参数链接多个选择语句。
$query
->select('a.*')
->select($db->quoteName('b.username', 'username'))
->select($db->quoteName('b.name', 'name'))
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b'), $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id'))
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
还可以使用第二个数组作为选择语句的第二个参数,以填充 AS 子句的值。记住在第二个数组中包含空值以对应于您不希望为其使用 AS 子句的第一数组中的列
$query
->select(['a.*'])
->select($db->quoteName(array('b.username', 'b.name'), ['username', 'name']))
->from($db->quoteName('#__content', 'a'))
->join('INNER', $db->quoteName('#__users', 'b') . ' ON (' . $db->quoteName('a.created_by') . ' = ' . $db->quoteName('b.id') . ')')
->where($db->quoteName('b.username') . ' LIKE :username')
->order($db->quoteName('a.created') . ' DESC')
->bind(':username', 'a%');
使用预处理语句
使用 Joomla! 4.0,我们将所有查询迁移为使用预处理语句。为了便于使用预处理语句,我们引入了一些辅助函数,并允许在数个函数调用中使用数组。使用预处理语句进行简单的查询。
$query = $this->db->getQuery(true)
->select($this->db->quoteName(array('id', 'password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->bind(':username', $credentials['username']);
您会发现我们没有将 $credentials['username'] 直接添加到查询中,而是将占位符:username添加到查询中并绑定变量到查询。当我们将变量绑定到查询时,我们无需转义或引用它。请注意,绑定变量始终是一个引用。这样做的好处是您可以在循环中修改查询。
$listOfUsernames = [ 'admin', 'user1' ];
$query = $this->db->getQuery(true)
->select($this->db->quoteName(array('id', 'password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->bind(':username', $username);
foreach($listOfUsernames as $name)
{
$username = $name;
$this->db->setQuery($query);
$user = $this->db->loadObject();
print_r($user);
}
在循环中,我们使用循环中的 $name
变量设置先前绑定的 $username
变量,然后我们必须再次设置查询(因为 Joomla 在查询执行后重置数据库驱动程序,这仅对 load* 函数适用)。结果将是具有不同用户名值的多条查询。我们可以使用数组一次性添加多个变量。
$query = $this->db->getQuery(true)
->select($this->db->quoteName(array('id', 'password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->where($this->db->quoteName('id') . ' = :id')
->bind(
[':username', ':id'],
[$credentials['username'], 42],
[Joomla\Database\ParameterType::STRING, Joomla\Database\ParameterType::INTEGER]
);
我们将 username 和 id 添加为绑定参数,并为每个变量设置正确的 ParameterType。还可以为所有绑定值和 ParameterTypes 使用一个变量。
$query = $this->db->getQuery(true)
->select($this->db->quoteName(array('id', 'password')))
->from($this->db->quoteName('#__users'))
->where($this->db->quoteName('username') . ' = :username')
->where($this->db->quoteName('password') . ' = :password')
->bind([':username', ':password'], $credentials['username']);
参数:username和:password设置为相同的值和默认的 ParameterType。函数 whereIn() 和 whereNotIn() 始终使用预处理语句,这些函数内部使用 bindArray 函数。可用于绑定变量数组,而无需指定占位符。
$userids = [1,2,3,4];
$query = $this->db->getQuery(true)
->select($this->db->quoteName(array('id', 'password')))
->from($this->db->quoteName('#__users'));
$parameterNames = $query->bindArray($userids);
$query->where($this->db->quoteName('id') . ' IN (' . implode(',', $parameterNames) . ')');
bindArray 函数返回占位符数组。索引在整个查询中是唯一的。
$placeholders = [
':preparedArray1',
':preparedArray2',
':preparedArray3',
':preparedArray4'
];