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