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