Doctrine聚合/虚拟列

时间:2014-11-17 08:53:24

标签: php mysql doctrine-orm

上周我开始使用Doctrine而且我非常积极,但我遇到了一些问题。我正在MySQL中实现分层数据:http://ftp.nchu.edu.tw/MySQL/tech-resources/articles/hierarchical-data.html并且我需要一些聚合的实体(不确定这是否是正确的术语,也许虚拟更好)。但是我需要两个额外的字段,即:Depth和ParentID。目前(使用本机查询)我通过子查询获取这些。 Doctrine的结果是一个数组,我想要一个对象。

这是我正在使用的表结构:

CREATE TABLE IF NOT EXISTS `category` (
  `ID` int(8) unsigned NOT NULL AUTO_INCREMENT,
  `Name` varchar(45) NOT NULL,
  `LeftID` int(10) NOT NULL,
  `RightID` int(10) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 AUTO_INCREMENT=4 ;

三个示例类别:

INSERT INTO `category` (`ID`, `Name`, `LeftID`, `RightID`) VALUES
  (1, 'cat#1', 1, 4),
  (2, 'cat#2', 5, 6),
  (3, 'cat#1.1', 2, 3);

我查看了doctrine(http://doctrine-orm.readthedocs.org/en/latest/cookbook/aggregate-fields.html)中的聚合字段示例,但我不清楚实际的实现。如果它和我想要的一样。

我现在使用的实体看起来像这样:

/**
 * @ORM\Entity (repositoryClass="Application\Entity\CategoryRepository") 
 * @ORM\Table(name="category")
 */
class Category
{
    protected $inputFilter;

    /**
     * @ORM\Id
     * @ORM\Column(type="integer");
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $ID;

    /**
     * @ORM\Column(type="string")
     */
    protected $Name;

    /**
     * @ORM\Column(type="integer")
     */
    protected $LeftID; 

    /**
     * @ORM\Column(type="integer")
     */
    protected $RightID;
}

我正在使用魔术setter / getter来公开受保护的属性(__set& __get),以接收我使用以下查询的数据:

SELECT 
    node.ID,
    node.Name,
    node.LeftID,
    node.RightID,
    (
        SELECT 
            ID 
        FROM 
            Category subtree 
        WHERE 
            subtree.LeftID < node.LeftID 
        AND 
            subtree.RightID > node.RightID 
        ORDER BY 
            subtree.RightID - node.RightID ASC 
        LIMIT 
            0, 1
    ) AS ParentID, 
    (
        SELECT 
            (COUNT(parent.ID) - 1)
        FROM 
            Category AS node, 
            Category AS parent 
        WHERE 
            node.LeftID BETWEEN parent.LeftID AND parent.RightID 
        AND 
            node.ID = 3
        GROUP BY 
            node.ID 
        ORDER BY 
            parent.LeftID
    ) As Depth
FROM 
    Category As node,
    Category As parent
WHERE 
    node.ID = 3
AND
    node.LeftID BETWEEN parent.LeftID AND parent.RightID
GROUP BY 
    node.ID

结果(使用给定的数据集)将是:

ID  Name    LeftID  RightID     ParentID    Depth   
3   cat#1.1     2   3           1           1

这正是我想要在Doctrine实体中拥有的结果,但找不到将实体作为Doctrine对象的解决方案。目前我有双重功能,一个给出我真正需要Depth和ParentID的数组,如果我需要对象,我使用默认的doctrine函数(例如find),但我希望有一个优雅的解决方案。此外,我想我将来还需要其他对象,例如:人的年龄(从生日算起)。

1 个答案:

答案 0 :(得分:0)

这应该可以得到你想要的东西(可能需要修改一下):

    $manager = $this->getDoctrine()->getEntityManager();
    $qb = $manager->createQueryBuilder();
    $expr = $qb->expr();

    $query = $qb
        ->select('node.ID, node.Name, node.LeftID, node.RightID')
        ->addSelect('
            (SELECT 
                ID 
            FROM 
                Category subtree 
            WHERE 
                subtree.LeftID < node.LeftID
                AND
                subtree.RightID > node.RightID 
            ORDER BY 
                subtree.RightID - node.RightID ASC 
            LIMIT 
                0, 1) AS ParentID
        ')
        ->addSelect('
            (SELECT
                (COUNT(parent.ID) - 1)
            FROM 
                Category AS node, 
                Category AS parent 
            WHERE 
                node.LeftID BETWEEN parent.LeftID AND parent.RightID
            AND
                node.ID = 3
            GROUP BY 
                node.ID 
            ORDER BY 
                parent.LeftID
            ) As Depth
        ')
        ->from('Category', 'node')
        ->join('Category', 'parent')
        ->where($expr->eq('node.ID', 3))
        ->andWhere($expr->between('node.LeftID', 'parent.LeftID', 'parent.RightID'))
        ->groupBy('node.ID')
        ->getQuery();

    $result = $query->getArrayResult();

编辑:没关系。刚注意到你需要一个对象,而不是一个数组......

您可能希望查看此Doctrine扩展名:https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/tree.md

或者,作为替代方法,使用您需要的字段创建一个新的对象类,将其标记为ReadOnly for Doctrine并为其创建自定义查询(您可以使用带有Doctrine的虚拟表进行阅读)。