therealworld / clirun-plugin
a script for oxid6 update preparations.
Package info
bitbucket.org/therealworld/clirun-plugin
Type:oxideshop-component
pkg:composer/therealworld/clirun-plugin
Requires
- php: ^8.3
- ext-json: *
- ext-pdo: *
- ifsnop/mysqldump-php: v2.*
- oxid-esales/oxideshop-ce: >=v7.4
- therealworld/suppress-deprecations-plugin: v1.0.*
- therealworld/tools-plugin: >=v3.1
Requires (Dev)
None
Suggests
None
Provides
None
Conflicts
None
Replaces
None
- v4.x-dev
- v4.1.1
- v4.1.0
- v4.0.44
- v4.0.43
- v4.0.42
- v4.0.41
- v4.0.40
- v4.0.39
- v4.0.38
- v4.0.37
- v4.0.36
- v4.0.35
- v4.0.34
- v4.0.33
- v4.0.32
- v4.0.31
- v4.0.30
- v4.0.29
- v4.0.28
- v4.0.27
- v4.0.26
- v4.0.25
- v4.0.24
- 4.0.23
- v4.0.22
- v4.0.21
- v4.0.20
- v4.0.19
- v4.0.18
- v4.0.17
- v4.0.16
- v4.0.15
- v4.0.14
- v4.0.13
- v4.0.12
- v4.0.11
- v4.0.10
- v4.0.9
- v4.0.8
- v4.0.7
- v4.0.6
- v4.0.5
- v4.0.4
- v4.0.3
- v4.0.2
- v4.0.1
- v4.0.0
- v3.2.20
- v3.2.19
- v3.2.18
- v3.2.17
- v3.2.16
- v3.2.15
- v3.2.14
- v3.2.13
- v3.2.12
- v3.2.11
- v3.2.10
- v3.2.9
- v3.2.8
- v3.2.7
- v3.2.6
- v3.2.5
- v3.2.4
- v3.2.3
- v3.2.2
- v3.2.1
- v3.2.0
- v3.1.19
- v3.1.18
- v3.1.17
- v3.1.16
- v3.1.15
- v3.1.14
- v3.1.13
- v3.1.12
- v3.1.11
- v3.1.10
- v3.1.9
- v3.1.8
- v3.1.7
- v3.1.6
- v3.1.5
- v3.1.4
- v3.1.3
- v3.1.2
- v3.1.1
- v3.1.0
- v3.0.17
- v3.0.16
- v3.0.15
- v3.0.14
- v3.0.13
- v3.0.12
- v3.0.11
- v3.0.10
- v3.0.9
- v3.0.8
- v3.0.7
- v3.0.6
- v3.0.5
- v3.0.4
- v3.0.3
- v3.0.2
- v3.0.1
- v3.0.0
- v2.x-dev
- v2.0.2
- v2.0.1
- v2.0.0
- v1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- dev-master
This package is auto-updated.
Last update: 2026-09-21 06:28:04 UTC
README
Deutsche Fassung: README.de.md

