/** * migrate * * @param Hook\Model\Collection $model * @param array $collection_config * * @return bool */ public function migrate($model, $collection_config, $is_dynamic = false) { $that = $this; $result = false; $connection = $model->getConnectionResolver()->connection(); // Ignore NoSQL databases. if (!$connection->getPdo()) { return; } // Get modified Schema\Grammar for hook features. $connection->setSchemaGrammar($this->getSchemaGrammar($connection)); // Set custom blueprint resolver $builder = $connection->getSchemaBuilder(); $builder->blueprintResolver(function ($table, $callback) { return new \Hook\Database\Schema\Blueprint($table, $callback); }); $table = $model->getTable(); $table_schema = Cache::get($table); $table_prefix = Context::getPrefix(); $collection_config = $this->sanitizeConfigs($table, $collection_config, $is_dynamic); $is_creating = !$builder->hasTable($table); if (!empty($collection_config['attributes']) || !empty($collection_config['relationships'])) { $migrate = function ($t) use($that, &$table, &$table_prefix, &$builder, &$is_creating, &$table_schema, $collection_config, &$result) { $table_columns = array('created_at', 'updated_at', 'deleted_at'); if ($is_creating) { $that->createCollection($t); } else { $table_columns = array_merge($table_columns, $builder->getColumnListing($table)); } foreach ($collection_config['attributes'] as $attribute) { if (!isset($attribute['name'])) { throw new MethodFailureException('invalid_schema'); } $field_name = strtolower(array_remove($attribute, 'name')); $type = camel_case(array_remove($attribute, 'type') ?: 'string'); $default = array_remove($attribute, 'default'); $index = array_remove($attribute, 'index'); $unique = array_remove($attribute, 'unique') || $index == 'unique'; $required = array_remove($attribute, 'required'); // Skip if column already exists // TODO: deprecate strtolower if (in_array($field_name, array_map('strtolower', $table_columns))) { continue; } // include field_name to list of collection columns array_push($table_columns, $field_name); if (count($attribute) > 0) { // the remaining attributes on field definition are // the data-type related collection_config, such as 'length', // 'allowed', 'total', 'places', etc. $column = $t->newColumn($type, $field_name, $attribute); } else { $column = $t->{$type}($field_name); } // apply default value if ($default !== NULL) { $required = true; $column->default($default); } // spatial indexes are NOT NULL by default $nullable = !$required && $type !== 'point'; // columns are nullable unless specified as 'required' if ($nullable) { $column->nullable(); } if ($index == 'spatial') { // apply geospatial index, only MyISAM $t->spatialIndex($field_name); } else { if ($index && !$unique) { // apply index if specified $column->index(); } } if ($unique) { // apply unique index if specified $unique_fields = !is_array($unique) ? $field_name : array_unique(array_merge(array($field_name), $unique)); $t->unique($unique_fields); } } // onDelete / onUpdate actions $actions = array('restrict' => "RESTRICT", 'cascade' => "CASCADE", 'none' => "NO ACTION", 'null' => "SET NULL", 'default' => "SET DEFAULT"); if (!isset($collection_config['relationships'])) { $collection_config['relationships'] = array(); } foreach ($collection_config['relationships'] as $relation => $fields) { // only create field on belongs_to relationships if ($relation == "belongs_to") { foreach ($fields as $field => $config) { // create 'foreign_key' column on collection. if (!in_array($config['foreign_key'], array_map('strtolower', $table_columns))) { $column = $t->unsignedInteger($config['foreign_key']); $column->nullable(); } // create collection if it doesn't exists if (!$builder->hasTable($config['collection'])) { $builder->create($table_prefix . $config['collection'], function ($t) use($that) { $that->createCollection($t); }); } // // // // create foreign key on database // // // // TODO: list foreign keys already defined before // // trying to create it. // // // $t->foreign($config['foreign_key']) // ->references($config['primary_key']) // ->on($table_prefix . $config['collection']) // ->onDelete($actions[$config['on_delete']]) // ->onUpdate($actions[$config['on_update']]); } } } // return true when any modification is present if (count($t->getColumns()) > 0 || count($t->getCommands()) > 0) { $result = true; } }; if ($is_creating) { // CREATE TABLE statement $builder->create($table_prefix . $table, $migrate); } else { // ALTER TABLE statement. $builder->table($table_prefix . $table, $migrate); } } // merge previous schema with new one. $table_schema = $this->mergeSchema($table_schema, $collection_config, $is_dynamic); // Cache table schema for further reference Cache::forever($table, $table_schema); $app_collections = Cache::get('app_collections'); Cache::forever('app_collections', array_unique(array_merge($app_collections, array($table)))); return $result; }