Automatically migrate given entity definitions.
@param \Asgard\Entity\Definition[]|\Asgard\Entity\Definition $definitions
482072
public function autoMigrate($definitions) {
		if(!is_array($definitions))
			$definitions = [$definitions];
		$entitiesSchema = $this->getEntitiesSchemas($definitions);
		$sqlSchema = $this->getSQLSchemas();
		$this->processSchemas($entitiesSchema, $sqlSchema);
	}
669537
public function merge($definition)
    {
        $constraints = $this->explodeConstraints($definition);

        foreach ($constraints as $index=>$definition) {

            list($name, $parameters) = is_array($definition) ?
                                       [$index, $definition] :
                                       $this->nameAndParameters($definition);

            $constraint = $this->newConstraint(
                Type::snake_case($name),
                $parameters,
                ''
            );
            $this->constraints[$constraint->name()] = $constraint;

        }
        return $this;
    }
184780
public function setData($definitions)
    {
        if (is_array($definitions)) {
            $this->parsedData = $definitions;
            return;
        }

        if (!is_file($definitions)) {
            throw new InvalidArgumentException(
                "The form definition file '{$definitions}' does not exists."
            );
        }

        try {
            $this->parsedData = Yaml::parse(file_get_contents($definitions));
        } catch (\Exception $caught) {
            throw new ParserErrorException(
                'Error parsing form definitions: '.$caught->getMessage(),
                0,
                $caught
            );
        }
    }
855413
public function parseMigrationDefinition(MigrationDefinition $definition)
    {
        $dbType = $this->getDBFromFile($definition->name);

        if (!in_array($dbType, $this->supportedDatabases))
        {
            return new MigrationDefinition(
                $definition->name,
                $definition->path,
                $definition->rawDefinition,
                MigrationDefinition::STATUS_INVALID,
                array(),
                "Unsupported or missing database: '$dbType'. The database name is the part of the filename after the 1st underscore"
            );
        }

        return new MigrationDefinition(
            $definition->name,
            $definition->path,
            $definition->rawDefinition,
            MigrationDefinition::STATUS_PARSED,
            array(
                new MigrationStep('sql', array($dbType => $definition->rawDefinition), array('path' => $definition->path))
            )
        );
    }
38773
private function filterOutAbstractDefinitions(array $definitions): array
    {
        foreach ($definitions as $key => $definition) {
            if ($definition->isAbstract()) {
                unset($definitions[$key]);
            }
        }

        return $definitions;
    }
1256418
public function generateMigration($definitions, $migrationName) {
		if(!is_array($definitions))
			$definitions = [$definitions];
		$entitiesSchema = $this->getEntitiesSchemas($definitions);
		$sqlSchema = $this->getSQLSchemas();
		$up = $this->buildMigration($entitiesSchema, $sqlSchema, false);
		$down = $this->buildMigration($sqlSchema, $entitiesSchema, true);

		if($up == '' && $down == '')
			return;

		return $this->MigrationManager->create($up, $down, $migrationName, '\Asgard\Migration\DBMigration');
	}
