2014-03-27 105 views
0

我們使用Doctrine 2與tree extension進行數據庫訪問。在具有多對多關聯的MySQL樹中搜索項目

第一個表格是由此source創建的歐盟位置的樹形表。

位置表:

id 
name 
parent 
lft 
rgt 
lvl 
root 
companies 

還有持有的公司名單與多對多的關係位置表公司表。這些地點是公司採取行動的地點。它可以是國家(0級),某些地區(1,2,3,4級),也可以是一些城市,城鎮或vilage(5級)。

公司表:

id 
name 
locations 

比我們有一個搜索形式與公司選擇的位置。

現在我需要創建DQL,它將選擇給定位置(嵌套)中的所有國家。

因此,當我選擇例如整個國家時,應選擇該國的所有公司,該國的地區以及該國的城鎮。

謝謝。

編輯:

我補充說: 「自動加入」 父地區爲公司的(也許是暫時的)。它將穿過所有父母並添加它們。因此,當公司在城市進行行爲和用戶選擇整個國家或地區時,將顯示該公司。

不幸的是仍然有一個問題。如果某個公司在全國行事並且用戶在該國選擇城市,公司將不會顯示。

回答

0

搜索並不昂貴,人們只會在一個搜索查詢中的幾個位置搜索。

所以我做的第一件事就是加載所有搜索到的位置的ID以及所有父母的ID。

獲得父母的IDS:BaseTreeFacade::getPathIds

public function getPathIds($entity) 
{ 
    $ids = $this->getPathQueryBuilder($entity) 
     ->select('node.id') 
     ->getQuery() 
     ->getResult(AbstractQuery::HYDRATE_SCALAR); 

    return array_map('current', $ids); 
} 

此方法返回與IDS簡單陣列。現在我必須通過搜索查詢和父母的ID一起加入ID。

擴大IDS:Helpers::expandTreeValues

public static function expandTreeValues(BaseTreeFacade $facade, array $values) 
{ 
    $result = []; 

    foreach ($values as $id) { 
     $entity = $facade->findOneById($id); 

     $result[] = [$id]; 
     $result[] = $facade->getPathIds($entity); 
    } 

    $result = call_user_func_array('array_merge', $result); 
    $result = array_unique($result, SORT_NUMERIC); 

    return $result; 
} 

下一步是在位置和他們的孩子尋找創建where子句。

創建where子句:Helpers::prepareWhereClauseForChildren

public static function prepareWhereClauseForChildren(BaseTreeFacade $facade, array $values) 
{ 
    $where = []; 
    $parameters = []; 

    foreach ($values as $id) { 
     $entity = $facade->findOneById($id); 

     $where[] = "(l.lvl > :level$id AND l.id > :left$id AND l.id < :right$id)"; 

     $parameters["left$id"] = $entity->lft; 
     $parameters["right$id"] = $entity->rgt; 
     $parameters["level$id"] = $entity->lvl; 
    } 

    $where = implode(' OR ', $where); 

    return (object) [ 
     'where' => $where === '' ? null : $where, 
     'parameters' => count($parameters) === 0 ? null : $parameters, 
    ]; 
} 

在那裏,我可以,因爲樹的左右分支的所有子地點進行搜索。最後一步是使用上一步創建的where子句更新原始QueryBuilder

結束寫入的QueryBuilder:Helpers::updateWithTreeEntity

public static function updateWithTreeEntity(QueryBuilder $qb, array $values, $addWhere = null, array $addParameters = null) 
{ 
    if ($addParameters === null) { 
     $addParameters = []; 
    } 

    $addParameters['locations'] = $values; 
    $appendQuery = $addWhere === null ? '' : " OR $addWhere"; 

    $qb->andWhere("l.id IN (:locations)$appendQuery"); 

    foreach ($addParameters as $key => $value) { 
     $qb->setParameter($key, $value); 
    } 
} 

用例:

public function createQueryBuilderForSearchingInLocations(array $locations) 
{ 
    $gb = $this->createQueryBuilderSomehow(); 
    $facade = $this->getLocationsFacadeSomehow(); 

    $locations = Helpers::expandTreeValues($facade, $locations); 
    $where = Helpers::prepareWhereClauseForChildren($facade, $locations); 

    Helpers::updateWithTreeEntity($qb, $locations, $where->where, $where->parameters); 

    return $qb; 
}