我有以下实体:
<?php
namespace Application\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Category
*
* @ORM\Table(name="zf_categories")
* @ORM\Entity
*/
class Category
{
/**
* @ORM\Id
* @ORM\Column(name="id", type="integer", precision=0, scale=0, nullable=false, unique=false)
* @ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* @ORM\Column(name="name", type="string", nullable=false, unique=true)
*/
private $name;
/**
* @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* @ORM\JoinColumn(name="extend", referencedColumnName="id")
*/
private $extend;
/**
* @ORM\OneToMany(targetEntity="Category", mappedBy="extend")
*/
private $children;
/**
* Constructor
*/
public function __construct()
{
$this->children = new \Doctrine\Common\Collections\ArrayCollection();
}
/**
* Get id
*
* @return integer
*/
public function getId()
{
return $this->id;
}
/**
* Set name
*
* @param string $name
* @return Category
*/
public function setName($name)
{
$this->name = $name;
return $this;
}
/**
* Get name
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Set extend
*
* @param \Application\Entity\Category $extend
* @return Category
*/
public function setExtend(\Application\Entity\Category $extend = null)
{
$this->extend = $extend;
return $this;
}
/**
* Get extend
*
* @return \Application\Entity\Category
*/
public function getExtend()
{
return $this->extend;
}
/**
* Add children
*
* @param \Application\Entity\Category $children
* @return Category
*/
public function addChild(\Application\Entity\Category $children)
{
$this->children[] = $children;
return $this;
}
/**
* Remove children
*
* @param \Application\Entity\Category $children
*/
public function removeChild(\Application\Entity\Category $children)
{
$this->children->removeElement($children);
}
/**
* Get children
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getChildren()
{
return $this->children;
}
}
我正在使用以下列格式保存数据的代码:
----------------------
| id | extend | name |
----------------------
| 1 | NULL | food |
| 2 | 1 | meat |
| 3 | 2 | pork |
----------------------
这个实体从'root'变为'leaf',所以当我尝试:
$categories = $em->getRepository('Application\Entity\Category')->findOneBy(array('id' => '3'));
foreach($categories->getChildren() as $children)
{
var_dump($children->getId());
}
它不起作用,这意味着它不显示'儿童' 我想从'leaf'到'root',所以当我有#3对象时,getChildren()应该返回对象#2,依此类推。
如何构建此类实体的任何提示?
的var_dump($类别 - &GT;的getChildren())
object(Doctrine\ORM\PersistentCollection)[428]
private 'snapshot' =>
array (size=0)
empty
private 'owner' =>
object(Application\Entity\Category)[415]
private 'id' => int 3
private 'name' => string 'pork' (length=4)
private 'extend' =>
object(DoctrineORMModule\Proxy\__CG__\Application\Entity\Category)[430]
public '__initializer__' =>
object(Closure)[417]
...
public '__cloner__' =>
object(Closure)[418]
...
public '__isInitialized__' => boolean false
private 'id' (Application\Entity\Category) => int 2
private 'name' (Application\Entity\Category) => null
private 'extend' (Application\Entity\Category) => null
private 'children' (Application\Entity\Category) => null
private 'children' =>
&object(Doctrine\ORM\PersistentCollection)[428]
private 'association' =>
array (size=15)
'fieldName' => string 'children' (length=8)
'mappedBy' => string 'extend' (length=6)
'targetEntity' => string 'Application\Entity\Category' (length=27)
'cascade' =>
array (size=0)
empty
'orphanRemoval' => boolean false
'fetch' => int 2
'type' => int 4
'inversedBy' => null
'isOwningSide' => boolean false
'sourceEntity' => string 'Application\Entity\Category' (length=27)
'isCascadeRemove' => boolean false
'isCascadePersist' => boolean false
'isCascadeRefresh' => boolean false
'isCascadeMerge' => boolean false
'isCascadeDetach' => boolean false
private 'em' =>
object(Doctrine\ORM\EntityManager)[342]
private 'config' =>
object(Doctrine\ORM\Configuration)[146]
protected '_attributes' =>
array (size=14)
...
private 'conn' =>
object(Doctrine\DBAL\Connection)[345]
protected '_conn' =>
object(Doctrine\DBAL\Driver\PDOConnection)[400]
...
protected '_config' =>
object(Doctrine\ORM\Configuration)[146]
...
protected '_eventManager' =>
object(Doctrine\Common\EventManager)[346]
...
protected '_expr' =>
object(Doctrine\DBAL\Query\Expression\ExpressionBuilder)[347]
...
private '_isConnected' => boolean true
private '_transactionNestingLevel' => int 0
private '_transactionIsolationLevel' => int 2
private '_nestTransactionsWithSavepoints' => null
private '_params' =>
array (size=8)
...
protected '_platform' =>
object(Doctrine\DBAL\Platforms\MySqlPlatform)[348]
...
protected '_schemaManager' => null
protected '_driver' =>
object(Doctrine\DBAL\Driver\PDOMySql\Driver)[344]
...
private '_isRollbackOnly' => boolean false
protected 'defaultFetchMode' => int 2
private 'metadataFactory' =>
object(Doctrine\ORM\Mapping\ClassMetadataFactory)[343]
private 'em' =>
&object(Doctrine\ORM\EntityManager)[342]
private 'targetPlatform' =>
object(Doctrine\DBAL\Platforms\MySqlPlatform)[348]
...
private 'driver' =>
object(Doctrine\ORM\Mapping\Driver\DriverChain)[150]
...
private 'evm' =>
object(Doctrine\Common\EventManager)[346]
...
protected 'cacheSalt' => string '$CLASSMETADATA' (length=14)
private 'cacheDriver' (Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory) =>
object(Doctrine\Common\Cache\ArrayCache)[149]
...
private 'loadedMetadata' (Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory) =>
array (size=1)
...
protected 'initialized' => boolean true
private 'reflectionService' (Doctrine\Common\Persistence\Mapping\AbstractClassMetadataFactory) =>
object(Doctrine\Common\Persistence\Mapping\RuntimeReflectionService)[368]
...
private 'unitOfWork' =>
object(Doctrine\ORM\UnitOfWork)[351]
private 'identityMap' =>
array (size=1)
...
private 'entityIdentifiers' =>
array (size=2)
...
private 'originalEntityData' =>
array (size=1)
...
private 'entityChangeSets' =>
array (size=0)
...
private 'entityStates' =>
array (size=2)
...
private 'scheduledForDirtyCheck' =>
array (size=0)
...
private 'entityInsertions' =>
array (size=0)
...
private 'entityUpdates' =>
array (size=0)
...
private 'extraUpdates' =>
array (size=0)
...
private 'entityDeletions' =>
array (size=0)
...
private 'collectionDeletions' =>
array (size=0)
...
private 'collectionUpdates' =>
array (size=0)
...
private 'visitedCollections' =>
array (size=0)
...
private 'em' =>
&object(Doctrine\ORM\EntityManager)[342]
private 'commitOrderCalculator' => null
private 'persisters' =>
array (size=1)
...
private 'collectionPersisters' =>
array (size=0)
...
private 'evm' =>
object(Doctrine\Common\EventManager)[346]
...
private 'listenersInvoker' =>
object(Doctrine\ORM\Event\ListenersInvoker)[352]
...
private 'orphanRemovals' =>
array (size=0)
...
private 'readOnlyObjects' =>
array (size=0)
...
private 'eagerLoadingEntities' =>
array (size=0)
...
private 'eventManager' =>
object(Doctrine\Common\EventManager)[346]
private '_listeners' =>
array (size=1)
...
private 'proxyFactory' =>
object(Doctrine\ORM\Proxy\ProxyFactory)[354]
private 'em' =>
&object(Doctrine\ORM\EntityManager)[342]
private 'uow' =>
object(Doctrine\ORM\UnitOfWork)[351]
...
private 'proxyNs' => string 'DoctrineORMModule\Proxy' (length=23)
private 'metadataFactory' (Doctrine\Common\Proxy\AbstractProxyFactory) =>
object(Doctrine\ORM\Mapping\ClassMetadataFactory)[343]
...
private 'proxyGenerator' (Doctrine\Common\Proxy\AbstractProxyFactory) =>
object(Doctrine\Common\Proxy\ProxyGenerator)[355]
...
private 'autoGenerate' (Doctrine\Common\Proxy\AbstractProxyFactory) => int 1
private 'definitions' (Doctrine\Common\Proxy\AbstractProxyFactory) =>
array (size=1)
...
private 'repositoryFactory' =>
object(Doctrine\ORM\Repository\DefaultRepositoryFactory)[350]
private 'repositoryList' =>
array (size=1)
...
private 'expressionBuilder' => null
private 'closed' => boolean false
private 'filterCollection' =>
object(Doctrine\ORM\Query\FilterCollection)[404]
private 'config' =>
object(Doctrine\ORM\Configuration)[146]
...
private 'em' =>
&object(Doctrine\ORM\EntityManager)[342]
private 'enabledFilters' =>
array (size=0)
...
private 'filterHash' => null
private 'filtersState' => int 1
private 'backRefFieldName' => string 'extend' (length=6)
private 'typeClass' =>
object(Doctrine\ORM\Mapping\ClassMetadata)[369]
public 'name' => string 'Application\Entity\Category' (length=27)
public 'namespace' => string 'Application\Entity' (length=18)
public 'rootEntityName' => string 'Application\Entity\Category' (length=27)
public 'customGeneratorDefinition' => null
public 'customRepositoryClassName' => null
public 'isMappedSuperclass' => boolean false
public 'parentClasses' =>
array (size=0)
empty
public 'subClasses' =>
array (size=0)
empty
public 'namedQueries' =>
array (size=0)
empty
public 'namedNativeQueries' =>
array (size=0)
empty
public 'sqlResultSetMappings' =>
array (size=0)
empty
public 'identifier' =>
array (size=1)
0 => string 'id' (length=2)
public 'inheritanceType' => int 1
public 'generatorType' => int 4
public 'fieldMappings' =>
array (size=2)
'id' =>
array (size=9)
...
'name' =>
array (size=8)
...
public 'fieldNames' =>
array (size=2)
'id' => string 'id' (length=2)
'name' => string 'name' (length=4)
public 'columnNames' =>
array (size=2)
'id' => string 'id' (length=2)
'name' => string 'name' (length=4)
public 'discriminatorValue' => null
public 'discriminatorMap' =>
array (size=0)
empty
public 'discriminatorColumn' => null
public 'table' =>
array (size=2)
'name' => string 'zf_categories' (length=13)
'options' =>
array (size=0)
...
public 'lifecycleCallbacks' =>
array (size=0)
empty
public 'entityListeners' =>
array (size=0)
empty
public 'associationMappings' =>
array (size=2)
'extend' =>
array (size=19)
...
'children' =>
array (size=15)
...
public 'isIdentifierComposite' => boolean false
public 'containsForeignIdentifier' => boolean false
public 'idGenerator' =>
object(Doctrine\ORM\Id\IdentityGenerator)[389]
private 'sequenceName' => null
public 'sequenceGeneratorDefinition' => null
public 'tableGeneratorDefinition' => null
public 'changeTrackingPolicy' => int 1
public 'isVersioned' => null
public 'versionField' => null
public 'reflClass' =>
object(ReflectionClass)[396]
public 'name' => string 'Application\Entity\Category' (length=27)
public 'isReadOnly' => boolean false
protected 'namingStrategy' =>
object(Doctrine\ORM\Mapping\DefaultNamingStrategy)[385]
public 'reflFields' =>
array (size=4)
'id' =>
object(ReflectionProperty)[386]
...
'name' =>
object(ReflectionProperty)[390]
...
'extend' =>
object(ReflectionProperty)[392]
...
'children' =>
object(ReflectionProperty)[409]
...
private '_prototype' (Doctrine\ORM\Mapping\ClassMetadataInfo) =>
object(Application\Entity\Category)[410]
private 'id' => null
private 'name' => null
private 'extend' => null
private 'children' => null
private 'isDirty' => boolean false
private 'initialized' => boolean false
private 'coll' =>
object(Doctrine\Common\Collections\ArrayCollection)[427]
private '_elements' =>
array (size=0)
empty
答案 0 :(得分:1)
所以问题是:如何实现一个行为像树的实体?
一种选择是使用Recursion。您实现了一个函数,该函数在循环遍历节点的子节点时创建列表。如果一个孩子是一个叶子,它会在列表中创建一个条目,如果孩子有自己的孩子,它会调用自己(这会创建一个新的列表等)。
因为Doctrine 2会延迟加载关联(子项),所以当节点树变大时,这种设置可能会成为问题,特别是当它变得越来越多时。您最终可以执行无数次查询来加载整个树。
Nested Set模型不是通过存储节点的父节点来形成树,而是通过存储左/右排序数和深度来形成树。这使您只需一次查询即可获取任意节点的整个树。
DoctrineExtensions库可以帮助您设置和使用遵循嵌套集模型的实体,阅读其Tree extension。如果您使用的是Symfony 2,则可以a bundle集成此库。如果您正在使用Zend Framework 2,您可以阅读如何集成它here。
递归对于插入/更新操作非常有效,对于读取(选择)操作来说效率非常低。
嵌套集对于读取操作是高效的,它甚至对更新操作有效,但对插入操作效率低。
如果插入的次数多于读取/更新次数,请转到递归。但是当你有比插入更多的读取/更新时,所以去嵌套集。
答案 1 :(得分:0)
以下代码可以帮助您从'leaf'转到'root'。
$category = $em->getRepository('Application\Entity\Category')->find(3);
$this->recursive($category);
...
public function recursive(Category $category)
{
if($category->getExtend()==null){
return;
}
echo $category->getName();
$this->recursive($category->getExtend());
}