1: <?php
2:
3: namespace PHPixie\ORM\Mappers\Conditions;
4:
5: use \PHPixie\ORM\Conditions\Condition\Collection;
6:
7: class Optimizer extends \PHPixie\Database\Conditions\Logic\Parser
8: {
9: protected $conditions;
10:
11: public function __construct($mappers, $conditions)
12: {
13: $this->mappers = $mappers;
14: $this->conditions = $conditions;
15: }
16:
17: public function optimize($conditions)
18: {
19: return $this->extractCollections($conditions, true);
20: }
21:
22: protected function normalize($condition)
23: {
24: return array($condition);
25: }
26:
27: protected function extractCollections($conditions, $parseLogic = false)
28: {
29:
30: $extracted = array();
31: $count = count($conditions);
32: foreach($conditions as $key => $condition) {
33:
34: if($condition instanceof \PHPixie\ORM\Conditions\Condition\In) {
35:
36: $condition = $this->mappers->conditionsNormalizer()->normalizeIn($condition);
37: $this->optimizeCollectionConditions($condition);
38:
39: }elseif($condition instanceof Collection) {
40: $condition = $this->cloneCollectionCondition($condition);
41: $this->optimizeCollectionConditions($condition);
42:
43: }
44:
45: if(!$this->isConditionCollection($condition) || $condition->isNegated()) {
46: $extracted[] = $condition;
47: continue;
48: }
49:
50: $minPrecedance = 0;
51: if($key > 0) {
52: $precedance = $this->logicPrecedance[$condition->logic()];
53: $minPrecedance = max($minPrecedance, $precedance);
54: }
55:
56: if($key < $count - 1) {
57: $precedance = $this->logicPrecedance[$conditions[$key+1]->logic()];
58: $minPrecedance = max($minPrecedance, $precedance);
59: }
60:
61: if(!$this->isExpandable($condition, $minPrecedance)) {
62: $extracted[] = $condition;
63: continue;
64: }
65:
66: $parseLogic = true;
67:
68: foreach($condition->conditions() as $key => $groupCondition) {
69: if($key == 0) {
70: $groupCondition->setLogic($condition->logic());
71: }
72: $extracted[]= $groupCondition;
73: }
74: }
75:
76: if($parseLogic) {
77: $extracted = $this->parseLogic($extracted);
78: if($extracted === null) {
79: $extracted = array ( );
80: }
81: }
82:
83: return $extracted;
84: }
85:
86: protected function isConditionCollection($condition)
87: {
88: if(!($condition instanceof Collection))
89: return false;
90:
91: if($condition instanceof Collection\RelatedTo)
92: return false;
93:
94: return true;
95: }
96:
97: protected function isExpandable($group, $minPrecedance)
98: {
99: $conditions = $group->conditions();
100: foreach($conditions as $key => $condition){
101: if($key == 0)
102: continue;
103:
104: if($this->logicPrecedance[$condition->logic()] < $minPrecedance)
105: return false;
106: }
107:
108: return true;
109: }
110:
111: protected function merge($left, $right)
112: {
113: if(count($right) !== 1)
114: return $this->mergeConditions($left, $right);
115:
116: $rightCondition = current($right);
117:
118: if ($rightCondition instanceof Collection\RelatedTo) {
119:
120: if(($target = $this->findMergeTarget($left, $rightCondition)) !== null) {
121: $this->mergeRelatedToCollections($left[$target], $rightCondition);
122:
123:
124: }else{
125: $left[]= $rightCondition;
126: }
127:
128: }else{
129: $left[]= $rightCondition;
130: }
131:
132: return $left;
133: }
134:
135: protected function mergeConditions($left, $right)
136: {
137: foreach($right as $condition) {
138: $left[]= $condition;
139: }
140:
141: return $left;
142: }
143:
144: protected function findMergeTarget($conditionList, $newCondition)
145: {
146: $isNextFree = true;
147: $newPrecedance = $this->logicPrecedance[$newCondition->logic()];
148:
149: for ($i = count($conditionList) - 1; $i >= 0; $i-- ) {
150: $current = $conditionList[$i];
151:
152: $currentPrecedance = 0;
153: if($i > 0) {
154: $currentPrecedance = $this->logicPrecedance[$current->logic()];
155: }
156:
157: $isCurrentFree = $isNextFree;
158:
159: $isNextFree = $currentPrecedance <= $newPrecedance;
160:
161: if (!$isNextFree || !$isCurrentFree)
162: continue;
163:
164: if ($current instanceof Collection\RelatedTo && $this->areMergeable($current, $newCondition)) {
165: return $i;
166: }
167:
168: if ($newPrecedance > $currentPrecedance)
169: return null;
170: }
171:
172: return null;
173: }
174:
175: protected function areMergeable($left, $right)
176: {
177:
178: if ($left->relationship() !== $right->relationship())
179: return false;
180:
181: if($left->isNegated() xor $right->isNegated())
182: return false;
183:
184: if($right->logic() === 'or' && !$left->isNegated())
185: return true;
186:
187: if($right->logic() === 'and' && $left->isNegated())
188: return true;
189:
190: return false;
191: }
192:
193: protected function mergeRelatedToCollections($left, $right)
194: {
195: $newLeft = $this->conditions->group();
196: $newRight = $this->conditions->group();
197:
198: $newLeft->setConditions($left->conditions());
199: $newRight->setConditions($right->conditions());
200:
201: $newRight->setLogic($right->logic());
202: if($left->isNegated()) {
203: $newRight->setLogic('or');
204: }
205:
206: $conditions = $this->extractCollections(array($newLeft, $newRight));
207: $left->setConditions($conditions);
208: }
209:
210: protected function cloneCollectionCondition($condition)
211: {
212: if($condition instanceof Collection\RelatedTo) {
213: $group = $this->conditions->relatedToGroup($condition->relationship());
214: }else{
215: $group = $this->conditions->group();
216: }
217:
218: $group->setLogic($condition->logic());
219: $group->setIsNegated($condition->isNegated());
220: $group->setConditions($condition->conditions());
221:
222: return $group;
223: }
224:
225: protected function optimizeCollectionConditions($collection)
226: {
227: $optimized = $this->optimize($collection->conditions());
228: $collection->setConditions($optimized);
229: }
230:
231: }