991824
protected function getEntitiesSchemas(array $definitions) {
		$dataMapper = $this->dataMapper;
		$schema = new \Doctrine\DBAL\Schema\Schema;
		foreach($definitions as $definition) {
			$table = $schema->createTable($dataMapper->getTable($definition));

			foreach($definition->properties() as $name=>$prop) {
				$col = [];
				if(method_exists($prop, 'getORMParameters'))
					$col = $prop->getORMParameters();
				if($ormCol = $prop->get('orm'))
					$col = array_merge($col, $ormCol);
				if(!isset($col['notnull']))
					$col['notnull'] = false;

				#relations
				if($prop->get('type') == 'entity') {
					$relation = $dataMapper->relation($definition, $name);
					#relations with one entity
					if(!$relation->get('many')) {
						$table->addColumn($relation->getLink(), 'integer', [
							'length'         => 11,
							'notnull'        => $col['notnull'],
							'autoincrement'  => false,
						]);
						if($relation->isPolymorphic()) {
							$table->addColumn($relation->getLinkType(), 'string', [
								'length'         => 50,
								'notnull'        => $col['notnull'],
								'autoincrement'  => false,
							]);
						}
						else {
							$foreign_table = $dataMapper->getTable($relation->getTargetDefinition());
							$column = $relation->getLink();
							$options = [];
							if($relation->get('onUpdate'))
								$options['onUpdate'] = $relation->get('onUpdate');
							if($relation->get('onDelete'))
								$options['onDelete'] = $relation->get('onDelete');
							$table->addForeignKeyConstraint($foreign_table, [$column], ['id'], $options);
						}
					}
					#HMABT relations
					elseif($relation->type() == 'HMABT' && !$relation->isPolymorphic()) {
						$table_name = $relation->getAssociationTable();
						#if table was not already created by the opposite entity
						if(!$schema->hasTable($table_name)) {
							$relTable = $schema->createTable($table_name);
							$relTable->addColumn($relation->getLinkB(), 'integer', [
								'length'         => 11,
								'notnull'        => $col['notnull'],
								'autoincrement'  => false,
							]);
							$relTable->addColumn($relation->getLinkA(), 'integer', [
								'length'         => 11,
								'notnull'        => $col['notnull'],
								'autoincrement'  => false,
							]);

							$options = [
								'onDelete' => 'CASCADE',
							];
							$foreign_table = $dataMapper->getTable($relation->getTargetDefinition());
							$relTable->addForeignKeyConstraint($table, [$relation->getLinkA()], ['id'], $options);
							$relTable->addForeignKeyConstraint($foreign_table, [$relation->getLinkB()], ['id'], $options);
						}
						if($relation->reverse()->isPolymorphic()) {
							$schema->getTable($table_name)->addColumn($relation->reverse()->getLinkType(), 'string', [
								'length'         => 50,
								'notnull'        => $col['notnull'],
								'autoincrement'  => false,
							]);
						}
						#sortable
						if($relation->get('sortable')) {
							$schema->getTable($table_name)->addColumn($relation->getPositionField(), 'integer', [
								'length'         => 11,
								'notnull'        => $col['notnull'],
								'autoincrement'  => false,
							]);
						}
					}
					continue;
				}

				if($prop->get('many')) {
					$type = 'blob';
					$col['length'] = 65535;
				}
				elseif(!isset($col['type']))
					throw new \Exception('Cannot convert '.get_class($prop).' type to SQL type.');
				else
					$type = $col['type'];
				unset($col['type']);

				if(!isset($col['autoincrement']))
					$col['autoincrement'] = false;

				if($prop->get('i18n')) {
					$table_name = $dataMapper->getTranslationTable($definition);
					if(!$schema->hasTable($table_name)) {
						$i18nTable = $schema->createTable($table_name);
						$i18nTable->addColumn('id', 'integer', [
							'length'         => 11,
							'notnull'        => true,
							'autoincrement'  => false,
						]);
						$i18nTable->addColumn('locale', 'string', [
							'length'         => 50,
							'notnull'        => true,
							'autoincrement'  => false,
						]);
					}
					$schema->getTable($table_name)->addColumn($name, $type, $col);
				}
				else {
					if($name === 'id') {
						$col['autoincrement'] = true;
						$col['notnull'] = true;
					}
					$table->addColumn($name, $type, $col);
				}
			}

			if(isset($definition->get('orm')['indexes'])) {
				foreach($definition->get('orm')['indexes'] as $index) {
					$index['type'] = strtoupper($index['type']);

					foreach($index['columns'] as $col) {
						if($index['type'] !== 'FULLTEXT' && in_array($table->getColumn($col)->getType()->getName(), ['text', 'blob']))
							throw new \Exception('Table '.$table->getName().' cannot have indexes with text/blog columns.');
					}

					if($index['type'] == 'PRIMARY')
						$table->setPrimaryKey($index['columns'], 'PRIMARY');
					else {
						$indexName = $_indexName = implode('_', $index['columns']);
						$i = 1;
						while($table->hasIndex($indexName))
							$indexName = $_indexName.$i++;
						if($index['type'] == 'UNIQUE')
							$table->addUniqueIndex($index['columns'], $indexName);
						elseif($index['type'] == 'INDEX')
							$table->addIndex($index['columns'], $indexName);
						elseif($index['type'] == 'FULLTEXT') {
							$table->addIndex($index['columns'], $indexName);
							$table->getIndex($indexName)->addFlag('fulltext');
						}
					}
				}
			}
			
			$table->setPrimaryKey(['id']);
		}

		if($cb = $this->entitiesSchemaCallback)
			$cb($this, $schema);

		return $schema;
	}
