PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/app/protected/core/utils/RedBeanDatabaseBuilderUtil.php

https://bitbucket.org/aleksame/zurmo
PHP | 706 lines | 592 code | 25 blank | 89 comment | 48 complexity | f70d7415bea044897f73a6280f1f4b00 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, BSD-3-Clause, LGPL-3.0, LGPL-2.1, BSD-2-Clause
  1. <?php
  2. /*********************************************************************************
  3. * Zurmo is a customer relationship management program developed by
  4. * Zurmo, Inc. Copyright (C) 2012 Zurmo Inc.
  5. *
  6. * Zurmo is free software; you can redistribute it and/or modify it under
  7. * the terms of the GNU General Public License version 3 as published by the
  8. * Free Software Foundation with the addition of the following permission added
  9. * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
  10. * IN WHICH THE COPYRIGHT IS OWNED BY ZURMO, ZURMO DISCLAIMS THE WARRANTY
  11. * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
  12. *
  13. * Zurmo is distributed in the hope that it will be useful, but WITHOUT
  14. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  15. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  16. * details.
  17. *
  18. * You should have received a copy of the GNU General Public License along with
  19. * this program; if not, see http://www.gnu.org/licenses or write to the Free
  20. * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  21. * 02110-1301 USA.
  22. *
  23. * You can contact Zurmo, Inc. with a mailing address at 113 McHenry Road Suite 207,
  24. * Buffalo Grove, IL 60089, USA. or at email address contact@zurmo.com.
  25. ********************************************************************************/
  26. /**
  27. * The purpose of this class is to drill through RedBeanModels,
  28. * populating them with made up data and saving them, in order
  29. * to build the database schema for freezing.
  30. */
  31. class RedBeanDatabaseBuilderUtil
  32. {
  33. const AUTO_BUILD_STATE_KEY = 'autoBuildState';
  34. const AUTO_BUILD_SAMPLE_MODELS_KEY = 'autoBuildModels';
  35. const AUTO_BUILD_STATE_INVALID = 'invalid';
  36. const AUTO_BUILD_STATE_VALID = 'valid';
  37. protected static $modelClassNamesToSampleModels;
  38. /**
  39. * Auto building of models (and therefore the database) involves...
  40. *
  41. * - Creating each model.
  42. * - Setting its members to made up values that conform to the rules specified for the member.
  43. * - Setting or adding to its relations while avoiding making new objects of the same types
  44. * as have already been made.
  45. * - Saving it so that the tables and columns are created.
  46. * - Deleting it so that it doesn't leave rows behind.
  47. * - (The database is now ready for freezing.)
  48. *
  49. * The aim of the auto build is to populate models in a 'valid enough way' to save them
  50. * such that RedBean creates the tables and columns it needs with the right column types.
  51. * This means it does not necessarily make models that are valid, for example it will set
  52. * a model's parent to itself if model and the parent are of the same type. These kinds of
  53. * inconsistencies do not matter for the purpose of auto building the database, and are
  54. * semantic information that is not available and not needed for this process. The idea is
  55. * to create as few models as possible.
  56. *
  57. * Call this an empty unfrozen database with all the models required for certain tests, or
  58. * all the models required for the production database. Then freeze the database.
  59. *
  60. * If a model references a non-leaf model in the hierarchy an example of a model subclassed
  61. * from that type must be included in the $modelClassNames. eg: 'Opportunity' references
  62. * Permitable via its permissions and an abstract 'Permitable' cannot be created, so 'User'
  63. * needs be created at the same time since it is concrete and can be used to create an
  64. * Opportunity.
  65. * ie: $modelClassNames = array('Opportunity', 'User').
  66. */
  67. public static function autoBuildModels(array $modelClassNames, & $messageLogger)
  68. {
  69. assert('AssertUtil::all($modelClassNames, "is_string")');
  70. assert('$messageLogger instanceof MessageLogger');
  71. if (!self::isAutoBuildStateValid())
  72. {
  73. self::deleteAllSampleModelsFromStatePersisterAndDatabase($messageLogger);
  74. }
  75. self::setAutoBuildStateInStatePersister(self::AUTO_BUILD_STATE_INVALID);
  76. AuditEvent::$isTableOptimized = false;
  77. self::$modelClassNamesToSampleModels = array();
  78. foreach ($modelClassNames as $modelClassName)
  79. {
  80. $messages[] = array('info' => "Auto building $modelClassName.");
  81. self::autoBuildSampleModel($modelClassName, $modelClassNames, $messageLogger);
  82. $messageLogger->addInfoMessage("Auto build of $modelClassName done.");
  83. }
  84. foreach (self::$modelClassNamesToSampleModels as $modelClassName => $model)
  85. {
  86. if (!$model instanceof OwnedModel && !$model instanceof OwnedCustomField && !$model instanceof OwnedMultipleValuesCustomField)
  87. {
  88. try
  89. {
  90. $model->setScenario('autoBuildDatabase');
  91. $saved = $model->save();
  92. if ($saved)
  93. {
  94. self::setSampleModelInStatePersister(get_class($model), $model->id);
  95. $metadata = $model->getMetadata();
  96. foreach ($metadata as $unused => $classMetadata)
  97. {
  98. if (!empty($classMetadata['relations']))
  99. {
  100. foreach ($classMetadata['relations'] as $relationName => $relationTypeModelClassNameAndOwns)
  101. {
  102. $relationType = $relationTypeModelClassNameAndOwns[0];
  103. $relatedModelClassName = $relationTypeModelClassNameAndOwns[1];
  104. $owned = isset($relationTypeModelClassNameAndOwns[2]) &&
  105. $relationTypeModelClassNameAndOwns[2] == RedBeanModel::OWNED;
  106. if (get_class($model) == get_class($model->$relationName) &&
  107. $model->id == $model->$relationName->id)
  108. {
  109. $messageLogger->addInfoMessage("Unset {$modelClassName}->{$relationName} to avoid recursion and thread stack overrun.");
  110. $model->$relationName = null;
  111. $model->save();
  112. }
  113. }
  114. }
  115. }
  116. }
  117. else
  118. {
  119. $messageLogger->addErrorMessage("*** Saving the sample $modelClassName failed.");
  120. $errors = $model->getErrors();
  121. if (count($errors) > 0)
  122. {
  123. $messageLogger->addErrorMessage('The attributes that did not validate probably need more rules, or are not deletable types.');
  124. $messageLogger->addErrorMessage(print_r($errors, true));
  125. }
  126. else
  127. {
  128. $messageLogger->addErrorMessage('No attributes failed to validate!');
  129. }
  130. }
  131. $messageLogger->addInfoMessage("Auto built $modelClassName saved.");
  132. }
  133. catch (NotSupportedException $e)
  134. {
  135. $messageLogger->addErrorMessage("*** Saving the sample $modelClassName failed.");
  136. if (is_subclass_of($modelClassName, 'OwnedCustomField') ||
  137. is_subclass_of($modelClassName, 'OwnedMultipleValuesCustomField') ||
  138. is_subclass_of($modelClassName, 'OwnedModel'))
  139. {
  140. $messageLogger->addErrorMessage('It is OWNED and was probably not saved via its owner, making it not a root model.');
  141. }
  142. else
  143. {
  144. $messageLogger->addErrorMessage('The save failed but there were no validation errors.');
  145. }
  146. }
  147. }
  148. }
  149. foreach (self::$modelClassNamesToSampleModels as $modelClassName => $model)
  150. {
  151. try
  152. {
  153. if (!$model->isDeleted())
  154. {
  155. if (!$model->delete())
  156. {
  157. if ($model->id < 0)
  158. {
  159. $messageLogger->addInfoMessage(get_class($model) . " Not Deleted but never saved so this is ok. (Most likely it is a - Has Many Owned)");
  160. }
  161. else
  162. {
  163. $messageLogger->addErrorMessage("*** Deleting the sample " . get_class($model) . " failed. It would not delete.");
  164. }
  165. }
  166. else
  167. {
  168. $messageLogger->addInfoMessage(get_class($model) . " Deleted (Not Owned).");
  169. }
  170. }
  171. else
  172. {
  173. $messageLogger->addInfoMessage(get_class($model) . " Deleted Already (Owned).");
  174. }
  175. AuditEvent::deleteAllByModel($model);
  176. unset(self::$modelClassNamesToSampleModels[$modelClassName]);
  177. }
  178. catch (NotSupportedException $e)
  179. {
  180. $messageLogger->addErrorMessage("*** Deleting the sample $modelClassName failed. It is marked not deletable.");
  181. }
  182. }
  183. if (count(self::$modelClassNamesToSampleModels))
  184. {
  185. $messageLogger->addErrorMessage('*** Deleting of the sample(s) ' . join(', ', array_keys(self::$modelClassNamesToSampleModels)) . " didn't happen.");
  186. }
  187. AuditEvent::$isTableOptimized = false;
  188. self::deleteAllSampleModelsFromStatePersister();
  189. self::setAutoBuildStateInStatePersister(self::AUTO_BUILD_STATE_VALID);
  190. }
  191. /**
  192. * @param array $modelClassNames
  193. * @param MessageLogger $messageLogger
  194. */
  195. public static function manageFrozenStateAndAutoBuildModels(array $modelClassNames, & $messageLogger)
  196. {
  197. RedBeanDatabase::unfreeze();
  198. self::autoBuildModels($modelClassNames, $messageLogger);
  199. RedBeanDatabase::freeze();
  200. }
  201. /**
  202. * Deletes all sample models from state persister and from database
  203. * @param MessageLogger $messageLogger
  204. */
  205. protected static function deleteAllSampleModelsFromStatePersisterAndDatabase($messageLogger)
  206. {
  207. $allSampleModels = self::getSampleModelsFromStatePersister();
  208. $allSampleModelsDeleted = true;
  209. if (!empty($allSampleModels))
  210. {
  211. foreach ($allSampleModels as $key => $sampleModel)
  212. {
  213. $allSampleModelsDeleted = $allSampleModelsDeleted && self::deleteSampleModelFromStatePersisterAndDatabase(
  214. $sampleModel['modelClassName'],
  215. $sampleModel['modelId'],
  216. $messageLogger);
  217. }
  218. }
  219. if ($allSampleModelsDeleted)
  220. {
  221. echo "All sample models from previous autobuild are deleted.";
  222. $messageLogger->addInfoMessage("All sample models from previous autobuild are deleted, but schema is not updated yet.");
  223. $messageLogger->addInfoMessage("If you want to update schema, run auto build process again.");
  224. self::setAutoBuildStateInStatePersister(self::AUTO_BUILD_STATE_VALID);
  225. }
  226. else
  227. {
  228. $messageLogger->addErrorMessage("All sample models couldn't be deleted.");
  229. }
  230. Yii::app()->end();
  231. }
  232. /**
  233. * Store sample model in state persister(database)
  234. * @param string $modelClassName
  235. * @param int $modelId
  236. */
  237. protected static function setSampleModelInStatePersister($modelClassName, $modelId)
  238. {
  239. $statePersister = Yii::app()->getStatePersister();
  240. $state = $statePersister->load();
  241. $sampleModel = array(
  242. 'modelClassName' => $modelClassName,
  243. 'modelId' => $modelId
  244. );
  245. $state[self::AUTO_BUILD_SAMPLE_MODELS_KEY][] = $sampleModel;
  246. $statePersister->save($state);
  247. }
  248. /**
  249. * Get all sample models from state persister
  250. * @return array | null
  251. */
  252. protected static function getSampleModelsFromStatePersister()
  253. {
  254. $statePersister = Yii::app()->getStatePersister();
  255. $state = $statePersister->load();
  256. if (isset($state[self::AUTO_BUILD_SAMPLE_MODELS_KEY]))
  257. {
  258. return $state[self::AUTO_BUILD_SAMPLE_MODELS_KEY];
  259. }
  260. else
  261. {
  262. return null;
  263. }
  264. }
  265. /**
  266. * Delete all sample models from state persister. If this function is used,
  267. * sample models should alreaady be deleted from database.
  268. */
  269. protected static function deleteAllSampleModelsFromStatePersister()
  270. {
  271. $statePersister = Yii::app()->getStatePersister();
  272. $state = $statePersister->load();
  273. unset($state[self::AUTO_BUILD_SAMPLE_MODELS_KEY]);
  274. $statePersister->save($state);
  275. return true;
  276. }
  277. /**
  278. * Delete sample model from application state persister and from database
  279. * @param string $modelClassName
  280. * @param inf $modelId
  281. * @param MessageLogger $messageLogger
  282. * @return boolean
  283. */
  284. protected static function deleteSampleModelFromStatePersisterAndDatabase($modelClassName, $modelId, $messageLogger)
  285. {
  286. $statePersister = Yii::app()->getStatePersister();
  287. $state = $statePersister->load();
  288. $result = true;
  289. if (isset($state[self::AUTO_BUILD_SAMPLE_MODELS_KEY]))
  290. {
  291. $sampleModels = $state[self::AUTO_BUILD_SAMPLE_MODELS_KEY];
  292. if (!empty($sampleModels))
  293. {
  294. foreach ($sampleModels as $key => $sampleModel)
  295. {
  296. if ($sampleModel['modelClassName'] == $modelClassName && $sampleModel['modelId'] == $modelId)
  297. {
  298. try
  299. {
  300. $model = $sampleModel['modelClassName']::getById($modelId);
  301. if ($model)
  302. {
  303. if ($model->delete())
  304. {
  305. unset($state[self::AUTO_BUILD_SAMPLE_MODELS_KEY][$key]);
  306. $messageLogger->addInfoMessage("Sample model {$sampleModel['modelClassName']}-> $modelId deleted.");
  307. }
  308. else
  309. {
  310. $messageLogger->addErrorMessage("Couldn't delete sample model {$sampleModel['modelClassName']}-> $modelId deleted.");
  311. $result = false;
  312. }
  313. }
  314. }
  315. catch (NotFoundException $e)
  316. {
  317. // Do nothing, model is already deleted
  318. }
  319. }
  320. }
  321. }
  322. }
  323. $statePersister->save($state);
  324. return $result;
  325. }
  326. /**
  327. * Set current state of auto build process in state persister.
  328. * This state is also used in BeginRequestBehavious, so if state is invalid,
  329. * we will exit application, and inform user about this case.
  330. * @param string $value
  331. */
  332. protected static function setAutoBuildStateInStatePersister($value)
  333. {
  334. $statePersister = Yii::app()->getStatePersister();
  335. $state = $statePersister->load();
  336. $state[self::AUTO_BUILD_STATE_KEY] = $value;
  337. $statePersister->save($state);
  338. }
  339. /**
  340. * Get auto build state from state persister
  341. * @return string | null
  342. */
  343. protected static function getAutoBuildStateFromStatePersister()
  344. {
  345. $statePersister = Yii::app()->getStatePersister();
  346. $state = $statePersister->load();
  347. if (isset($state[self::AUTO_BUILD_STATE_KEY]))
  348. {
  349. return $state[self::AUTO_BUILD_STATE_KEY];
  350. }
  351. else
  352. {
  353. return null;
  354. }
  355. }
  356. /**
  357. * Check if auto build process completed sucessfully
  358. * @return boolean
  359. */
  360. public static function isAutoBuildStateValid()
  361. {
  362. $autoBuildState = self::getAutoBuildStateFromStatePersister();
  363. if (!isset($autoBuildState) || $autoBuildState == 'valid')
  364. {
  365. return true;
  366. }
  367. else
  368. {
  369. return false;
  370. }
  371. }
  372. public static function autoBuildSampleModel($modelClassName, array $modelClassNames, & $messageLogger)
  373. {
  374. assert('$messageLogger instanceof MessageLogger');
  375. if (!empty(self::$modelClassNamesToSampleModels[$modelClassName]))
  376. {
  377. return self::$modelClassNamesToSampleModels[$modelClassName];
  378. }
  379. $messageLogger->addInfoMessage("$modelClassName Being Created.");
  380. $model = new $modelClassName();
  381. $model->setScenario('autoBuildDatabase');
  382. self::$modelClassNamesToSampleModels[$modelClassName] = $model;
  383. $metadata = $model->getMetadata();
  384. foreach ($metadata as $unused => $classMetadata)
  385. {
  386. if (!empty($classMetadata['members']))
  387. {
  388. foreach ($classMetadata['members'] as $memberName)
  389. {
  390. if (!$model->isAttributeReadOnly($memberName))
  391. {
  392. $messageLogger->addInfoMessage("Setting $modelClassName->$memberName.");
  393. self::setMadeUpMemberValue($model, $memberName);
  394. }
  395. }
  396. }
  397. }
  398. foreach ($metadata as $unused => $classMetadata)
  399. {
  400. if (!empty($classMetadata['relations']))
  401. {
  402. foreach ($classMetadata['relations'] as $relationName => $relationTypeModelClassNameAndOwns)
  403. {
  404. //Always use the current user to ensure the model can later be saved and removed.
  405. if ($relationName == 'owner' && $model instanceof OwnedSecurableItem)
  406. {
  407. $model->owner = Yii::app()->user->userModel;
  408. }
  409. elseif (!$model->isAttributeReadOnly($relationName))
  410. {
  411. $messageLogger->addInfoMessage("Setting $modelClassName->$relationName.");
  412. $relationType = $relationTypeModelClassNameAndOwns[0];
  413. $relatedModelClassName = $relationTypeModelClassNameAndOwns[1];
  414. $owned = isset($relationTypeModelClassNameAndOwns[2]) &&
  415. $relationTypeModelClassNameAndOwns[2] == RedBeanModel::OWNED;
  416. $message = $relationType == RedBeanModel::HAS_ONE_BELONGS_TO ? "HAS_ONE_BELONGS_TO" :
  417. ($relationType == RedBeanModel::HAS_MANY_BELONGS_TO ? "HAS_MANY_BELONGS_TO" :
  418. ($relationType == RedBeanModel::HAS_ONE ? "HAS_ONE" :
  419. ($relationType == RedBeanModel::HAS_MANY ? "HAS_MANY" :
  420. ($relationType == RedBeanModel::MANY_MANY ? "MANY_MANY" : '????'))));
  421. $messageLogger->addInfoMessage($message);
  422. if ($relationType == RedBeanModel::HAS_ONE &&
  423. $model->$relationName->id < 0 &&
  424. $relatedModelClassName == $modelClassName &&
  425. !$owned)
  426. {
  427. $messageLogger->addInfoMessage($relatedModelClassName);
  428. $messageLogger->addInfoMessage('(Set self)');
  429. $model->$relationName = $model;
  430. }
  431. else
  432. {
  433. $relatedModel = null;
  434. if ($relatedModelClassName::isTypeDeletable() || $owned)
  435. {
  436. $messageLogger->addInfoMessage($relatedModelClassName);
  437. $relatedModel = self::autoBuildSampleModel($relatedModelClassName, $modelClassNames, $messageLogger);
  438. }
  439. else
  440. {
  441. foreach ($modelClassNames as $otherModelClassName)
  442. {
  443. if (is_subclass_of($otherModelClassName, $relatedModelClassName) &&
  444. $otherModelClassName::isTypeDeletable())
  445. {
  446. $messageLogger->addInfoMessage("$relatedModelClassName (subst)");
  447. $relatedModel = self::autoBuildSampleModel($otherModelClassName, $modelClassNames, $messageLogger);
  448. break;
  449. }
  450. }
  451. }
  452. if (isset($relatedModel))
  453. {
  454. if (in_array($relationType, array(RedBeanModel::HAS_ONE_BELONGS_TO,
  455. RedBeanModel::HAS_MANY_BELONGS_TO,
  456. RedBeanModel::HAS_ONE)))
  457. {
  458. $messageLogger->addInfoMessage('(Set)');
  459. $model->$relationName = $relatedModel;
  460. }
  461. elseif ($model->$relationName->count() == 0)
  462. {
  463. assert('in_array($relationType, array(RedBeanModel::HAS_MANY,
  464. RedBeanModel::MANY_MANY))');
  465. $messageLogger->addInfoMessage('(Added)');
  466. $model->$relationName->add($relatedModel);
  467. }
  468. }
  469. }
  470. }
  471. }
  472. }
  473. }
  474. return $model;
  475. }
  476. protected static function setMadeUpMemberValue($model, $memberName)
  477. {
  478. $memberSet = false;
  479. $minValue = null;
  480. $maxValue = null;
  481. $minLength = 1;
  482. $maxLength = null;
  483. $unique = false;
  484. $ignoreDefaultValue = false;
  485. foreach ($model->getValidators($memberName) as $validator)
  486. {
  487. switch (get_class($validator))
  488. {
  489. case 'CBooleanValidator':
  490. $model->$memberName = 1;
  491. $memberSet = true;
  492. break;
  493. case 'RedBeanModelDateTimeDefaultValueValidator':
  494. break;
  495. case 'CDefaultValueValidator':
  496. case 'RedBeanModelDefaultValueValidator':
  497. if ($validator->value === null || $validator->value === '')
  498. {
  499. throw new NotSupportedException();
  500. }
  501. $model->$memberName = $validator->value;
  502. $memberSet = true;
  503. break;
  504. case 'CEmailValidator':
  505. $model->$memberName = 'someone@somewhere.net';
  506. $memberSet = true;
  507. break;
  508. case 'CInlineValidator':
  509. break;
  510. case 'RedBeanModelNumberValidator':
  511. if ($validator->min !== null)
  512. {
  513. $minValue = $validator->min;
  514. }
  515. if ($validator->max !== null)
  516. {
  517. $maxValue = $validator->max;
  518. }
  519. break;
  520. case 'CStringValidator':
  521. if ($validator->min !== null)
  522. {
  523. $minLength = $validator->min;
  524. }
  525. if ($validator->max !== null)
  526. {
  527. $maxLength = $validator->max;
  528. }
  529. break;
  530. case 'RedBeanModelUniqueValidator':
  531. $unique = true;
  532. break;
  533. case 'CUrlValidator':
  534. $model->$memberName = 'http://www.example.com';
  535. $memberSet = true;
  536. break;
  537. case 'CRegularExpressionValidator':
  538. case 'CRequiredValidator':
  539. case 'CSafeValidator':
  540. case 'CUnsafeValidator':
  541. case 'RedBeanModelCompareDateTimeValidator':
  542. case 'RedBeanModelRequiredValidator':
  543. case 'UsernameLengthValidator':
  544. case 'ValidateTimeZone':
  545. break;
  546. case 'RedBeanModelTypeValidator':
  547. case 'TypeValidator':
  548. if ($validator->type == 'float' || $validator->type == 'integer' || $validator->type == 'string')
  549. {
  550. //A number or string default value could be set in the rules, but we should ignore this and try
  551. //to make the largest sized number possible for this column.
  552. $ignoreDefaultValue = true;
  553. }
  554. break;
  555. case 'CCaptchaValidator':
  556. case 'CCompareValidator':
  557. case 'CDateValidator':
  558. case 'CExistValidator':
  559. case 'CFileValidator':
  560. case 'CFilterValidator':
  561. case 'CNumberValidator':
  562. case 'CRangeValidator':
  563. case 'CUniqueValidator':
  564. default:
  565. // This just means that supported needs to be
  566. // added for a validator that has been used,
  567. // not that it can't or shouldn't be added.
  568. echo get_class($validator) . "\n";
  569. throw new NotSupportedException();
  570. }
  571. if ($validator instanceof CStringValidator)
  572. {
  573. }
  574. }
  575. if (!$memberSet || $ignoreDefaultValue)
  576. {
  577. foreach ($model->getValidators($memberName) as $validator)
  578. {
  579. if ($validator instanceof TypeValidator)
  580. {
  581. switch ($validator->type)
  582. {
  583. case 'integer':
  584. $i = 2147483647;
  585. if ($minValue !== null)
  586. {
  587. $i = max($i, $minValue);
  588. }
  589. if ($maxValue !== null)
  590. {
  591. $i = min($i, $maxValue);
  592. }
  593. $model->$memberName = $i;
  594. break;
  595. case 'float':
  596. $f = 3.14;
  597. if ($minValue !== null)
  598. {
  599. $f = max($f, $minValue);
  600. }
  601. if ($maxValue !== null)
  602. {
  603. $f = min($f, $maxValue);
  604. }
  605. $model->$memberName = $f;
  606. break;
  607. case 'date':
  608. $model->$memberName = '2000-01-01';
  609. break;
  610. case 'time':
  611. $model->$memberName = '12:00';
  612. break;
  613. case 'datetime':
  614. $model->$memberName = '2000-01-01 12:00:00';
  615. break;
  616. case 'array';
  617. throw new NotSupportedException();
  618. case 'string':
  619. default:
  620. // Makes a string like 'Diald' respecting
  621. // the minimum and maximum length rules
  622. // and if that does not validate guesses
  623. // that it should be all lowercase or all
  624. // uppercase. If it still doesn't validate,
  625. // well, we'll see...
  626. $modelClassName = get_class($model);
  627. do
  628. {
  629. $s = self::getRandomString($minLength, $maxLength);
  630. }
  631. while ($unique && $modelClassName::getSubset(null, null, null, "$memberName = '$s'"));
  632. $model->$memberName = $s;
  633. if (!$model->validate(array($memberName)))
  634. {
  635. $model->$memberName = strtolower($model->$memberName);
  636. if (!$model->validate(array($memberName)))
  637. {
  638. $model->$memberName = strtoupper($model->$memberName);
  639. if (!$model->validate(array($memberName)))
  640. {
  641. $model->$memberName = $s;
  642. }
  643. }
  644. }
  645. }
  646. }
  647. }
  648. }
  649. }
  650. protected static function getRandomString($minLength, $maxLength)
  651. {
  652. if ($minLength == null)
  653. {
  654. $minLength = 1;
  655. }
  656. $s = chr(rand(ord('A'), ord('Z')));
  657. $length = min($minLength, $maxLength);
  658. while (strlen($s) < $length)
  659. {
  660. $s .= chr(rand(ord('a'), ord('z')));
  661. }
  662. return $s;
  663. }
  664. }
  665. ?>