├── .github ├── .kodiak.toml └── workflows │ ├── codesniffer.yml │ ├── coverage.yml │ ├── phpstan.yml │ └── tests.yml ├── LICENSE ├── composer.json └── src ├── DI ├── Helpers │ └── SmartStatement.php └── MigrationsExtension.php ├── DependencyFactoryCreator.php ├── Exceptions ├── LogicalException.php └── RuntimeException.php └── Migration ├── ContainerAwareInterface.php └── MigrationFactoryDecorator.php /.github/.kodiak.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [merge] 4 | automerge_label = "automerge" 5 | blacklist_title_regex = "^WIP.*" 6 | blacklist_labels = ["WIP"] 7 | method = "rebase" 8 | delete_branch_on_merge = true 9 | notify_on_conflict = true 10 | optimistic_updates = false 11 | -------------------------------------------------------------------------------- /.github/workflows/codesniffer.yml: -------------------------------------------------------------------------------- 1 | name: "Codesniffer" 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | push: 8 | branches: ["*"] 9 | 10 | schedule: 11 | - cron: "0 8 * * 1" 12 | 13 | jobs: 14 | codesniffer: 15 | name: "Codesniffer" 16 | uses: contributte/.github/.github/workflows/codesniffer.yml@master 17 | with: 18 | php: "8.3" 19 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: "Coverage" 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | push: 8 | branches: ["*"] 9 | 10 | schedule: 11 | - cron: "0 8 * * 1" 12 | 13 | jobs: 14 | coverage: 15 | name: "Nette Tester" 16 | uses: contributte/.github/.github/workflows/nette-tester-coverage-v2.yml@master 17 | with: 18 | php: "8.3" 19 | -------------------------------------------------------------------------------- /.github/workflows/phpstan.yml: -------------------------------------------------------------------------------- 1 | name: "Phpstan" 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | push: 8 | branches: ["*"] 9 | 10 | schedule: 11 | - cron: "0 8 * * 1" 12 | 13 | jobs: 14 | phpstan: 15 | name: "Phpstan" 16 | uses: contributte/.github/.github/workflows/phpstan.yml@master 17 | with: 18 | php: "8.3" 19 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: "Nette Tester" 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | push: 8 | branches: [ "*" ] 9 | 10 | schedule: 11 | - cron: "0 8 * * 1" 12 | 13 | jobs: 14 | test84: 15 | name: "Nette Tester" 16 | uses: contributte/.github/.github/workflows/nette-tester.yml@master 17 | with: 18 | php: "8.4" 19 | 20 | test83: 21 | name: "Nette Tester" 22 | uses: contributte/.github/.github/workflows/nette-tester.yml@master 23 | with: 24 | php: "8.3" 25 | 26 | test82: 27 | name: "Nette Tester" 28 | uses: contributte/.github/.github/workflows/nette-tester.yml@master 29 | with: 30 | php: "8.2" 31 | 32 | testlower: 33 | name: "Nette Tester" 34 | uses: contributte/.github/.github/workflows/nette-tester.yml@master 35 | with: 36 | php: "8.2" 37 | composer: "composer update --no-interaction --no-progress --prefer-dist --prefer-stable --prefer-lowest" 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Nettrine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nettrine/migrations", 3 | "description": "Doctrine Migrations for Nette Framework", 4 | "keywords": ["nette", "doctrine", "migrations"], 5 | "type": "library", 6 | "license": "MIT", 7 | "homepage": "https://github.com/contributte/doctrine-migrations", 8 | "authors": [ 9 | { 10 | "name": "Milan Felix Šulc", 11 | "homepage": "https://f3l1x.io" 12 | }, 13 | { 14 | "name": "Josef Benjac", 15 | "homepage": "http://josefbenjac.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=8.2", 20 | "nette/di": "^3.2.3", 21 | "doctrine/migrations": "^3.8.2" 22 | }, 23 | "require-dev": { 24 | "doctrine/orm": "^3.3.0", 25 | "mockery/mockery": "^1.6.12", 26 | "contributte/qa": "^0.4.0", 27 | "contributte/tester": "^0.4.0", 28 | "contributte/phpstan": "^0.2.0", 29 | "symfony/console": "^7.2.0" 30 | }, 31 | "autoload": { 32 | "psr-4": { 33 | "Nettrine\\Migrations\\": "src/" 34 | } 35 | }, 36 | "autoload-dev": { 37 | "psr-4": { 38 | "Tests\\": "tests" 39 | } 40 | }, 41 | "minimum-stability": "dev", 42 | "prefer-stable": true, 43 | "config": { 44 | "sort-packages": true, 45 | "allow-plugins": { 46 | "dealerdirect/phpcodesniffer-composer-installer": true 47 | } 48 | }, 49 | "extra": { 50 | "branch-alias": { 51 | "dev-master": "0.11.x-dev" 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/DI/Helpers/SmartStatement.php: -------------------------------------------------------------------------------- 1 | required()->assert(fn ($input) => str_starts_with($input, '@') || class_exists($input) || interface_exists($input)), 40 | Expect::type(Statement::class)->required(), 41 | ); 42 | 43 | return Expect::structure([ 44 | 'table' => Expect::string('doctrine_migrations'), 45 | 'column' => Expect::string('version'), 46 | 'directories' => Expect::arrayOf(Expect::string(), Expect::string())->required(), 47 | 'versionsOrganization' => Expect::anyOf( 48 | null, 49 | Configuration::VERSIONS_ORGANIZATION_BY_YEAR, 50 | Configuration::VERSIONS_ORGANIZATION_BY_YEAR_AND_MONTH 51 | ), 52 | 'customTemplate' => Expect::string(), 53 | 'allOrNothing' => Expect::bool(false), 54 | 'logger' => (clone $expectService), 55 | 'migrationFactory' => (clone $expectService), 56 | 'connection' => Expect::string(), 57 | 'manager' => Expect::string(), 58 | ]); 59 | } 60 | 61 | public function loadConfiguration(): void 62 | { 63 | $builder = $this->getContainerBuilder(); 64 | $config = $this->config; 65 | 66 | // Register configuration 67 | $storage = $builder->addDefinition($this->prefix('configuration.tableStorage')); 68 | $storage 69 | ->setFactory(TableMetadataStorageConfiguration::class) 70 | ->addSetup('setTableName', [$config->table]) 71 | ->addSetup('setVersionColumnName', [$config->column]); 72 | 73 | $configuration = $builder->addDefinition($this->prefix('configuration')); 74 | $configuration 75 | ->setFactory(Configuration::class) 76 | ->addSetup('setCustomTemplate', [$config->customTemplate]) 77 | ->addSetup('setMetadataStorageConfiguration', [$storage]) 78 | ->addSetup('setAllOrNothing', [$config->allOrNothing]); 79 | 80 | if ($config->connection !== null) { 81 | $configuration->addSetup('setConnectionName', [$config->connection]); 82 | } 83 | 84 | if ($config->manager !== null) { 85 | $configuration->addSetup('setEntityManagerName', [$config->manager]); 86 | } 87 | 88 | foreach ($config->directories as $namespace => $directory) { 89 | $configuration->addSetup('addMigrationsDirectory', [$namespace, $directory]); 90 | } 91 | 92 | if ($config->versionsOrganization === Configuration::VERSIONS_ORGANIZATION_BY_YEAR) { 93 | $configuration->addSetup('setMigrationsAreOrganizedByYear'); 94 | } elseif ($config->versionsOrganization === Configuration::VERSIONS_ORGANIZATION_BY_YEAR_AND_MONTH) { 95 | $configuration->addSetup('setMigrationsAreOrganizedByYearAndMonth'); 96 | } 97 | 98 | $dependencyFactory = $builder->addDefinition($this->prefix('dependencyFactory')) 99 | ->setType(DependencyFactory::class) 100 | ->setFactory(DependencyFactoryCreator::class . '::create'); 101 | 102 | if ($config->migrationFactory !== null) { 103 | $dependencyFactory->addSetup('setService', [MigrationFactory::class, SmartStatement::from($config->migrationFactory)]); 104 | } 105 | 106 | // Register commands 107 | 108 | $builder->addDefinition($this->prefix('currentCommand')) 109 | ->setFactory(CurrentCommand::class) 110 | ->setAutowired(false) 111 | ->addTag('console.command', CurrentCommand::getDefaultName()); 112 | 113 | $builder->addDefinition($this->prefix('diffCommand')) 114 | ->setFactory(DiffCommand::class) 115 | ->setAutowired(false) 116 | ->addTag('console.command', DiffCommand::getDefaultName()); 117 | 118 | $builder->addDefinition($this->prefix('dumpSchemaCommand')) 119 | ->setFactory(DumpSchemaCommand::class) 120 | ->setAutowired(false) 121 | ->addTag('console.command', DumpSchemaCommand::getDefaultName()); 122 | 123 | $builder->addDefinition($this->prefix('executeCommand')) 124 | ->setFactory(ExecuteCommand::class) 125 | ->setAutowired(false) 126 | ->addTag('console.command', ExecuteCommand::getDefaultName()); 127 | 128 | $builder->addDefinition($this->prefix('generateCommand')) 129 | ->setFactory(GenerateCommand::class) 130 | ->setAutowired(false) 131 | ->addTag('console.command', GenerateCommand::getDefaultName()); 132 | 133 | $builder->addDefinition($this->prefix('latestCommand')) 134 | ->setFactory(LatestCommand::class) 135 | ->setAutowired(false) 136 | ->addTag('console.command', LatestCommand::getDefaultName()); 137 | 138 | $builder->addDefinition($this->prefix('listCommand')) 139 | ->setFactory(ListCommand::class) 140 | ->setAutowired(false) 141 | ->addTag('console.command', ListCommand::getDefaultName()); 142 | 143 | $builder->addDefinition($this->prefix('migrateCommand')) 144 | ->setFactory(MigrateCommand::class) 145 | ->setAutowired(false) 146 | ->addTag('console.command', MigrateCommand::getDefaultName()); 147 | 148 | $builder->addDefinition($this->prefix('rollupCommand')) 149 | ->setFactory(RollupCommand::class) 150 | ->setAutowired(false) 151 | ->addTag('console.command', RollupCommand::getDefaultName()); 152 | 153 | $builder->addDefinition($this->prefix('statusCommand')) 154 | ->setFactory(StatusCommand::class) 155 | ->setAutowired(false) 156 | ->addTag('console.command', StatusCommand::getDefaultName()); 157 | 158 | $builder->addDefinition($this->prefix('syncMetadataCommand')) 159 | ->setFactory(SyncMetadataCommand::class) 160 | ->setAutowired(false) 161 | ->addTag('console.command', SyncMetadataCommand::getDefaultName()); 162 | 163 | $builder->addDefinition($this->prefix('upToDateCommand')) 164 | ->setFactory(UpToDateCommand::class) 165 | ->setAutowired(false) 166 | ->addTag('console.command', UpToDateCommand::getDefaultName()); 167 | 168 | $builder->addDefinition($this->prefix('versionCommand')) 169 | ->setFactory(VersionCommand::class) 170 | ->setAutowired(false) 171 | ->addTag('console.command', VersionCommand::getDefaultName()); 172 | } 173 | 174 | } 175 | -------------------------------------------------------------------------------- /src/DependencyFactoryCreator.php: -------------------------------------------------------------------------------- 1 | dependencyFactory->getConnection(), 61 | $this->dependencyFactory->getLogger() 62 | ); 63 | 64 | assert($migration instanceof AbstractMigration); 65 | 66 | return $migration; 67 | } 68 | 69 | }; 70 | 71 | $dependencyFactory->setService(MigrationFactory::class, new MigrationFactoryDecorator($container, $migrationFactory)); 72 | 73 | return $dependencyFactory; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/Exceptions/LogicalException.php: -------------------------------------------------------------------------------- 1 | migrationFactory->createVersion($migrationClassName); 22 | 23 | // Call setContainer 24 | if ($instance instanceof ContainerAwareInterface) { 25 | $instance->setContainer($this->container); 26 | } 27 | 28 | // Allow to use inject<> 29 | $this->container->callInjects($instance); 30 | 31 | return $instance; 32 | } 33 | 34 | } 35 | --------------------------------------------------------------------------------