{"id":51404751,"url":"https://github.com/investblog/spintax-opencart","last_synced_at":"2026-07-04T10:00:44.893Z","repository":{"id":369223046,"uuid":"1287539418","full_name":"investblog/spintax-opencart","owner":"investblog","description":"Free \u0026 open-source OpenCart 3.x SEO extension — mass-generate unique meta tags, descriptions, SEO-URL keywords and product attributes from spintax templates, across every language and store. A faithful port of the Spintax WordPress plugin.","archived":false,"fork":false,"pushed_at":"2026-07-04T07:22:58.000Z","size":244,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-07-04T08:18:45.064Z","etag":null,"topics":["content-generation","ecommerce","ecommerce-seo","meta-tags","multilingual","ocmod","opencart","opencart-3","opencart-extension","opencart-module","php","seo","seo-tools","seo-urls","spintax"],"latest_commit_sha":null,"homepage":"https://spintax.net","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/investblog.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-07-02T19:31:44.000Z","updated_at":"2026-07-04T07:23:01.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/investblog/spintax-opencart","commit_stats":null,"previous_names":["investblog/spintax-opencart"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/investblog/spintax-opencart","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/investblog%2Fspintax-opencart","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/investblog%2Fspintax-opencart/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/investblog%2Fspintax-opencart/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/investblog%2Fspintax-opencart/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/investblog","download_url":"https://codeload.github.com/investblog/spintax-opencart/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/investblog%2Fspintax-opencart/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35117336,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-04T02:00:05.987Z","response_time":113,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["content-generation","ecommerce","ecommerce-seo","meta-tags","multilingual","ocmod","opencart","opencart-3","opencart-extension","opencart-module","php","seo","seo-tools","seo-urls","spintax"],"created_at":"2026-07-04T10:00:27.421Z","updated_at":"2026-07-04T10:00:44.878Z","avatar_url":"https://github.com/investblog.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spintax SEO for OpenCart\n\nMass-generate **unique** SEO content for your OpenCart catalog — product meta\ntitles, meta descriptions, meta keywords and descriptions — by rendering\n[spintax](https://spintax.net/docs/syntax) templates into stored fields. Preview\nevery change before it is written, apply in bulk safely, and keep manual edits.\n\nFree and open source (GPL-3.0). A faithful OpenCart port of the WordPress\n[**Spintax**](https://wordpress.org/plugins/spintax) plugin.\n\n![License](https://img.shields.io/badge/license-GPL--3.0-blue)\n![PHP](https://img.shields.io/badge/PHP-8.1%2B-777bb4)\n![OpenCart](https://img.shields.io/badge/OpenCart-3.0.x-1c9dd9)\n\n📖 **[User guide](docs/USAGE.md)** — install, quick start, the Dry run → Apply workflow, cron, and troubleshooting.\n\n---\n\n## Why\n\nWriting unique meta for hundreds or thousands of products by hand is impossible;\nduplicated boilerplate hurts SEO. Spintax templates let you describe *many*\nnatural variants once and render a distinct one per product/language:\n\n```\n{Buy|Order|Shop for} %name% at a {great|competitive|fair} price.\n{Fast|Quick} delivery, {easy|hassle-free} returns.\n```\n\nrenders, per product, to e.g. *“Order Apple Cinema 30 at a competitive price.\nFast delivery, easy returns.”* — a different combination each time.\n\n## Features\n\n- **Spintax engine** — synonyms `{a|b|c}`, permutations `[\u003cconfig\u003ea|b|c]`,\n  variables `%var%` / `#set`, conditionals `{?VAR?then|else}`, and locale-aware\n  plurals `{plural N: one|few|many}` (RU/UK 3-form, EN-style default).\n  Full syntax reference: **\u003chttps://spintax.net/docs/syntax\u003e**.\n- **Entities \u0026 targets** — **products, categories, information pages, and\n  manufacturers**. Fill description-columns (meta title/description/keyword +\n  the HTML description), **SEO-URL keywords** (`oc_seo_url`, with a per-store\n  collision guard + optional `-\u003cid\u003e` disambiguation), and **product custom\n  attributes** (`oc_product_attribute`, with a resolve-and-verify guard so a\n  deleted attribute is skipped, never orphaned).\n- **Bindings** — map an entity + target + template, per behavior: seed empty\n  only, regenerate on save, preserve manual edits, clear-on-empty (with a hard\n  guard so required columns like `meta_title` are never emptied). Optional\n  **per-product source override**, and `#include` for reusable template snippets.\n- **Multistore** — SEO-URL generation fans out across every store an entity is\n  assigned to, restrictable per binding.\n- **Automatic updates** — run on the entity's save event, and/or on a\n  self-scheduled, token-protected **cron** endpoint (opt-in per binding) that\n  re-applies when a template changes and re-seeds missing SEO URLs.\n- **Test panel** — preview a single entity/language and see the exact decision\n  (write / skip + reason code) plus a **word-level diff** before anything is\n  written. Same engine as Apply.\n- **Bulk Apply** — **Dry run → Apply**: the dry run previews counts and never\n  writes; Apply is chunked, shows a walk lock, and **stops on the first write\n  error** (never “best effort”). A server-side snapshot token guarantees Apply\n  acts on the exact config you previewed.\n- **Activity log** — every apply (save / bulk / cron) is recorded in-admin.\n- **All active languages**, HTML sanitisation for description bodies, and\n  Cyrillic→latin transliteration for SEO-URL keywords.\n- **Zero-config first run** — a ready demo binding is installed; nothing is\n  written until you explicitly Dry run → Apply.\n\n## Requirements\n\n- OpenCart **3.0.x** (built and verified on 3.0.5.0)\n- PHP **8.1+**\n- MySQL / MariaDB\n\n## Install\n\n1. **Extensions → Installer** → upload **`spintax_seo.ocmod.zip`**\n   (from [Releases](../../releases), or build it yourself — see *Development*).\n2. **Extensions → Modifications** → **Refresh** (adds the sidebar menu entry).\n3. **Extensions → Extensions → Modules** → **Spintax SEO** → **Install**\n   (creates tables, registers the save events, grants your admin group access).\n4. Open **Spintax SEO** from the sidebar (under Extensions).\n\nUninstall is non-destructive by default (your bindings/templates are kept). To\nremove fully: uninstall the module, then remove the modification in **Extensions\n→ Modifications**.\n\n## First run — nothing is written until you say so\n\nA demo binding is seeded **enabled** but with *Run on product save* **off**, so it\nnever writes on its own.\n\n1. On the Spintax SEO page, go to **Bulk Apply**, pick the demo binding.\n2. **Dry run** — previews how many cells would be written (no writes).\n3. **Apply** — fills exactly those cells.\n\nUse the **Test** panel to preview one product/language first. Turn on *Run on\nproduct save* per binding for automatic updates on every save.\n\n\u003e **Coverage note:** the save event fires for every product write that goes\n\u003e through OpenCart’s product model (the admin form and well-behaved\n\u003e extensions/imports). A raw-SQL bulk importer bypasses the model — run **Bulk\n\u003e Apply** after such an import. (OpenCart 3.0.x core ships no product CSV import\n\u003e or product-write API, so this only concerns third-party tools.)\n\n## How it works (architecture)\n\nThe engine kernel is framework-agnostic PHP under\n`upload/system/library/spintax/`; the OpenCart admin layer is a thin controller\nover it. Writes use targeted direct SQL (never the destructive `editProduct`\nfull-rewrite) plus a cache flush. See [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md).\n\n## Development\n\nA Docker stack (OpenCart 3.0.5.0 + PHP 8.1 + MariaDB) and a PHPUnit harness are\nincluded.\n\n```bash\ndocker compose up -d                 # start the stack (see README notes for cli_install)\nscripts/dev-provision.sh             # enable SEO URLs + a 2nd language for testing\nscripts/test.ps1                     # run the engine test suite (Bash: scripts/test.sh)\nscripts/deploy.sh                    # copy extension/upload -\u003e the docroot\ndocker compose exec -T web php /tmp/build-ocmod.php /opt/spintax /opt/spintax/spintax_seo.ocmod.zip\n```\n\nThe engine kernel is unit-tested (with byte-identity checks against the original\nWordPress kernel); the binding/apply/walk layers are covered by DB integration\ntests that self-skip when no database is reachable. CI runs on PHP 8.1 and 8.3.\n\n## Status\n\nPre-release, feature-complete for the core plan: all four entities\n(product / category / information / manufacturer), all three target kinds\n(description columns / SEO-URL keywords / product attributes), per-entity sources,\n`#include`, multistore SEO-URL fan-out, save-event + self-scheduled cron + Bulk\nApply, an activity log, and the opt-in storefront credit. See\n[`CHANGELOG.md`](CHANGELOG.md) for the full list. It is published as a pre-release\nwhile it soaks against more real stores.\n\n## Credits\n\nPorted from the WordPress **Spintax** plugin by [301.st](https://301.st):\n\n- WordPress plugin: \u003chttps://wordpress.org/plugins/spintax\u003e · \u003chttps://spintax.net\u003e\n- Source: \u003chttps://github.com/investblog/spintax\u003e\n\n## License\n\n[GPL-3.0-or-later](LICENSE). © 301st.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finvestblog%2Fspintax-opencart","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Finvestblog%2Fspintax-opencart","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Finvestblog%2Fspintax-opencart/lists"}