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:
63: public function through($column)
64: {
65: trigger_error(__METHOD__ . '() is deprecated; use ' . __CLASS__ . '::related("' . $this->name . '", "' . $column . '") instead.', E_USER_DEPRECATED);
66: $this->column = $column;
67: $this->delimitedColumn = $this->refTable->connection->getSupplementalDriver()->delimite($this->column);
68: return $this;
69: }
70:
71:
72: public function select($columns)
73: {
74: if (!$this->sqlBuilder->getSelect()) {
75: $this->sqlBuilder->addSelect("$this->name.$this->column");
76: }
77:
78: return call_user_func_array('parent::select', func_get_args());
79: }
80:
81:
82: public function order($columns)
83: {
84: if (!$this->sqlBuilder->getOrder()) {
85:
86: $this->sqlBuilder->addOrder("$this->name.$this->column" . (preg_match('~\bDESC\z~i', $columns) ? ' DESC' : ''));
87: }
88:
89: return call_user_func_array('parent::order', func_get_args());
90: }
91:
92:
93:
94:
95:
96: public function aggregation($function)
97: {
98: $aggregation = & $this->getRefTable($refPath)->aggregation[$refPath . $function . $this->getSql() . json_encode($this->sqlBuilder->getParameters())];
99:
100: if ($aggregation === NULL) {
101: $aggregation = array();
102:
103: $selection = $this->createSelectionInstance();
104: $selection->getSqlBuilder()->importConditions($this->getSqlBuilder());
105: $selection->select($function);
106: $selection->select("$this->name.$this->column");
107: $selection->group("$this->name.$this->column");
108:
109: foreach ($selection as $row) {
110: $aggregation[$row[$this->column]] = $row;
111: }
112: }
113:
114: if (isset($aggregation[$this->active])) {
115: foreach ($aggregation[$this->active] as $val) {
116: return $val;
117: }
118: }
119: }
120:
121:
122: public function count($column = NULL)
123: {
124: $return = parent::count($column);
125: return isset($return) ? $return : 0;
126: }
127:
128:
129:
130:
131:
132: protected function execute()
133: {
134: if ($this->rows !== NULL) {
135: $this->observeCache = $this;
136: return;
137: }
138:
139: $accessedColumns = $this->accessedColumns;
140: $this->loadRefCache();
141:
142: if (!isset($this->refCacheCurrent['data'])) {
143:
144: $this->accessedColumns = $accessedColumns;
145:
146: $limit = $this->sqlBuilder->getLimit();
147: $rows = count($this->refTable->rows);
148: if ($limit && $rows > 1) {
149: $this->sqlBuilder->setLimit(NULL, NULL);
150: }
151: parent::execute();
152: $this->sqlBuilder->setLimit($limit, NULL);
153: $data = array();
154: $offset = array();
155: $this->accessColumn($this->column);
156: foreach ((array) $this->rows as $key => $row) {
157: $ref = & $data[$row[$this->column]];
158: $skip = & $offset[$row[$this->column]];
159: if ($limit === NULL || $rows <= 1 || (count($ref) < $limit && $skip >= $this->sqlBuilder->getOffset())) {
160: $ref[$key] = $row;
161: } else {
162: unset($this->rows[$key]);
163: }
164: $skip++;
165: unset($ref, $skip);
166: }
167:
168: $this->refCacheCurrent['data'] = $data;
169: $this->data = & $this->refCacheCurrent['data'][$this->active];
170: }
171:
172: $this->observeCache = $this;
173: if ($this->data === NULL) {
174: $this->data = array();
175: } else {
176: foreach ($this->data as $row) {
177: $row->setTable($this);
178: }
179: reset($this->data);
180: }
181: }
182:
183:
184: protected function getRefTable(& $refPath)
185: {
186: $refObj = $this->refTable;
187: $refPath = $this->name . '.';
188: while ($refObj instanceof GroupedSelection) {
189: $refPath .= $refObj->name . '.';
190: $refObj = $refObj->refTable;
191: }
192:
193: return $refObj;
194: }
195:
196:
197: protected function loadRefCache()
198: {
199: $hash = $this->getSpecificCacheKey();
200: $referencing = & $this->refCache['referencing'][$this->getGeneralCacheKey()];
201: $this->observeCache = & $referencing['observeCache'];
202: $this->refCacheCurrent = & $referencing[$hash];
203: $this->accessedColumns = & $referencing[$hash]['accessed'];
204: $this->specificCacheKey = & $referencing[$hash]['specificCacheKey'];
205: $this->rows = & $referencing[$hash]['rows'];
206:
207: if (isset($referencing[$hash]['data'][$this->active])) {
208: $this->data = & $referencing[$hash]['data'][$this->active];
209: }
210: }
211:
212:
213:
214:
215:
216: public function insert($data)
217: {
218: if ($data instanceof \Traversable && !$data instanceof Selection) {
219: $data = iterator_to_array($data);
220: }
221:
222: if (Nette\Utils\Arrays::isList($data)) {
223: foreach (array_keys($data) as $key) {
224: $data[$key][$this->column] = $this->active;
225: }
226: } else {
227: $data[$this->column] = $this->active;
228: }
229:
230: return parent::insert($data);
231: }
232:
233:
234: public function update($data)
235: {
236: $builder = $this->sqlBuilder;
237:
238: $this->sqlBuilder = clone $this->sqlBuilder;
239: $this->where($this->column, $this->active);
240: $return = parent::update($data);
241:
242: $this->sqlBuilder = $builder;
243: return $return;
244: }
245:
246:
247: public function delete()
248: {
249: $builder = $this->sqlBuilder;
250:
251: $this->sqlBuilder = clone $this->sqlBuilder;
252: $this->where($this->column, $this->active);
253: $return = parent::delete();
254:
255: $this->sqlBuilder = $builder;
256: return $return;
257: }
258:
259: }
260: