3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 class CRM_Contact_BAO_GroupNestingCache
{
24 public static function update() {
25 // lets build the tree in memory first
28 SELECT n.child_group_id as child ,
29 n.parent_group_id as parent
30 FROM civicrm_group_nesting n,
33 WHERE n.child_group_id = gc.id
34 AND n.parent_group_id = gp.id
37 $dao = CRM_Core_DAO
::executeQuery($sql);
40 while ($dao->fetch()) {
41 if (!array_key_exists($dao->child
, $tree)) {
42 $tree[$dao->child
] = [
48 if (!array_key_exists($dao->parent
, $tree)) {
49 $tree[$dao->parent
] = [
55 $tree[$dao->child
]['parents'][] = $dao->parent
;
56 $tree[$dao->parent
]['children'][] = $dao->child
;
59 if (self
::checkCyclicGraph($tree)) {
60 CRM_Core_Error
::fatal(ts("We detected a cycle which we can't handle. aborting"));
63 // first reset the current cache entries
69 CRM_Core_DAO
::executeQuery($sql);
72 foreach (array_keys($tree) as $id) {
73 $parents = implode(',', $tree[$id]['parents']);
74 $children = implode(',', $tree[$id]['children']);
75 $parents = $parents == NULL ?
'null' : "'$parents'";
76 $children = $children == NULL ?
'null' : "'$children'";
79 SET parents = $parents ,
83 CRM_Core_DAO
::executeQuery($sql);
86 // this tree stuff is quite useful, so lets store it in the cache
87 Civi
::cache('groups')->set('nestable tree hierarchy', $tree);
95 public static function checkCyclicGraph(&$tree) {
96 // lets keep this simple, we should probably use a graph algorithm here at some stage
98 // foreach group that has a parent or a child, ensure that
99 // the ancestors and descendants dont intersect
100 foreach ($tree as $id => $dontCare) {
101 if (self
::isCyclic($tree, $id)) {
115 public static function isCyclic(&$tree, $id) {
116 $parents = $children = [];
117 self
::getAll($parent, $tree, $id, 'parents');
118 self
::getAll($child, $tree, $id, 'children');
120 $one = array_intersect($parents, $children);
121 $two = array_intersect($children, $parents);
125 CRM_Core_Error
::debug($id, $tree);
126 CRM_Core_Error
::debug($id, $one);
127 CRM_Core_Error
::debug($id, $two);
139 public static function getPotentialCandidates($id, &$groups) {
140 $tree = Civi
::cache('groups')->get('nestable tree hierarchy');
142 if ($tree === NULL) {
144 $tree = Civi
::cache('groups')->get('nestable tree hierarchy');
147 $potential = $groups;
149 // remove all descendants
150 self
::invalidate($potential, $tree, $id, 'children');
152 // remove all ancestors
153 self
::invalidate($potential, $tree, $id, 'parents');
155 return array_keys($potential);
164 public static function invalidate(&$potential, &$tree, $id, $token) {
165 unset($potential[$id]);
167 if (!isset($tree[$id]) ||
168 empty($tree[$id][$token])
173 foreach ($tree[$id][$token] as $tokenID) {
174 self
::invalidate($potential, $tree, $tokenID, $token);
184 public static function getAll(&$all, &$tree, $id, $token) {
185 // if seen before, dont do anything
186 if (isset($all[$id])) {
191 if (!isset($tree[$id]) ||
192 empty($tree[$id][$token])
197 foreach ($tree[$id][$token] as $tokenID) {
198 self
::getAll($all, $tree, $tokenID, $token);
205 public static function json() {
206 $tree = Civi
::cache('groups')->get('nestable tree hierarchy');
208 if ($tree === NULL) {
210 $tree = Civi
::cache('groups')->get('nestable tree hierarchy');
213 // get all the groups
214 $groups = CRM_Core_PseudoConstant
::group();
216 foreach ($groups as $id => $name) {
217 $string = "id:'$id', name:'$name'";
218 if (isset($tree[$id])) {
220 if (!empty($tree[$id]['children'])) {
221 foreach ($tree[$id]['children'] as $child) {
222 $children[] = "{_reference:'$child'}";
224 $children = implode(',', $children);
225 $string .= ", children:[$children]";
226 if (empty($tree[$id]['parents'])) {
227 $string .= ", type:'rootGroup'";
230 $string .= ", type:'middleGroup'";
234 $string .= ", type:'leafGroup'";
238 $string .= ", children:[], type:'rootGroup'";
240 $values[] = "{ $string }";
243 $items = implode(",\n", $values);