{"id":13684079,"url":"https://github.com/josegonzalez/cakephp-version","last_synced_at":"2025-04-09T15:09:24.303Z","repository":{"id":22752502,"uuid":"26097971","full_name":"josegonzalez/cakephp-version","owner":"josegonzalez","description":"CakePHP3: plugin that facilitates versioned database entities","archived":false,"fork":false,"pushed_at":"2024-01-09T14:57:21.000Z","size":154,"stargazers_count":50,"open_issues_count":7,"forks_count":19,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-09T15:09:15.462Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/josegonzalez.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","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}},"created_at":"2014-11-03T00:41:17.000Z","updated_at":"2025-02-13T20:11:26.000Z","dependencies_parsed_at":"2024-04-10T02:56:06.763Z","dependency_job_id":"162b360b-1a9f-49ab-a310-40092db6e864","html_url":"https://github.com/josegonzalez/cakephp-version","commit_stats":{"total_commits":115,"total_committers":20,"mean_commits":5.75,"dds":0.7652173913043478,"last_synced_commit":"fcb038b6d1fe209e85f9bdd87e6592cfdcdd08f1"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegonzalez%2Fcakephp-version","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegonzalez%2Fcakephp-version/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegonzalez%2Fcakephp-version/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/josegonzalez%2Fcakephp-version/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/josegonzalez","download_url":"https://codeload.github.com/josegonzalez/cakephp-version/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248055282,"owners_count":21040157,"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":[],"created_at":"2024-08-02T14:00:24.934Z","updated_at":"2025-04-09T15:09:24.284Z","avatar_url":"https://github.com/josegonzalez.png","language":"PHP","funding_links":[],"categories":["Auditing / Logging","Plugins"],"sub_categories":["Auditing / Logging"],"readme":"[![Build Status](https://img.shields.io/travis/josegonzalez/cakephp-version/master.svg?style=flat-square)](https://travis-ci.org/josegonzalez/cakephp-version)\n[![Coverage Status](https://img.shields.io/coveralls/josegonzalez/cakephp-version.svg?style=flat-square)](https://coveralls.io/r/josegonzalez/cakephp-version?branch=master)\n[![Total Downloads](https://img.shields.io/packagist/dt/josegonzalez/cakephp-version.svg?style=flat-square)](https://packagist.org/packages/josegonzalez/cakephp-version)\n[![Latest Stable Version](https://img.shields.io/packagist/v/josegonzalez/cakephp-version.svg?style=flat-square)](https://packagist.org/packages/josegonzalez/cakephp-version)\n[![Documentation Status](https://readthedocs.org/projects/cakephp-version/badge/?version=latest\u0026style=flat-square)](https://readthedocs.org/projects/cakephp-version/?badge=latest)\n[![Gratipay](https://img.shields.io/gratipay/josegonzalez.svg?style=flat-square)](https://gratipay.com/~josegonzalez/)\n\n# Version\n\nA CakePHP 4.x plugin that facilitates versioned database entities\n\n## Installation\n\nAdd the following lines to your application's `composer.json`:\n\n```json\n\"require\": {\n    \"josegonzalez/cakephp-version\": \"dev-master\"\n}\n```\n\nfollowed by the command:\n\n`composer update`\n\nOr run the following command directly without changing your `composer.json`:\n\n`composer require josegonzalez/cakephp-version:dev-master`\n\n## Usage\n\nIn your app's `config/bootstrap.php` add:\n\n```php\nPlugin::load('Josegonzalez/Version', ['bootstrap' =\u003e true]);\n```\n\n## Usage\n\nRun the following schema migration:\n\n```sql\nCREATE TABLE `version` (\n    `id` int(11) NOT NULL AUTO_INCREMENT,\n    `version_id` int(11) DEFAULT NULL,\n    `model` varchar(255) NOT NULL,\n    `foreign_key` int(10) NOT NULL,\n    `field` varchar(255) NOT NULL,\n    `content` text NULL,\n    `created` datetime NOT NULL,\n    PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n```\n\n\u003e Note that the `content` field must be nullable if you want to be able to version any nullable fields in your application.\n\n\u003e You may optionally add a `version_id` field of type `integer` to the table which is being versioned. This will store the latest version number of a given page.\n\nIf you wish to create the table using `cakephp/migrations` then you will need to use a migration that looks something like this:\n\n```php\n\u003c?php\n\nuse Phinx\\Migration\\AbstractMigration;\n\nclass CreateVersions extends AbstractMigration\n{\n    public function change()\n    {\n        $this-\u003etable('version')\n             -\u003eaddColumn('version_id', 'integer', ['null' =\u003e true])\n             -\u003eaddColumn('model', 'string')\n             -\u003eaddColumn('foreign_key', 'integer')\n             -\u003eaddColumn('field', 'string')\n             -\u003eaddColumn('content', 'text', ['null' =\u003e true])\n             -\u003eaddColumn('created', 'datetime')\n             -\u003ecreate();\n    }\n}\n```\n\nAdd the following line to your entities:\n\n```php\nuse \\Josegonzalez\\Version\\Model\\Behavior\\Version\\VersionTrait;\n```\n\nAnd then include the trait in the entity class:\n\n```php\nclass PostEntity extends Entity {\n    use VersionTrait;\n}\n```\n\nAttach the behavior in the models you want with:\n\n```php\npublic function initialize(array $config) {\n    $this-\u003eaddBehavior('Josegonzalez/Version.Version');\n}\n```\n\nWhenever an entity is persisted - whether via insert or update - that entity is also persisted to the `version` table. You can access a given revision by executing the following code:\n\n```php\n// Will contain a generic `Entity` populated with data from the specified version.\n$version = $entity-\u003eversion(1);\n```\n\nYou can optionally retrieve all the versions:\n\n```php\n$versions = $entity-\u003eversions();\n```\n\n### Storing Additional Meta Data\n\n`cakephp-version` dispatches an event `Model.Version.beforeSave` which you can optionally handle to attach additional meta-data about the version.\n\nAdd the necessary additional fields to your migration, for example:\n\n```sql\nCREATE TABLE `version` (\n    `id` int(11) NOT NULL AUTO_INCREMENT,\n    `version_id` int(11) DEFAULT NULL,\n    `model` varchar(255) NOT NULL,\n    `foreign_key` int(10) NOT NULL,\n    `field` varchar(255) NOT NULL,\n    `content` text,\n    `created` datetime NOT NULL,\n    `custom_field1` varchar(255) NOT NULL, /* column to store our metadata */\n    `custom_field2` varchar(255) NOT NULL, /* column to store our metadata */\n    PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n```\n\nThen define an event listener to handle the event and pass in additional metadata, for example:\n\n```php\nuse Cake\\Event\\Event;\nuse Cake\\Event\\EventListenerInterface;\n\nclass VersionListener implements EventListenerInterface {\n\n    public function implementedEvents() {\n        return array(\n            'Model.Version.beforeSave' =\u003e 'insertAdditionalData',\n        );\n    }\n\n    public function insertAdditionalData(Event $event) {\n        return [\n            'custom_field1' =\u003e 'foo',\n            'custom_field2' =\u003e 'bar'\n        ];\n    }\n}\n```\n\nYour event listener can then be attached in your project, for example:\n\n```php\nuse App\\Event\\VersionListener;\nuse Cake\\Event\\EventManager;\n\n$VersionListener = new VersionListener();\nEventManager::instance()-\u003eon($VersionListener);\n```\n\nNote that handling this event also allows you to modify/overwrite values generated by the plugin.\nThis can provide useful functionality, but ensure that if your event listener returns array keys called\n`version_id`, `model`, `foreign_key`, `field`, `content` or `created` that this is the intended behavior.\n\n#### Storing user_id as Meta Data\nTo store the `user_id` as additional meta data is easiest in combination with [Muffin/Footprint](https://github.com/UseMuffin/Footprint).\nThe above `insertAdditionalData()` method could then look like this:\n\n```php\n    /**\n     * @param \\Cake\\Event\\Event $event\n     *\n     * @return array\n     */\n    public function insertAdditionalData(Event $event)\n    {\n        $data = [\n            ...\n        ];\n\n        if ($event-\u003edata('_footprint')) {\n            $user = $event-\u003edata('_footprint');\n            $data += [\n                'user_id' =\u003e $user-\u003eid,\n            ];\n        }\n\n        return $data;\n    }\n```\nAny controller with the `FootprintAwareTrait` used will then provide the `_footprint` data into the model layer for this event callback to use.\n\n### Bake Integration\n\nIf you load the plugin using `'bootstrap' =\u003e true`, this plugin can be used to autodetect usage via the properly named database table. To do so, simply create a table with the `version` schema above named after the table you'd like to revision plus the suffix `_versions`. For instance, to version the following table:\n\n```sql\nCREATE TABLE `posts` (\n    `id` int(11) unsigned NOT NULL AUTO_INCREMENT,\n    `category_id` int(11) DEFAULT NULL,\n    `user_id` int(11) DEFAULT NULL,\n    `status` varchar(255) NOT NULL DEFAULT 'published',\n    `visibility` varchar(255) NOT NULL DEFAULT 'public',\n    `title` varchar(255) NOT NULL DEFAULT '',\n    `route` varchar(255) DEFAULT NULL,\n    `content` text,\n    `published_date` datetime DEFAULT NULL,\n    `created` datetime NOT NULL,\n    `modified` datetime NOT NULL,\n    PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n```\n\nCreate the following table:\n\n```sql\nCREATE TABLE `posts_versions` (\n    `id` int(11) NOT NULL AUTO_INCREMENT,\n    `version_id` int(11) NOT NULL,\n    `model` varchar(255) NOT NULL,\n    `foreign_key` int(11) NOT NULL,\n    `field` varchar(255) NOT NULL,\n    `content` text,\n    `created` datetime NOT NULL,\n    PRIMARY KEY (`id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8;\n```\n\nYou can create a migration for this with the following bake command:\n\n```shell\nbin/cake bake migration create_posts_versions version_id:integer model foreign_key:integer field content:text created\n```\n\n\u003e You'll also want to set the `content` field in this migration to nullable, otherwise you won't be able to version fields that can be nulled.\n\nTo track the current version in the `posts` table, you can create a migration to add the `version_id` field to the table:\n\n```shell\nbin/cake bake migration add_version_id_to_posts version_id:integer\n```\n\n### Configuration\n\nThere are five behavior configurations that may be used:\n\n- `versionTable`: (Default: `version`) The name of the table to be used to store versioned data. It may be useful to use a different table when versioning multiple types of entities.\n- `versionField`: (Default: `version_id`) The name of the field in the versioned table that will store the current version. If missing, the plugin will continue to work as normal.\n- `additionalVersionFields`: (Default `['created']`) The additional or custom fields of the versioned table to be exposed as well. By default prefixed with `version_`, e.g. `'version_user_id'` for `'user_id'`.\n- `referenceName`: (Default: db table name) Discriminator used to identify records in the version table.\n- `onlyDirty`: (Default: false) Set to true to version only dirty properties.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosegonzalez%2Fcakephp-version","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjosegonzalez%2Fcakephp-version","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjosegonzalez%2Fcakephp-version/lists"}