Esempio n. 1
0
 /**
  * Add an configution for a model.
  *
  * @param ModelConfig $config
  */
 protected function registerModel($config)
 {
     if (isset($this->configs[$config->name])) {
         warning('Overwriting model: "' . $config->name . '"');
         // @todo? Allow overwritting models? or throw Exception?
     }
     $this->collectionMappings[$config->name] = array_flip($config->properties);
     // Add properties to the collectionMapping
     if ($config->class === null) {
         // Detect class
         $config->class = false;
         // fallback to a generated class
         foreach ($this->namespaces as $namespace) {
             $class = $namespace . $config->name;
             if (class_exists($class)) {
                 // Is the class known?
                 $config->class = $class;
                 break;
             }
         }
     }
     if ($config->class === false) {
         // Should registerBackand generate a class?
         if (empty(self::$instances['default']) || self::$instances['default'] !== $this) {
             $config->class = '\\Generated\\' . $this->ref() . '\\' . $config->name;
             // Multiple Repositories have multiple namespaces
         } else {
             $config->class = '\\Generated\\' . $config->name;
         }
     }
     if (substr($config->class, 0, 1) !== '\\') {
         $config->class = '\\' . $config->class;
     }
     if ($config->plural === null) {
         $config->plural = Inflector::pluralize($config->name);
     }
     $this->configs[$config->name] = $config;
     $this->created[$config->name] = [];
     if (isset($this->plurals[$config->plural])) {
         warning('Overwriting plural[' . $config->plural . '] "' . $this->plurals[$config->plural] . '" with "' . $config->name . '"');
     }
     $this->plurals[$config->plural] = $config->name;
 }
 /**
  * Create model configs based on the tables in the database schema.
  *
  * @param string $dbLink
  */
 public function inspectDatabase($dbLink = 'default', $tablePrefix = '')
 {
     // Pass 1: Retrieve and parse schema information
     $db = Connection::instance($dbLink);
     if (self::$cacheTimeout === false) {
         $schema = $this->getSchema($db, $tablePrefix);
     } else {
         $cacheIdentifier = $dbLink . ' ' . $tablePrefix . ' ';
         if (count($db->logger->entries) > 0) {
             $cacheIdentifier .= $db->logger->entries[0][0];
             // Add the connect statement to the identifier.
         }
         $backend = $this;
         $schema = \Sledgehammer\cache('DatabaseRepositoryBackend[' . md5($cacheIdentifier) . ']', self::$cacheTimeout, function () use($backend, $db, $tablePrefix) {
             return $backend->getSchema($db, $tablePrefix);
         });
     }
     $models = [];
     $junctions = [];
     foreach ($schema as $tableName => $table) {
         $name = Inflector::modelize($tableName, ['prefix' => $tablePrefix, 'singularizeLast' => true]);
         $plural = Inflector::modelize($tableName, ['prefix' => $tablePrefix, 'singularizeLast' => false]);
         $config = new ModelConfig($name, ['plural' => $plural, 'backendConfig' => $table]);
         $config->backendConfig['dbLink'] = $dbLink;
         $config->backendConfig['collection'] = ['columns' => []];
         // database collection config.
         $config->id = $table['primaryKeys'];
         foreach ($table['columns'] as $column => $info) {
             $default = @$info['default'];
             if (empty($info['foreignKeys'])) {
                 $property = Inflector::variablize($column);
                 $config->properties[$column] = $property;
                 $config->defaults[$property] = $default;
                 if (substr($info['type'], -10) === 'tinyint(1)' && $info['null'] === false) {
                     $config->readFilters[$column] = __CLASS__ . '::valueToBool';
                     $config->writeFilters[$column] = __CLASS__ . '::boolToValue';
                     $config->defaults[$property] = self::valueToBool($config->defaults[$column]);
                 }
             } else {
                 if (count($info['foreignKeys']) > 1) {
                     notice('Multiple foreign-keys per column not supported');
                 }
                 $foreignKey = $info['foreignKeys'][0];
                 $property = $this->stripIdSuffix($column);
                 if ($column !== $property && array_key_exists($property, $table['columns'])) {
                     $property = $column;
                     // restore prefix, to prevent a naming collision.
                 }
                 if (array_key_exists($property, $table['columns']) && $property != $column) {
                     notice('Unable to use belongsTo["' . $property . '"], an column with the same name exists', ['belongsTo' => $info, 'Exising column' => $table['columns'][$property]]);
                 }
                 $foreignModel = Inflector::modelize($foreignKey['table'], ['prefix' => $tablePrefix, 'singularizeLast' => true]);
                 $config->belongsTo[$property] = ['reference' => $column, 'model' => $foreignModel, 'id' => $foreignKey['column'], 'default' => $default];
             }
         }
         // Detect junction table
         if (count($config->id) === 2) {
             $primaryKeyAreForeignKeys = true;
             foreach ($config->belongsTo as $belongsTo) {
                 if (in_array($belongsTo['reference'], $config->id) === false) {
                     $primaryKeyAreForeignKeys = false;
                     break;
                 }
             }
             if ($primaryKeyAreForeignKeys) {
                 $junctions[$tableName] = $config;
                 $this->junctions[$config->name] = $config;
                 continue;
             }
         }
         $models[$tableName] = $config;
         if (isset($this->configs[$config->name])) {
             notice('Model "' . $config->name . '" (for the "' . $tableName . '" table) is defined in both "' . $this->configs[$config->name]->backendConfig['dbLink'] . '" and "' . $dbLink . '" ');
             $suffix = 2;
             while (isset($this->configs[$config->name . '_' . $suffix])) {
                 ++$suffix;
             }
             $this->configs[$config->name . '_' . $suffix] = $config;
         } else {
             $this->configs[$config->name] = $config;
         }
     }
     // Pass 2: hasMany
     foreach ($this->configs as $config) {
         $table = $schema[$config->backendConfig['table']];
         foreach ($table['referencedBy'] as $i => $reference) {
             $property = $reference['table'];
             if ($tablePrefix != '' && substr($property, 0, strlen($tablePrefix)) == $tablePrefix) {
                 $property = substr($property, strlen($tablePrefix));
                 // Strip prefix
             }
             foreach ($table['referencedBy'] as $j => $otherRef) {
                 if ($i != $j && $otherRef['table'] === $reference['table']) {
                     // The model has relations to the same table.
                     $property = $this->stripIdSuffix($reference['column']) . '_' . $property;
                 }
             }
             $property = Inflector::variablize($property);
             if (in_array($property, $config->getPropertyNames())) {
                 notice('Unable to use ' . $config->name . '->hasMany[' . $property . '] a property with the same name exists');
                 break;
             }
             if (isset($models[$reference['table']])) {
                 // One-to-many relation
                 $belongsToModel = $models[$reference['table']];
                 foreach ($belongsToModel->belongsTo as $belongsToProperty => $belongsTo) {
                     if ($belongsTo['model'] == $config->name && $belongsTo['reference'] == $reference['column']) {
                         $config->hasMany[$property] = ['model' => $belongsToModel->name, 'reference' => $reference['column'], 'belongsTo' => $belongsToProperty];
                         $config->defaults[$property] = [];
                         break;
                     }
                 }
             } elseif (empty($junctions[$reference['table']])) {
                 notice('Missing a model or relation for "' . $reference['table'] . '" in the database schema');
             }
         }
     }
     // Pass 3: hasMany junctions
     foreach ($this->configs as $config) {
         $table = $schema[$config->backendConfig['table']];
         foreach ($table['referencedBy'] as $reference) {
             if (isset($junctions[$reference['table']])) {
                 // Many-to-many realtion
                 $junction = $junctions[$reference['table']];
                 $hasMany = ['through' => $junction->name, 'fields' => $junction->properties];
                 foreach ($junction->belongsTo as $belongsToProperty => $belongsTo) {
                     if ($belongsTo['model'] === $config->name) {
                         $hasMany['reference'] = $belongsTo['reference'];
                     } else {
                         $property = Inflector::pluralize($belongsToProperty);
                         $hasMany['model'] = $belongsTo['model'];
                         $hasMany['id'] = $belongsTo['reference'];
                     }
                 }
                 if (in_array($property, $config->getPropertyNames())) {
                     $property = Inflector::variablize($reference['table']);
                 }
                 if (in_array($property, $config->getPropertyNames())) {
                     notice('Unable to use ' . $property . '->hasMany[' . $property . '] a property with the same name exists');
                     break;
                 }
                 $config->hasMany[$property] = $hasMany;
                 $config->defaults[$property] = [];
             }
         }
     }
 }