1204958
private void migrateTenantDefinitions() {
        DBService dbservice = DBService.instance();
        List<String> keyspaces = null;
        if (dbservice instanceof ThriftService) {
            keyspaces = ((ThriftService)dbservice).getDoradusKeyspaces();
        } else if (dbservice instanceof CQLService) {
            keyspaces = ((CQLService)dbservice).getDoradusKeyspaces();
        } else {
            return;
        }
        for (String keyspace : keyspaces) {
            migrateTenantDefinition(keyspace);
        }
    }
73738
public function load(array $definitions)
    {
        $this->data = $definitions['data'] ?? [];
        $this->parentAliases = $definitions['parent_aliases'] ?? [];
        foreach ($this->data as $value) {
            $this->validateDefinitions($value);
        }
    }
687517
public function getAssociatedTags(EntityDefinition $definition, Entity $entity, Context $context): array
    {
        $associations = $definition->getFields()->filterInstance(AssociationField::class);

        $keys = [$this->getEntityTag($entity->getUniqueIdentifier(), $definition)];

        foreach ($context->getLanguageIdChain() as $languageId) {
            $keys[] = $this->getEntityTag($languageId, $this->languageDefinition);
        }

        $translationDefinition = $definition->getTranslationDefinition();

        if ($translationDefinition) {
            /* @var EntityDefinition $translationDefinition */
            $keys[] = $translationDefinition->getEntityName() . '.language_id';
        }

        /** @var AssociationField[] $associations */
        foreach ($associations as $association) {
            if ($association->is(Extension::class)) {
                $value = $entity->getExtension($association->getPropertyName());
            } else {
                try {
                    $value = $entity->get($association->getPropertyName());
                } catch (\Exception $e) {
                    continue;
                }
            }

            if (!$value) {
                continue;
            }

            if ($association instanceof ManyToOneAssociationField || $association instanceof OneToOneAssociationField) {
                /* @var Entity $value */
                $nested = $this->getAssociatedTags($association->getReferenceDefinition(), $value, $context);
                foreach ($nested as $key) {
                    $keys[] = $key;
                }

                continue;
            }

            if ($association instanceof OneToManyAssociationField) {
                /** @var Entity[] $value */
                foreach ($value as $item) {
                    $nested = $this->getAssociatedTags($association->getReferenceDefinition(), $item, $context);
                    foreach ($nested as $key) {
                        $keys[] = $key;
                    }
                }

                continue;
            }

            if ($association instanceof ManyToManyAssociationField) {
                /** @var Entity[] $value */
                foreach ($value as $item) {
                    $nested = $this->getAssociatedTags($association->getToManyReferenceDefinition(), $item, $context);
                    foreach ($nested as $key) {
                        $keys[] = $key;
                    }
                }
            }
        }

        return array_keys(array_flip($keys));
    }
1369949
public function updateDefinition(WorkflowDefinition $definition)
    {
        $existingActions = array();

        $existing = $definition->Actions()->column('Title');
        $structure = array_keys($this->structure);

        $removeNames = array_diff($existing, $structure);

        foreach ($definition->Actions() as $action) {
            if (in_array($action->Title, $removeNames)) {
                $action->delete();
                continue;
            }
            $existingActions[$action->Title] = $action;
        }

        $actions = array();
        $transitions = new ArrayObject;
        $sort = 1;
        // now, go through the structure and create/realign things
        foreach ($this->structure as $relationName => $relationTemplate) {
            $isAction = isset($relationTemplate['type']);
            $isUsers = ($relationName == 'users');
            $isGroups = ($relationName == 'groups');

            if ($isAction) {
                $action = null;
                if (isset($existingActions[$relationName])) {
                    $action = $existingActions[$relationName];
                } else {
                    $action = $this->createAction($relationName, $relationTemplate, $definition, $transitions);
                }

                // add a sort value in!
                $action->Sort = $sort++;
                $action->write();

                $actions[$relationName] = $action;

                $newTransitions = $this->updateActionTransitions($relationTemplate, $action);
                foreach ($newTransitions as $t) {
                    $transitions->append($t);
                }
            }
            // Process users on WorkflowDefinition from the template
            if ($isUsers) {
                $this->createUsers($relationTemplate, $definition, true);
            }
            // Process groups on WorkflowDefinition from the template
            if ($isGroups) {
                $this->createGroups($relationTemplate, $definition, true);
            }
        }

        foreach ($transitions as $transition) {
            if (isset($actions[$transition->Target])) {
                $transition->NextActionID = $actions[$transition->Target]->ID;
            }
            $transition->write();
        }

        // Set the version and do the write at the end so that we don't trigger an infinite loop!!
        $definition->Description = $this->getDescription();
        $definition->TemplateVersion = $this->getVersion();
        $definition->RemindDays = $this->getRemindDays();
        $definition->Sort = $this->getSort();
        $definition->write();
    }
