DQL 关系限制
由于 EAV 使用无模式设计模式,因此没有直接的方法来使用 DQL (QueryBuilder) 使用 ORM 完成过滤或排序,因为数据库中的结果值 (Book::$attributeValues) 是不明确的:
[
['value' => 'name_value'],
['value' => 'description_value']
]
简单地说,ORM 并不打算用于这种类型的“报告”。
DQL 解决方法
解决上述关系问题 (Book::$attributeValues) 的一种解决方法是手动映射查询构建器,以便隔离 NAME 属性和关联值,然后可以使用这些值进行过滤 (= , IN(), LIKE) 或已排序。
排序NAME属性值
使用AS HIDDEN 添加任意别名连接列,可用于排序。
$qb = $this->createQueryBuilder('book');
$expr = $qb->expr();
$qbS = $this->_em->createQueryBuilder()
->select('na.id')
->from(BookAttribute::class, 'na')
->where($expr->eq('na.handle', ':attribute_name'));
$qb->addSelect([
'bookAttributeValues',
'bookAttribute',
'nav.value AS HIDDEN name_value',
])
->leftJoin('book.attributeValues', 'bookAttributeValues')
->leftJoin('bookAttributeValues.bookAttribute', 'bookAttribute')
//isolate the associated name attribute value as a separate column
->leftJoin(BookAttributeValue::class, 'nav', 'WITH', $expr->andX(
$expr->eq('book.id', 'IDENTITY(nav.book)'),
$expr->in('IDENTITY(nav.attribute)', $qbS->getQuery()->getDQL())
))
->andWhere($expr->in('bookAttribute.handle', ':attributes'))
->setParameter('attribute_name', BookAttribute::NAME)
->setParameter('attributes', [BookAttribute::NAME, BookAttribute::DESCRIPTION])
->addOrderBy('name_value')
->addOrderBy('a.name', 'ASC'); //Z-A (Name, Description)
按NAME 属性值过滤结果
只需将条件添加到您的陈述中。
$qb->andWhere($expr->eq('nav.value', ':attribute_value'))
->setParameter('attribute_value', '<desired_name_value>');
SQL 查询替代
由于限制,我建议将 DQL 转换为 SQL 查询,并为属性及其关联值使用单独的 nested JOIN statements。创建关系的数据透视表。然后您可以通过别名name 连接列值进行排序。
名称属性相关值
SELECT nav.value AS name
#...
LEFT JOIN (book_attribute_value AS nav
INNER JOIN book_attribute AS na
ON na.id = nav.attribute_id
AND na.handle = BookAttribute::NAME)
ON book.id = nav.book_id
描述属性相关值
SELECT dav.value AS description
#...
LEFT JOIN (book_attribute_value AS dav
INNER JOIN book_attribute AS da
ON da.id = dav.attribute_id
AND da.handle = BookAttribute::DESCRIPTION)
ON book.id = dav.book_id
完整示例 DB-Fiddle
嵌套连接将导致关联图书的描述或名称属性值缺失,在该列中返回为NULL,而不是排除整行。
class BookRepository
{
/*
* @return array|string[][]
*/
public function filterBooks()
{
$sql = <<<SQL
SELECT
book.*,
nav.value AS name,
dav.value AS description
FROM book
LEFT JOIN (book_attribute_value AS nav
INNER JOIN book_attribute AS na
ON na.id = nav.attribute_id
AND na.handle = :attr_name)
ON book.id = nav.book_id
LEFT JOIN (book_attribute_value AS dav
INNER JOIN book_attribute AS da
ON da.id = dav.attribute_id
AND da.handle = :attr_descr)
ON book.id = dav.book_id
ORDER BY name
SQL;
$stmt = $this->_em->getConnection()->prepare($sql);
$stmt->bindValue('attr_name', BookAttribute::NAME);
$stmt->bindValue('attr_descr', BookAttribute::DESCRIPTION);
return $stmt->executeQuery()->fetchAllAssociative();
}
}
结果
| id |
name |
description |
| 1 |
Book_1_Name |
Book_1_Description |
| 2 |
Book_2_Name |
Book_2_Description |
[
{"id": "1", "name": "Book_1_Name", "description": "Book_1_Description"},
{"id": "2", "name": "Book_2_Name", "description": "Book_2_Description"}
]
根据需要迭代结果。
$books = $em->getRepository(Book::class)->filterBooks();
foreach ($books as $book) {
//ksort($book, SORT_NATURAL); #optionally sort by the attribute column
//printf('Book %s:<br>', $book['id']); #display the book id
unset($book['id']); //remove the id column
foreach ($book as $attribute => $value) {
printf('%s: %s<br>', $attribute, $value);
}
}
输出
name: Book_1_Name
description: Book_1_Description
name: Book_2_Name
description: Book_2_Description
要限制指定名称值的结果,请将LEFT JOIN nav 更改为INNER JOIN nav 并将所需条件(=、IN()、LIKE)添加到语句的ON 子句中.
查询示例 DB-Fiddle
SELECT
book.*,
nav.value AS name,
dav.value AS description
FROM book
INNER JOIN (book_attribute_value AS nav
INNER JOIN book_attribute AS na
ON na.id = nav.attribute_id
AND na.handle = :attr_name)
ON book.id = nav.book_id
AND nav.value = :name_value
LEFT JOIN (book_attribute_value AS dav
INNER JOIN book_attribute AS da
ON da.id = dav.attribute_id
AND da.handle = :attr_descr)
ON book.id = dav.book_id
确保将值绑定到语句条件。
$stmt->bindValue('name_value', 'Book_1_Name');