description
New oe-console commands
- trw:allmodules:active [--env=XXXX] [--shop-id=1] (short: t:a:a)
- trw:allmodules:deactive [--env=XXXX] [--shop-id=1] (short: t:a:d)
- trw:allmodules:install [--env=XXXX] (short: t:a:i)
- trw:clear:cache (short: t:cl:c)
- trw:clear:phantomimages [--dry-run] [--force] (short: t:cl:p)
- trw:config:export [--env=XXXX] [--shop-id=1] (short: t:co:e)
- trw:config:import [--env=XXXX] [--shop-id=1] (short: t:co:i)
- trw:db:charset [--apply] [--charset=utf8mb3|utf8mb4] [--allow-downgrade] [--table=XXXX] [--exclude=t.col,...] (short: t:d:c)
- trw:db:decode-legacy [--key=XXXX] [--dry-run] [--no-schema] [--force] (short: t:d:d)
- trw:db:export [--yaml=XXXX] (short: t:d:e)
- trw:db:keycollation [--apply] [--schema-only] [--table=XXXX] [--exclude=t.col,...] (short: t:d:k)
- trw:db:import [--yaml=XXXX] (short: t:d:i)
- trw:db:migrate [migration-command] [versions] [--project=XXXX] [--edition=XXXX] [--dry-run] [--up] [--down] [--auto-sync] [--strict-migrations] (short: t:d:m)
- trw:generate:yaml:config [--env=XXXX] [--shop-id=1] (short: t:g:y:c)
- trw:ide:helper [--shop-id=1] (short: t:i:h)
- trw:module:migrate module-id [--down] (short: t:m:m)
- trw:module:refresh module-id [--uninstall] [--shop-id=1] (short: t:m:r)
- trw:module:setup module|path [--develop] [--no-takecare] [--shop-id=1] [--env=XXXX] (short: t:m:s)
- trw:module:wipeout module-id [--shop-id=1] [--env=XXXX] [--keep-migrations] [--force] (short: t:m:w)
- trw:update:after [--env=XXXX] [--shop-id=1] (short: t:u:a)
- trw:update:before [--env=XXXX] [--shop-id=1] (short: t:u:b)
- trw:update:views (short: t:u:v)
The short forms are Symfony's built-in command abbreviations: each colon segment is shortened to the smallest prefix that is still unambiguous. Because
clear/configcollide onc(and would be ambiguous), their second segment needs two letters (t:cl:…/t:co:…). The first segmenttresolves totrwas long as no other command namespace starts witht.
trw:clear:cacheandtrw:clear:phantomimagesshare the same namespace, so their last segment needs a letter of its own as well:t:cl:candt:cl:p.
All-Module actions (deactivate, activate, install)
The "All-Modules" Activate Commands set the Shop in Maintenance Mode
All-Module active (trw:allmodules:active)
- activate all Modules in a certain order
- option --env=XXXX:
- get the Plugin-Config from file trwclirun/XXXX.1.yaml
- option --shop-id=YYYY:
- get the Plugin-Config from file trwclirun/XXXX.YYYY.yaml or trwclirun/YYYY.yaml
- activate the Modules in Shop with this ID
All-Module deactive (trw:allmodules:deactive)
- deactivate all Modules
- option --env=XXXX:
- get the Plugin-Config from file trwclirun/XXXX.1.yaml
- option --shop-id=YYYY:
- get the Plugin-Config from file trwclirun/XXXX.YYYY.yaml or trwclirun/YYYY.yaml
- deactivate the Modules in Shop with this ID
- if not set, the command check if Plugin-Configs exists for all Shop-Ids
All-Module install (trw:allmodules:install)
- deactivate all Modules
- option --env=XXXX:
- get the Plugin-Config from file trwclirun/XXXX.1.yaml
Clear-Commands
clean Cache (trw:clear:cache)
- Clean OXID cache
clean phantom product images (trw:clear:phantomimages)
Removes product images that are invisible in the admin while the frontend renders a broken picture. Two flavours are detected:
- broken: the file exists on disk but carries no image data (a 0-byte download, a stored html error page)
- missing: an article references a picture file that does not exist at all
The matching oxpic field is cleared and the generated derivatives are dropped, so the
article falls back to "no picture".
- option --dry-run:
- only report what was found, change nothing
- option --force:
- clear references to missing files even when the picture folder looks out of sync
- without it, a folder that holds none of its referenced pictures aborts the run: that is the signature of a picture folder that was not deployed, not of missing images, and clearing every reference would destroy the assignments
Cache warmup
warm up the caches (trw:cache:warmup)
Builds the caches the shop would otherwise build while answering its first requests, so the first visitor after a deployment does not pay for all of them.
Two stages that share nothing but their purpose. --stage picks one, the default is both.
Stage "local" — compiled templates, template chain, language cache
Runs in this process, no network involved. Worth doing because the shop runs with
iDebug = 0, which switches twig's auto_reload off: the compile cache never expires by
itself, so its cost is paid exactly once after every trw:clear:cache — by whoever visits
first, unless this ran before them.
Measured on a mid-sized shop, first page after clearing the cache:
| first page load | templates compiled while answering | |
|---|---|---|
| without warmup | 4.67 s | 87 |
| with warmup | 0.94 s | 18 |
The 18 that remain are templates included under a computed name, which cannot be found without rendering the page.
The stage restarts itself in a subprocess whose working directory is source/. That is
not cosmetic: twig builds the NAME of each compiled file from the template path relative to
getcwd(). Started from the shop root it would write a complete second set of cache files
that the shop never reads — silently, because nothing breaks, the shop is just as slow as
before. A preflight check therefore compares what this process would write against the
files already on disk and refuses to run when none of them match (--force overrides).
Backend templates are reported separately and not compiled: this process runs in frontend mode, so their parent templates are not loadable here. Warming the backend needs its own process and is not implemented yet.
Stage "pages" — the full page cache
Requests the shop's own urls over http, which is the only way a full page cache can be filled at all: it is written from inside the output pipeline while a response is rendered. Everything below it — compiled templates, template chain, seo lookups, opcache — gets warm on the way.
Urls come from oxseo, the live routing table, not from a sitemap. A sitemap answers "what
should a search engine index", which is a different question: cms pages are opt-in there
(OXISSITEMAP defaults to 0 for oxcontents), so a sitemap driven warmup would skip the
terms of service, the shipping information and every other page visitors actually open.
Where the seo module is installed, its sitemap table still contributes the ORDER, so the
pages that matter are warm first if the run is cut short.
Already cached urls are skipped without requesting them, which makes a long run resumable — an interrupted crawl picks up where it stopped. That works through a contract in the tools plugin, so this command knows nothing about which page cache module is installed; without one it simply requests everything, which is slower and never wrong.
Options
--stage=local|pages|all- default
all
- default
--base-url=<url>- the address to request. The default is the shop url from the configuration, and the command prints it before it starts. That address is meant for browsers; this command runs ON the server, where the same host often does not resolve — behind a container boundary, a load balancer, or a name that only exists in public dns. The base url is requested once up front, and an unreachable shop ends the run with one sentence instead of one connection error per url.
--shop-id=<id>--types=<a,b>- page stage only: limit to these oxseo types (
oxarticle,oxcategory,oxcontent,oxmanufacturer,oxvendor,static, plus whatever modules register)
- page stage only: limit to these oxseo types (
--include-inactive- do not check the object behind an seo row for its active flag
--only=<fragment>- local stage only: just the templates whose name contains this
--no-skip- request urls even when the page cache already holds them
--dry-run- show the plan and change nothing
--force- skip the confirmation, and run the local stage even when the preflight objects
--limit=<n>/--max-time=<seconds>- stop after this many items / seconds; combined with the skip above, a nightly run can simply continue where yesterday's stopped
--sleep=<ms>/--max-load=<load>- pace the run: pause after each item / while the one minute load average is above this.
Throttling time does not count against
--max-time
- pace the run: pause after each item / while the one minute load average is above this.
Throttling time does not count against
--timeout=<seconds>- http timeout per request, default 60
--min-free=<size>- stop when the page cache volume drops below this, default
1G. Accepts2G,500M,5%or plain bytes
- stop when the page cache volume drops below this, default
--progress=<n>/--samples=<n>- progress line every n items (0 = off), and how many failures to list at the end
Examples
# after a deployment: templates only, nothing over the network
vendor/bin/oe-console trw:cache:warmup --stage=local
# see what would be requested, and against which address
vendor/bin/oe-console trw:cache:warmup --stage=pages --dry-run
# nightly, gentle, resumable
vendor/bin/oe-console trw:cache:warmup --base-url=https://127.0.0.1/shop \
--force --sleep=200 --max-load=4 --max-time=3600
Exit code is 1 when anything failed — an unreachable shop, a template that does not compile, a url that answered with something other than 200.
config im- and export commands
config export (trw:config:export)
- export the values of Modules, Themes, Shop and ShopTable configs into a transfer YAML — purpose: hand the values over to another shop (e.g. an OXID-6 → OXID-7 migration) without dragging structural module wiring along
- what is included per module:
id+moduleSettings - what is stripped before writing the transfer YAML, so the target system's
own metadata.php / shops/{id}/modules/*.yaml stay authoritative:
classExtensions,controllers,eventstemplates,templateBlocks,smartyPluginDirectories- the merged top-level
moduleChainsblock
- themes, shopconfig and shoptable are exported as values-only by design
- option --env=XXXX:
- get the Plugin-Config from file trwclirun/XXXX.1.yaml
- option --shop-id=YYYY:
- get the Plugin-Config from file trwclirun/XXXX.YYYY.yaml or trwclirun/YYYY.yaml
- export the Config from Shop with this ID
- if not set, the command check if Plugin-Configs exists for all Shop-Ids
config import (trw:config:import)
- import the Modules-, Themes-, Shop- and ShopTableConfigs from the transfer YAML
- whitelist protection: every incoming variable is checked against what the
target shop actually declares. Unknown modules / themes / variables are
skipped with a
<comment>warning instead of being silently written. Source of truth per section:- modules: per-module YAMLs under
var/configuration/shops/{id}/modules/*.yaml(the modules must therefore be installed/activated in the target shop before the import runs) - themes:
oxconfigrows whereOXMODULE = 'theme:{themeId}' - shopconfig:
oxconfigrows whereOXMODULE = '' - shoptable: declared columns of the
oxshopstable
- modules: per-module YAMLs under
- option --env=XXXX:
- get the Plugin-Config from file trwclirun/XXXX.1.yaml
- option --shop-id=YYYY:
- get the Plugin-Config from file trwclirun/XXXX.YYYY.yaml or trwclirun/YYYY.yaml
- import the Config from Shop with this ID
- if not set, the command check if Plugin-Configs exists for all Shop-Ids
DB im- and export commands
DB decode legacy (trw:db:decode-legacy)
One-time OXID 6 → OXID 7 migration command.
In OXID 6 the columns oxconfig.OXVARVALUE and oxuserpayments.OXVALUE were
encrypted via the MySQL ENCODE() function and decrypted on the fly with
DECODE(). OXID 7 expects plaintext. The standard migration path uses an
SQL-side DECODE() step, which only works on databases that still ship the
function — i.e. MySQL ≤ 5.7 and MariaDB ≤ 11.1. On MySQL ≥ 8.0 and
MariaDB ≥ 11.2 the function has been removed.
This command performs the same migration in PHP, using a port of MySQL's
sql_crypt class. It is meant to be run exactly once per shop, after
importing the OXID-6 dump 1:1 into the new database and before any
OXID-7 boot (in particular before trw:update:after).
What it does
For each affected table (oxconfig, oxuserpayments):
- Walk every row and decrypt the cipher value in PHP while the column is
still
BLOB. Rows that already look like plaintext are left untouched, so the command is idempotent — safe to re-run after a partial first run. Decoded plaintext is written back into the still-BLOBcolumn. - Only after the row-level decode has finished: migrate the value column from
BLOBtoTEXT, carrying over itsNOT NULLflag and column comment.
The table charset and collation are left alone. Charset and collation are
set on the value column only, and default to the table's own — so the result is
identical to what a bare MODIFY ... TEXT would produce. The one exception is a
table whose default charset is single-byte (e.g. latin1): since BLOB → TEXT
reinterprets the stored bytes rather than converting them, and the decoded
plaintext is UTF-8, that column alone is pinned to utf8mb4_general_ci and the
command says so on stdout.
An earlier version issued ALTER TABLE … CONVERT TO CHARACTER SET utf8mb4 on
the whole table. That was wrong: OXID declares the neighbouring id columns as
latin1_general_ci on purpose (Setup/Sql/database_schema.sql — oxconfig.OXID,
OXMODULE, OXVARNAME, OXVARTYPE, oxuserpayments.OXID, OXUSERID,
OXPAYMENTSID). Converting them costs index usage on every join against
oxuser / oxpayments / oxorder, inflates those indexes from 32 to 128 bytes
per char(32) entry, and is flagged as a schema deviation by the shop's system
health check. Shops that ran the old version can be put right with the two charset commands instead of
hand-written DDL, and both report before they change anything:
- the id columns:
trw:db:keycollation --schema-only— it reads the declarations out ofdatabase_schema.sql, so it repairs exactly those and leaves the columns OXID means to follow the table charset alone - the value columns left on utf8mb4 while the rest of the database is utf8mb3:
trw:db:charset --allow-downgrade— the tell-tale sign is a default run reportingskipped … already utf8mb4, would be a downgrade. Both columns carry the decoded or re-encrypted payload, so take an export of the two tables first; the command's own content check refuses any column holding a character the target cannot store.
The decode-before-DDL ordering is critical: running MODIFY ... TEXT first
would coerce the binary cipher bytes through the target column charset, and with
sql_mode = '' MySQL silently truncates / replaces invalid UTF-8 sequences —
destroying the ciphertext before the decoder ever sees it. The same hazard
applies to OXID's own migration Version20180228160418, which flips
oxconfig.OXVARVALUE to TEXT, so run this command before
oe:migration:migrate. (oxuserpayments.OXVALUE is not covered by any OXID
migration at all — that column relies on this command.)
Options
--key=XXXX:- Configuration key (corresponds to
$this->sConfigKeyfrom the old OXID-6config.inc.php) - Default: the OXID stock key (
fq45QS09_fqyx09239QQ) - Applies to
oxconfigonly —oxuserpaymentsuses a separate derived key (str_rot13()of the OXID stock key, i.e.sd45DF09_sdlk09239DD) that OXID-6'sUserPayment::__construct()builds at runtime. The command applies that derivation automatically, no flag needed.
- Configuration key (corresponds to
--dry-run:- Report what would be changed without writing
- Note: dry-run reports the decode pass only; it does not exercise the schema-DDL path. A real run is the only way to verify schema migration.
--no-schema:- Skip the
BLOB → TEXTand charset DDL — only run the row-level decode
- Skip the
--forceor-f:- Skip the safety confirmation prompt
Recommended workflow
# 1. dump the OXID-6 source database 1:1 (cipher columns left untouched)
mysqldump -h <old-host> -u <user> -p <db> > oxid6.sql
# 2. import into the new MariaDB ≥ 11.2 / MySQL ≥ 8.0 instance
mysql -h <new-host> -u <user> -p <db> < oxid6.sql
# 3. install the OXID-7 project on top of the imported database
composer install
# 4. preview
vendor/bin/oe-console trw:db:decode-legacy --dry-run
# 5. real run
vendor/bin/oe-console trw:db:decode-legacy [--key=<sConfigKey>]
# 6. continue with the regular OXID-7 update sequence
vendor/bin/oe-console trw:update:after
The command uses a direct PDO connection built from source/config.inc.php.
It does not boot the OXID Registry/Config stack, so the
unserialize() warnings caused by reading still-encrypted oxconfig rows
during a regular bootstrap do not occur.
DB export (trw:db:export)
- export the DB to an sql-file
- it is possible to export all or only certain tables. Furthermore, sensitive data can be anonymized during export
- option --yaml=XXXX:
- get the Export-Options from a yaml-file
- If the configuration file is not provided, then a sample file is placed here: var/trwcliexportimport/example.yaml
DB import (trw:db:import)
- import the DB from an sql-file
- option --yaml=XXXX:
- get the Export-Options from a yaml-file
- If the configuration file is not provided, then a sample file is placed here: var/trwcliexportimport/example.yaml
DB migrate (trw:db:migrate)
Wrapper for OXID Doctrine Migrations (oe-eshop-db_migrate) integrated into oe-console with enhanced features.
Available Commands
- status - Show the status of all migrations
- migrate - Execute all pending migrations
- execute - Execute a single migration version
- generate - Generate a new migration class
- sync-metadata-storage - Synchronize the metadata storage table
- version - Manually add/delete migration versions
- up-to-date - Check if the database is up to date
- list - List all available migration commands
Options
--project=XXXXor-p XXXX:- Specify the migration project (ce, pe, ee, or module-id)
- Automatically builds the full namespace for the migration
- Examples:
-p ce,-p yourmoduleid
--edition=XXXX:- The shop edition (CE, PE, EE) — usually not needed, it is derived from the installation
--auto-sync:- Automatically sync metadata storage if out of date
- Useful for development environments
--dry-run:- Execute the migration as a dry run (for migrate command)
--up:- Execute the migration up (for execute command)
--down:- Execute the migration down (for execute command)
--strict-migrations:- Opt out of the default empty-suite handling and let Doctrine fail when a migration suite is configured but has no migration files
- By default, the
migratecommand forwards Doctrine's--allow-no-migrationflag so that empty suites (typically the project suite when no project migrations exist) are skipped with a warning instead of aborting the run — which would otherwise prevent any subsequent module suites from being processed
Examples
Check migration status (all projects):
vendor/bin/oe-console trw:db:migrate status
Run all pending migrations:
vendor/bin/oe-console trw:db:migrate migrate
Dry-run migrations:
vendor/bin/oe-console trw:db:migrate migrate --dry-run
Sync metadata storage (fixes "not up to date" errors):
vendor/bin/oe-console trw:db:migrate sync-metadata-storage
Execute specific migration with full namespace:
vendor/bin/oe-console trw:db:migrate execute "Your\Module\Namespace\Version20221026214950" --up
Execute specific migration with project shortcut:
vendor/bin/oe-console trw:db:migrate execute 20221026214950 --project yourmoduleid --up
vendor/bin/oe-console trw:db:migrate execute Version20170718124421 -p ce --down
Auto-fix metadata storage issues:
vendor/bin/oe-console trw:db:migrate migrate --auto-sync
Fail on empty migration suites (opt-out of the default "skip empty"):
vendor/bin/oe-console trw:db:migrate migrate --strict-migrations
Supported Project Shortcuts
ce- OXID Shop Community Editionpe- OXID Shop Professional Editionee- OXID Shop Enterprise Editionyourmoduleid- The migration of your module (uses module-id as namespace)[custom]- Use full namespace for other modules
Note
OXID uses separate migration tables for each project/module:
oxmigrations_ce(Shop Core)oxmigrations_project(Project)oxmigrations_[module](Each module)
This command is equivalent to calling vendor/bin/oe-eshop-db_migrate directly, but integrated into the oe-console for better discoverability and enhanced error handling.
Empty Migration Suites
The OXID migration wrapper runs Doctrine's migrations:migrate once per suite
(ce, project, and every module). If a suite is configured (e.g. via
source/migration/project_migrations.yml) but has no Version*.php files,
Doctrine aborts with a non-zero exit code, which makes the wrapper stop and
skip all subsequent module suites.
To avoid this, trw:db:migrate migrate forwards Doctrine's
--allow-no-migration flag by default. Empty suites are then reported as a
warning and the run continues with the next suite. Pass --strict-migrations
to restore the default Doctrine behavior.
exportimport yaml example
YAML example:
dumpFileName: dump.sql
onlyTables: { }
anonymizeRowsInTables:
oxuser:
- oxfname
- oxlname
oxorder:
- oxbillfname
- oxbilllname
- oxdelfname
- oxdellname
DB charset and collation
Two commands for the character set of the database, one per direction. Both report by
default and change nothing without --apply — an ALTER across the tables of a live shop
is not something to trigger by forgetting a flag.
Neither is a migration, on purpose: the character set of a table is a property of the installation, not of a module's schema. A migration runs once and is then marked done forever, so it can neither react to a database that gains utf8mb4 later nor repair a table that already existed when it first ran — which is exactly how a single latin1 table can survive every migration written for it. These commands have no memory: they look at the database as it is now and can be run again after every upgrade.
DB charset (trw:db:charset)
Lifts the character set of the whole database to what the installation can actually carry, and reports what stands in the way where it cannot. Two different situations end up here:
- drift — a table sits below the level the rest of the database is on, usually because it was created long ago by something that named no charset and has been carried from dump to dump ever since. A default run finds exactly this case: the target is what the installation already carries, so anything the run lists is a table that is behind.
- the ceiling — everything is as declared and the whole database could go one step
higher. That is
--charset=utf8mb4, and it is a decision, not a repair.
The ceiling is the connection, not the tables. MySQL converts between the connection
charset and the column charset on every read and write, so a utf8mb4 column behind a
utf8mb3 connection still receives ? instead of an emoji. dbCharset in
source/config.inc.php decides this and is left to the operator — the command reports the
mismatch and says what to set.
Held back in every direction:
latin1_general_ci— OXID spells every char(32) id column this way, in core and module tables alike. They hold hex ids, and converting them would widen every primary and foreign key index for nothing.latin1_bin—oxseo.OXSEOURL. Byte-exact matching is the point of that column.- columns that look like an id (type
char(32), or a name ending inID) but are not onlatin1_general_ci. Those are reported rather than skipped silently, naming which rule took hold: a column that really is an id belongs onlatin1_general_ci, and the place to put it there is the migration of the module that created it. - generated columns — information_schema does not hand back the expression in a form a MODIFY could repeat.
Before it writes anything the run estimates the index widths for the target charset and
reports the tables where a key would pass the engine's limit (1000 bytes on MyISAM, 3072 on
InnoDB with DYNAMIC row format, 767 with COMPACT). Those tables are skipped instead of
taking the run down halfway through, and the ALTERs are issued one table at a time so a
failure leaves the others alone. Afterwards the shop views are regenerated, because the
oxv_* views repeat the column types of their base tables.
- option --apply:
- execute the conversion; without it the command only reports
- option --charset=utf8mb3|utf8mb4:
- target charset; default is the highest one the connection carries
latin1is not a reachable target — going further down istrw:db:keycollation
- option --allow-downgrade:
- also pull columns that sit above the target down to it
- for the one case that needs it: a shop where something once followed what the server could do instead of what the installation uses, leaving single columns above everything else
- it cannot overshoot — the target is one value for the whole run, taken from the connection, and only utf8mb3 and utf8mb4 are valid targets
- and never at the cost of content: every candidate is round-tripped through the target charset in the database and compared byte for byte first, and a single row that would come back different leaves that column where it is
- option --table=XXXX:
- limit the run to a single table
- option --exclude=table.column,...:
- hold single columns back, comma separated
# what is behind? (reports only)
vendor/bin/oe-console t:d:c
# raise the whole database one step - check the report first
vendor/bin/oe-console t:d:c --charset=utf8mb4
vendor/bin/oe-console t:d:c --charset=utf8mb4 --apply
# clean up single columns left above the rest by an earlier tool
vendor/bin/oe-console t:d:c --allow-downgrade
vendor/bin/oe-console t:d:c --allow-downgrade --apply
DB key collation (trw:db:keycollation)
Puts id columns back on latin1_general_ci where they drifted off it — the counterpart to
trw:db:charset, and the same defect seen from the other side. A module table created by an
onActivate() event in the OXID 6 days and then carried into OXID 7 by a database dump
keeps whatever the server default was back then. The migration written for it later guards
its CREATE with hasTable(), so it counts as executed without ever having touched the
table, and the schema in the database quietly differs from the schema the module declares.
Going down from utf8mb3 to latin1 is the one direction that can lose data, so it is done far more carefully than the way up:
- which columns must be latin1 is read out of OXID's own
database_schema.sql, not guessed. That file declarescharacter set latin1 collate latin1_general_cion 164 columns and deliberately leaves it off others —oxuser.OXUSTID,oxorder.OXTRANSID,oxorderarticles.OXWRAPIDand the affiliate ids onoxshopsare meant to follow the table charset. A command that pulled every column named…IDdown to latin1 would break exactly those. A column the core schema knows and does not declare latin1 is never touched. - for everything the core schema does not know — module tables, and module columns added to
core tables — the shape rules apply: type
char(32), or a name ending inID. Those are a convention, so they are reported under their own reason and are meant to be read before--apply, not after. - no column is converted before its content has been proven to survive. Every candidate is round-tripped through latin1 in the database and compared byte for byte; one row that would come back different blocks that column. An id holds hex, so this passes — but it is what separates a safe downgrade from a silent one.
Repairing the database is only half of it: where a module's own migration declares the column wrongly, a fresh install recreates the drift. Those belong in the module, and the report names them.
- option --apply:
- execute the conversion; without it the command only reports
- option --schema-only:
- only convert what
database_schema.sqldeclares and ignore the shape rules - the risk-free way in, and the recommended first run
- only convert what
- option --table=XXXX:
- limit the run to a single table
- option --exclude=table.column,...:
- hold single columns back, comma separated
# full report, both rules
vendor/bin/oe-console t:d:k
# only what OXID itself declares
vendor/bin/oe-console t:d:k --schema-only
vendor/bin/oe-console t:d:k --schema-only --apply
configuration commands
configuration (trw:generate:yaml:config)
- create a configfile via oe-console-command: oe-console trw:generate:yaml:config
Example: 1.yaml.dist
YAML example:
moduleTakeCare: - yourmodule1
moduleTakeCareDevelop:
- yourmodule2
themeTakeCare:
- apex
dontRunIfFileExits:
- dummyfile1.txt
runIfFileExits:
- dummyfile2.txt
dontRunIfOptionValue: bDemoOption1: true bDemoOption2: false bDemoOption3: abc bDemoOption4: 1
runIfOptionValue: bDemoOption5: true bDemoOption6: false bDemoOption7: abc bDemoOption8: 1
runIfEnvironmentVariable: DEV_ENVIRONMENT1: true DEV_ENVIRONMENT2: false DEV_ENVIRONMENT3: abc DEV_ENVIRONMENT4: 1
prepareShopForUpdate: false
configExportImportPath: transfer
configExportNotAllowedVars:
- bOptionNotAllowed1
- bOptionNotAllowed2
configImportNotAllowedVars:
- bOptionNotAllowed1
- bOptionNotAllowed2
...
- option --env=XXXX: write the Plugin-Config in file trwclirun/XXXX.1.yaml
option --shop-id=YYYY:
- write the Plugin-Config in file trwclirun/XXXX.YYYY.yaml or trwclirun/YYYY.yaml
- if not set, the command write the Plugin-Configs for all Shop-Ids
moduleTakeCare: (necessary for command trw:update:before, trw:allmodules:active and trw:allmodules:deactive)
- List of ModuleIds for deletetion and activation, before and after update commands
- The sorting ensures the order of activation
moduleTakeCareDevelop: (necessary for command trw:update:before and trw:allmodules:deactive)
- List of ModuleIds for deletetion and deactivation, before update commands
themeTakeCare: (necessary for command trw:update:before)
- List of Themes, that will be deleted before update commands
- the OXID-Shop-installer will create the themes again
prepareShopForUpdate: (necessary for command trw:update:before)
- prepare the shop, so that the install-routine from OXID starts during "composer install"
- please be careful, start it only if the standard OXID routines of "composer install" start after executing the command
- the OXID-Shop-installer will create the themes again
dontRunIfFileExits: (necessary for command trw:update:before and trw:update:after)
- dont run this commands if file exists
- its a possibility to stop automatic updates
runIfFileExits: (necessary for command trw:update:before and trw:update:after)
- run this commands only if file exists
- its a possibility to stop automatic updates
dontRunIfOptionValue: (necessary for command trw:update:before and trw:update:after)
- dont run this commands if an shop-config-value exists and active
- its a possibility to stop automatic updates
runIfOptionValue: (necessary for command trw:update:before and trw:update:after)
- run this commands only if if an shop-config-value exists and active
- its a possibility to stop automatic updates
runIfEnvironmentVariable: (necessary for command trw:update:before and trw:update:after)
- run this commands only if if an ENVIRONMENT-VARIABLE exists
- its a possibility to stop automatic updates
configExportImportPath: (necessary for command trw:config:export and trw:config:import)
- The path in which the config export is saved
- relative to root/var/configuration
configExportNotAllowedVars: (necessary for command trw:config:export and trw:config:import)
- Dont export this config-vars
configImportNotAllowedVars: (necessary for command trw:config:export and trw:config:import)
- Dont import this config-vars
Create IDE-Helper file (trw:ide:helper)
- option --shop-id=YYYY:
- the id of the shop
- if not set, the create the helper-files for all Shop-Ids
- creates a helper file for the modules currently installed in the shop so that IDEs such as phpStorm can find the class chains and thus code completion is possible
- it does not matter whether the module is active or not. what is important is whether it is installed for the shop
- the file is stored in the root directory and has the name ".ide-helper_modules_1.php", for example. The 1 stands for the shop ID
- the file(s) are parallel to the ".ide-helper.php" which is created via "vendor/bin/oe-eshop-ide_helper"
- if no shop ID is provided optionally, the file for the standard shop is created with the ID 1
Module Refresh (trw:module:refresh)
- argument module-id:
- the id of the module — required, given as an argument, not as an option
- option --uninstall:
- If set, the module will be deactivated and uninstalled during the refresh. The default is to simply (re-)install the module and (re-)activate it.
- option --shop-id=YYYY:
- the id of the shop
- if not set, the command refresh the Module for all Shop-Ids
- helper-command for developers
- deactivate, install and activate a module in one step
- The command observe an existing module chain and deactivates and activates all dependent modules in the process
Module Migrate (trw:module:migrate)
- short form:
t:m:m - argument
module-id: the module whose Doctrine suite to migrate - option --down: roll the suite back to zero (full
down()teardown) and drop the suite's metadata table; without it the suite is migrated up to the latest version - scoped to the single module suite (via the edition selector)
- used internally by
trw:module:setup/trw:module:wipeoutas their migration step, and usable standalone
Xdebug shutdown-segfault handling
On builds with an active Xdebug (xdebug.mode including debug/profile), the PHP process
segfaults at shutdown after a migration has actually executed DDL — the migration itself
succeeds and the database stays consistent, but the process exit code becomes 139, which
breaks CI checks and command chaining. (A no-op migration — "already at latest" — does not
trigger it.)
To keep a clean exit code, the migration is transparently run in a subprocess started with
XDEBUG_MODE=off whenever Xdebug is loaded and active. This applies to t:m:m, t:m:s and
t:m:w alike. On servers without Xdebug (typically production) the migration runs
in-process — no subprocess, no overhead; the XDEBUG_MODE variable is meaningless there and
never set. The recursion into the subprocess is guarded by XDEBUG_MODE=off, so the
subprocess runs the migration in-process itself.
Module Setup (trw:module:setup)
- short form:
t:m:s - the counterpart to
trw:module:wipeout— brings a module fully into the shop - argument
module: two ways to address the module —- path way (works for a module installed anywhere on the server, as OXID allows):
pass the path to the module directory or directly to its
metadata.php. The module id is read from the metadata (the file is included, so an id defined via a class constant like'id' => Module::MODULE_IDresolves through the autoloader) - id way (only for modules under
vendor/, i.e. previously pulled in via composer and therefore part ofcomposer.lock, or modules already installed in the shop): pass the module id. It is resolved from the installed configuration, or by scanning the composeroxideshop-modulepackages incomposer.lock. A fresh module living outsidevendor/cannot be found by id — use the path way for it
- path way (works for a module installed anywhere on the server, as OXID allows):
pass the path to the module directory or directly to its
- option --develop: register the module in
moduleTakeCareDevelopinstead ofmoduleTakeCare - option --no-takecare: do not register the module in the trwclirun config at all
- option --shop-id=YYYY: the shop id; if not set, all shop ids (in CE: shop 1)
- option --env=XXXX: environment used to locate the trwclirun config file
Steps, in this order:
- install the module files/configuration (
oe:module:install, idempotent) - migrate up to the latest version (schema build, via
trw:module:migrate) — deliberately before activation, because a fresh module whoseonActivatetouches its own tables would otherwise fail (the tables only exist after the migration). Scoped to the module suite; a no-op for modules without amigration/migrations.yml. Xdebug-safe (runs in a clean subprocess when Xdebug is active — see Module Migrate above) - activate the module in the targeted shop(s)
- clear the cache (via
trw:clear:cache) - update the views (via
trw:update:views) - register the module id in
var/trwclirun/<shop-id>.yaml—moduleTakeCareby default,moduleTakeCareDevelopwith--develop, or skipped with--no-takecare. The id is kept in exactly one of the two lists (removed from the other if present)
Module Wipeout (trw:module:wipeout)
- short form:
t:m:w - argument
module-id: the id of the module to remove (required) - option --shop-id=YYYY:
- the id of the shop
- if not set, the module is wiped out for all shop ids (in CE: shop 1)
- option --env=XXXX:
- environment used to locate the trwclirun config file to clean up
- option --keep-migrations:
- do not roll back the module migrations — the module schema/data stays in place
- default (without the flag) is a full rollback to version 0, i.e. every
down()runs
- option --force (-f):
- skip the safety confirmation prompt
Completely removes a module from the shop setup, in this order:
- deactivate the module — and, first, any modules that depend on it (dependency
chain, like
trw:module:refresh --uninstall); dependents are left inactive and reported at the end - roll back migrations to version 0 (schema teardown via
down()) — done while the module is still installed so its migration suite is still discoverable. Scoped to the module suite only. After a successful rollback the module's Doctrine metadata table (table_storage.table_namefrom itsmigration/migrations.yml, e.g.oxmigrations_trwblockforge) is dropped too, so no trace is left behind. The shared shop-edition logs (oxmigrations_ceetc.) are never touched. Legacy flat-layout migrations (migration/Version*.phpwithoutmigration/migrations.yml) cannot be isolated and are reported for manual handling. Xdebug-safe (runs in a clean subprocess when Xdebug is active — see Module Migrate below) - uninstall the module (
oe:module:uninstall) - clear the cache (via
trw:clear:cache) - update the views (via
trw:update:views) - remove the module id from
moduleTakeCare/moduleTakeCareDevelopinvar/trwclirun/<shop-id>.yaml - composer hints: when the module lives under
vendor/, print best-effort follow-up hints — thecomposer remove <vendor/package>line, the authoritative provenance fromcomposer.lock(source.url+ reference anddist.url, i.e. where composer actually resolved the package from), and any matchingrepositoriesentry from the rootcomposer.json(matched via the package short name — note the lock source url and the repository url can differ). The composer package itself and its files undervendor/are not touched automatically, since that is a project-composer decision.
Destructive. The default migration rollback drops the module's tables/columns and their data. Take a database backup first; the command asks for confirmation unless
--forceis given.
shopupdate-commands
The shopupdate-commands are intended to speed up the shop composer update process. The following commands can be executed before and after the shop update:
after Update (trw:update:after)
- option --env=XXXX: get the Plugin-Config from file trwclirun/XXXX.1.yaml
- option --shop-id=YYYY:
- get the Plugin-Config from file trwclirun/XXXX.YYYY.yaml or trwclirun/YYYY.yaml
- "after updates"-processes in Shop with this ID
- if not set, the command check if Plugin-Configs exists for all Shop-Ids
- activate all Modules in a certain order (via trw:allmodules:active command)
- set Shop Maintenance Mode "off"
- clean cache (via trw:clear:cache command)
- upate views
- run migrations
before the Update (trw:update:before)
- option --env=XXXX: get the Plugin-Config from file trwclirun/XXXX.1.yaml
- option --shop-id=YYYY:
- get the Plugin-Config from file trwclirun/XXXX.YYYY.yaml or trwclirun/YYYY.yaml
- "before updates"-processes in Shop with this ID
- if not set, the command check if Plugin-Configs exists for all Shop-Ids
- check whether certain conditions are met
- set Shop Maintenance Mode "on"
- deactivate all Modules (via trw:allmodules:deactive command)
- clean broken Modules Options
- delete all old themes
Views Update (trw:update:views)
- update the Shop views