1234167
public function addMigration(MigrationDefinition $migrationDefinition)
    {
        $this->createTableIfNeeded();

        $conn = $this->getConnection();

        $migration = new APIMigration(
            $migrationDefinition->name,
            md5($migrationDefinition->rawDefinition),
            $migrationDefinition->path,
            null,
            APIMigration::STATUS_TODO
        );
        try {
            $conn->insert($this->tableName, $this->migrationToArray($migration));
        } catch (UniqueConstraintViolationException $e) {
            throw new \Exception("Migration '{$migrationDefinition->name}' already exists");
        }

        return $migration;
    }
5896
public function previewMigration($definitions, $migrationName) {
		if(!is_array($definitions))
			$definitions = [$definitions];
		$entitiesSchema = $this->getEntitiesSchemas($definitions);
		$sqlSchema = $this->getSQLSchemas();
		$up = $this->buildMigration($entitiesSchema, $sqlSchema, false);
		$down = $this->buildMigration($sqlSchema, $entitiesSchema, true);

		if($up == '' && $down == '')
			return;

		return $this->MigrationManager->createCode($up, $down, $migrationName, '\Asgard\Migration\DBMigration');
	}
491762
public function configure(...$definitions): ContainerConfiguration
    {
        $builder = new ContainerBuilder(ContainerConfiguration::class);

        $builder->useAutowiring(false)->useAnnotations(false)->ignorePhpDocErrors(false);

        $wiring = $this->config[WIRING] ?? null;
        if (fn\hasValue($wiring, [WIRING\REFLECTION, WIRING\AUTO])) {
            $builder->useAutowiring(true);
        } else if ($wiring === WIRING\STRICT) {
            $builder->useAnnotations(true)->ignorePhpDocErrors(false);
        } else if ($wiring === WIRING\TOLERANT) {
            $builder->useAnnotations(true)->ignorePhpDocErrors(true);
        }

        empty($this->config[CACHE]) || $builder->enableDefinitionCache();
        empty($this->config[PROXY]) || $builder->writeProxiesToFile(true, $this->config[PROXY]);
        empty($this->config[COMPILE]) || $builder->enableCompilation($this->config[COMPILE]);
        $this->container && $builder->wrapContainer($this->container);

        foreach ($definitions as $definition) {
            $builder->addDefinitions($definition);
        }

        /** @noinspection PhpUnhandledExceptionInspection */
        /** @noinspection PhpIncompatibleReturnTypeInspection */
        $built = $builder->build();
        return $built instanceof ContainerConfiguration
            ? $built
            : new ContainerConfiguration($built);
    }
328667
private static function define($domainId, $definition) {
		$definition = ConfigHelper::normalizeItemConfig($domainId, $definition);
		if(!\App::$domain->has($domainId)) {
			\App::$domain->set($domainId, $definition);
		}
	}
431898
public function setDefinition($id, Definition $definition)
    {
        if ($this->isCompiled()) {
            throw new BadMethodCallException('Adding definition to a compiled container is not allowed');
        }

        $id = $this->normalizeId($id);

        unset($this->aliasDefinitions[$id], $this->removedIds[$id]);

        return $this->definitions[$id] = $definition;
    }
73458
protected function setDefinition($id, Definition $definition)
    {
        $this->container->removeBindings($id);

        if ($this->isLoadingInstanceof) {
            if (!$definition instanceof ChildDefinition) {
                throw new InvalidArgumentException(sprintf('Invalid type definition "%s": ChildDefinition expected, "%s" given.', $id, \get_class($definition)));
            }
            $this->instanceof[$id] = $definition;
        } else {
            $this->container->setDefinition($id, $definition instanceof ChildDefinition ? $definition : $definition->setInstanceofConditionals($this->instanceof));
        }
    }