1: <?php
2:
3: /**
4: * PHP version 5
5: * @package generalDriver
6: * @author Christian Schiffler <c.schiffler@cyberspectrum.de>
7: * @copyright The MetaModels team.
8: * @license LGPL.
9: * @filesource
10: */
11:
12: namespace DcGeneral\Data;
13:
14: use DcGeneral\Exception\DcGeneralException;
15:
16: /**
17: * Class TableRowsAsRecordsDataProvider.
18: *
19: * This data provider allows to map multiple rows of a SQL table into a single model for usage in a MultiColumnWizard.
20: *
21: * @package DcGeneral\Data
22: */
23: class TableRowsAsRecordsDataProvider extends DefaultDataProvider
24: {
25: /**
26: * Grouping column to use to tie rows together.
27: *
28: * @var string
29: */
30: protected $strGroupCol = 'pid';
31:
32: /**
33: * Sorting column to sort the entries by.
34: *
35: * @var string
36: */
37: protected $strSortCol = '';
38:
39: /**
40: * Set base config with source and other necessary parameter.
41: *
42: * @param array $arrConfig The configuration to use.
43: *
44: * @return void
45: *
46: * @throws DcGeneralException When no source has been defined.
47: */
48: public function setBaseConfig(array $arrConfig)
49: {
50: parent::setBaseConfig($arrConfig);
51:
52: if (!$arrConfig['group_column'])
53: {
54: throw new DcGeneralException('DcGeneral\Data\TableRowsAsRecordsDriver needs a grouping column.', 1);
55:
56: }
57: $this->strGroupCol = $arrConfig['group_column'];
58:
59: if ($arrConfig['sort_column'])
60: {
61: $this->strSortCol = $arrConfig['sort_column'];
62: }
63: }
64:
65: /**
66: * Exception throwing convenience method.
67: *
68: * Convenience method in this data provider that simply throws an Exception stating that the passed method name
69: * should not be called on this data provider, as it is only intended to display an edit mask.
70: *
71: * @param string $strMethod The name of the method being called.
72: *
73: * @throws DcGeneralException Throws always an exception telling that the method (see param $strMethod) must not be
74: * called.
75: *
76: * @return void
77: */
78: protected function youShouldNotCallMe($strMethod)
79: {
80: throw new DcGeneralException(sprintf(
81: 'Error, %s not available, as the data provider is intended for edit mode only.',
82: $strMethod
83: ), 1);
84: }
85:
86:
87: /**
88: * Unsupported in this data provider, throws an Exception.
89: *
90: * @param mixed $item Unused.
91: *
92: * @return void
93: *
94: * @throws DcGeneralException Always throws exception.
95: */
96: public function delete($item)
97: {
98: $this->youShouldNotCallMe(__METHOD__);
99: }
100:
101: /**
102: * Fetch a single record by id.
103: *
104: * This data provider only supports retrieving by id so use $objConfig->setId() to populate the config with an Id.
105: *
106: * @param ConfigInterface $objConfig The configuration to use.
107: *
108: * @return ModelInterface
109: *
110: * @throws DcGeneralException If config object does not contain an Id.
111: */
112: public function fetch(ConfigInterface $objConfig)
113: {
114: if (!$objConfig->getId())
115: {
116: throw new DcGeneralException(
117: 'Error, no id passed, DcGeneral\Data\TableRowsAsRecordsDriver is only intended for edit mode.',
118: 1
119: );
120: }
121:
122: $strQuery = sprintf(
123: 'SELECT %s FROM %s WHERE %s=?',
124: $this->buildFieldQuery($objConfig),
125: $this->strSource,
126: $this->strGroupCol
127: );
128:
129: if ($this->strSortCol)
130: {
131: $strQuery .= ' ORDER BY ' . $this->strSortCol;
132: }
133:
134: $objResult = $this->objDatabase
135: ->prepare($strQuery)
136: ->execute($objConfig->getId());
137:
138: $objModel = $this->getEmptyModel();
139: if ($objResult->numRows)
140: {
141: $objModel->setProperty('rows', $objResult->fetchAllAssoc());
142: }
143:
144: $objModel->setID($objConfig->getId());
145:
146: return $objModel;
147: }
148:
149: /**
150: * Unsupported in this data provider, throws an Exception.
151: *
152: * @param ConfigInterface $objConfig Unused.
153: *
154: * @return void
155: *
156: * @throws DcGeneralException Always throws exception.
157: */
158: public function fetchAll(ConfigInterface $objConfig)
159: {
160: $this->youShouldNotCallMe(__METHOD__);
161: }
162:
163: /**
164: * Unsupported in this data provider, throws an Exception.
165: *
166: * @param ConfigInterface $objConfig Unused.
167: *
168: * @return void
169: *
170: * @throws DcGeneralException Always throws exception.
171: */
172: public function getCount(ConfigInterface $objConfig)
173: {
174: $this->youShouldNotCallMe(__METHOD__);
175: }
176:
177: /**
178: * Unsupported in this data provider, throws an Exception.
179: *
180: * @param string $strField Unused.
181: *
182: * @param mixed $varNew Unused.
183: *
184: * @param int $intId Unused.
185: *
186: * @return void
187: *
188: * @throws DcGeneralException Always throws exception.
189: */
190: public function isUniqueValue($strField, $varNew, $intId = null)
191: {
192: $this->youShouldNotCallMe(__METHOD__);
193: }
194:
195: /**
196: * Unsupported in this data provider, throws an Exception.
197: *
198: * @param string $strField Unused.
199: *
200: * @return void
201: *
202: * @throws DcGeneralException Always throws exception.
203: */
204: public function resetFallback($strField)
205: {
206: $this->youShouldNotCallMe(__METHOD__);
207: }
208:
209: /**
210: * Save a model to the database.
211: *
212: * In general, this method fetches the solely property "rows" from the model and updates the local table against
213: * these contents.
214: *
215: * The parent id (id of the model) will get checked and reflected also for new items.
216: *
217: * When rows with duplicate ids are encountered (like from MCW for example), the dupes are inserted as new rows.
218: *
219: * @param ModelInterface $objItem The model to save.
220: *
221: * @param bool $recursive Ignored as not relevant in this data provider.
222: *
223: * @return ModelInterface The passed Model.
224: *
225: * @throws DcGeneralException When the passed model does not contain a property named "rows", an Exception is thrown.
226: */
227: public function save(ModelInterface $objItem, $recursive = false)
228: {
229: $arrData = $objItem->getProperty('rows');
230: if (!($objItem->getID() && $arrData))
231: {
232: throw new DcGeneralException('invalid input data in model.', 1);
233: }
234:
235: $arrKeep = array();
236: foreach ($arrData as $arrRow)
237: {
238: // TODO: add an option to restrict this to some allowed fields?
239: $arrSQL = $arrRow;
240:
241: // Update all.
242: $intId = intval($arrRow['id']);
243:
244: // Work around the fact that multicolumnwizard does not clear any hidden fields when copying a dataset.
245: // therefore we do consider any dupe as new dataset and save it accordingly.
246: if (in_array($intId, $arrKeep))
247: {
248: $intId = 0;
249: unset($arrSQL['id']);
250: }
251:
252: if ($intId > 0)
253: {
254: $this->objDatabase
255: ->prepare(sprintf('UPDATE %s %%s WHERE id=? AND %s=?', $this->strSource, $this->strGroupCol))
256: ->set($arrSQL)
257: ->execute($intId, $objItem->getId());
258: $arrKeep[] = $intId;
259: } else {
260: // Force group col value.
261: $arrSQL[$this->strGroupCol] = $objItem->getId();
262: $arrKeep[] = $this->objDatabase
263: ->prepare(sprintf('INSERT INTO %s %%s', $this->strSource))
264: ->set($arrSQL)
265: ->execute()
266: ->insertId;
267: }
268: }
269: // House keeping, kill the rest.
270: $this->objDatabase
271: ->prepare(sprintf(
272: 'DELETE FROM %s WHERE %s=? AND id NOT IN (%s)',
273: $this->strSource,
274: $this->strGroupCol,
275: implode(',', $arrKeep)
276: ))
277: ->execute($objItem->getId());
278: return $objItem;
279: }
280:
281: /**
282: * Unsupported in this data provider, throws an Exception.
283: *
284: * @param CollectionInterface $objItems Unused.
285: *
286: * @return void
287: *
288: * @throws DcGeneralException Always throws exception.
289: */
290: public function saveEach(CollectionInterface $objItems)
291: {
292: $this->youShouldNotCallMe(__METHOD__);
293: }
294:
295: /**
296: * Check if the property exists in the table.
297: *
298: * This data provider only returns true for the tstamp property.
299: *
300: * @param string $strField The name of the property to check.
301: *
302: * @return boolean
303: */
304: public function fieldExists($strField)
305: {
306: return in_array($strField, array('tstamp'));
307: }
308:
309: /**
310: * Unsupported in this data provider, throws an Exception.
311: *
312: * @param mixed $mixID Unused.
313: *
314: * @param mixed $mixVersion Unused.
315: *
316: * @return void
317: *
318: * @throws DcGeneralException Always throws exception.
319: */
320: public function getVersion($mixID, $mixVersion)
321: {
322: $this->youShouldNotCallMe(__METHOD__);
323: }
324:
325: /**
326: * Return null as versioning is not supported in this data provider.
327: *
328: * @param mixed $mixID Unused.
329: *
330: * @param boolean $blnOnlyActive Unused.
331: *
332: * @return null
333: */
334: public function getVersions($mixID, $blnOnlyActive = false)
335: {
336: // Sorry, versioning not supported.
337: return null;
338: }
339:
340: /**
341: * Unsupported in this data provider, throws an Exception.
342: *
343: * @param ModelInterface $objModel Unused.
344: *
345: * @param string $strUsername Unused.
346: *
347: * @return void
348: *
349: * @throws DcGeneralException Always throws exception.
350: */
351: public function saveVersion(ModelInterface $objModel, $strUsername)
352: {
353: $this->youShouldNotCallMe(__METHOD__);
354: }
355:
356: /**
357: * Unsupported in this data provider, throws an Exception.
358: *
359: * @param mixed $mixID Unused.
360: *
361: * @param mixed $mixVersion Unused.
362: *
363: * @return void
364: *
365: * @throws DcGeneralException Always throws exception.
366: */
367: public function setVersionActive($mixID, $mixVersion)
368: {
369: $this->youShouldNotCallMe(__METHOD__);
370: }
371:
372: /**
373: * Unsupported in this data provider, throws an Exception.
374: *
375: * @param mixed $mixID Unused.
376: *
377: * @return void
378: *
379: * @throws DcGeneralException Always throws exception.
380: */
381: public function getActiveVersion($mixID)
382: {
383: $this->youShouldNotCallMe(__METHOD__);
384: }
385:
386: /**
387: * Unsupported in this data provider, throws an Exception.
388: *
389: * @param ModelInterface $objModel1 Unused.
390: *
391: * @param ModelInterface $objModel2 Unused.
392: *
393: * @return void
394: *
395: * @throws DcGeneralException Always throws exception.
396: */
397: public function sameModels($objModel1, $objModel2)
398: {
399: $this->youShouldNotCallMe(__METHOD__);
400: }
401:
402: /**
403: * Unsupported in this data provider, throws an Exception.
404: *
405: * @param string $strSourceSQL Unused.
406: *
407: * @param string $strSaveSQL Unused.
408: *
409: * @param string $strTable Unused.
410: *
411: * @return void
412: *
413: * @throws DcGeneralException Always throws exception.
414: */
415: protected function insertUndo($strSourceSQL, $strSaveSQL, $strTable)
416: {
417: $this->youShouldNotCallMe(__METHOD__);
418: }
419: }
420: