1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Database\Table;
9:
10: use Nette;
11: use Nette\Database\Context;
12: use Nette\Database\IConventions;
13:
14:
15: 16: 17: 18:
19: class GroupedSelection extends Selection
20: {
21:
22: protected $refTable;
23:
24:
25: protected $refCacheCurrent;
26:
27:
28: protected $column;
29:
30:
31: protected $active;
32:
33:
34: 35: 36: 37: 38: 39: 40: 41: 42:
43: public function __construct(Context $context, IConventions $conventions, $tableName, $column, Selection $refTable, Nette\Caching\IStorage $cacheStorage = NULL)
44: {
45: $this->refTable = $refTable;
46: $this->column = $column;
47: parent::__construct($context, $conventions, $tableName, $cacheStorage);
48: }
49:
50:
51: 52: 53: 54: 55: 56:
57: public function setActive($active)
58: {
59: $this->active = $active;
60: return $this;
61: }
62:
63:
64: public function select($columns)
65: {
66: if (!$this->sqlBuilder->getSelect()) {
67: $this->sqlBuilder->addSelect("$this->name.$this->column");
68: }
69:
70: return call_user_func_array('parent::select', func_get_args());
71: }
72:
73:
74: public function order($columns)
75: {
76: if (!$this->sqlBuilder->getOrder()) {
77:
78: $this->sqlBuilder->addOrder("$this->name.$this->column" . (preg_match('~\bDESC\z~i', $columns) ? ' DESC' : ''));
79: }
80:
81: return call_user_func_array('parent::order', func_get_args());
82: }
83:
84:
85:
86:
87:
88: public function aggregation($function)
89: {
90: $aggregation = & $this->getRefTable($refPath)->aggregation[$refPath . $function . $this->getSql() . json_encode($this->sqlBuilder->getParameters())];
91:
92: if ($aggregation === NULL) {
93: $aggregation = array();
94:
95: $selection = $this->createSelectionInstance();
96: $selection->getSqlBuilder()->importConditions($this->getSqlBuilder());
97: $selection->select($function);
98: $selection->select("$this->name.$this->column");
99: $selection->group("$this->name.$this->column");
100:
101: foreach ($selection as $row) {
102: $aggregation[$row[$this->column]] = $row;
103: }
104: }
105:
106: if (isset($aggregation[$this->active])) {
107: foreach ($aggregation[$this->active] as $val) {
108: return $val;
109: }
110: }
111: }
112:
113:
114: public function count($column = NULL)
115: {
116: $return = parent::count($column);
117: return isset($return) ? $return : 0;
118: }
119:
120:
121:
122:
123:
124: protected function execute()
125: {
126: if ($this->rows !== NULL) {
127: $this->observeCache = $this;
128: return;
129: }
130:
131: $accessedColumns = $this->accessedColumns;
132: $this->loadRefCache();
133:
134: if (!isset($this->refCacheCurrent['data'])) {
135:
136: $this->accessedColumns = $accessedColumns;
137:
138: $limit = $this->sqlBuilder->getLimit();
139: $rows = count($this->refTable->rows);
140: if ($limit && $rows > 1) {
141: $this->sqlBuilder->setLimit(NULL, NULL);
142: }
143: parent::execute();
144: $this->sqlBuilder->setLimit($limit, NULL);
145: $data = array();
146: $offset = array();
147: $this->accessColumn($this->column);
148: foreach ((array) $this->rows as $key => $row) {
149: $ref = & $data[$row[$this->column]];
150: $skip = & $offset[$row[$this->column]];
151: if ($limit === NULL || $rows <= 1 || (count($ref) < $limit && $skip >= $this->sqlBuilder->getOffset())) {
152: $ref[$key] = $row;
153: } else {
154: unset($this->rows[$key]);
155: }
156: $skip++;
157: unset($ref, $skip);
158: }
159:
160: $this->refCacheCurrent['data'] = $data;
161: $this->data = & $this->refCacheCurrent['data'][$this->active];
162: }
163:
164: $this->observeCache = $this;
165: if ($this->data === NULL) {
166: $this->data = array();
167: } else {
168: foreach ($this->data as $row) {
169: $row->setTable($this);
170: }
171: reset($this->data);
172: }
173: }
174:
175:
176: protected function getRefTable(& $refPath)
177: {
178: $refObj = $this->refTable;
179: $refPath = $this->name . '.';
180: while ($refObj instanceof self) {
181: $refPath .= $refObj->name . '.';
182: $refObj = $refObj->refTable;
183: }
184:
185: return $refObj;
186: }
187:
188:
189: protected function loadRefCache()
190: {
191: $hash = $this->getSpecificCacheKey();
192: $referencing = & $this->refCache['referencing'][$this->getGeneralCacheKey()];
193: $this->observeCache = & $referencing['observeCache'];
194: $this->refCacheCurrent = & $referencing[$hash];
195: $this->accessedColumns = & $referencing[$hash]['accessed'];
196: $this->specificCacheKey = & $referencing[$hash]['specificCacheKey'];
197: $this->rows = & $referencing[$hash]['rows'];
198:
199: if (isset($referencing[$hash]['data'][$this->active])) {
200: $this->data = & $referencing[$hash]['data'][$this->active];
201: }
202: }
203:
204:
205: protected function emptyResultSet($saveCache = TRUE, $deleteRererencedCache = TRUE)
206: {
207: parent::emptyResultSet($saveCache, FALSE);
208: }
209:
210:
211:
212:
213:
214: public function insert($data)
215: {
216: if ($data instanceof \Traversable && !$data instanceof Selection) {
217: $data = iterator_to_array($data);
218: }
219:
220: if (Nette\Utils\Arrays::isList($data)) {
221: foreach (array_keys($data) as $key) {
222: $data[$key][$this->column] = $this->active;
223: }
224: } else {
225: $data[$this->column] = $this->active;
226: }
227:
228: return parent::insert($data);
229: }
230:
231:
232: public function update($data)
233: {
234: $builder = $this->sqlBuilder;
235:
236: $this->sqlBuilder = clone $this->sqlBuilder;
237: $this->where($this->column, $this->active);
238: $return = parent::update($data);
239:
240: $this->sqlBuilder = $builder;
241: return $return;
242: }
243:
244:
245: public function delete()
246: {
247: $builder = $this->sqlBuilder;
248:
249: $this->sqlBuilder = clone $this->sqlBuilder;
250: $this->where($this->column, $this->active);
251: $return = parent::delete();
252:
253: $this->sqlBuilder = $builder;
254: return $return;
255: }
256:
257: }
258: