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