/**
  * Provided a modelClassName, members and corresponding rules columns array is generated for schema generation.
  * Members with unique validators are tracked separately to be used with index array generation later.
  * @param string $modelClassName
  * @param array $members
  * @param array $rules
  * @param $messageLogger
  * @return array
  * @throws CException
  */
 public static function resolve($modelClassName, array $members, array $rules, &$messageLogger)
 {
     $messageLogger->addInfoMessage(Zurmo::t('Core', 'Building Column definitions for {{model}}', array('{{model}}' => $modelClassName)));
     $membersWithRules = array();
     $columns = array();
     foreach ($rules as $rule) {
         if (in_array($rule[0], $members)) {
             $membersWithRules[$rule[0]][] = $rule;
         }
     }
     foreach ($membersWithRules as $member => $rules) {
         $column = RedBeanModelMemberRulesToColumnAdapter::resolve($modelClassName, $rules, $messageLogger);
         if ($column) {
             $columns[] = $column;
         } else {
             $errorMessage = Zurmo::t('Core', 'Failed to resolve {{model}}.{{member}} to column', array('{{model}}' => $modelClassName, '{{member}}' => $member));
             $messageLogger->addErrorMessage($errorMessage);
             throw new CException($errorMessage);
         }
     }
     if (count($members) != count($columns)) {
         $errorMessage = Zurmo::t('Core', 'Not all members for {{model}} could be translated to columns.', array('{{model}}' => $modelClassName));
         $messageLogger->addErrorMessage($errorMessage);
         $errorMessage .= Zurmo::t('Core', 'Members') . ': (';
         $errorMessage .= join(', ', $members);
         $errorMessage .= '),' . Zurmo::t('Core', 'Columns') . ' (';
         // Not Coding Standard
         $columnNames = RedBeanModelMemberToColumnUtil::resolveColumnNamesArrayFromColumnSchemaDefinition($columns);
         $columnNames = join(', ', $columnNames);
         $errorMessage .= $columnNames . ')';
         throw new CException($errorMessage);
     }
     $messageLogger->addInfoMessage(Zurmo::t('Core', 'Column definitions Built'));
     return $columns;
 }
 /**
  * Generates Table schema for a model class using its metadata of members, relations, mixins and indexes.
  * @param string $modelClassName
  * @param $messageLogger
  * @return array|bool
  */
 public static function resolve($modelClassName, &$messageLogger)
 {
     if (empty($modelClassName) || !@class_exists($modelClassName) || !$modelClassName::getCanHaveBean()) {
         return false;
     }
     $metadata = $modelClassName::getMetadata();
     $modelMetadata = array();
     if (isset($metadata[$modelClassName])) {
         $modelMetadata = $metadata[$modelClassName];
     }
     $memberColumns = array();
     $relationColumns = array();
     $indexes = array();
     $uniqueIndexesFromValidators = array();
     $parentColumnName = null;
     if (isset($modelMetadata['members'])) {
         if (!isset($modelMetadata['rules'])) {
             $errorMessage = Zurmo::t('Core', '{{model}} must have both, members and rules, set.', array('{{model}}' => $modelClassName));
             $messageLogger->addErrorMessage($errorMessage);
             throw new CException($errorMessage);
         }
         $memberColumns = RedBeanModelMemberRulesToColumnsAdapter::resolve($modelClassName, $modelMetadata['members'], $modelMetadata['rules'], $messageLogger);
         $uniqueIndexesFromValidators = RedBeanModelMemberRulesToColumnAdapter::resolveUniqueIndexesFromValidator($modelClassName);
     }
     if (isset($modelMetadata['relations'])) {
         $relationColumns = RedBeanModelRelationsToColumnsAdapter::resolve($modelClassName, $modelMetadata['relations'], $messageLogger);
     }
     if (isset($modelMetadata['indexes']) || !empty($uniqueIndexesFromValidators)) {
         $indexesMetadata = $uniqueIndexesFromValidators;
         if (!empty($modelMetadata['indexes'])) {
             if (!empty($indexesMetadata)) {
                 $indexesMetadata = CMap::mergeArray($indexesMetadata, $modelMetadata['indexes']);
             } else {
                 $indexesMetadata = $modelMetadata['indexes'];
             }
         }
         if (!empty($indexesMetadata)) {
             $indexes = RedBeanModelMemberIndexesMetadataAdapter::resolve($modelClassName, $indexesMetadata, $messageLogger);
         }
     }
     $parentColumnName = RedBeanModelChildParentRelationshipToColumnAdapter::resolve($modelClassName);
     if ($parentColumnName) {
         $memberColumns[] = $parentColumnName;
     }
     $mixinColumns = RedBeanModelMixinsToColumnsAdapter::resolve($modelClassName, $messageLogger);
     $columns = CMap::mergeArray($memberColumns, $mixinColumns, $relationColumns);
     $tableName = $modelClassName::getTableName();
     $schemaDefinition = array($tableName => array('columns' => $columns, 'indexes' => $indexes));
     return $schemaDefinition;
 }
 /**
  * Adds externalSystemId column to specific table if it does not exist
  * @param $tableName
  * @param $maxLength
  * @param $columnName
  */
 public static function addExternalIdColumnIfMissing($tableName, $maxLength = 255, $columnName = null)
 {
     if (!isset($columnName)) {
         $columnName = static::EXTERNAL_SYSTEM_ID_COLUMN_NAME;
     }
     // check if external_system_id exists in fields
     $columnExists = ZurmoRedBean::$writer->doesColumnExist($tableName, $columnName);
     if (!$columnExists) {
         // if not, update model and add an external_system_id field
         $type = 'string';
         $length = null;
         RedBeanModelMemberRulesToColumnAdapter::resolveStringTypeAndLengthByMaxLength($type, $length, $maxLength);
         $columns = array();
         $columns[] = RedBeanModelMemberToColumnUtil::resolveColumnMetadataByHintType($columnName, $type, $length);
         $schema = CreateOrUpdateExistingTableFromSchemaDefinitionArrayUtil::getTableSchema($tableName, $columns);
         CreateOrUpdateExistingTableFromSchemaDefinitionArrayUtil::generateOrUpdateTableBySchemaDefinition($schema, new MessageLogger());
     }
 }
 protected static function getReservedColumnMetadata()
 {
     $columns = array();
     $reservedColumnsTypes = array('status' => 'integer', 'serializedMessages' => 'string', 'analysisStatus' => 'integer', 'serializedAnalysisMessages' => 'string');
     foreach ($reservedColumnsTypes as $columnName => $type) {
         $length = null;
         $unsigned = null;
         if ($type === 'string') {
             // populate the proper type given it would be 1024 char string depending on db type.
             RedBeanModelMemberRulesToColumnAdapter::resolveStringTypeAndLengthByMaxLength($type, $length, 1024);
         } else {
             // forcing integers to be unsigned
             $unsigned = DatabaseCompatibilityUtil::resolveUnsignedByHintType($type, false);
         }
         // last argument is false because we do not want these column names to be resolved to lower characters
         $columns[] = RedBeanModelMemberToColumnUtil::resolveColumnMetadataByHintType($columnName, $type, $length, $unsigned, null, null, null, false);
     }
     return $columns;
 }
 /**
  * @depends testResolveUniqueIndexesFromValidator
  */
 public function testResolveWithUniqueValidator()
 {
     $unsigned = null;
     $assumedSigned = RedBeanModelMemberRulesToColumnAdapter::ASSUME_SIGNED;
     if (!$assumedSigned) {
         $unsigned = 'UNSIGNED';
     }
     $modelClassName = 'AuditEvent';
     $rules = array(array('attributeName', 'unique'), array('attributeName', 'type', 'type' => 'integer'));
     $column = RedBeanModelMemberRulesToColumnAdapter::resolve($modelClassName, $rules, static::$messageLogger);
     $this->assertNotEmpty($column);
     $this->assertArrayHasKey('name', $column);
     $this->assertArrayHasKey('type', $column);
     $this->assertArrayHasKey('unsigned', $column);
     $this->assertArrayHasKey('notNull', $column);
     $this->assertArrayHasKey('collation', $column);
     $this->assertArrayHasKey('default', $column);
     $this->assertEquals('attributename', $column['name']);
     $this->assertEquals('INT(11)', $column['type']);
     $this->assertEquals($unsigned, $column['unsigned']);
     $this->assertEquals('NULL', $column['notNull']);
     // Not Coding Standard
     $this->assertNull($column['collation']);
     $this->assertEquals('DEFAULT NULL', $column['default']);
     // Not Coding Standard
     $uniqueIndex = RedBeanModelMemberRulesToColumnAdapter::resolveUniqueIndexesFromValidator($modelClassName);
     $this->assertNotEmpty($uniqueIndex);
     $this->assertCount(1, $uniqueIndex);
     $indexName = key($uniqueIndex);
     $this->assertCount(2, $uniqueIndex[$indexName]);
     $this->assertArrayHasKey('members', $uniqueIndex[$indexName]);
     $this->assertArrayHasKey('unique', $uniqueIndex[$indexName]);
     $this->assertCount(1, $uniqueIndex[$indexName]['members']);
     $this->assertEquals('attributeName', $uniqueIndex[$indexName]['members'][0]);
     $this->assertTrue($uniqueIndex[$indexName]['unique']);
 }