{"id":15041075,"url":"https://github.com/aimeos/upscheme","last_synced_at":"2025-05-13T23:07:27.944Z","repository":{"id":39002618,"uuid":"246103356","full_name":"aimeos/upscheme","owner":"aimeos","description":"Database migrations and schema updates made easy","archived":false,"fork":false,"pushed_at":"2025-01-01T16:36:51.000Z","size":440,"stargazers_count":1932,"open_issues_count":1,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-10T01:03:43.838Z","etag":null,"topics":["database","database-management","database-migrations","database-schema","database-schema-migration","db","migration","migrations","mysql","php","postgresql","rdbms","schema","sql","sql-server","sqlite","sqlserver","sqlsrv"],"latest_commit_sha":null,"homepage":"https://upscheme.org","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aimeos.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-03-09T17:46:24.000Z","updated_at":"2025-04-09T22:25:36.000Z","dependencies_parsed_at":"2024-10-25T12:21:11.298Z","dependency_job_id":"2e7f816c-aed8-4bb7-913c-9a3f4ee54437","html_url":"https://github.com/aimeos/upscheme","commit_stats":{"total_commits":265,"total_committers":1,"mean_commits":265.0,"dds":0.0,"last_synced_commit":"482933a39bed123f8bad097fa4307a78dbe74091"},"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fupscheme","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fupscheme/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fupscheme/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aimeos%2Fupscheme/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aimeos","download_url":"https://codeload.github.com/aimeos/upscheme/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248137893,"owners_count":21053775,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","database-management","database-migrations","database-schema","database-schema-migration","db","migration","migrations","mysql","php","postgresql","rdbms","schema","sql","sql-server","sqlite","sqlserver","sqlsrv"],"created_at":"2024-09-24T20:45:29.855Z","updated_at":"2025-04-10T01:03:46.612Z","avatar_url":"https://github.com/aimeos.png","language":"PHP","readme":"\u003ca class=\"badge\" href=\"https://circleci.com/gh/aimeos/upscheme\"\u003e\u003cimg src=\"https://circleci.com/gh/aimeos/upscheme.svg?style=shield\" alt=\"Build Status\" height=\"20\"\u003e\u003c/a\u003e\n\u003ca class=\"badge\" href=\"https://coveralls.io/github/aimeos/upscheme\"\u003e\u003cimg src=\"https://coveralls.io/repos/github/aimeos/upscheme/badge.svg\" alt=\"Coverage Status\" height=\"20\"\u003e\u003c/a\u003e\n\u003ca class=\"badge\" href=\"https://packagist.org/packages/aimeos/upscheme\"\u003e\u003cimg src=\"https://poser.pugx.org/aimeos/upscheme/license.svg\" alt=\"License\" height=\"20\"\u003e\u003c/a\u003e\n\n# Upscheme: Database schema updates made easy\n\nEasy to use PHP package for updating the database schema of your application\nand migrate data between versions.\n\n```bash\ncomposer req aimeos/upscheme\n```\n\n**Table of contents**\n\n* [Why Upscheme](#why-upscheme)\n* [Database support](#database-support)\n* [Integrating Upscheme](#integrating-upscheme)\n* [Writing migrations](#writing-migrations)\n  * [Naming](#naming-migrations)\n  * [Dependencies](#dependencies)\n  * [Messages](#messages)\n  * [Schemas](#schemas)\n  * [Generate from database](#generate-from-database)\n* [Database](#database)\n  * [Accessing objects](#accessing-objects)\n  * [Checking existence](#checking-existence)\n  * [Renaming objects](#renaming-objects)\n  * [Removing objects](#removing-objects)\n  * [Query/modify table rows](#Query-modify-table-rows)\n  * [Executing custom SQL](#executing-custom-sql)\n  * [Database methods](#database-methods)\n* [Tables](#tables)\n  * [Creating tables](#creating-tables)\n  * [Setting table options](#setting-table-options)\n  * [Checking table existence](#checking-table-existence)\n  * [Changing tables](#changing-tables)\n  * [Renaming tables](#renaming-tables)\n  * [Dropping tables](#dropping-tables)\n  * [Table methods](#table-methods)\n* [Columns](#columns)\n  * [Adding columns](#adding-columns)\n  * [Available column types](#available-column-types)\n  * [Column modifiers](#column-modifiers)\n  * [Checking column existence](#checking-column-existence)\n  * [Changing columns](#changing-columns)\n  * [Renaming columns](#renaming-columns)\n  * [Dropping columns](#dropping-columns)\n  * [Column methods](#column-methods)\n* [Foreign keys](#foreign-keys)\n  * [Creating foreign keys](#creating-foreign-keys)\n  * [Checking foreign key existence](#checking-foreign-key-existence)\n  * [Dropping foreign keys](#dropping-foreign-keys)\n  * [Foreign key methods](#foreign-key-methods)\n* [Sequences](#sequences)\n  * [Adding sequences](#adding-sequences)\n  * [Checking sequence existence](#checking-sequence-existence)\n  * [Dropping sequences](#dropping-sequences)\n  * [Sequence methods](#sequence-methods)\n* [Indexes](#indexes)\n  * [Adding indexes](#adding-indexes)\n  * [Checking index existence](#checking-index-existence)\n  * [Renaming indexes](#renaming-indexes)\n  * [Dropping indexes](#dropping-indexes)\n  * [Custom index naming](#custom-index-naming)\n* [Customizing Upscheme](#customizing-upscheme)\n  * [Adding custom methods](#adding-custom-methods)\n  * [Implementing custom columns](#implementing-custom-columns)\n* [Upgrade Upscheme](#upgrade-upscheme)\n\n\n\n## Why Upscheme\n\nMigrations are like version control for your database. They allow you to get the\nexact same state in every installation. Using Upscheme, you get:\n\n* one place for defining tables, columns, indexes, etc. easily\n* upgrades from any state in between to the expected schema\n* consistent, reliable and hassle-free schema upgrades\n* minimal code required for writing migrations\n* perfect solution for continuous deployments\n* best package for cloud-based PHP applications\n\nHere's an example of a table definition that you can adapt whenever your table\nlayout must change. Then, Upscheme will automatically add and modify existing\ncolumns and table properties (but don't delete anything for safety reasons):\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $t ) {\n\t$t-\u003eengine = 'InnoDB';\n\n\t$t-\u003eid();\n\t$t-\u003estring( 'domain', 32 );\n\t$t-\u003estring( 'code', 64 )-\u003eopt( 'charset', 'binary', ['mariadb', 'mysql'] );\n\t$t-\u003estring( 'label', 255 );\n\t$t-\u003eint( 'pos' )-\u003edefault( 0 );\n\t$t-\u003esmallint( 'status' );\n\t$t-\u003edefault();\n\n\t$t-\u003eunique( ['domain', 'code'] );\n\t$t-\u003eindex( ['status', 'pos'] );\n} );\n```\n\nFor upgrading relational database schemas, two packages are currently used most\noften: Doctrine DBAL and Doctrine migrations. While Doctrine DBAL does a good job\nin abstracting the differences of several database implementations, it's API\nrequires writing a lot of code. Doctrine migrations on the other site has some\ndrawbacks which make it hard to use in all applications that support 3rd party\nextensions.\n\n### Doctrine DBAL drawbacks\n\nThe API of DBAL is very verbose and you need to write lots of code even for simple\nthings. Upscheme uses Doctrine DBAL to offer an easy to use API for upgrading the\ndatabase schema of your application with minimal code. For the Upscheme example\nabove, these lines of code are the equivalent for DBAL in a migration:\n\n```php\n$dbalManager = $conn-\u003ecreateSchemaManager();\n$from = $manager-\u003ecreateSchema();\n$to = $manager-\u003ecreateSchema();\n\nif( $to-\u003ehasTable( 'test' ) ) {\n\t$table = $to-\u003egetTable( 'test' );\n} else {\n\t$table = $to-\u003ecreateTable( 'test' );\n}\n\n$table-\u003eaddOption( 'engine', 'InnoDB' );\n\n$table-\u003eaddColumn( 'id', 'integer', ['autoincrement' =\u003e true] );\n$table-\u003eaddColumn( 'domain', 'string', ['length' =\u003e 32] );\n\n$platform = $conn-\u003egetDatabasePlatform();\nif( $platform instanceof \\Doctrine\\DBAL\\Platform\\MySQLPlatform\n\t|| $platform instanceof \\Doctrine\\DBAL\\Platform\\MariaDBPlatform\n) {\n\t$table-\u003eaddColumn( 'code', 'string', ['length' =\u003e 64, 'customSchemaOptions' =\u003e ['charset' =\u003e 'binary']] );\n} else {\n\t$table-\u003eaddColumn( 'code', 'string', ['length' =\u003e 64]] );\n}\n\n$table-\u003eaddColumn( 'label', 'string', ['length' =\u003e 255] );\n$table-\u003eaddColumn( 'pos', 'integer', ['default' =\u003e 0] );\n$table-\u003eaddColumn( 'status', 'smallint', [] );\n$table-\u003eaddColumn( 'mtime', 'datetime', [] );\n$table-\u003eaddColumn( 'ctime', 'datetime', [] );\n$table-\u003eaddColumn( 'editor', 'string', ['length' =\u003e 255] );\n\n$table-\u003esetPrimaryKey( ['id'] );\n$table-\u003eaddUniqueIndex( ['domain', 'code'] );\n$table-\u003eaddIndex( ['status', 'pos'] );\n\nforeach( $from-\u003egetMigrateToSql( $to, $conn-\u003egetDatabasePlatform() ) as $sql ) {\n\t$conn-\u003eexecuteStatement( $sql );\n}\n```\n\n### Doctrine Migration drawbacks\n\nDoctrine Migration relies on migration classes that are named by the time they\nhave been created to ensure a certain order. Furthermore, it stores which migrations\nhas been executed in a table of your database. There are three major problems that\narise from that:\n\n* dependencies between 3rd party extensions\n* tracking changes is out of sync\n* data loss when using `down()`\n\nIf your application supports 3rd party extensions, these extensions are likely to\nadd columns to existing tables and migrate data themselves. As there's no way to\ndefine dependencies between migrations, it can get almost impossible to run\nmigrations in an application with several 3rd party extensions without conflicts.\nTo avoid that, Upscheme offers easy to use `before()` and `after()` methods in\neach migration task where the tasks can define its dependencies to other tasks.\n\nBecause Doctrine Migrations uses a database table to record which migration\nalready has been executed, these records can get easily out of sync in case of\nproblems. Contrary, Upscheme only relies on the actual schema so it's possible\nto upgrade from any state, regardless of what has happend before.\n\nDoctrine Migrations also supports the reverse operations in `down()` methods so\nyou can roll back migrations which Upscheme does not. Experience has shown that\nit's often impossible to roll back migrations, e.g. after adding a new colum,\nmigrating the data of an existing column and dropping the old column afterwards.\nIf the migration of the data was lossy, you can't recreate the same state in a\n`down()` method. The same is the case if you've dropped a table. Thus, Upscheme\nonly offers scheme upgrading but no downgrading to avoid implicit data loss.\n\n\n## Database support\n\nUpscheme uses Doctrine DBAL for abstracting from different database server\nimplementations. DBAL supports all major relationsal database management systems\n(RDBMS) but with a different level of support for the available features:\n\n**Good support:**\n\n* MySQL\n* MariaDB\n* PostgreSQL\n* SQLite\n* SQL server\n\n**Limited support:**\n\n* DB2\n* Oracle\n* SQL Anywhere\n\n## Integrating Upscheme\n\nAfter you've installed the `aimeos/upscheme` package using composer, you can use\nthe `Up` class to execute your migration tasks:\n\n```php\n$config = [\n\t'driver' =\u003e 'pdo_mysql',\n\t'host' =\u003e '127.0.0.1',\n\t'dbname' =\u003e '\u003cdatabase\u003e',\n\t'user' =\u003e '\u003cdbuser\u003e',\n\t'password' =\u003e '\u003csecret\u003e'\n];\n\n\\Aimeos\\Upscheme\\Up::use( $config, 'src/migrations' )-\u003eup();\n```\n\nThe `Up::use()` method requires two parameters: The database configuration and\nthe path(s) to the migration tasks. For the config, the array keys and the values\nfor *driver* must be supported by Doctrine DBAL. Available drivers are:\n\n- pdo_mysql\n- pdo_pgsql\n- pdo_sqlite\n- pdo_sqlsrv\n- pdo_oci\n- ibm_db2\n- mysqli\n- oci8\n- sqlanywhere\n- sqlsrv\n\nSome databases require different parameters, most notable SQLite and Oracle:\n\n**SQLite:**\n\n```php\n$config = [\n\t'driver' =\u003e 'pdo_sqlite',\n\t'path' =\u003e 'path/to/file.sq3'\n];\n```\n\n**Oracle:**\n\n```php\n$config = [\n\t'driver' =\u003e 'pdo_oci',\n\t'host' =\u003e '\u003chost or IP\u003e',\n\t'dbname' =\u003e '\u003cSID or service name (Oracle 18+)\u003e',\n\t'service' =\u003e true, // for Oracle 18+ only\n\t'user' =\u003e '\u003cdbuser\u003e',\n\t'password' =\u003e '\u003csecret\u003e'\n];\n```\n\nIf you didn't use Doctrine DBAL before, your database configuration may have a\ndifferent structure and/or use different values for the database type. Upscheme\nallows you to register a custom method that transforms your configration into\nvalid DBAL settings, e.g.:\n\n```php\n\\Aimeos\\Upscheme\\Up::macro( 'connect', function( array $cfg ) {\n\n\treturn \\Doctrine\\DBAL\\DriverManager::getConnection( [\n\t\t'driver' =\u003e $cfg['adapter'],\n\t\t'host' =\u003e $cfg['host'],\n\t\t'dbname' =\u003e $cfg['database'],\n\t\t'user' =\u003e $cfg['username'],\n\t\t'password' =\u003e $cfg['password']\n\t] );\n} );\n```\n\nUpscheme also supports several database connections which you can distinguish\nby their key name:\n\n```php\n$config = [\n\t'db' =\u003e [\n\t\t'driver' =\u003e 'pdo_mysql',\n\t\t'host' =\u003e '127.0.0.1',\n\t\t'dbname' =\u003e '\u003cdatabase\u003e',\n\t\t'user' =\u003e '\u003cdbuser\u003e',\n\t\t'password' =\u003e '\u003csecret\u003e'\n\t],\n\t'temp' =\u003e [\n\t\t'driver' =\u003e 'pdo_sqlite',\n\t\t'path' =\u003e '/tmp/mydb.sqlite'\n\t]\n];\n\n\\Aimeos\\Upscheme\\Up::use( $config, 'src/migrations' )-\u003eup();\n```\n\nOf course, you can also pass several migration paths to the `Up` class:\n\n```php\n\\Aimeos\\Upscheme\\Up::use( $config, ['src/migrations', 'ext/migrations'] )-\u003eup();\n```\n\nTo enable (debugging) output, use the verbose() method:\n\n```php\n\\Aimeos\\Upscheme\\Up::use( $config, 'src/migrations' )-\u003everbose()-\u003eup(); // most important only\n\\Aimeos\\Upscheme\\Up::use( $config, 'src/migrations' )-\u003everbose( 'vv' )-\u003eup(); // more verbose\n\\Aimeos\\Upscheme\\Up::use( $config, 'src/migrations' )-\u003everbose( 'vvv' )-\u003eup(); // debugging\n```\n\n\n## Writing migrations\n\nA migration task only requires implementing the `up()` method and must be stored\nin one of the directories passed to the `Up` class:\n\n```php\n\u003c?php\n\nnamespace Aimeos\\Upscheme\\Task;\nuse Aimeos\\Upscheme\\Schema\\Table;\n\n\nreturn new class( $this ) extends Base {\n\n\tpublic function up()\n\t{\n\t\t$this-\u003edb()-\u003etable( 'test', function( Table $t ) {\n\t\t\t$t-\u003eid();\n\t\t\t$t-\u003estring( 'label' );\n\t\t\t$t-\u003ebool( 'status' );\n\t\t} );\n\t}\n};\n```\n\nIn your PHP file, always include the `namespace` statement first. The `use`\nstatement is optional and only needed as shortcut for the type hint for the\nclosure function argument. Your class also has to extend from the \"Base\" task\nclass or implement the [\"Iface\" task interface](https://github.com/aimeos/upscheme/blob/master/src/Task/Iface.php).\n\nAlternatively to anonymous classes, you can use named classes for migration tasks:\n\n```php\n\u003c?php\n\nnamespace Aimeos\\Upscheme\\Task;\nuse Aimeos\\Upscheme\\Schema\\Table;\n\n\nclass TestTable extends Base\n{\n\tpublic function up()\n\t{\n\t\t$this-\u003edb()-\u003etable( 'test', function( Table $t ) {\n\t\t\t$t-\u003eid();\n\t\t\t$t-\u003estring( 'label' );\n\t\t\t$t-\u003ebool( 'status' );\n\t\t} );\n\t}\n}\n```\n\nThe file your class is stored in must have the same name (case sensitive) as\nthe class itself and the `.php` suffix, e.g:\n\n```\nclass TestTable -\u003e TestTable.php\n```\n\n### Naming migrations\n\nThere's no strict convention how to name migration task files. You can\neither name them by what they do (e.g. \"CreateTestTable.php\"), what they operate\non (e.g. \"TestTable.php\") or even use a timestamp (e.g. \"20201231_Test.php\").\n\nIf the tasks doesn't contain dependencies, they are sorted and executed in\nalphabethical order according to the file name and the sorting would be:\n\n```\n20201231_Test.php\nCreateTestTable.php\nTestTable.php\n```\n\n### Dependencies\n\nTo specify dependencies to other migration tasks, use the `after()` and `before()`\nmethods. Your task is executed after the tasks returned by `after()` and before\nthe tasks returned by `before()`:\n\n```php\nreturn new class( $this ) extends Base {\n\n\tpublic function after() : array\n\t{\n\t\treturn ['CreateRefTable'];\n\t}\n\n\tpublic function before() : array\n\t{\n\t\treturn ['InsertTestData'];\n\t}\n}\n```\n\nThe task names are the file names of the tasks without the `.php` suffix. If the\nexample migration is stored in the file `TestTable.php`, the order of execution\nwould be:\n\n```\nCreateRefTable.php -\u003e TestTable.php -\u003e InsertTestData.php\n```\n\n### Messages\n\nTo output messages in your migration task use the `info()` method:\n\n```php\n$this-\u003einfo( 'some message' );\n$this-\u003einfo( 'more verbose message', 'vv' );\n$this-\u003einfo( 'very verbose debug message', 'vvv' );\n```\n\nThe second parameter is the verbosity level and none or `v` are standard messages,\n`vv` are messages that are only displayed if more verbosity is wanted while `vvv` is\nfor debugging messages. There's also a third parameter for indenting the messages:\n\n```php\n$this-\u003einfo( 'some message' );\n$this-\u003einfo( 'second level message', 'v', 1 );\n$this-\u003einfo( 'third level message', 'v', 2 );\n```\n\nThis will display:\n\n```\nsome message\n  second level message\n    third level message\n```\n\nPrerequisite is that the `verbose()` method of the `Up` class has been called before:\n\n```php\n\\Aimeos\\Upscheme\\Up::use( $config, '...' )-\u003everbose()-\u003eup();\n```\n\n### Schemas\n\nIn the `up()` method, you have access to the database schema using the `db()`\nmethod. In case you've passed more than one database configuration to `Up::use()`,\nyou can access the different schemas by their configuration key:\n\n```php\n// $config = ['db' =\u003e [...], 'temp' =\u003e [...]];\n// \\Aimeos\\Upscheme\\Up::use( $config, '...' )-\u003eup();\n\n$this-\u003edb();\n$this-\u003edb( 'db' );\n$this-\u003edb( 'temp' );\n```\n\nIf you pass no config key or one that doesn't exist, the first configuration is\nreturned (\"db\" in this case). By using the available methods of the database schema\nobject, you can add, update or drop tables, columns, indexes and other database\nobjects. Also, you can use [`insert()`](#dbinsert), [`select()`](#dbselect),\n[`update()`](#dbupdate), [`delete()`](#dbdelete) and [`stmt()`](#dbstmt) to\nmanipulate the records of the tables.\n\nAfter each migration task, the schema updates made in the task are automatically\napplied to the database. If you need to persist a change immediately because you\nwant to insert data, call `$this-\u003edb()-\u003eup()` yourself. The `up()` method is also\navailable in any table, sequence, and column object so you can call `up()`\neverywhere.\n\nIn cases you need two different database connections because you want to execute\nSELECT and INSERT/UPDATE/DELETE statements at the same time, pass TRUE as second\nparameter to `db()` to get the database schema including a new connection:\n\n```php\n$db1 = $this-\u003edb();\n$db2 = $this-\u003edb( 'db', true );\n\nforeach( $db1-\u003eselect( 'users', ['status' =\u003e false] ) as $row ) {\n\t$db2-\u003einsert( 'oldusers', $row );\n}\n\n$db2-\u003edelete( 'users', ['status' =\u003e false] );\n```\n\nAll schema changes made are applied to the database before the schema with the\nnew connection is returned. To avoid database connections to pile up until the\ndatabase server rejects new connections, always calll [`close()`](#dbclose) for\nnew connections created by `db( '\u003cname\u003e', true )`:\n\n```php\n$db2-\u003eclose();\n```\n\n### Generate from database\n\nInstead of writing migrations for your database objects by hand, you can generate\nthe migration files automatically using:\n\n```php\n$config = [\n\t'db' =\u003e [\n\t\t'driver' =\u003e 'pdo_mysql',\n\t\t'host' =\u003e '127.0.0.1',\n\t\t'dbname' =\u003e '\u003cdatabase\u003e',\n\t\t'user' =\u003e '\u003cdbuser\u003e',\n\t\t'password' =\u003e '\u003csecret\u003e'\n\t]\n];\n\n\\Aimeos\\Upscheme\\Up::use( $config, 'migrations' )-\u003ecreate();\n```\n\nThis will generate one file for each sequence, table and view in the passed\ndirectory (`./migrations/` in this example). If you have several databases and\nwant to create migrations for all of them at once, pass the connection keys\nfrom the configuration to `create()`:\n\n```php\n$config = [\n\t'db' =\u003e [\n\t\t'driver' =\u003e 'pdo_mysql',\n\t\t// ...\n\t],\n\t'order' =\u003e [\n\t\t'driver' =\u003e 'pdo_oci',\n\t\t// ...\n\t]\n];\n\n\\Aimeos\\Upscheme\\Up::use( $config, 'migrations' )-\u003ecreate( ['db', 'order'] );\n```\n\n\n## Database\n\n### Accessing objects\n\nYou get the database schema object in your task by calling `$this-\u003edb()` as\ndescribed in the [schema section](#schemas). It gives you full access to the\ndatabase schema including all tables, sequences and other schema objects:\n\n```php\n$table = $this-\u003edb()-\u003etable( 'users' );\n$seq = $this-\u003edb()-\u003esequence( 'seq_users' );\n```\n\nIf the table or seqence doesn't exist, it will be created. Otherwise, the existing\ntable or sequence object is returned. In both cases, you can modify the objects\nafterwards and add e.g. new columns to the table.\n\n### Checking existence\n\nYou can test for tables, columns, indexes, foreign keys and sequences using the\ndatabase schema returned by `$this-\u003edb()`:\n\n```php\n$db = $this-\u003edb();\n\nif( $db-\u003ehasTable( 'users' ) ) {\n    // The \"users\" table exists\n}\n\nif( $db-\u003ehasColumn( 'users', 'name' ) ) {\n    // The \"name\" column in the \"users\" table exists\n}\n\nif( $db-\u003ehasIndex( 'users', 'idx_name' ) ) {\n    // The \"idx_name\" index in the \"users\" table exists\n}\n\nif( $db-\u003ehasForeign( 'users_address', 'fk_users_id' ) ) {\n    // The foreign key \"fk_users_id\" in the \"users_address\" table exists\n}\n\nif( $db-\u003ehasSequence( 'seq_users' ) ) {\n    // The \"seq_users\" sequence exists\n}\n\nif( $db-\u003ehasView( 'testview' ) ) {\n    // The \"testview\" view exists\n}\n```\n\n### Renaming objects\n\nThe database object returned by `$this-\u003edb()` offers the possibility to rename\ntables, columns and indexes using the [`renameTable()`](#dbrenametable),\n[`renameColumn()`](#dbrenamecolumn) and [`renameIndex()`](#dbrenameindex):\n\n```php\n$db = $this-\u003edb();\n\n// Renames the table \"users\" to \"accounts\"\n$db-\u003erenameTable( 'users', 'account' );\n\n// Renames the column \"label\" to \"name\" in the \"users\" table\n$db-\u003erenameColumn( 'users', 'label', 'name' );\n\n// Renames the column \"idx_label\" to \"idx_name\" in the \"users\" table\n$db-\u003erenameIndex( 'users', 'idx_label', 'idx_name' );\n```\n\n### Removing objects\n\nThe database object returned by `$this-\u003edb()` also has methods for dropping tables,\ncolumns, indexes, foreign keys and sequences:\n\n```php\n$db = $this-\u003edb();\n\n// Drops the foreign key \"fk_users_id\" from the \"users_address\" table\n$db-\u003edropForeign( 'users_address', 'fk_users_id' );\n\n// Drops the \"idx_name\" index from the \"users\" table\n$db-\u003edropIndex( 'users', 'idx_name' );\n\n// Drops the \"name\" column from the \"users\" table\n$db-\u003edropColumn( 'users', 'name' );\n\n// Drops the \"seq_users\" sequence\n$db-\u003edropSequence( 'seq_users' );\n\n// Drops the \"users\" table\n$db-\u003edropTable( 'users' );\n\n// Drops the \"testview\" view\n$db-\u003edropView( 'testview' );\n```\n\nIf the table, column, index, foreign key or sequence doesn't exist, it is silently\nignored. For cases where you need to know if they exist, use the\n[`hasTable()`](#dbhastable), [`hasColumn()`](#dbhascolumn), [`hasIndex()`](#dbhasindex),\n[`hasForeign()`](#dbhasforeign) and [`hasSeqence()`](#dbhassequence) methods before\nlike described in the [\"Checking for existence\"](#checking-for-existence) section.\n\n### Query/modify table rows\n\nThe [`insert()`](#dbinsert), [`select()`](#dbselect), [`update()`](#dbupdate) and\n[`delete()`](#dbdelete) methods are an easy way to add, retrieve, modify and\nremove rows in any table:\n\n```php\n$this-\u003edb()-\u003etransaction( function( $db ) {\n\n\t$db2 = $this-\u003edb( 'db', true );\n\n\tforeach( $db2-\u003eselect( 'users', ['status' =\u003e false] ) as $row )\n\t{\n\t\t$db-\u003einsert( 'newusers', ['userid' =\u003e $row['id'], 'status' =\u003e true] );\n\t\t$db-\u003eupdate( 'users', ['refid' =\u003e $db-\u003elastId()], ['id' =\u003e $row['id']] );\n\t}\n\n\t$db-\u003edelete( 'newusers', ['status' =\u003e false] );\n\t$db2-\u003eclose();\n} );\n```\n\nIf you use [`select()`](#dbselect) simultaniously with [`insert()`](#dbinsert),\n[`update()`](#dbupdate) or [`delete()`](#dbdelete), you must create a second\ndatabase connection because the [`select()`](#dbselect) statement will return\nrows while you send new commands to the database server. This only works on\nseparate connections, not on the same.\n\nTo wrap all delete/insert/update operations into a transaction, you must use\nthe [`transaction()`](#dbtransaction) method of the [database](#database) object:\n\n```php\n$this-\u003edb()-\u003etransaction( function( $db ) {\n\t// $db-\u003einsert( ... )\n\t// $db-\u003eupdate( ... )\n\t// $db-\u003edelete( ... )\n} );\n```\n\nThis ensures that all write operations are performed atomically or none of them\nin case of an error. The [`transaction()`](#dbtransaction) method ensures that\nthe transaction is committed or rolled back automatically after your anonymous\nfunction returns control to the method.\n\nIf you need additional parameters within your anonymous function, you can hand\nthem over in the `use` list of your function:\n\n```php\n$userid = 123;\n$this-\u003edb()-\u003etransaction( function( $db ) use ( $userid ) {\n\t$db-\u003einsert( 'newusers', ['userid' =\u003e userid, 'status' =\u003e true] );\n} );\n```\n\nYou can only pass simple key/value pairs for conditions to the methods which are\ncombined by AND. If you need more complex queries, use the [`stmt()`](#dbstmt)\ninstead:\n\n```php\n$db = $this-\u003edb();\n\n$result = $db-\u003estmt()-\u003eselect( 'id', 'name' )\n\t-\u003efrom( 'users' )\n\t-\u003ewhere( 'status != ?' )\n\t-\u003esetParameter( 0, false )\n\t-\u003eexecuteQuery();\n\n$db-\u003estmt()-\u003edelete( 'users' )\n\t-\u003ewhere( 'status != ?' )\n\t-\u003esetParameter( 0, false )\n\t-\u003eexecuteStatement();\n\n$db-\u003estmt()-\u003eupdate( 'users' )\n\t-\u003eset( 'status', '?' )\n\t-\u003ewhere( 'status != ?' )\n\t-\u003esetParameters( [true, false] )\n\t-\u003eexecuteStatement();\n```\n\nThe [`stmt()`](#dbstmt) method returns a `Doctrine\\DBAL\\Query\\QueryBuilder` object\nwhich enables you to build more advanced statement. Please have a look into the\n[Doctrine Query Builder](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html)\ndocumentation for more details.\n\nIf you want to use values directly in a SQL statement (use prepared statements for\nsecurity reasons whenever possible!), you have to quote the values using the\n[`q()`](#dbq) method:\n\n```php\n$db = $this-\u003edb();\n\n$result = $db-\u003estmt()-\u003eselect( '*' )-\u003efrom( 'products' )\n\t-\u003ewhere( 'status = ' . $db-\u003eq( $_GET['status'] ) )-\u003eexecuteQuery();\n```\n\nSimilarly, if your schema contains reserved keywords, e.g. as column names, you\nhave to quote them as well using the [`qi()`](#dbqi) method:\n\n```php\n$db = $this-\u003edb();\n\n$result = $db-\u003estmt()-\u003eselect( $db-\u003eqi( 'key' ) )-\u003efrom( 'products' )-\u003eexecuteQuery();\n```\n\n### Executing custom SQL\n\nDoctrine only supports a common subset of SQL statements and not all possibilities\nthe database vendors have implemented. To remove that limit, Upscheme offers the\n[`exec()`](#dbexec), [`for()`](#dbfor) and [`query()`](#dbquery) methods to execute\ncustom SQL statements not supported by Doctrine DBAL.\n\nTo execute custom SQL queries use the [`query()`](#dbquery) method which returns a\nresult set you can iterate over:\n\n```php\n$sql = 'SELECT id, label, status FROM product WHERE label LIKE ?';\n$result = $this-\u003edb()-\u003equery( $sql, ['test%'] );\n\nforeach( $result-\u003eiterateAssociative() as $row ) {\n\t// ...\n}\n```\n\nFor all other SQL statements use the [`exec()`](#dbexec) method wich returns the\nnumber of affected rows:\n\n```php\n$sql = 'UPDATE product SET status=? WHERE status=?';\n$num = $this-\u003edb()-\u003eexec( $sql, [1, 0] );\n```\n\nUsing the [`for()`](#dbfor) method, you can also execute statements depending on\nthe database platform:\n\n```php\n$this-\u003edb()-\u003efor( 'mysql', 'CREATE FULLTEXT INDEX idx_text ON product (text)' );\n```\n\nSpecifying the database platform is very useful for creating special types\nof indexes where the syntax differs between the database implementations.\n\n### Database methods\n\n\u003cnav\u003e\n\u003cdiv class=\"method-header\"\u003e\u003ca href=\"#database\"\u003eDatabase\u003c/a\u003e\u003c/div\u003e\n\u003cul class=\"method-list\"\u003e\n\t\u003cli\u003e\u003ca href=\"#db__call\"\u003e__call()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbclose\"\u003eclose()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbdelete\"\u003edelete()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbdropcolumn\"\u003edropColumn()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbdropforeign\"\u003edropForeign()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbdropindex\"\u003edropIndex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbdropsequence\"\u003edropSequence()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbdroptable\"\u003edropTable()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbdropview\"\u003edropView()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbexec\"\u003eexec()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbfor\"\u003efor()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbhascolumn\"\u003ehasColumn()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbhasforeign\"\u003ehasForeign()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbhasindex\"\u003ehasIndex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbhassequence\"\u003ehasSequence()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbhastable\"\u003ehasTable()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbhasview\"\u003ehasView()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbinsert\"\u003einsert()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dblastid\"\u003elastId()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbname\"\u003ename()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbq\"\u003eq()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbqi\"\u003eqi()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbquery\"\u003equery()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbrenamecolumn\"\u003erenameColumn()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbrenameindex\"\u003erenameIndex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbrenametable\"\u003erenameTable()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbreset\"\u003ereset()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbselect\"\u003eselect()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbsequence\"\u003esequence()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbstmt\"\u003estmt()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbtable\"\u003etable()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbtoarray\"\u003etoArray()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbtransaction\"\u003etransaction()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbtype\"\u003etype()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbup\"\u003eup()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbupdate\"\u003eupdate()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#dbview\"\u003eview()\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/nav\u003e\n\n\n#### DB::__call()\n\nCalls custom methods or passes unknown method calls to the Doctrine schema object\n\n```php\npublic function __call( string $method, array $args )\n```\n\n* @param **string** `$method` Name of the method\n* @param **array\u0026#60;mixed\u0026#62;** `$args` Method parameters\n* @return **mixed** Return value of the called method\n\n**Examples:**\n\nYou can register custom methods that have access to the class properties of the\nUpscheme DB object:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\DB::macro( 'hasFkIndexes', function( $val ) {\n\treturn $this-\u003eto-\u003ehasExplicitForeignKeyIndexes();\n} );\n\n$db-\u003ehasFkIndexes();\n// returns true/false\n```\n\nAvailable class properties are:\n\n`$this-\u003efrom`\n: Original Doctrine database schema representing the current database\n\n`$this-\u003eto`\n: Doctrine database schema containing the changes made up to now\n\n`$this-\u003econn`\n: Doctrine database connection\n\n`$this-\u003eup`\n: Upscheme object\n\nFurthermore, you can call any [Doctrine schema](https://github.com/doctrine/dbal)\nmethod from *src/Schema/Schema.php* directly, e.g.:\n\n```php\n$db-\u003ehasExplicitForeignKeyIndexes();\n```\n\n\n#### DB::close()\n\nCloses the database connection\n\n```php\npublic function close() : void\n```\n\nCall `close()` only for DB schema objects created with `$this-\u003edb( '...', true )`.\nOtherwise, you will close the main connection and DBAL has to reconnect to the\nserver which will degrade performance!\n\n**Examples:**\n\n```php\n$db = $this-\u003edb( 'temp', true );\n$db-\u003edropTable( 'test' );\n$db-\u003eclose();\n```\n\n\n#### DB::delete()\n\nDeletes the records from the given table\n\n```php\npublic function delete( string $table, array $conditions = [] ) : self\n```\n\n* @param **string** `$table` Name of the table\n* @param **array\u0026#60;string,mixed\u0026#62;** `$conditions` Key/value pairs of column names and value to compare with\n* @return **self** Same object for fluid method calls\n\n**Warning:** The condition values are escaped but the table name and condition\ncolumn names are not! Only use fixed strings for table name and condition\ncolumn names but no external input!\n\n**Examples:**\n\n```php\n$db-\u003edelete( 'test', ['status' =\u003e false, 'type' =\u003e 'old'] );\n$db-\u003edelete( 'test' );\n```\n\nSeveral conditions passed in the second parameter are combined by \"AND\". If you\nneed more complex statements, use the [`stmt()`](#dbstmt) method instead.\n\n\n#### DB::dropColumn()\n\nDrops the column given by its name if it exists\n\n```php\npublic function dropColumn( string $table, $name ) : self\n```\n\n* @param **string** `$table` Name of the table the column belongs to\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the column or columns\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003edropColumn( 'test', 'oldcol' );\n$db-\u003edropColumn( 'test', ['oldcol', 'oldcol2'] );\n```\n\nIf the column or one of the columns doesn't exist, it will be silently ignored.\n\n\n#### DB::dropForeign()\n\nDrops the foreign key constraint given by its name if it exists\n\n```php\npublic function dropForeign( string $table, $name ) : self\n```\n\n* @param **string** `$table` Name of the table the foreign key constraint belongs to\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the foreign key constraint or constraints\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003edropForeign( 'test', 'fk_old' );\n$db-\u003edropForeign( 'test', ['fk_old', 'fk_old2'] );\n```\n\nIf the foreign key constraint or one of the constraints doesn't exist, it will be\nsilently ignored.\n\n\n#### DB::dropIndex()\n\nDrops the index given by its name if it exists\n\n```php\npublic function dropIndex( string $table, $name ) : self\n```\n\n* @param **string** `$table` Name of the table the index belongs to\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the index or indexes\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003edropIndex( 'test', 'idx_old' );\n$db-\u003edropIndex( 'test', ['idx_old', 'idx_old2'] );\n```\n\nIf the index or one of the indexes doesn't exist, it will be silently ignored.\n\n\n#### DB::dropSequence()\n\nDrops the sequence given by its name if it exists\n\n```php\npublic function dropSequence( $name ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the sequence or sequences\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003edropSequence( 'seq_old' );\n$db-\u003edropSequence( ['seq_old', 'seq_old2'] );\n```\n\nIf the sequence or one of the sequences doesn't exist, it will be silently ignored.\n\n\n#### DB::dropTable()\n\nDrops the table given by its name if it exists\n\n```php\npublic function dropTable( $name ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the table or tables\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003edropTable( 'test' );\n$db-\u003edropTable( ['test', 'test2'] );\n```\n\nIf the table or one of the tables doesn't exist, it will be silently ignored.\n\n\n#### DB::dropView()\n\nDrops the view given by its name if it exists\n\n```php\npublic function dropView( $name ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the view or views\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003edropView( 'test' );\n$db-\u003edropView( ['test', 'test2'] );\n```\n\nIf the view or one of the views doesn't exist, it will be silently ignored.\n\n\n#### DB::exec()\n\nExecutes a custom SQL statement\n\n```php\npublic function exec( string $sql, array $params = [], array $types = [] ) : int\n```\n\n* @param **string** `$sql` Custom SQL statement\n* @param **array\u0026#60;int\u0026#124;string,mixed\u0026#62;** `$params` List of positional parameters or associative list of placeholders and parameters\n* @param **array\u0026#60;int\u0026#124;string,mixed\u0026#62;** `$types` List of DBAL data types for the positional or associative placeholder parameters\n* @return **int** Number of affected rows\n\nThe database changes are not applied immediately so always call up()\nbefore executing custom statements to make sure that the tables you want\nto use has been created before!\n\n**Examples:**\n\n```php\n$sql = 'UPDATE product SET status=? WHERE status=?';\n$num = $this-\u003edb()-\u003eexec( $sql, [1, 0] );\n```\n\n\n#### DB::for()\n\nExecutes a custom SQL statement if the database is of the given type\n\n```php\npublic function for( $type, $sql ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$type` Database type the statement should be executed for\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$sql` Custom SQL statement or statements\n* @return **self** Same object for fluid method calls\n\nAvailable database platform types are:\n\n- mysql\n- mariadb\n- postgresql\n- sqlite\n- sqlserver\n- oracle\n- db2\n\nThe database changes are not applied immediately so always call `up()`\nbefore executing custom statements to make sure that the tables you want\nto use has been created before!\n\n**Examples:**\n\n```php\n$db-\u003efor( 'mysql', 'CREATE INDEX idx_test_label ON test (label(16))' );\n\n$db-\u003efor( ['mysql', 'sqlite'], [\n\t'DROP INDEX unq_test_status',\n\t'UPDATE test SET status = 0 WHERE status IS NULL',\n] );\n```\n\n\n#### DB::hasColumn()\n\nChecks if the column or columns exists\n\n```php\npublic function hasColumn( string $table, $name ) : bool\n```\n\n* @param **string** `$table` Name of the table the column belongs to\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the column or columns\n* @return **bool** TRUE if the columns exists, FALSE if not\n\n**Examples:**\n\n```php\n$db-\u003ehasColumn( 'test', 'testcol' );\n$db-\u003ehasColumn( 'test', ['testcol', 'testcol2'] );\n```\n\n\n#### DB::hasForeign()\n\nChecks if the foreign key constraints exists\n\n```php\npublic function hasForeign( string $table, $name ) : bool\n```\n\n* @param **string** `$table` Name of the table the foreign key constraint belongs to\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the foreign key constraint or constraints\n* @return **bool** TRUE if the foreign key constraint exists, FALSE if not\n\n**Examples:**\n\n```php\n$db-\u003ehasForeign( 'test', 'fk_testcol' );\n$db-\u003ehasForeign( 'test', ['fk_testcol', 'fk_testcol2'] );\n```\n\n\n#### DB::hasIndex()\n\nChecks if the indexes exists\n\n```php\npublic function hasIndex( string $table, $name ) : bool\n```\n\n* @param **string** `$table` Name of the table the index belongs to\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the index or indexes\n* @return **bool** TRUE if the index exists, FALSE if not\n\n**Examples:**\n\n```php\n$db-\u003ehasIndex( 'test', 'idx_test_col' );\n$db-\u003ehasIndex( 'test', ['idx_test_col', 'idx_test_col2'] );\n```\n\n\n#### DB::hasSequence()\n\nChecks if the sequences exists\n\n```php\npublic function hasSequence( $name ) : bool\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the sequence or sequences\n* @return **bool** TRUE if the sequence exists, FALSE if not\n\n**Examples:**\n\n```php\n$db-\u003ehasSequence( 'seq_test' );\n$db-\u003ehasSequence( ['seq_test', 'seq_test2'] );\n```\n\n\n#### DB::hasTable()\n\nChecks if the tables exists\n\n```php\npublic function hasTable( $name ) : bool\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the table or tables\n* @return **bool** TRUE if the table exists, FALSE if not\n\n**Examples:**\n\n```php\n$db-\u003ehasTable( 'test' );\n$db-\u003ehasTable( ['test', 'test2'] );\n```\n\n\n#### DB::hasView()\n\nChecks if the views exists\n\n```php\npublic function hasView( $name ) : bool\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the view or views\n* @return **bool** TRUE if the view exists, FALSE if not\n\n**Examples:**\n\n```php\n$db-\u003ehasView( 'test' );\n$db-\u003ehasView( ['test', 'test2'] );\n```\n\n\n#### DB::insert()\n\nInserts a record into the given table\n\n```php\n\tpublic function insert( string $table, array $data ) : self\n```\n\n* @param **string** `$table` Name of the table\n* @param **array\u0026#60;string,mixed\u0026#62;** `$data` Key/value pairs of column name/value to insert\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003einsert( 'test', ['label' =\u003e 'myvalue', 'status' =\u003e true] );\n```\n\n\n#### DB::lastId()\n\nReturns the ID of the last inserted row into any database table\n\n```php\npublic function lastId() : string\n```\n\n* @return **string** Generated ID from the database\n\n**Caution:** This doesn't work for the Oracle platform because Doctrine DBAL doesn't support Oracle IDENTITY columns at the moment.\n\n**Examples:**\n\n```php\n$db-\u003elastId();\n```\n\n\n#### DB::name()\n\nReturns the name of the database\n\n```php\npublic function name() : string\n```\n\n* @return **string** Database name\n\n**Examples:**\n\n```php\n$db-\u003ename();\n```\n\n\n#### DB::q()\n\nQuotes a value\n\n```php\npublic function q( $value, $type = \\Doctrine\\DBAL\\ParameterType::STRING ) : string\n```\n\n* @param **mixed** `$value` Value to use in a non-prepared SQL query\n* @param **mixed** `$type` DBAL parameter type\n* @return **string** Quoted value\n\n**Examples:**\n\n```php\n$result = $db-\u003estmt()-\u003eselect( '*' )-\u003efrom( 'products' )\n\t-\u003ewhere( 'status = ' . $db-\u003eq( $_GET['status'] ) )-\u003eexecuteQuery();\n```\n\n\n#### DB::qi()\n\nQuotes a database identifier\n\n```php\npublic function qi( string $identifier ) : string\n```\n\n* @param **string** `$identifier` Identifier like table or column name\n* @return **string** Quoted identifier\n\n**Examples:**\n\n```php\n$result = $db-\u003estmt()-\u003eselect( $db-\u003eqi( 'key' ) )-\u003efrom( 'products' )-\u003eexecuteQuery();\n```\n\n\n#### DB::query()\n\nExecutes a custom SQL query\n\n```php\npublic function query( string $sql, array $params = [], array $types = [] ) : \\Doctrine\\DBAL\\Result\n```\n\n* @param **string** `$sql` Custom SQL statement\n* @param **array\u0026#60;int\u0026#124;string,mixed\u0026#62;** `$params` List of positional parameters or associative list of placeholders and parameters\n* @param **array\u0026#60;int\u0026#124;string,mixed\u0026#62;** `$types` List of DBAL data types for the positional or associative placeholder parameters\n* @return **\\Doctrine\\DBAL\\Result** DBAL result set object\n\n**Examples:**\n\n```php\n$result = $db-\u003equery( 'SELECT id, label, status FROM product WHERE label LIKE ?', ['test%'] );\n\nforeach( $result-\u003eiterateAssociative() as $row ) {\n\t// ...\n}\n```\n\n**Tip:** Check the [DBAL methods for retrieving data](https://github.com/doctrine/dbal) from *src/Result.php* for more information.\n\n\n#### DB::renameColumn()\n\nRenames a column or a list of columns\n\n```php\npublic function renameColumn( string $table, $from, string $to = null ) : self\n```\n\n* @param **string** `$table` Name of the table\n* @param **array\u0026#60;string,string\u0026#62;\u0026#124;string** `$from` Column name or array of old/new column names\n* @param **string\u0026#124;null** `$to` New column name ignored if first parameter is an array\n* @return **self** Same object for fluid method calls\n\nIf the column doesn't exist yet, the method will succeed but nothing will happen. No call to `up()` is required.\n\n**Limitations**\n\n* SQLite since 3.25.0\n\n**Examples:**\n\n```php\n// single column\n$db-\u003erenameColumn( 'testtable', 'test_col', 'test_column' );\n\n// rename several columns at once\n$db-\u003erenameColumn( 'testtable', ['tcol' =\u003e 'testcol', 'tcol2' =\u003e 'testcol2'] );\n```\n\n\n#### DB::renameIndex()\n\nRenames a column or a list of columns\n\n```php\npublic function renameIndex( string $table, $from, string $to = null ) : self\n```\n\n* @param **string** `$table` Name of the table\n* @param **array\u0026#60;string,string\u0026#62;\u0026#124;string** `$from` Index name or array of old/new index names\n* @param **string\u0026#124;null** `$to` New index name ignored if first parameter is an array\n* @return **self** Same object for fluid method calls\n\nIf the index doesn't exist yet, the method will succeed but nothing will happen. No call to `up()` is required.\n\n**Examples:**\n\n```php\n// single index\n$db-\u003erenameIndex( 'testtable', 'idxcol', 'idx_column' );\n\n// rename several indexes at once\n$db-\u003erenameIndex( 'testtable', ['idxcol' =\u003e 'idx_column', 'idxcol2' =\u003e 'idx_column2'] );\n```\n\n\n#### DB::renameTable()\n\nRenames a table or a list of tables\n\n```php\npublic function renameTable( $from, string $to = null ) : self\n```\n\n* @param **array\u0026#60;string,string\u0026#62;\u0026#124;string** `$from` Table name or array of old/new table names\n* @param **string\u0026#124;null** `$to` New table name ignored if first parameter is an array\n* @return **self** Same object for fluid method calls\n* @throws \\RuntimeException If an error occured\n\nIf the table doesn't exist yet, the method will succeed but nothing will happen. No call to `up()` is required.\n\n**Examples:**\n\n```php\n// single table\n$db-\u003erenameTable( 'testtable', 'newtable' );\n\n// rename several tables at once\n$db-\u003erenameTable( ['testtable' =\u003e 'newtable', 'oldtable' =\u003e 'testtable2'] );\n```\n\n\n#### DB::reset()\n\nReloads the actual Doctrine schema for the current database\n\n```php\npublic function reset() : self\n```\n\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003ereset();\n```\n\n\n#### DB::select()\n\nReturns the records from the given table\n\n```php\npublic function select( string $table, array $conditions = null ) : array\n```\n\n* @param **string** `$table` Name of the table\n* @param **array\u0026#60;string\u0026#62;\u0026#124;null** `$conditions` Key/value pairs of column names and value to compare with\n* @return **array\u0026#60;int,array\u0026#60;string,mixed\u0026#62;\u0026#62;** List of associative arrays containing column name/value pairs\n\n**Examples:**\n\n```php\n$db-\u003eselect( 'test', ['status' =\u003e false, 'type' =\u003e 'old'] );\n$db-\u003eselect( 'test' );\n```\n\nSeveral conditions passed in the second parameter are combined by \"AND\". If you\nneed more complex statements, use the [`stmt()`](#dbstmt) method instead.\n\n\n#### DB::sequence()\n\nReturns the sequence object for the given name\n\n```php\npublic function sequence( string $name, \\Closure $fcn = null ) : Sequence\n```\n\n* @param **string** `$name` Name of the sequence\n* @param **\\Closure\u0026#124;null** `$fcn` Anonymous function with ($sequence) parameter creating or updating the sequence definition\n* @return **\\Aimeos\\Upscheme\\Schema\\Sequence** Sequence object\n\nIf the sequence doesn't exist yet, it will be created. To persist the changes in the\ndatabase, you have to call `up()`.\n\n**Examples:**\n\n```php\n$sequence = $db-\u003esequence( 'seq_test' );\n\n$sequence = $db-\u003esequence( 'seq_test', function( $seq ) {\n\t$seq-\u003estart( 1000 )-\u003estep( 2 )-\u003ecache( 100 );\n} )-\u003eup();\n```\n\n\n#### DB::stmt()\n\nReturns the query builder for a new SQL statement\n\n```php\npublic function stmt() : \\Doctrine\\DBAL\\Query\\QueryBuilder\n```\n\n* @return **\\Doctrine\\DBAL\\Query\\QueryBuilder** Query builder object\n\n**Examples:**\n\n```php\n$db-\u003estmt()-\u003edelete( $db-\u003eqi( 'test' ) )\n\t-\u003ewhere( $db-\u003eqi( 'stat' ) . ' = ?' )-\u003esetParameter( 0, false )\n\t-\u003eexecuteStatement();\n\n$db-\u003estmt()-\u003eupdate( $db-\u003eqi( 'test' ) )\n\t-\u003ewhere( $db-\u003eqi( 'stat' ) . '', '?' )-\u003esetParameter( 0, true )\n\t-\u003eexecuteStatement();\n\n$result = $db-\u003estmt()-\u003eselect( $db-\u003eqi( 'id' ), $db-\u003eqi( 'code' ) )\n\t-\u003efrom( $db-\u003eqi( 'test' ) )\n\t-\u003ewhere( $db-\u003eqi( 'stat' ) . ' = 1' )\n\t-\u003eexecuteQuery();\n\nwhile( $row = $result-\u003efetchAssociative() ) {\n    $id = $row['id'];\n}\n```\n\n**Caution: ** You have to quote all table and column names yourself using `$db-\u003eqi()` method!\n\nFor more details about the available Doctrine QueryBuilder methods, please have\na look at the [Doctrine documentation](https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/query-builder.html#building-a-query).\n\n\n#### DB::table()\n\nReturns the table object for the given name\n\n```php\npublic function table( string $name, \\Closure $fcn = null ) : Table\n```\n\n* @param **string** `$name` Name of the table\n* @param **\\Closure\u0026#124;null** `$fcn` Anonymous function with ($table) parameter creating or updating the table definition\n* @return **\\Aimeos\\Upscheme\\Schema\\Table** Table object\n\nIf the table doesn't exist yet, it will be created. To persist the changes in the\ndatabase, you have to call `up()`.\n\n**Examples:**\n\n```php\n$table = $db-\u003etable( 'test' );\n\n$table = $db-\u003etable( 'test', function( $t ) {\n\t$t-\u003eid();\n\t$t-\u003estring( 'label' );\n\t$t-\u003ebool( 'status' );\n} )-\u003eup();\n```\n\n\n#### DB::transaction()\n\nExecutes the given closure within a transaction\n\n```php\npublic function transaction( \\Closure $fcn ) : self\n```\n\n* @param \\Closure $fcn Anonymous function with (\\Aimeos\\Upscheme\\Schema $db) parameter\n* @return self Same object for fluid method calls\n* @throws \\Exception If an error occurred\n\n**Examples:**\n\n```php\n$this-\u003edb()-\u003etransaction( function( $db ) {\n\t// $db-\u003einsert( ... )\n\t// $db-\u003eupdate( ... )\n\t// $db-\u003edelete( ... )\n} );\n```\n\n\n#### DB::toArray()\n\nReturns the objects as array from the database\n\n```php\npublic function toArray() : array\n```\n\n* @return array Associative list of sequences, tables and views\n\n**Examples:**\n\n```php\n$this-\u003edb()-\u003etoArray();\n```\n\nThe structure of the returned array is:\n\n```php\n[\n\t'sequence' =\u003e [\n\t\t'testseq' =\u003e [\n\t\t\t'name' =\u003e 'testseq',\n\t\t\t'cache' =\u003e null,\n\t\t\t'start' =\u003e 1000,\n\t\t\t'step' =\u003e 1\n\t\t]\n\t],\n\t'table' =\u003e [\n\t\t'testtable' =\u003e [\n\t\t\t'name' =\u003e 'testtable',\n\t\t\t'opt' =\u003e [\n\t\t\t\t'engine' =\u003e 'InnoDB',\n\t\t\t\t'collation' =\u003e 'utf8mb4_unicode_ci',\n\t\t\t\t'charset' =\u003e 'utf8mb4',\n\t\t\t\t'autoincrement' =\u003e 1,\n\t\t\t\t'comment' =\u003e ''\n\t\t\t],\n\t\t\t'col' =\u003e [\n\t\t\t\t'id' =\u003e [\n\t\t\t\t\t'name' =\u003e 'id',\n\t\t\t\t\t'type' =\u003e 'integer',\n\t\t\t\t\t'length' =\u003e null,\n\t\t\t\t\t'precision' =\u003e null,\n\t\t\t\t\t'scale' =\u003e 0,\n\t\t\t\t\t'null' =\u003e false,\n\t\t\t\t\t'seq' =\u003e 1\n\t\t\t\t\t'default' =\u003e null,\n\t\t\t\t\t'fixed' =\u003e false,\n\t\t\t\t\t'unsigned' =\u003e false,\n\t\t\t\t\t'comment' =\u003e '',\n\t\t\t\t\t'opt' =\u003e []\n\t\t\t\t],\n\t\t\t\t'parentid' =\u003e [\n\t\t\t\t\t'name' =\u003e 'parentid',\n\t\t\t\t\t'type' =\u003e 'bigint',\n\t\t\t\t\t'length' =\u003e null,\n\t\t\t\t\t'precision' =\u003e null,\n\t\t\t\t\t'scale' =\u003e 0,\n\t\t\t\t\t'null' =\u003e false,\n\t\t\t\t\t'seq' =\u003e false,\n\t\t\t\t\t'default' =\u003e null,\n\t\t\t\t\t'fixed' =\u003e false,\n\t\t\t\t\t'unsigned' =\u003e false,\n\t\t\t\t\t'comment' =\u003e '',\n\t\t\t\t\t'opt' =\u003e []\n\t\t\t\t],\n\t\t\t\t'label' =\u003e [\n\t\t\t\t\t'name' =\u003e 'label',\n\t\t\t\t\t'type' =\u003e 'string',\n\t\t\t\t\t'length' =\u003e 255,\n\t\t\t\t\t'precision' =\u003e null,\n\t\t\t\t\t'scale' =\u003e 0,\n\t\t\t\t\t'null' =\u003e false,\n\t\t\t\t\t'seq' =\u003e false,\n\t\t\t\t\t'default' =\u003e null,\n\t\t\t\t\t'fixed' =\u003e false,\n\t\t\t\t\t'unsigned' =\u003e false,\n\t\t\t\t\t'comment' =\u003e '',\n\t\t\t\t\t'opt' =\u003e [\n\t\t\t\t\t\t'charset' =\u003e 'utf8mb4',\n\t\t\t\t\t\t'collation' =\u003e 'utf8mb4_unicode_ci'\n\t\t\t\t\t]\n\t\t\t\t]\n\t\t\t],\n\t\t\t'index' =\u003e [\n\t\t\t\t'PRIMARY' =\u003e [\n\t\t\t\t\t'columns' =\u003e [\n\t\t\t\t\t\t\t0 =\u003e 'id'\n\t\t\t\t\t\t],\n\t\t\t\t\t'name' =\u003e 'PRIMARY',\n\t\t\t\t\t'flags' =\u003e [],\n\t\t\t\t\t'options' =\u003e [\n\t\t\t\t\t\t\t'lengths' =\u003e [\n\t\t\t\t\t\t\t\t\t0 =\u003e null\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t],\n\t\t\t\t\t'unique' =\u003e 1,\n\t\t\t\t\t'primary' =\u003e 1\n\t\t\t\t],\n\t\t\t],\n\t\t\t'foreign' =\u003e [\n\t\t\t\t'FK_6C73FFCA343B91AE' =\u003e [\n\t\t\t\t\t'localcol' =\u003e [\n\t\t\t\t\t\t0 =\u003e 'parentid'\n\t\t\t\t\t],\n\t\t\t\t\t'fktable' =\u003e 'test',\n\t\t\t\t\t'fkcol' =\u003e [\n\t\t\t\t\t\t0 =\u003e 'id'\n\t\t\t\t\t],\n\t\t\t\t\t'name' =\u003e 'FK_6C73FFCA343B91AE',\n\t\t\t\t\t'onDelete' =\u003e 'CASCADE',\n\t\t\t\t\t'onUpdate' =\u003e 'CASCADE'\n\t\t\t\t]\n\t\t\t]\n\t\t]\n\t],\n\t'view' =\u003e [\n\t\t'testview' =\u003e [\n\t\t\t'name' =\u003e 'testview',\n\t\t\t'sql' =\u003e 'select `testtable`.`id` AS `id`,`testtable`.`label` AS `label` from `testtable`'\n\t\t]\n\t]\n]\n```\n\n\n#### DB::type()\n\nReturns the type of the database\n\n```php\npublic function type() : string\n```\n\n* @return **string** Database type\n\nPossible values are:\n\n* db2\n* mariadb\n* mysql\n* oracle\n* postgresql\n* sqlite\n* sqlserver\n\n**Examples:**\n\n```php\n$type = $db-\u003etype();\n```\n\n\n#### DB::up()\n\nApplies the changes to the database schema\n\n```php\npublic function up() : self\n```\n\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003eup();\n```\n\n\n#### DB::update()\n\nUpdates the records from the given table\n\n```php\npublic function update( string $table, array $data, array $conditions = [] ) : self\n```\n\n* @param **string** `$table` Name of the table\n* @param **array\u0026#60;string,mixed\u0026#62;** `$data` Key/value pairs of column name/value to update\n* @param **array\u0026#60;string,mixed\u0026#62;** `$conditions` Key/value pairs of column names and value to compare with\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$db-\u003eupdate( 'test', ['status' =\u003e true] );\n$db-\u003eupdate( 'test', ['status' =\u003e true], ['status' =\u003e false, 'type' =\u003e 'new'] );\n```\n\nSeveral conditions passed in the second parameter are combined by \"AND\". If you\nneed more complex statements, use the [`stmt()`](#dbstmt) method instead.\n\n\n#### DB::view()\n\nCreates a view with the given name if it doesn't exist yet\n\n```php\npublic function view( string $name, string $sql, $for = null ) : self\n```\n\n* @param **string** `$name` Name of the view\n* @param **string** `$sql` SELECT statement for populating the view\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string\u0026#124;null** `$for` Database type this SQL should be used for (\"mysql\", \"mariadb\", \"postgresql\", \"sqlite\", \"sqlserver\", \"oracle\", \"db2\")\n* @return **self** Same object for fluid method calls\n\nIf the view doesn't exist yet, it will be created. Otherwise, nothing will happen.\n\n**Examples:**\n\n```php\n$db-\u003eview( 'testview', 'SELECT * FROM testtable' );\n$db-\u003eview( 'testview', 'SELECT id, label, status FROM testtable WHERE status = 1' );\n$db-\u003eview( 'testview', 'SELECT * FROM `testtable` WHERE `status` = 1', 'mysql' );\n```\n\n\n\n## Tables\n\n### Creating tables\n\nThe table scheme object you get by calling [`table()`](#dbtable) in your migration\ntask gives you full access to the table and you can add, change or remove columns,\nindexes and foreign keys, e.g.:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003eid();\n\t$table-\u003estring( 'label' );\n\t$table-\u003ecol( 'status', 'tinyint' )-\u003edefault( 0 );\n} );\n```\n\nBesides the [`col()`](#tablecol) method which can add columns of arbitrary types,\nthere are some shortcut methods for types available in all database server implementations:\n\n| Column type | Description |\n|-------------|-------------|\n| [bigid](#tablebigid) | BIGINT column with a sequence/autoincrement and a primary key assigned |\n| [bigint](#tablebigint) | BIGINT column with a range from −9223372036854775808 to 9223372036854775807 |\n| [binary](#tablebinary) | VARBINARY column with up to 255 bytes |\n| [blob](#tableblob) | BLOB column with up to 2GB |\n| [bool](#tablebool) | BOOLEAN/BIT/NUMBER colum, alias for \"boolean\" |\n| [boolean](#tableboolean) | BOOLEAN/BIT/NUMBER colum for TRUE/FALSE resp. 0/1 values |\n| [char](#tablechar) | CHAR column with a fixed number of characters |\n| [date](#tabledate) | DATE column in ISO date format (\"YYYY-MM-DD) without time and timezone |\n| [datetime](#tabledatetime) | DATETIME column in ISO date/time format (\"YYYY-MM-DD HH:mm:ss\" ) |\n| [tablesdatetimetz](#tabledatetimetz) | DATETIMETZ column in ISO date/time format but with varying timezone format |\n| [decimal](#tabledecimal) | DECIMAL column for numeric data with fixed-point precision (string in PHP) |\n| [float](#tablefloat) | FLOAT column for numeric data with a 8-byte floating-point precision |\n| [guid](#tableguid) | Globally unique identifier with 36 bytes |\n| [id](#tableid) | INTEGER column with a sequence/autoincrement and a primary key assigned |\n| [int](#tableint) | INTEGER colum, alias for \"integer\" |\n| [integer](#tableinteger) | INTEGER colum with a range from −2147483648 to 2147483647 |\n| [json](#tablejson) | JSON column for UTF-8 encoded JSON data |\n| [smallint](#tablesmallint) | INTEGER colum with a range from −32768 to 32767 |\n| [string](#tablestring) | VARCHAR column with up to 255 characters |\n| [text](#tabletext) | TEXT/CLOB column with up to 2GB characters |\n| [time](#tabletime) | TIME column in 24 hour \"HH:MM\" fromat, e.g. \"05:30\" or \"22:15\" |\n| [uuid](#tableuuid) | Globally unique identifier with 36 bytes, alias for \"guid\" |\n\n### Setting table options\n\nMySQL (or MariaDB, etc.) supports a few options to define aspects of the table. The *engine*\noption will specify the storage engine used for the table:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003eopt( 'engine', 'InnoDB' );\n} );\n```\n\nAs a shortcut, it's also possible to set the option as property:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003eengine = 'InnoDB';\n} );\n```\n\nTo create a *temporary* table, use:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003etemporary = true;\n} );\n```\n\nIt's also possible to set the default *charset* and *collation* for string and text columns:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003echarset = 'utf8mb4';\n\t$table-\u003ecollation = 'utf8mb4_unicode_ci';\n} );\n```\n\n**Note:** Collations are also supported by PostgreSQL and SQL Server but their values\nare different. Thus, it's not possible to use the same value for all server types. To\ncircumvent that problem, use the column [`opt()`](#columnopt) method and pass the database\nserver type as third parameter:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003eopt( 'charset', 'utf8mb4', 'mysql' );\n\t$table-\u003eopt( 'collation', 'utf8mb4_unicode_ci', 'mysql' );\n} );\n```\n\nNow, the default *charset* and *collation* will be only set for MySQL database servers\n(or MariaDB and similar forks).\n\nIn case you need to know the current values of the table options:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t// return the used table engine (only MySQL, MariaDB, etc.)\n\t$engine = $table-\u003eengine;\n\n\t// returns TRUE if it's a temporary table\n\t$isTemp = $table-\u003etemporary;\n\n\t// return the current charset\n\t$charset = $table-\u003echarset;\n\n\t// return the current collation\n\t$collation = $table-\u003ecollation;\n} );\n```\n\n### Checking table existence\n\nTo check if a table already exists, use the [`hasTable()`](#dbhastable) method:\n\n```php\nif( $this-\u003edb()-\u003ehasTable( 'users' ) ) {\n    // The \"users\" table exists\n}\n```\n\nYou can check for several tables at once too:\n\n```php\nif( $this-\u003edb()-\u003ehasTable( ['users', 'addresses'] ) ) {\n    // The \"users\" and \"addresses\" tables exist\n}\n```\n\nThe [`hasTable()`](#dbhastable) method will only return TRUE if all tables exist.\n\n### Changing tables\n\nBesides creating and accessing tables, the [`table()`](#dbtable) method from the schema object\ncan be used to update a table schema too. It accepts the table name and a closure\nthat will receive the table schema object.\n\nLet's create a table named *test* first including three columns:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003eid();\n\t$table-\u003estring( 'label' );\n\t$table-\u003ecol( 'status', 'tinyint' )-\u003edefault( 0 );\n} );\n```\n\nNow, we want to update the table in another migration by adding a *code* column and\nchanging the default value of the existing *status* column:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'code' );\n\t$table-\u003ecol( 'status', 'tinyint' )-\u003edefault( 1 );\n} );\n```\n\nThe changes will be persisted in the database as soon as the [`table()`](#dbtable) method\nreturns so there's no need to call [`up()`](#dbup) yourself afterwards. For the available\ncolumn types and options, refer to the [columns section](#columns).\n\n### Renaming tables\n\nThe database object returned by `$this-\u003edb()` can rename tables when using the\n[`renameTable()`](#dbrenametable) method:\n\n```php\n// Renames the table \"users\" to \"accounts\"\n$this-\u003edb()-\u003erenameTable( 'users', 'account' );\n```\n\nIt's also possible to rename several tables at once if you pass an associative\narray which old and new names as key/value pairs:\n\n```php\n// Renames the table \"users\" to \"accounts\" and \"blog\" to \"posts\"\n$this-\u003edb()-\u003erenameTable( ['users' =\u003e 'account', 'blog' =\u003e 'posts'] );\n```\n\nTables are only renamed if they exist. If a table doesn't exist any more, no error\nis reported:\n\n```php\n$this-\u003edb()-\u003erenameTable( 'notexist', 'newtable' );\n```\n\nIn that case, the method call will succeed but nothing will happen.\n\n### Dropping tables\n\nTo remove a table, you should use the [`dropTable()`](#dbdroptable) method from\nthe database schema:\n\n```php\n$this-\u003edb()-\u003edropTable( 'users' );\n```\n\nYou can also drop several tables at once by passing the list as array:\n\n```php\n$this-\u003edb()-\u003edropTable( ['users', 'addresses'] );\n```\n\nTables are only removed if they exist. If a table doesn't exist any more, no error\nis reported:\n\n```php\n$this-\u003edb()-\u003edropTable( 'notexist' );\n```\n\nIn that case, the method call will succeed but nothing will happen.\n\n### Table methods\n\n\u003cnav\u003e\n\u003cdiv class=\"method-header\"\u003e\u003ca href=\"#tables\"\u003eTables\u003c/a\u003e\u003c/div\u003e\n\u003cul class=\"method-list\"\u003e\n\t\u003cli\u003e\u003ca href=\"#table__call\"\u003e__call()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#table__get\"\u003e__get()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#table__set\"\u003e__set()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablebigid\"\u003ebigid()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablebigint\"\u003ebigint()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablebinary\"\u003ebinary()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableblob\"\u003eblob()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablebool\"\u003ebool()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableboolean\"\u003eboolean()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablechar\"\u003echar()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablecol\"\u003ecol()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledate\"\u003edate()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledatetime\"\u003edatetime()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledatetimetz\"\u003edatetimetz()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledecimal\"\u003edecimal()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledropcolumn\"\u003edropColumn()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledropindex\"\u003edropIndex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledropforeign\"\u003edropForeign()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabledropprimary\"\u003edropPrimary()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablefloat\"\u003efloat()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableforeign\"\u003eforeign()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableguid\"\u003eguid()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablehascolumn\"\u003ehasColumn()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablehasindex\"\u003ehasIndex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablehasforeign\"\u003ehasForeign()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableid\"\u003eid()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableindex\"\u003eindex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableint\"\u003eint()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableinteger\"\u003einteger()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablejson\"\u003ejson()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablename\"\u003ename()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableopt\"\u003eopt()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableprimary\"\u003eprimary()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablerenamecolumn\"\u003erenameColumn()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablerenameindex\"\u003erenameIndex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablesmallint\"\u003esmallint()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablespatial\"\u003espatial()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tablestring\"\u003estring()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabletext\"\u003etext()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tabletime\"\u003etime()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableunique\"\u003eunique()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableuuid\"\u003euuid()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#tableup\"\u003eup()\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/nav\u003e\n\n\n#### Table::__call()\n\nCalls custom methods or passes unknown method calls to the Doctrine table object\n\n```php\npublic function __call( string $method, array $args )\n```\n\n* @param **string** `$method` Name of the method\n* @param **array\u0026#60;mixed\u0026#62;** `$args` Method parameters\n* @return **mixed** Return value of the called method\n\n**Examples:**\n\nYou can register custom methods that have access to the class properties of the\nUpscheme Table object:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Table::macro( 'addConstraint', function( array $columns ) {\n\treturn $this-\u003eto-\u003eaddUniqueConstraint( $columns );\n} );\n\n$table-\u003eaddConstraint( ['col1', 'col2'] );\n```\n\nAvailable class properties are:\n\n`$this-\u003etable`\n: Doctrine table schema\n\n`$this-\u003eup`\n: Upscheme object\n\nFurthermore, you can call any [Doctrine table](https://github.com/doctrine/dbal)\nmethod from *src/Schema/Table.php* directly, e.g.:\n\n```php\n$table-\u003eaddUniqueConstraint( ['col1', 'col2'] );\n```\n\n#### Table::__get()\n\nReturns the value for the given table option\n\n```php\npublic function __get( string $name )\n```\n\n* @param **string** `$name` Table option name\n* @return **mixed** Table option value\n\nThe list of available table options are:\n\n* charset (MySQL)\n* collation (MySQL)\n* engine (MySQL)\n* temporary (MySQL)\n\n**Examples:**\n\n```php\n$engine = $table-\u003eengine;\n\n// same as\n$engine = $table-\u003eopt( 'engine' );\n```\n\n\n#### Table::__set()\n\nSets the new value for the given table option\n\n```php\npublic function __set( string $name, $value )\n```\n\n* @param **string** `$name` Table option name\n* @param mixed Table option value\n\nThe list of available table options are:\n\n* charset (MySQL)\n* collation (MySQL)\n* engine (MySQL)\n* temporary (MySQL)\n\n**Examples:**\n\n```php\n$table-\u003eengine = 'InnoDB';\n\n// same as\n$table-\u003eopt( 'engine', 'InnoDB' );\n```\n\n\n#### Table::bigid()\n\nCreates a new ID column of type \"bigint\" or returns the existing one\n\n```php\npublic function bigid( string $name = null ) : Column\n```\n\n* @param **string\u0026#124;null** `$name` Name of the ID column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThe column gets a sequence (autoincrement) and a primary key assigned automatically.\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003ebigid();\n$table-\u003ebigid( 'uid' );\n```\n\n\n#### Table::bigint()\n\nCreates a new column of type \"bigint\" or returns the existing one\n\n```php\npublic function bigint( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003ebigint( 'testcol' );\n```\n\n\n#### Table::binary()\n\nCreates a new column of type \"binary\" or returns the existing one\n\n```php\npublic function binary( string $name, int $length = 255 ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @param **int** `$length` Length of the column in bytes\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003ebinary( 'testcol' );\n$table-\u003ebinary( 'testcol', 32 );\n```\n\n\n#### Table::blob()\n\nCreates a new column of type \"blob\" or returns the existing one\n\n```php\npublic function blob( string $name, int $length = 0x7fff ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @param **int** `$length` Length of the column in bytes\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThe maximum length of a \"blob\" column is 2GB.\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003eblob( 'testcol' );\n$table-\u003eblob( 'testcol', 0x7fffffff );\n```\n\n\n#### Table::bool()\n\nCreates a new column of type \"boolean\" or returns the existing one\n\n```php\npublic function bool( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThis method is an alias for boolean().\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003ebool( 'testcol' );\n```\n\n\n#### Table::boolean()\n\nCreates a new column of type \"boolean\" or returns the existing one\n\n```php\npublic function boolean( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003eboolean( 'testcol' );\n```\n\n\n#### Table::char()\n\nCreates a new column of type \"char\" with a fixed type or returns the existing one\n\n```php\npublic function char( string $name, int $length ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @param **int** `$length` Length of the column in characters\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003echar( 'testcol', 3 );\n```\n\n\n#### Table::col()\n\nCreates a new column or returns the existing one\n\n```php\npublic function col( string $name, string $type = null ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @param **string\u0026#124;null** `$type` Type of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003ecol( 'testcol' );\n$table-\u003ecol( 'testcol', 'tinyint' );\n```\n\n\n#### Table::date()\n\nCreates a new column of type \"date\" or returns the existing one\n\n```php\npublic function date( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003edate( 'testcol' );\n```\n\n\n#### Table::datetime()\n\nCreates a new column of type \"datetime\" or returns the existing one\n\n```php\npublic function datetime( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003edatetime( 'testcol' );\n```\n\n\n#### Table::datetimetz()\n\nCreates a new column of type \"datetimetz\" or returns the existing one\n\n```php\npublic function datetimetz( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003edatetimetz( 'testcol' );\n```\n\n\n#### Table::decimal()\n\nCreates a new column of type \"decimal\" or returns the existing one\n\n```php\npublic function decimal( string $name, int $digits, int $decimals = 2 ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @param **int** `$digits` Total number of decimal digits including decimals\n* @param **int** `$decimals` Number of digits after the decimal point\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003edecimal( 'testcol', 10 ); // 10 digits incl. 2 decimals\n$table-\u003edecimal( 'testcol', 10, 4 ); // 10 digits incl. 4 decimals\n```\n\n\n#### Table::dropColumn()\n\nDrops the column given by its name if it exists\n\n```php\npublic function dropColumn( $name ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the column or columns\n* @return **self** Same object for fluid method calls\n\nIf the column or one of the columns doesn't exist, it will be silently ignored.\nThe change won't be applied until the migration task finishes or `up()` is called.\n\n**Examples:**\n\n```php\n$table-\u003edropColumn( 'testcol' );\n$table-\u003edropColumn( ['testcol', 'testcol2'] );\n```\n\n\n#### Table::dropIndex()\n\nDrops the index given by its name if it exists\n\n```php\npublic function dropIndex( $name ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the index or indexes\n* @return **self** Same object for fluid method calls\n\nIf the index or one of the indexes doesn't exist, it will be silently ignored.\nThe change won't be applied until the migration task finishes or `up()` is called.\n\n**Examples:**\n\n```php\n$table-\u003edropIndex( 'idx_test_col' );\n$table-\u003edropIndex( ['idx_test_col', 'idx_test_col2'] );\n```\n\n\n#### Table::dropForeign()\n\nDrops the foreign key constraint given by its name if it exists\n\n```php\npublic function dropForeign( $name ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the foreign key constraint or constraints\n* @return **self** Same object for fluid method calls\n\nIf the foreign key constraint or one of the constraints doesn't exist, it will be\nsilently ignored. The change won't be applied until the migration task finishes\nor `up()` is called.\n\n**Examples:**\n\n```php\n$table-\u003edropForeign( 'fk_test_col' );\n$table-\u003edropForeign( ['fk_test_col', 'fk_test_col2'] );\n```\n\n\n#### Table::dropPrimary()\n\nDrops the primary key if it exists\n\n```php\npublic function dropPrimary() : self\n```\n\n* @return **self** Same object for fluid method calls\n\nIf the primary key doesn't exist, it will be silently ignored. The change won't\nbe applied until the migration task finishes or `up()` is called.\n\n**Examples:**\n\n```php\n$table-\u003edropPrimary();\n```\n\n\n#### Table::float()\n\nCreates a new column of type \"float\" or returns the existing one\n\n```php\npublic function float( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003efloat( 'testcol' );\n```\n\n\n#### Table::foreign()\n\nCreates a new foreign key or returns the existing one\n\n```php\npublic function foreign( $localcolumn, string $foreigntable, $foreigncolumn = 'id', string $name = null ) : Foreign\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$localcolumn` Name of the local column or columns\n* @param **string** `$foreigntable` Name of the referenced table\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$foreigncolumn` Name of the referenced column or columns\n* @param **string\u0026#124;null** `$name` Name of the foreign key constraint and foreign key index or NULL for autogenerated name\n* @return **\\Aimeos\\Upscheme\\Schema\\Foreign** Foreign key constraint object\n\nThe length of the foreign key name shouldn't be longer than 30 characters for\nmaximum compatibility.\n\n**Examples:**\n\n```php\n$table-\u003eforeign( 'parentid', 'test' );\n$table-\u003eforeign( 'parentid', 'test', 'uid' );\n$table-\u003eforeign( 'parentid', 'test', 'id', 'fk_test_pid' );\n$table-\u003eforeign( ['parentid', 'siteid'], 'test', ['uid', 'siteid'] );\n```\n\n\n#### Table::guid()\n\nCreates a new column of type \"guid\" or returns the existing one\n\n```php\npublic function guid( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003eguid( 'testcol' );\n```\n\n\n#### Table::hasColumn()\n\nChecks if the column exists\n\n```php\npublic function hasColumn( $name ) : bool\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the column or columns\n* @return **bool** TRUE if the columns exists, FALSE if not\n\n**Examples:**\n\n```php\n$table-\u003ehasColumn( 'testcol' );\n$table-\u003ehasColumn( ['testcol', 'testcol2'] );\n```\n\n\n#### Table::hasIndex()\n\nChecks if the index exists\n\n```php\npublic function hasIndex( $name ) : bool\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the index or indexes\n* @return **bool** TRUE if the indexes exists, FALSE if not\n\n**Examples:**\n\n```php\n$table-\u003ehasIndex( 'idx_test_col' );\n$table-\u003ehasIndex( ['idx_test_col', 'idx_test_col2'] );\n```\n\n\n#### Table::hasForeign()\n\nChecks if the foreign key constraint exists\n\n```php\npublic function hasForeign( $name ) : bool\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$name` Name of the foreign key constraint or constraints\n* @return **bool** TRUE if the foreign key constraints exists, FALSE if not\n\n**Examples:**\n\n```php\n$table-\u003ehasForeign( 'fk_test_col' );\n$table-\u003ehasForeign( ['fk_test_col', 'fk_test_col2'] );\n```\n\n\n#### Table::id()\n\nCreates a new ID column of type \"integer\" or returns the existing one\n\n```php\npublic function id( string $name = null ) : Column\n```\n\n* @param **string\u0026#124;null** `$name` Name of the ID column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThe column gets a sequence (autoincrement) and a primary key assigned automatically.\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003eid();\n$table-\u003eid( 'uid' );\n```\n\n\n#### Table::index()\n\nCreates a new index or replaces an existing one\n\n```php\npublic function index( $columns, string $name = null ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$columns` Name of the columns or columns spawning the index\n* @param **string\u0026#124;null** `$name` Index name or NULL for autogenerated name\n* @return **self** Same object for fluid method calls\n\nThe length of the index name shouldn't be longer than 30 characters for maximum\ncompatibility.\n\n**Examples:**\n\n```php\n$table-\u003eindex( 'testcol' );\n$table-\u003eindex( ['testcol', 'testcol2'] );\n$table-\u003eindex( 'testcol', 'idx_test_testcol );\n```\n\n\n#### Table::int()\n\nCreates a new column of type \"integer\" or returns the existing one\n\n```php\npublic function int( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThis method is an alias for integer().\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003eint( 'testcol' );\n```\n\n\n#### Table::integer()\n\nCreates a new column of type \"integer\" or returns the existing one\n\n```php\npublic function integer( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003einteger( 'testcol' );\n```\n\n\n#### Table::json()\n\nCreates a new column of type \"json\" or returns the existing one\n\n```php\npublic function json( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003ejson( 'testcol' );\n```\n\n\n#### Table::name()\n\nReturns the name of the table\n\n```php\npublic function name() : string\n```\n\n* @return **string** Table name\n\n**Examples:**\n\n```php\n$tablename = $table-\u003ename();\n```\n\n\n#### Table::opt()\n\nSets a custom schema option or returns the current value\n\n```php\npublic function opt( string $name, $value = null )\n```\n\n* @param **string** `$name` Name of the table-related custom schema option\n* @param **mixed** `$value` Value of the custom schema option\n* @return **self\u0026#124;mixed** Same object for setting value, current value without second parameter\n\nAvailable custom schema options are:\n\n* charset (MySQL)\n* collation (MySQL)\n* engine (MySQL)\n* temporary (MySQL)\n\n**Examples:**\n\n```php\n$charset = $table-\u003eopt( 'charset' );\n$table-\u003eopt( 'charset', 'utf8' )-\u003eopt( 'collation', 'utf8_bin' );\n\n// Magic methods:\n$charset = $table-\u003echarset;\n$table-\u003echarset = 'binary';\n```\n\n\n#### Table::primary()\n\nCreates a new primary index or replaces an existing one\n\n```php\npublic function primary( $columns, string $name = null ) : self\n```\n\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string** `$columns` Name of the columns or columns spawning the index\n* @param **string\u0026#124;null** `$name` Index name or NULL for autogenerated name\n* @return **self** Same object for fluid method calls\n\nThe length of the index name shouldn't be longer than 30 characters for maximum\ncompatibility.\n\n**Examples:**\n\n```php\n$table-\u003eprimary( 'testcol' );\n$table-\u003eprimary( ['testcol', 'testcol2'] );\n$table-\u003eprimary( 'testcol', 'pk_test_testcol' );\n```\n\n\n#### Table::renameColumn()\n\nRenames a column or a list of columns\n\n```php\npublic function renameColumn( $from, string $to = null ) : self\n```\n\n* @param **array\u0026#60;string,string\u0026#62;\u0026#124;string** `$from` Column name or array of old/new column names\n* @param **string\u0026#124;null** `$to` New column name ignored if first parameter is an array\n* @return **self** Same object for fluid method calls\n* @throws \\RuntimeException If an error occured\n\n**Examples:**\n\n```php\n// single column\n$table-\u003erenameColumn( 'test_col', 'test_column' );\n\n// rename several columns at once\n$table-\u003erenameColumn( ['tcol' =\u003e 'testcol', 'tcol2' =\u003e 'testcol2'] );\n```\n\n\n#### Table::renameIndex()\n\nRenames an index or a list of indexes\n\n```php\npublic function renameIndex( $from, string $to = null ) : self\n```\n\n* @param **array\u0026#124;string** `$from` Index name or array of old/new index names (if new index name is NULL, it will be generated)\n* @param **string\u0026#124;null** `$to` New index name or NULL for autogenerated name (ignored if first parameter is an array)\n* @return **self** Same object for fluid method calls\n\nThe length of the indexes name shouldn't be longer than 30 characters for maximum\ncompatibility.\n\n**Examples:**\n\n```php\n// generate a new name automatically\n$table-\u003erenameIndex( 'test_col_index' );\n\n// custom name\n$table-\u003erenameIndex( 'test_col_index', 'idx_test_col' );\n\n// rename several indexes at once\n$table-\u003erenameIndex( ['test_col_index' =\u003e null, 'test_index' =\u003e 'idx_test_col'] );\n```\n\n\n#### Table::smallint()\n\nCreates a new column of type \"smallint\" or returns the existing one\n\n```php\npublic function smallint( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003esmallint( 'testcol' );\n```\n\n\n#### Table::spatial()\n\nCreates a new spatial index or replaces an existing one\n\n```php\npublic function spatial( $columns, string $name = null ) : self\n```\n\n* @param **array\u0026#124;string** `$columns` Name of the columns or columns spawning the index\n* @param **string\u0026#124;null** `$name` Index name or NULL for autogenerated name\n* @return **self** Same object for fluid method calls\n\nThe length of the index name shouldn't be longer than 30 characters for maximum\ncompatibility.\n\n**Examples:**\n\n```php\n$table-\u003espatial( 'testcol' );\n$table-\u003espatial( ['testcol', 'testcol2'] );\n$table-\u003espatial( 'testcol', 'idx_test_testcol' );\n```\n\n\n#### Table::string()\n\nCreates a new column of type \"string\" or returns the existing one\n\n```php\npublic function string( string $name, int $length = 255 ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @param **int** `$length` Length of the column in characters\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThis type should be used for up to 255 characters. For more characters, use the\n\"text\" type. If the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003estring( 'testcol' );\n$table-\u003estring( 'testcol', 32 );\n```\n\n\n#### Table::text()\n\nCreates a new column of type \"text\" or returns the existing one\n\n```php\npublic function text( string $name, int $length = 0xffff ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @param **int** `$length` Length of the column in characters\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThe maximum length of a \"text\" column is 2GB.\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003etext( 'testcol' );\n$table-\u003etext( 'testcol', 0x7fffffff );\n```\n\n\n#### Table::time()\n\nCreates a new column of type \"time\" or returns the existing one\n\n```php\npublic function time( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nIf the column doesn't exist yet, it will be created.\nThis datatype is not available when using Oracle databases.\n\n**Examples:**\n\n```php\n$table-\u003etime( 'testcol' );\n```\n\n\n#### Table::unique()\n\nCreates a new unique index or replaces an existing one\n\n```php\npublic function unique( $columns, string $name = null ) : self\n```\n\n* @param **array\u0026#124;string** `$columns` Name of the columns or columns spawning the index\n* @param **string\u0026#124;null** `$name` Index name or NULL for autogenerated name\n* @return **self** Same object for fluid method calls\n\nThe length of the index name shouldn't be longer than 30 characters for maximum\ncompatibility.\n\n**Examples:**\n\n```php\n$table-\u003eunique( 'testcol' );\n$table-\u003eunique( ['testcol', 'testcol2'] );\n$table-\u003eunique( 'testcol', 'unq_test_testcol' );\n```\n\n\n#### Table::uuid()\n\nCreates a new column of type \"guid\" or returns the existing one\n\n```php\npublic function uuid( string $name ) : Column\n```\n\n* @param **string** `$name` Name of the column\n* @return **\\Aimeos\\Upscheme\\Schema\\Column** Column object\n\nThis method is an alias for guid().\nIf the column doesn't exist yet, it will be created.\n\n**Examples:**\n\n```php\n$table-\u003euuid( 'testcol' );\n```\n\n\n#### Table::up()\n\nApplies the changes to the database schema\n\n```php\npublic function up() : self\n```\n\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$table-\u003eup();\n```\n\n\n\n## Columns\n\n### Adding columns\n\nThe column schema object you get by calling [`col()`](#tablecol) in your migration\ntask gives you access to all column properties. There are also shortcuts available\nfor column types supported by all databases. Each column can be changed by one or\nmore modifier methods and you can also add indexes to single columns, e.g.:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003eid()-\u003eunsigned( true );\n\t$table-\u003estring( 'label' )-\u003eindex();\n\t$table-\u003ecol( 'status', 'tinyint' )-\u003edefault( 0 );\n} );\n```\n\nThe example will add the following columns:\n\n* *id* of type integer with unsigend modifier\n* *label* of type string with 255 chars and an index\n* *status* of type tinyint (MySQL only) with a default value of zero\n\n### Available column types\n\nThere are some shortcut methods for column types available in all database server\nimplementations:\n\n| Column type | Description |\n|-------------|-------------|\n| [bigid](#tablebigid) | BIGINT column with a sequence/autoincrement and a primary key assigned |\n| [bigint](#tablebigint) | BIGINT column with a range from −9223372036854775808 to 9223372036854775807 |\n| [binary](#tablebinary) | VARBINARY column with up to 255 bytes |\n| [blob](#tableblob) | BLOB column with up to 2GB |\n| [bool](#tablebool) | BOOLEAN/BIT/NUMBER colum, alias for \"boolean\" |\n| [boolean](#tableboolean) | BOOLEAN/BIT/NUMBER colum for TRUE/FALSE resp. 0/1 values |\n| [char](#tablechar) | CHAR column with a fixed number of characters |\n| [date](#tabledate) | DATE column in ISO date format (\"YYYY-MM-DD) without time and timezone |\n| [datetime](#tabledatetime) | DATETIME column in ISO date/time format (\"YYYY-MM-DD HH:mm:ss\" ) |\n| [tablesdatetimetz](#tabledatetimetz) | DATETIMETZ column in ISO date/time format but with varying timezone format |\n| [decimal](#tabledecimal) | DECIMAL column for numeric data with fixed-point precision (string in PHP) |\n| [float](#tablefloat) | FLOAT column for numeric data with a 8-byte floating-point precision |\n| [guid](#tableguid) | Globally unique identifier with 36 bytes |\n| [id](#tableid) | INTEGER column with a sequence/autoincrement and a primary key assigned |\n| [int](#tableint) | INTEGER colum, alias for \"integer\" |\n| [integer](#tableinteger) | INTEGER colum with a range from −2147483648 to 2147483647 |\n| [json](#tablejson) | JSON column for UTF-8 encoded JSON data |\n| [smallint](#tablesmallint) | INTEGER colum with a range from −32768 to 32767 |\n| [string](#tablestring) | VARCHAR column with up to 255 characters |\n| [text](#tabletext) | TEXT/CLOB column with up to 2GB characters |\n| [time](#tabletime) | TIME column in 24 hour \"HH:MM\" fromat, e.g. \"05:30\" or \"22:15\" |\n| [uuid](#tableuuid) | Globally unique identifier with 36 bytes, alias for \"guid\" |\n\nTo add database specific column types, use the [`col()`](#tablecol) method, e.g.:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003ecol( 'status', 'tinyint' );\n} );\n```\n\n### Column modifiers\n\nIt's also possible to change column definitions by calling one or more column\nmodifier methods:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003eint( 'number' )-\u003enull( true )-\u003eunsigned( true );\n} );\n```\n\nThe available column modifier methods are:\n\n| Column modifier | Description |\n|-----------------|-------------|\n| [autoincrement(true)](#columnautoincrement) | Set INTEGER columns as auto-incrementing (alias for [`seq()`](#columnseq)) |\n| [charset('utf8')](#columncharset) | The character set used by the column (MySQL) |\n| [collation('binary')](#columncollation) | The column collation (MySQL/PostgreSQL/Sqlite/SQLServer but not compatible) |\n| [comment('comment')](#columncomment) | Add a comment to a column (MySQL/PostgreSQL/Oracle/SQLServer) |\n| [default(1)](#columndefault) | Default value of the column if no value was specified (default: `NULL`) |\n| [fixed(true)](#columnfixed) | If string or binary columns should have a fixed length |\n| [index('idx_col')](#columnindex) | Add an index to the column, index name is optional |\n| [length(32)](#columnlength) | The max. length of string and binary columns |\n| [null(true)](#columnnull) | Allow NULL values to be inserted into the column |\n| [precision(12)](#columnlength) | The max. number of digits stored in DECIMAL and FLOAT columns incl. decimal digits |\n| [primary('pk_col')](#columnprimary) | Add a primary key to the column, primary key name is optional |\n| [scale(2)](#columnscale) | The exact number of decimal digits used in DECIMAL and FLOAT columns |\n| [seq(true)](#columnseq) | Set INTEGER columns as auto-incrementing if no value was specified |\n| [spatial('idx_col')](#columnspatial) | Add a spatial (geo) index to the column, index name is optional |\n| [unique('unq_col')](#columnunique) | Add an unique index to the column, index name is optional |\n| [unsigned(true)](#columnunsigned) | Allow unsigned INTEGER values only (MySQL) |\n\nTo set custom schema options for columns, use the [`opt()`](#columnopt) method, e.g.:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'code' )-\u003eopt( 'collation', 'utf8mb4' );\n} );\n```\n\nIt's even possible to set column modifiers for a specific database implementation\nby passing the database type as third parameter:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'code' )-\u003eopt( 'collation', 'utf8mb4', 'mysql' );\n} );\n```\n\n### Checking column existence\n\nTo check if a column already exists, use the [`hasColumn()`](#dbhascolumn) method:\n\n```php\nif( $this-\u003edb()-\u003ehasColumn( 'users', 'name' ) ) {\n    // The \"name\" column in the \"users\" table exists\n}\n```\n\nYou can check for several columns at once too. In that case, the [`hasColumn()`](#dbhascolumn)\nmethod will only return TRUE if all columns exist:\n\n```php\nif( $this-\u003edb()-\u003ehasColumn( 'users', ['name', 'status'] ) ) {\n    // The \"name\" and \"status\" columns in the \"users\" table exists\n}\n```\n\nIf you already have a table object, you can use [`hasColumn()`](#tablehascolumn)\nas well:\n\n```php\nif( $table-\u003ehasColumn( 'name' ) ) {\n    // The \"name\" column in the table exists\n}\n\nif( $table-\u003ehasColumn( ['name', 'status'] ) ) {\n    // The \"name\" and \"status\" columns in the table exists\n}\n```\n\nBesides columns, you can also check if column modifiers are set and which value\nthey have:\n\n```php\nif( $table-\u003estring( 'code' )-\u003enull() ) {\n\t// The \"code\" columns is nullable\n}\n```\n\nRetrieving the current column modifier values is possible using these methods:\n\n| Column modifier | Description |\n|-----------------|-------------|\n| [autoincrement()](#columnautoincrement) | TRUE if the the column is auto-incrementing (alias for [`seq()`](#columnseq)) |\n| [charset()](#columncharset) | Used character set (MySQL) |\n| [collation()](#columncollation) | Used collation (MySQL/PostgreSQL/Sqlite/SQLServer but not compatible) |\n| [comment()](#columncomment) | Comment associated to the column (MySQL/PostgreSQL/Oracle/SQLServer) |\n| [default()](#columndefault) | Default value of the column |\n| [fixed()](#columnfixed) | TRUE if the string or binary column has a fixed length |\n| [length()](#columnlength) | The maximum length of the string or binary column |\n| [null()](#columnnull) | TRUE if NULL values are allowed |\n| [precision()](#columnlength) | The maximum number of digits stored in DECIMAL and FLOAT columns incl. decimal digits |\n| [scale()](#columnscale) | The exact number of decimal digits used in DECIMAL and FLOAT columns |\n| [seq()](#columnseq) | TRUE if the column is auto-incrementing |\n| [unsigned()](#columnunsigned) | TRUE if only unsigned INTEGER values are allowed (MySQL) |\n\nTo check for non-standard column modifiers, use the [`opt()`](#columnopt) method\nwithout second parameter. Then, it will return the current value of the column modifier:\n\n```php\nif( $table-\u003estring( 'code' )-\u003eopt( 'charset' ) === 'utf8' ) {\n\t// The \"code\" columns uses UTF-8 charset (MySQL only)\n}\n```\n\n### Changing columns\n\nIt's possible to change most column modifiers like the length of a string column:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'code' )-\u003elength( 64 );\n} );\n```\n\nSome methods also offer additional parameters to set most often used modifiers\ndirectly:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'code', 64 );\n} );\n```\n\nIf you need to change the column modifiers immediately because you want to migrate\nthe rows afterwards, use the [`up()`](#columnup) method to persist the changes:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'code', 64 )-\u003enull( true )-\u003eup();\n\t// modify rows from \"test\" table\n} );\n```\n\nChanging the column type is possible by using the new method for the appropriate\ntype or the [`col()`](#tablecol) method:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003etext( 'code' );\n\t// or\n\t$table-\u003ecol( 'code', 'text' );\n} );\n```\n\nBe aware that not all column types can be changed into another type or at\nleast not without data loss. You can change an INTEGER column to a BIGINT column\nwithout problem but the other way round will fail. The same happens if you want\nto change a VARCHAR column (string) into an INTEGER column.\n\n### Renaming columns\n\nTo rename columns, use the [`renameColumn()`](#dbrenamecolumn) method of the DB schema:\n\n```php\n// single column\n$this-\u003edb()-\u003erenameColumn( 'testtable', 'label', 'name' );\n\n// multiple columns\n$this-\u003edb()-\u003erenameColumn( 'testtable', ['label' =\u003e 'name', 'stat' =\u003e 'status'] );\n```\n\nIf a table object is already available, you can use its [`renameColumn()`](#tablerenamecolumn)\nmethod to rename one or more columns:\n\n```php\n$this-\u003edb()-\u003etable( 'testtable', function( $table ) {\n\t// single column\n\t$table-\u003erenameColumn( 'label', 'name' );\n\n\t// multiple columns\n\t$table-\u003erenameColumn( ['label' =\u003e 'name', 'stat' =\u003e 'status'] );\n} );\n```\n\nIn all cases, columns are only removed if they exist. No error is reported if one\nor more columns doesn't exist in the table.\n\n### Dropping columns\n\nTo drop columns, use the [`dropColumn()`](#dbdropcolumn) method from the DB schema\nobject:\n\n```php\n$this-\u003edb()-\u003edropColumn( 'users', 'name' );\n```\n\nYou can drop several columns at once if you pass the name of all columns you want\nto drop as array:\n\n```php\n$this-\u003edb()-\u003edropColumn( 'users', ['name', 'status'] );\n```\n\nIf you already have a table object, you can use [`dropColumn()`](#tabledropcolumn)\ntoo:\n\n```php\n// single column\n$table-\u003edropColumn( 'name' );\n\n// multiple columns\n$table-\u003edropColumn( ['name', 'status'] );\n```\n\nIn all cases, columns are only removed if they exist. No error is reported if one\nor more columns doesn't exist in the table.\n\n### Column methods\n\n\u003cnav\u003e\n\u003cdiv class=\"method-header\"\u003e\u003ca href=\"#columns\"\u003eColumns\u003c/a\u003e\u003c/div\u003e\n\u003cul class=\"method-list\"\u003e\n\t\u003cli\u003e\u003ca href=\"#column__call\"\u003e__call()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#column__get\"\u003e__get()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#column__set\"\u003e__set()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnautoincrement\"\u003eautoincrement()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columncharset\"\u003echarset()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columncollation\"\u003ecollation()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columncomment\"\u003ecomment()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columndefault\"\u003edefault()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnfixed\"\u003efixed()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnindex\"\u003eindex()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnlength\"\u003elength()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnname\"\u003ename()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnnull\"\u003enull()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnopt\"\u003eopt()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnprecision\"\u003eprecision()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnprimary\"\u003eprimary()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnscale\"\u003escale()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnseq\"\u003eseq()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnspatial\"\u003espatial()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columntype\"\u003etype()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnunique\"\u003eunique()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnunsigned\"\u003eunsigned()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#columnup\"\u003eup()\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/nav\u003e\n\n\n#### Column::__call()\n\nCalls custom methods or passes unknown method calls to the Doctrine column object\n\n```php\npublic function __call( string $method, array $args )\n```\n\n* @param **string** `$method` Name of the method\n* @param **array\u0026#60;mixed\u0026#62;** `$args` Method parameters\n* @return **mixed** Return value of the called method\n\n**Examples:**\n\nYou can register custom methods that have access to the class properties of the\nUpscheme Column object:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Column::macro( 'platform', function( array $options ) {\n\treturn $this-\u003eto-\u003esetPlatformOptions( $options );\n} );\n\n$column-\u003eplatform( ['option' =\u003e 'value'] );\n```\n\nAvailable class properties are:\n\n`$this-\u003edb`\n: Upscheme DB object\n\n`$this-\u003etable`\n: Doctrine table schema\n\n`$this-\u003ecolumn`\n: Doctrine column schema\n\nFurthermore, you can call any [Doctrine column](https://github.com/doctrine/dbal)\nmethod from *src/Schema/Column.php* directly, e.g.:\n\n```php\n$column-\u003esetPlatformOptions( ['option' =\u003e 'value'] );\n```\n\n#### Column::__get()\n\nReturns the value for the given column option\n\n```php\npublic function __get( string $name )\n```\n\n* @param **string** `$name` Column option name\n* @return **mixed** Column option value\n\nThe list of available column options are:\n\n* charset (MySQL)\n* collation (MySQL, PostgreSQL, Sqlite and SQL Server)\n* check\n* unique (All)\n\n**Examples:**\n\n```php\n$charset = $column-\u003echarset;\n\n// same as\n$charset = $column-\u003eopt( 'charset' );\n```\n\n\n#### Column::__set()\n\nSets the new value for the given column option\n\n```php\npublic function __set( string $name, $value )\n```\n\n* @param **string** `$name` Column option name\n* @param **mixed** `$value` Column option value\n\nThe list of available column options are:\n\n* charset (MySQL)\n* collation (MySQL, PostgreSQL, Sqlite and SQL Server)\n* check\n* unique (All)\n\n**Examples:**\n\n```php\n$column-\u003echarset = 'utf8';\n\n// same as\n$column-\u003eopt( 'charset', 'utf8' );\n```\n\n\n#### Column::autoincrement()\n\nSets the column as autoincrement or returns the current value\n\n```php\npublic function autoincrement( bool $value = null )\n```\n\n* @param **bool\u0026#124;null** `$value` New autoincrement flag or NULL to return current value\n* @return **self\u0026#124;bool** Same object for setting the value, current value without parameter\n\nThis method is an alias for the [`seq()`](#columnseq) method.\n\n**Examples:**\n\n```php\n$value = $column-\u003eautoincrement();\n$column-\u003eautoincrement( true );\n```\n\n\n#### Column::charset()\n\nSets the column charset or returns the current value\n\n```php\npublic function charset( string $value = null )\n```\n\n* @param **string\u0026#124;null** `$value` New column charset or NULL to return current value\n* @return **self\u0026#124;string** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$comment = $column-\u003echarset();\n$column-\u003echarset( 'utf8' );\n```\n\n\n#### Column::collation()\n\nSets the column collation or returns the current value\n\n```php\npublic function collation( string $value = null )\n```\n\n* @param **string\u0026#124;null** `$value` New column collation or NULL to return current value\n* @return **self\u0026#124;string** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$comment = $column-\u003ecollation();\n$column-\u003ecollation( 'binary' );\n```\n\n\n#### Column::comment()\n\nSets the column comment or returns the current value\n\n```php\npublic function comment( string $value = null )\n```\n\n* @param **string\u0026#124;null** `$value` New column comment or NULL to return current value\n* @return **self\u0026#124;string** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$comment = $column-\u003ecomment();\n$column-\u003ecomment( 'column comment' );\n```\n\n\n#### Column::default()\n\nSets the column default value or returns the current value\n\n```php\npublic function default( $value = null )\n```\n\n* @param **mixed** `$value` New column default value or NULL to return current value\n* @return **self\u0026#124;mixed** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003edefault();\n$column-\u003edefault( 0 );\n```\n\n\n#### Column::fixed()\n\nSets the column fixed flag or returns the current value\n\n```php\npublic function fixed( bool $value = null )\n```\n\n* @param **bool\u0026#124;null** `$value` New column fixed flag or NULL to return current value\n* @return **self\u0026#124;bool** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003efixed();\n$column-\u003efixed( true );\n```\n\n\n#### Column::index()\n\nCreates a regular index for the column\n\n```php\npublic function index( string $name = null ) : self\n```\n\n* @param **string\u0026#124;null** `$name` Name of the index or NULL to generate automatically\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$column-\u003eindex();\n$column-\u003eindex( 'idx_col' );\n```\n\n\n#### Column::length()\n\nSets the column length or returns the current value\n\n```php\npublic function length( int $value = null )\n```\n\n* @param **int\u0026#124;null** `$value` New column length or NULL to return current value\n* @return **self\u0026#124;int** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003elength();\n$column-\u003elength( 32 );\n```\n\n\n#### Column::name()\n\nReturns the name of the column\n\n```php\npublic function name() : string\n```\n\n* @return **string** Column name\n\n**Examples:**\n\n```php\n$name = $column-\u003ename();\n```\n\n\n#### Column::null()\n\nSets the column null flag or returns the current value\n\n```php\npublic function null( bool $value = null )\n```\n\n* @param **bool\u0026#124;null** `$value` New column null flag or NULL to return current value\n* @return **self\u0026#124;bool** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003enull();\n$column-\u003enull( true );\n```\n\n\n#### Column::opt()\n\nSets the column option value or returns the current value\n\n```php\npublic function opt( string $option, $value = null, $for = null )\n```\n\n* @param **string** `$option` Column option name\n* @param **mixed** `$value` New column option value or NULL to return current value\n* @param **array\u0026#60;string\u0026#62;\u0026#124;string\u0026#124;null** `$for` Database type this option should be used for (\"mysql\", \"mariadb\", \"postgresql\", \"sqlite\", \"sqlserver\", \"oracle\", \"db2\")\n* @return **self\u0026#124;mixed** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003eopt( 'length' );\n$column-\u003eopt( 'length', 64 );\n```\n\n\n#### Column::precision()\n\nSets the column precision or returns the current value\n\n```php\npublic function precision( int $value = null )\n```\n\n* @param **int\u0026#124;null** `$value` New column precision value or NULL to return current value\n* @return **self\u0026#124;int** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003eprecision();\n$column-\u003eprecision( 10 );\n```\n\n\n#### Column::primary()\n\nCreates a primary index for the column\n\n```php\npublic function primary( string $name = null ) : self\n```\n\n* @param **string\u0026#124;null** `$name` Name of the index or NULL to generate automatically\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$column-\u003eprimary();\n$column-\u003eprimary( 'pk_col' );\n```\n\n\n#### Column::scale()\n\nSets the column scale or returns the current value\n\n```php\npublic function scale( int $value = null )\n```\n\n* @param **int\u0026#124;null** `$value` New column scale value or NULL to return current value\n* @return **self\u0026#124;int** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003escale();\n$column-\u003escale( 3 );\n```\n\n\n#### Column::seq()\n\nSets the column as autoincrement or returns the current value\n\n```php\npublic function seq( bool $value = null )\n```\n\n* @param **bool\u0026#124;null** `$value` New autoincrement flag or NULL to return current value\n* @return **self\u0026#124;bool** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003eseq();\n$column-\u003eseq( true );\n```\n\n\n#### Column::spatial()\n\nCreates a spatial index for the column\n\n```php\npublic function spatial( string $name = null ) : self\n```\n\n* @param **string\u0026#124;null** `$name` Name of the index or NULL to generate automatically\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$column-\u003espatial();\n$column-\u003espatial( 'idx_col' );\n```\n\n\n#### Column::type()\n\nSets the column type or returns the current value\n\n```php\npublic function type( string $value = null )\n```\n\n* @param **string\u0026#124;null** `$value` New column type or NULL to return current value\n* @return **self\u0026#124;string** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003etype();\n$column-\u003etype( 'tinyint' );\n```\n\n\n#### Column::unique()\n\nCreates an unique index for the column\n\n```php\npublic function unique( string $name = null ) : self\n```\n\n* @param **string\u0026#124;null** `$name` Name of the index or NULL to generate automatically\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$column-\u003eunique();\n$column-\u003eunique( 'unq_col' );\n```\n\n\n#### Column::unsigned()\n\nSets the column unsigned flag or returns the current value\n\n```php\npublic function unsigned( bool $value = null )\n```\n\n* @param **bool\u0026#124;null** `$value` New column unsigned flag or NULL to return current value\n* @return **self\u0026#124;bool** Same object for setting the value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $column-\u003eunsigned();\n$column-\u003eunsigned( true );\n```\n\n\n#### Column::up()\n\nApplies the changes to the database schema\n\n```php\npublic function up() : self\n```\n\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$column-\u003eup();\n```\n\n\n\n## Foreign keys\n\n### Creating foreign keys\n\nUpscheme offers support for foreign key constraints, which enforce the integrity\nof data between two tables. For example, if the `parentid` column of the\n`users_address` table references the `id` column of the `users` table, there can\nbe no rows in the `users_address` table without a matching row in the `users`\ntable. Calling the [`foreign()`](#tableforeign) method will create such a\nconstraint:\n\n```php\n$this-\u003edb()-\u003etable( 'users', function( $table ) {\n\t$table-\u003eid();\n} );\n\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003eforeign( 'parentid', 'users' );\n} );\n```\n\n**Note:** The column (`parentid`) will and must have the same data type and column\nmodifiers as the referenced column (`id`). The [`foreign()`](#tableforeign) method\nensures that and will create a new index with the same name as the foreign key\nconstraint automatically.\n\nIf the ID column in the `users` table is named differently, pass its name as third\nparameter to the [`foreign()`](#tableforeign) method:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003eforeign( 'parentid', 'users', 'uid' );\n} );\n```\n\nIt's recommended to pass the name of the foreign key constraint as forth parameter\nso it's easier to change or drop constraints later:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003eforeign( 'parentid', 'users', 'id', 'fk_test_pid' );\n} );\n```\n\nIn case there's more than one column required to get the unique values required\nby foreign keys, pass the column names as array:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003eforeign( ['parentid', 'siteid'], 'users_address', ['id', 'siteid'] );\n} );\n```\n\nForeign key constraints can perform different actions if the referenced column\nin the foreign table is deleted of updated. The standard action is to restrict\ndeleting the row or updating the referenced ID value. To change the behaviour,\nuse the [`onDelete()`](#foreignondelete) and [`onUpdate()`](#foreignonupdate)\nmethods:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003eforeign( 'parentid', 'users' )-\u003eonDelete( 'SET NULL' )-\u003eonUpdate( 'RESTRICT' );\n} );\n```\n\nThere's a shortcut if you want to set both values to the same value:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003eforeign( 'parentid', 'users' )-\u003edo( 'SET NULL' );\n} );\n```\n\nPossible values for both methods are:\n\n* CASCADE : Update referenced value\n* NO ACTION : No change in referenced value (same as RESTRICT)\n* RESTRICT : Forbid changing values\n* SET DEFAULT : Set referenced value to the default value\n* SET NULL : Set referenced value to NULL\n\nThe default action when deleting or updating rows is *CASCADE* so the values of\nthe foreign key column are updated to the same values as in the foreign table.\n\n### Checking foreign key existence\n\nTo check if a foreign key already exists, use the [`hasForeign()`](#dbhasforeign) method:\n\n```php\nif( $this-\u003edb()-\u003ehasForeign( 'users_address', 'fk_usrad_parentid' ) ) {\n    // The \"fk_usrad_parentid\" foreign key in the \"users_address\" table exists\n}\n```\n\nIt's also possible checking for several foreign key constraints at once. Then, the\n[`hasForeign()`](#dbhasforeign) method will only return TRUE if all constraints\nexist in the tables passed as first argument:\n\n```php\nif( $this-\u003edb()-\u003ehasForeign( 'users_address', ['fk_usrad_parentid', 'fk_usrad_siteid'] ) ) {\n    // The \"fk_usrad_parentid\" and \"fk_usrad_siteid\" foreign keys exist in the \"users_address\" table\n}\n```\n\nIf a table object available, the [`hasForeign()`](#tablehasforeign) method of the\ntable can be used instead:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003ehasForeign( 'fk_usrad_parentid' ) ) {\n\t    // The \"fk_usrad_parentid\" foreign key in the \"users_address\" table exists\n\t}\n} );\n\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003ehasForeign( ['fk_usrad_parentid', 'fk_usrad_siteid'] ) ) {\n\t    // The \"fk_usrad_parentid\" and \"fk_usrad_siteid\" foreign keys exist in the \"users_address\" table\n\t}\n} );\n```\n\nIn case you need the current values of an existing constraint:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$fk = $table-\u003eforeign( 'parentid', 'users' );\n\n\t// returns the name of the constraint\n\t$name = $fk-\u003ename()\n\n\t// returns the action when deleting rows\n\t$action = $fk-\u003eonDelete;\n\n\t// returns the action when updating the foreign ID\n\t$action = $fk-\u003eonUpdate;\n} );\n```\n\n### Dropping foreign keys\n\nTo remove a foreign key constraint from a table, use the [`dropForeign()`](#dbdropforeign)\nmethod and pass the name of the table and foreign key name as arguments:\n\n```php\n$this-\u003edb()-\u003edropForeign( 'users_address', 'fk_usrad_parentid' );\n```\n\nYou can also pass several foreign key names to drop them at once:\n\n```php\n$this-\u003edb()-\u003edropForeign( 'users_address', ['fk_usrad_parentid', 'fk_usrad_siteid'] );\n```\n\nWithin the anonymous function passed to the [`table()`](#dbtable) method, you\ncan also use the [`dropForeign()`](#tabledropforeign) method:\n\n```php\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003edropForeign( 'fk_usrad_parentid' );\n} );\n\n$this-\u003edb()-\u003etable( 'users_address', function( $table ) {\n\t$table-\u003edropForeign( ['fk_usrad_parentid', 'fk_usrad_siteid'] );\n} );\n```\n\n### Foreign key methods\n\n\u003cnav\u003e\n\u003cdiv class=\"method-header\"\u003e\u003ca href=\"#foreign-keys\"\u003eForeign keys\u003c/a\u003e\u003c/div\u003e\n\u003cul class=\"method-list\"\u003e\n\t\u003cli\u003e\u003ca href=\"#foreign__call\"\u003e__call()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#foreign__get\"\u003e__get()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#foreign__set\"\u003e__set()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#foreignname\"\u003ename()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#foreignondelete\"\u003eonDelete()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#foreignonupdate\"\u003eonUpdate()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#foreignup\"\u003eup()\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/nav\u003e\n\n\n#### Foreign::__call()\n\nCalls custom methods\n\n```php\npublic function __call( string $method, array $args )\n```\n\n* @param **string** `$method` Name of the method\n* @param **array\u0026#60;mixed\u0026#62;** `$args` Method parameters\n* @return **mixed** Return value of the called method\n\n**Examples:**\n\nYou can register custom methods that have access to the class properties of the Upscheme Foreign object:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Foreign::macro( 'default', function() {\n\t$this-\u003eopts = ['onDelete' =\u003e 'SET NULL', 'onUpdate' =\u003e 'SET NULL'];\n} );\n\n$foreign-\u003edefault();\n```\n\nAvailable class properties are:\n\n`$this-\u003edbaltable`\n: Doctrine table schema\n\n`$this-\u003etable`\n: Upscheme Table object\n\n`$this-\u003elocalcol`\n: Local column name or names\n\n`$this-\u003efktable`\n: Foreign table name\n\n`$this-\u003efkcol`\n: Foreign column name or names\n\n`$this-\u003ename`\n: Foreign key name\n\n`$this-\u003eopts`\n: Associative list of foreign key options (mainly \"onDelete\" and \"onUpdate\")\n\n\n#### Foreign::__get()\n\nReturns the value for the given foreign key option\n\n```php\npublic function __get( string $name )\n```\n\n* @param **string** `$name` Foreign key option name\n* @return **mixed** Foreign key option value\n\nThe list of available foreign key options are:\n\n* onDelete\n* onUpdate\n\nPossible values for both options are:\n\n* CASCADE : Update referenced value\n* NO ACTION : No change in referenced value (same as RESTRICT)\n* RESTRICT : Forbid changing values\n* SET DEFAULT : Set referenced value to the default value\n* SET NULL : Set referenced value to NULL\n\n**Examples:**\n\n```php\n$value = $foreign-\u003eonDelete;\n// same as\n$value = $foreign-\u003eopt( 'onDelete' );\n```\n\n\n#### Foreign::__set()\n\nSets the new value for the given Foreign key option\n\n```php\npublic function __set( string $name, $value )\n```\n\n* @param **string** `$name` Foreign key option name\n* @param mixed Foreign key option value\n\nThe list of available Foreign key options are:\n\n* onDelete\n* onUpdate\n\nPossible values for both options are:\n\n* CASCADE : Update referenced value\n* NO ACTION : No change in referenced value (same as RESTRICT)\n* RESTRICT : Forbid changing values\n* SET DEFAULT : Set referenced value to the default value\n* SET NULL : Set referenced value to NULL\n\n**Examples:**\n\n```php\n$foreign-\u003eonDelete = 'SET NULL';\n// same as\n$foreign-\u003eonDelete( 'SET NULL' );\n$foreign-\u003eopt( 'onDelete', 'SET NULL' );\n```\n\n\n#### Foreign::do()\n\nSets the new value for the given Foreign key option\n\n```php\npublic function do( string $action ) : self\n```\n\n* @param **string** `$action` Performed action\n* @return **self** Same object for fluid method calls\n\nPossible actions are:\n\n* CASCADE : Delete or update referenced value\n* NO ACTION : No change in referenced value (same as RESTRICT)\n* RESTRICT : Forbid changing values\n* SET DEFAULT : Set referenced value to the default value\n* SET NULL : Set referenced value to NULL\n\n**Examples:**\n\n```php\n$foreign-\u003edo( 'RESTRICT' );\n```\n\n\n#### Foreign::name()\n\n* Returns the current name of the foreign key constraint\n\n```php\npublic function name()\n```\n\n* @return **string\u0026#124;null** Name of the constraint or NULL if no name is available\n\n**Examples:**\n\n```php\n$fkname = $foreign-\u003ename();\n```\n\n\n#### Foreign::onDelete()\n\n* Sets the action if the referenced row is deleted or returns the current value\n\n```php\npublic function onDelete( string $value = null )\n```\n\n* @param **string\u0026#124;null** `$value` Performed action or NULL to return current value\n* @return **self\u0026#124;string** Same object for setting the value, current value without parameter\n\n* Available actions are:\n* - CASCADE : Delete referenced value\n* - NO ACTION : No change in referenced value\n* - RESTRICT : Forbid changing values\n* - SET DEFAULT : Set referenced value to the default value\n* - SET NULL : Set referenced value to NULL\n\n**Examples:**\n\n```php\n$value = $foreign-\u003eonDelete();\n\n$foreign-\u003eonDelete( 'SET NULL' );\n// same as\n$foreign-\u003eonDelete = 'SET NULL';\n// same as\n$foreign-\u003eopt( 'onDelete', 'SET NULL' );\n\n$foreign-\u003eonDelete( 'SET NULL' )-\u003eonUpdate( 'SET NULL' );\n```\n\n\n#### Foreign::onUpdate()\n\n* Sets the action if the referenced row is updated or returns the current value\n\n```php\npublic function onUpdate( string $value = null )\n```\n\n* @param **string\u0026#124;null** `$value` Performed action or NULL to return current value\n* @return **self\u0026#124;string** Same object for setting the value, current value without parameter\n\n* Available actions are:\n* - CASCADE : Update referenced value\n* - NO ACTION : No change in referenced value\n* - RESTRICT : Forbid changing values\n* - SET DEFAULT : Set referenced value to the default value\n* - SET NULL : Set referenced value to NULL\n\n**Examples:**\n\n```php\n$value = $foreign-\u003eonUpdate();\n\n$foreign-\u003eonUpdate( 'SET NULL' );\n// same as\n$foreign-\u003eonUpdate = 'SET NULL';\n// same as\n$foreign-\u003eopt( 'onUpdate', 'SET NULL' );\n\n$foreign-\u003eonUpdate( 'SET NULL' )-\u003eonDelete( 'SET NULL' );\n```\n\n\n#### Foreign::up()\n\n* Applies the changes to the database schema\n\n```php\npublic function up() : self\n```\n\n* @return **self** Same object for fluid method calls\n\n**Examples:**\n\n```php\n$foreign-\u003eup();\n```\n\n\n\n## Sequences\n\n### Adding sequences\n\nA few database implementations offer sequences instead of auto-increment/identity\ncolumns, namely Oracle and PostgreSQL. Sequences are functions which create\nsequentially increasing numbers that are applied to a table column when inserting\nnew rows. To create a new sequence named *seq_test* use the [`sequence()`](#dbsequence)\nmethod:\n\n```php\n$this-\u003edb()-\u003esequence( 'seq_test' );\n```\n\nTo use a different start value and step width than `1`, call the [`start()`](#sequencestart)\nand [`step()`](#sequencestep) methods:\n\n```php\n$this-\u003edb()-\u003esequence( 'seq_test', function( $seq ) {\n\t$seq-\u003estart( 1000 )-\u003estep( 2 );\n} );\n```\n\n### Checking sequence existence\n\nTo check if a sequence already exists, use the [`hasSequence()`](#dbhassequence) method:\n\n```php\nif( $this-\u003edb()-\u003ehasSequence( 'seq_test' ) ) {\n    // The \"seq_test\" sequence exists\n}\n```\n\nIt's also possible checking for several sequences at once. Then, the\n[`hasSequence()`](#dbhassequence) method will only return TRUE if all sequences exist:\n\n```php\nif( $this-\u003edb()-\u003ehasSequence( ['seq_id', 'seq_test'] ) ) {\n    // The \"seq_id\" and \"seq_test\" sequences exist\n}\n```\n\nIn case you need to know the current values of the table options:\n\n```php\n$this-\u003edb()-\u003esequence( 'seq_test', function( $seq ) {\n\t// returns how many generated numbers are cached\n\t$cache = $seq-\u003ecache;\n\n\t// returns the number the sequence has started from\n\t$start = $seq-\u003estart;\n\n\t// returns the step width for newly generated numbers\n\t$step = $seq-\u003estep;\n} );\n```\n\n### Dropping sequences\n\nTo remove a sequence, use the [`dropSequence()`](#dbdropsequence) method and\npass the name of the sequence as argument:\n\n```php\n$this-\u003edb()-\u003edropSequence( 'seq_id' );\n```\n\nYou can also pass several sequence names to drop them at once:\n\n```php\n$this-\u003edb()-\u003edropSequence( ['seq_id', 'seq_test'] );\n```\n\n### Sequence methods\n\n\u003cnav\u003e\n\u003cdiv class=\"method-header\"\u003e\u003ca href=\"#sequences\"\u003eSequences\u003c/a\u003e\u003c/div\u003e\n\u003cul class=\"method-list\"\u003e\n\t\u003cli\u003e\u003ca href=\"#sequences__call\"\u003e__call()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#sequences__get\"\u003e__get()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#sequences__set\"\u003e__set()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#sequencescache\"\u003ecache()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#sequencesname\"\u003ename()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#sequencesstart\"\u003estart()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#sequencestep\"\u003estep()\u003c/a\u003e\u003c/li\u003e\n\t\u003cli\u003e\u003ca href=\"#sequencesup\"\u003eup()\u003c/a\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003c/nav\u003e\n\n\n#### Sequence::__call()\n\nCalls custom methods or passes unknown method calls to the Doctrine table object\n\n```php\npublic function __call( string $method, array $args )\n```\n\n* @param **string** `$method` Name of the method\n* @param **array\u0026#60;mixed\u0026#62;** `$args` Method parameters\n* @return **mixed** Return value of the called method\n\n**Examples:**\n\nYou can register custom methods that have access to the class properties of the\nUpscheme Sequence object:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Sequence::macro( 'default', function() {\n\t$this-\u003estart( 1 )-\u003estep( 2 );\n} );\n\n$sequence-\u003edefault();\n```\n\nAvailable class properties are:\n\n`$this-\u003edb`\n: Upscheme DB object\n\n`$this-\u003esequence`\n: Doctrine sequence schema\n\n\n#### Sequence::__get()\n\nReturns the value for the given sequence option\n\n```php\npublic function __get( string $name )\n```\n\n* @param **string** `$name` Sequence option name\n* @return **mixed** Sequence option value\n\n**Examples:**\n\n```php\n$value = $sequence-\u003egetInitialValue();\n// same as\n$value = $sequence-\u003estart();\n```\n\n\n#### Sequence::__set()\n\nSets the new value for the given sequence option\n\n```php\npublic function __set( string $name, $value )\n```\n\n* @param **string** `$name` Sequence option name\n* @param mixed Sequence option value\n\n**Examples:**\n\n```php\n$value = $sequence-\u003esetInitialValue( 1000 );\n// same as\n$value = $sequence-\u003estart( 1000 );\n```\n\n\n#### Sequence::cache()\n\nSets the cached size of the sequence or returns the current value\n\n```php\npublic function cache( int $value = null )\n```\n\n* @param **int** `$value` New number of sequence IDs cached by the client or NULL to return current value\n* @return **self\u0026#124;int** Same object for setting value, current value without parameter\n\n**Examples:**\n\n```php\n$value = $sequence-\u003ecache();\n$sequence-\u003ecache( 100 );\n```\n\n\n#### Sequence::name()\n\nReturns the name of the sequence\n\n```php\npublic function name()\n```\n\n* @return **string** Sequence name\n\n```php\n$name = $sequence-\u003ename();\n```\n\n\n#### Sequence::start()\n\nSets the new start value of the sequence or returns the current value\n\n```php\npublic function start( int $value = null )\n```\n\n* @param **int** `$value` New start value of the sequence or NULL to return current value\n* @return **self\u0026#124;int** Same object for setting value, current value without parameter\n\n```php\n$value = $sequence-\u003estart();\n$sequence-\u003estart( 1000 );\n```\n\n\n#### Sequence::step()\n\nSets the step size of new sequence values or returns the current value\n\n```php\npublic function step( int $value = null )\n```\n\n* @param **int** `$value` New step size the sequence is incremented or decremented by or NULL to return current value\n* @return **self\u0026#124;int** Same object for setting value, current value without parameter\n\n```php\n$value = $sequence-\u003estep();\n$sequence-\u003estep( 2 );\n```\n\n\n#### Sequence::up()\n\nApplies the changes to the database schema\n\n```php\npublic function up() : self\n```\n\n* @return **self** Same object for fluid method calls\n\n```php\n$sequence-\u003eup();\n```\n\n\n\n## Indexes\n\nIndexes speed up database queries and the time a query needs can drop from several\nminutes to milliseconds if used correctly. There are several index types available:\n\n* primary : All values must be unique, no NULL values and only one index per table is allowed\n* unique : Values must be unique but NULL values are allowed (and more than once)\n* index : Standard index with no restrictions\n* spatial : Fast lookup in coordinates systems like geographic maps\n\nAll indexes can consist of one or more columns but the order of the columns has a\ngreat impact if indexes are used for a query or not.\n\n### Adding indexes\n\nAll indexes are bound to the table which contains the columns the index covers.\nThe simplest way to create an index over a single column is to use the\n[`index()`](#columnindex) method of the column object:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'label' )-\u003eindex();\n} );\n```\n\nThe second parameter of the [`index()`](#columnindex) method allows you to set\na custom name for the index:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003estring( 'label' )-\u003eindex( 'idx_test_label' );\n} );\n```\n\n**Note:** For a maximum compatibility between different database types, the\nlength of the index names should be 30 characters or less.\n\nThe same is possible for primary, unique and spatial indexes:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t// primary key\n\t$table-\u003eint( 'id' )-\u003eprimary();\n\t$table-\u003eint( 'id' )-\u003eprimary( 'pk_test_id' ); // ignored by MySQL, MariaDB, etc.\n\n\t// unique key\n\t$table-\u003estring( 'code' )-\u003eunique();\n\t$table-\u003estring( 'code' )-\u003eunique( 'unq_test_code' );\n\n\t// spatial index\n\t$table-\u003ecol( 'location', 'point' )-\u003espatial();\n\t$table-\u003ecol( 'location', 'point' )-\u003espatial( 'idx_test_location' );\n} );\n```\n\nFor multi-column indexes, the [`primary()`](#tableprimary), [`unique()`](#tableunique)\nand [`index()`](#tableindex) methods are available in the table object:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t// primary composite index\n\t$table-\u003eprimary( ['siteid', 'code'] );\n\n\t// unique composite index\n\t$table-\u003eunique( ['parentid', 'type'] );\n\n\t// regular composite index\n\t$table-\u003eindex( ['label', 'status'] );\n} );\n```\n\nSpatial indexes can NOT span multiple columns but creating them is also possible\nusing the [`spatial()`](#tablespatial) method of the table object:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003espatial( 'location' );\n} );\n```\n\n### Checking index existence\n\nTo check if an index already exists, use the [`hasIndex()`](#dbhasindex) method:\n\n```php\nif( $this-\u003edb()-\u003ehasIndex( 'users', 'idx_users_name' ) ) {\n    // The \"idx_users_name\" index in the \"users\" table exists\n}\n```\n\nYou can check for several indexes at once too. In that case, the\n[`hasIndex()`](#dbhasindex) method will only return TRUE if all indexes exist:\n\n```php\nif( $this-\u003edb()-\u003ehasIndex( 'users', ['idx_users_name', 'idx_users_status'] ) ) {\n    // The \"idx_users_name\" and \"idx_users_status\" indexes in the \"users\" table exists\n}\n```\n\nIf you already have a table object, you can use [`hasIndex()`](#tablehasindex) as well:\n\n```php\nif( $table-\u003ehasIndex( 'idx_users_name' ) ) {\n    // The \"idx_users_name\" index in the table exists\n}\n\nif( $table-\u003ehasIndex( ['idx_users_name', 'idx_users_status'] ) ) {\n    // The \"idx_users_name\" and \"idx_users_status\" indexes in the table exists\n}\n```\n\n### Renaming indexes\n\nTo rename indexes directly, using the [`renameIndex()`](#dbrenameindex) method\nof the DB schema:\n\n```php\n// single index\n$this-\u003edb()-\u003erenameIndex( 'testtable', 'idx_test_label', 'idx_test_name' );\n\n// multiple indexes\n$this-\u003edb()-\u003erenameIndex( 'testtable', ['idx_test_label' =\u003e 'idx_test_name', 'idx_text_stat' =\u003e 'idx_test_status'] );\n```\n\nIf a table object is already available, you can use its [`renameIndex()`](#tablerenameindex)\nmethod to rename one or more indexes:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t// single index\n\t$table-\u003erenameIndex( 'idx_test_label', 'idx_test_name' );\n\n\t// multiple indexes\n\t$table-\u003erenameIndex( ['idx_test_label' =\u003e 'idx_test_name', 'idx_text_stat' =\u003e 'idx_test_status'] );\n} );\n```\n\nIn all cases, indexes are only renamed if they exist. No error is reported if one\nor more indexes doesn't exist in the table.\n\n### Dropping indexes\n\nTo drop indexes, use the [`dropIndex()`](#dbdropindex) method from the DB schema object:\n\n```php\n$this-\u003edb()-\u003edropIndex( 'users', 'idx_test_name' );\n```\n\nYou can drop several indexes at once if you pass the name of all indexes you want\nto drop as array:\n\n```php\n$this-\u003edb()-\u003edropIndex( 'users', ['idx_test_name', 'idx_test_status'] );\n```\n\nIf you already have a table object, you can use [`dropIndex()`](#tabledropindex) too:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t// single index\n\t$table-\u003edropIndex( 'idx_test_name' );\n\n\t// multiple indexes\n\t$table-\u003edropIndex( ['idx_test_name', 'idx_test_status'] );\n} );\n```\n\nIn all cases, indexes are only removed if they exist. No error is reported if one\nor more indexes doesn't exist in the table.\n\n### Custom index naming\n\nIt's not necessary to pass a custom index name when creating new indexes. Then,\nthe index name is generated automatically but their name will consist of a hash\nthat is hard to read. Also, you don't know which columns the indexes span from the\nindex name.\n\nUpscheme allows you to add your own naming function for indexes which is used if\nnot index name is passed to the methods for creating indexes. Before running the\nmigrations, register your nameing function using the [`macro()`](#macro)\nmethod in the table objects:\n\n```php\nuse \\Aimeos\\Upscheme\\Schema\\Table;\n\nTable::marco( 'nameIndex', function( string $table, array $columns, string $type ) {\n\treturn $type . '_' . $table . '_' . join( '_', $columns );\n} );\n\n\\Aimeos\\Upscheme\\Up::use( $config, './migrations/' )-\u003eup()\n```\n\nFor a table \"testtable\", a column \"label\" and the type \"idx\", this will return\n*idx_testtable_label* instead of a hash.\n\nAvailable index types are:\n\n* idx : Regular and spatial indexes\n* fk : Foreign key index\n* pk : Primary key index\n* unq : Unique index\n\n**Note:** For compatibility to all supported database types, the maximum length\nof the index names must be not longer than 30 characters!\n\n\n\n## Customizing Upscheme\n\n### Adding custom methods\n\nYou can add new methods to all Upscheme objects using the `macro()` method. Each\ncustom method has access to the class properties and methods of the class it's\nregistered for including the Doctrine DBAL objects.\n\nTo register a method named `test()` in the DB schema object with two parameters\n`$arg1` and `$arg2` which has access to the same class properties as the DB\n[`__call()`](#db__call) method use:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\DB::marco( 'test', function( $arg1, $arg2 ) {\n\t// $this-\u003econn : Doctrine connection\n\t// $this-\u003efrom : Doctrine start schema\n\t// $this-\u003eto : Doctrine current schema\n\t// $this-\u003eup : Upscheme object\n\t// return $this or a value\n} );\n\n$db-\u003etest( 'key', 'value' );\n```\n\nRegistering a method `test()` in the Table schema object with one parameter `$arg1`\nwhich has access to the same class properties as the Table [`__call()`](#table__call)\nmethod use:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Table::marco( 'test', function( $arg1 ) {\n\t// $this-\u003edb : Upscheme DB object\n\t// $this-\u003etable : Doctrine Table object\n\t// return $this or a value\n} );\n\n$table-\u003etest( 'something' );\n```\n\nSame for a method `test()` in the Column schema object with an optional parameter\n`$value` which has access to the same class properties as the Column\n[`__call()`](#column__call) method use:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Column::marco( 'test', function( $value = null ) {\n\t// $this-\u003edb : Upscheme DB object\n\t// $this-\u003etable : Upscheme Table object\n\t// $this-\u003ecolumn : Doctrine Column object\n\t// return $this or a value\n} );\n\n$column-\u003etest();\n```\n\nTo extend the Foreign object for foreign key constraints with a `test()` method\nwith no parameter having access to the same class properties as the Foreign\n[`__call()`](#foreign__call) method use:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Foreign::marco( 'test', function() {\n\t// $this-\u003etable : Upscheme Table object\n\t// $this-\u003edbaltable : Doctrine Table object\n\t// $this-\u003elocalcol : Array of local column names\n\t// $this-\u003efktable : Foreign table name\n\t// $this-\u003efkcol : Foreign table column names\n\t// $this-\u003ename : Foreign key name\n\t// $this-\u003eopts : Array of foreign key options (\"onDelete\" and \"onUpdate\")\n\t// return $this or a value\n} );\n\n$foreign-\u003etest();\n```\n\nFinally, extending the Sequence object with a `test()` method having no parameters\nand access to the same class properties as the Sequence [`__call()`](#sequence__call)\nmethod use:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Sequence::marco( 'test', function() {\n\t// $this-\u003edb : Upscheme DB object\n\t// $this-\u003esequence : Doctrine Sequence object\n\t// return $this or a value\n} );\n\n$sequence-\u003etest();\n```\n\n### Implementing custom columns\n\nInstead of calling the [`col()`](#tablecol) method of the Table object with all\nparameters and modifiers each time, you can create your own shortcut methods, e.g.:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Table::marco( 'utinyint', function( string $name ) {\n\treturn $this-\u003ecol( $name, 'tinyint' )-\u003eunsigned( true );\n} );\n```\n\nIt's also possible to create several columns at once if you want to add them to\nseveral tables:\n\n```php\n\\Aimeos\\Upscheme\\Schema\\Table::marco( 'defaults', function() {\n\t$this-\u003eid();\n\t$this-\u003edatetime( 'ctime' );\n\t$this-\u003edatetime( 'mtime' );\n\t$this-\u003estring( 'editor' );\n\treturn $this;\n} );\n```\n\nThen, use your custom methods when creating or updating tables:\n\n```php\n$this-\u003edb()-\u003etable( 'test', function( $table ) {\n\t$table-\u003edefaults();\n\t$table-\u003eutinyint( 'status' );\n} );\n```\n\n## Upgrade Upscheme\n\n### To 0.9.0\n\nVersion 0.9+ supports Doctrine DBAL 3.x/4.x and dropped support for Doctrine DBAL 2.x.\n\n* [`DB::type()`](#dbtype) returns `mariadb` instead of `mysql` for MariaDDB database\n* [`DB::type()`](#dbtype) returns `sqlserver` instead of `mssql` for Microsoft SQLServer database\n* [`DB::for()`](#dbfor), [`DB::view()`](#dbview) and [`Column::opt`](#columnopt) require `['mariadb', 'mysql']` to get the same results\n* [`DB::lastId()`](#dblastid) doesn't require/support passing a sequence name because Doctrine DBAL removed it but doesn't support Oracle IDENTITY columns at the moment\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faimeos%2Fupscheme","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faimeos%2Fupscheme","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faimeos%2Fupscheme/lists"}