public function apply(ProjectState $state, SchemaEditor $schemaEditor)
    {
        /** @var $operation Operation */
        foreach ($this->operations as $operation) {
            if (!$operation->isReducibleToSql()) {
                $schemaEditor->addSql(
                    '-- MIGRATION NOW PERFORMS'.
                    ' OPERATION THAT CANNOT BE WRITTEN AS SQL:'
                );
            }
            $schemaEditor->addSql(
                sprintf(
                    '<fg=yellow>-- %s</>',
                    $operation->getDescription()
                )
            );

            if ($operation instanceof AddField && $operation->field instanceof ManyToManyField
                && !$operation->field->auto && $operation->field->relation->through) {
                $through = $operation->field->relation->through;
                if (!is_string($through)) {
                    $through = sprintf("%s\%s", $operation->field->relation->through->getMeta()->getNSModelName(),
                        $operation->field->relation->through->getMeta()->getModelName());
                }
                $schemaEditor->addSql(sprintf(' -- an M2M field which use `%s` as join model', $through));
            }

            if (!$operation->isReducibleToSql()) {
                continue;
            }

            // preserve state before operation
            $oldState = $state->deepClone();

            $operation->setAppLabel($this->getAppLabel());
            $operation->updateState($state);

            $schemaEditor->connection->beginTransaction();

            try {
                $operation->databaseForwards($schemaEditor, $oldState, $state);
                $schemaEditor->connection->commit();
            } catch (\Exception $e) {
                $schemaEditor->connection->rollBack();
                throw new CommandError($e->getMessage());
            }
        }

        return $state;
    }