如果我有一个 Querybuilder 实例并应用这样的东西:
$queryBuilder->addSelect("
CASE
WHEN c.modifyStamp > c.createStamp THEN 'draft'
ELSE 'published'
END AS state
");
然后,使用
getResult
,我会得到这样的东西:
[
0 => [
0 => \App\Entity\Test instance,
'state' => 'draft'
],
1 => [
0 => \App\Entity\Test instance,
'state' => 'published'
]
]
这不是很理想(但我知道默认情况下是预期的)。对于我的实体,我尝试添加这个:
private $state;
public function getState(): string
{
return $this->state;
}
public function setState(string $state)
{
$this->state = $state;
}
...希望 hydrator 可以看到正确名称的属性(或 setter),像任何其他属性一样填充它,然后只给我一个常规的对象数组(纯),而不是数组数组(混合)。例如,如果您使用 Symfony 序列化程序,您可以轻松地获取数据数组并将其应用于具有与数组键同名的属性的对象。
getScalarResult
会将每条记录中的所有字段捆绑在一起,但我不想在这里使用数组。我想要一组对象,就像一个普通的getResult
如果我没有使用上面的语句会给我。
我想我可以遍历结果(当处于“混合”形式时)并直接调用设置器,或者使用
getScalarResult
并以某种方式手动补水,但这些选项看起来很笨重。所以:
谢谢:)
EDIT:使用 PHP 后搜索是不可行的,因为
draft
字段(上面的示例)也需要成为一组搜索过滤器的一部分(通过也有分页的 UI - 所以所有需要做的数据库而不是事后的 PHP 代码。)
您可以使用 Doctrine postLoad 事件 为您的实体添加“真实”属性并按原样使用它
/**
* Test
*
* @ORM\Entity
* @ORM\HasLifecycleCallbacks
*/
class Test
{
...
private $state;
public function getState(): string
{
return $this->state;
}
/**
* @ORM\PostLoad()
*/
public function initState()
{
$this->state = $this->modifyStamp > $this->createStamp ? 'draft' : 'published';
}
}
(我还没有测试过这个方法)
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Test {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int|null $id = null;
#[ORM\Column(type: 'integer')]
private int|null $modifyStamp = null;
#[ORM\Column(type: 'integer')]
private int|null $createStamp = null;
#[ORM\Column(
type: "string",
insertable: false,
updatable: false,
generated: "ALWAYS",
columnDefinition: "GENERATED ALWAYS AS (CASE WHEN modifyStamp > createStamp THEN 'draft' ELSE 'published' END)"
)]
private string|null $state = null;
}
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->select('c')->from('Entity\Test', 'c');
echo '<pre>';
var_export($queryBuilder->getQuery()->getResult());
echo '</pre>';
参见:https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/attributes-reference.html#column和:https://dev.mysql.com/doc /refman/5.7/en/create-table-generated-columns.html
(丑陋但不需要 MySQL 支持)
我唯一能找到让它在 PHP 中工作的东西是对水合代码的“丑陋”破解。而这个 hack 因 Doctrine
ObjectHydrator
. 的一些内部约定而变得更加丑陋。
use Doctrine\ORM\Internal\Hydration\ObjectHydrator;
class CustomHydrator extends ObjectHydrator {
private function remapScalars($mixedResultRow) {
$object = $mixedResultRow[0];
$remainingScalars = [];
foreach($mixedResultRow as $name => $value) {
if ($name == 0) {
continue;
}
$setter = 'set' . ucfirst($name);
if (method_exists($object, $setter)) {
$object->$setter($value);
} else {
$remainingScalars[$name] = $value;
}
}
return count($remainingScalars) == 0 ?
$object :
array_merge([$object], $remainingScalars);
}
protected function hydrateRowData(array $row, array &$result) {
parent::hydrateRowData($row, $result);
$latestKey = array_key_last($result);
$latestItem = $result[$latestKey];
if (is_array($latestItem)) {
$result[$latestKey] = $this->remapScalars($latestItem);
}
}
}
实体几乎就是你拥有它的样子:
namespace Entity;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Test {
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int|null $id = null;
#[ORM\Column(type: 'integer')]
private int|null $modifyStamp = null;
#[ORM\Column(type: 'integer')]
private int|null $createStamp = null;
private string|null $state = null;
public function setState(string $newState) {
$this->state = $newState;
}
}
我在控制器中使用以下内容:
$entityManager->getConfiguration()->addCustomHydrationMode(
'CustomHydrator',
'CustomHydrator'
);
$queryBuilder = $entityManager->createQueryBuilder();
$queryBuilder->select('c')->from('Entity\Test', 'c')->addSelect(
"CASE WHEN c.modifyStamp > c.createStamp
THEN 'draft' ELSE 'published' END AS state"
);
echo '<pre>';
var_export($queryBuilder->getQuery()->getResult('CustomHydrator'));
echo '</pre>';
如果一切都失败了,你可以使用 MySQL 视图并使你的实体映射到它。这样视图就完成了
CASE WHEN
,从您实体的角度来看,它只是另一列。