{"id":20148629,"url":"https://github.com/ideasonpurpose/wp-data-model","last_synced_at":"2025-03-03T00:26:30.480Z","repository":{"id":40926328,"uuid":"261368730","full_name":"ideasonpurpose/wp-data-model","owner":"ideasonpurpose","description":"Base package for building data model plugins for WordPress sites.","archived":false,"fork":false,"pushed_at":"2024-03-26T15:33:46.000Z","size":1616,"stargazers_count":0,"open_issues_count":15,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-27T08:04:16.463Z","etag":null,"topics":["wordpress"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ideasonpurpose.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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-05-05T05:23:40.000Z","updated_at":"2021-10-19T16:43:29.000Z","dependencies_parsed_at":"2024-03-26T16:50:52.173Z","dependency_job_id":null,"html_url":"https://github.com/ideasonpurpose/wp-data-model","commit_stats":{"total_commits":122,"total_committers":3,"mean_commits":"40.666666666666664","dds":0.180327868852459,"last_synced_commit":"db6e313ce48971aa00a6ee4703f06920a47ab85f"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ideasonpurpose%2Fwp-data-model","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ideasonpurpose%2Fwp-data-model/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ideasonpurpose%2Fwp-data-model/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ideasonpurpose%2Fwp-data-model/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ideasonpurpose","download_url":"https://codeload.github.com/ideasonpurpose/wp-data-model/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":241587786,"owners_count":19986628,"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":["wordpress"],"created_at":"2024-11-13T22:38:20.492Z","updated_at":"2025-03-03T00:26:30.473Z","avatar_url":"https://github.com/ideasonpurpose.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# wp-data-model\n\n#### Version 1.0.0\n\nBase package for building data model plugins for WordPress sites at [Ideas On Purpose](https://www.ideasonpurpose.com).\n\n[![Packagist](https://badgen.net/packagist/v/ideasonpurpose/wp-data-model)](https://packagist.org/packages/ideasonpurpose/wp-data-model)\n[![codecov](https://codecov.io/gh/ideasonpurpose/wp-data-model/branch/master/graph/badge.svg)](https://codecov.io/gh/ideasonpurpose/wp-data-model)\n[![Coverage Status](https://coveralls.io/repos/github/ideasonpurpose/wp-data-model/badge.svg)](https://coveralls.io/github/ideasonpurpose/wp-data-model)\n[![Maintainability](https://api.codeclimate.com/v1/badges/4aa4c56b9e813dd66f9a/maintainability)](https://codeclimate.com/github/ideasonpurpose/wp-data-model/maintainability)\n[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)\n\n## Example data-model Plugin\n\nTo start a new data-model plugin, copy the **example** directory. Create new CPTs and Taxonomies in the **lib** subdirectory, then instantiate those from **main.php**. Connect Taxonomies to Post Types with a `taxonomyMap` and rename built-in Post Types and Taxonomies with static calls to `WP\\Rename`.\n\n### Create Custom Post Types and Taxonomies\n\nNew Custom Post*Types are created as PHP classes which extend `IdeasOnPurpose\\WP\\CPT`, new Taxonomies extend `IdeasOnPurpose\\WP\\Taxonomy`. New classes \\_must include* a `props` method which defines `$this-\u003eargs`.\n\n### Taxonomy Maps and post_type assignment\n\nThe **main.php** file connects post_types to taxonomies using an array-map where each key is a single taxonomy and each value is an array of post_types to attach to. An example looks like this:\n\n```php\n// main.php\n$this-\u003etaxonomyMap = [\n  \"category\" =\u003e [\"post\", \"policy\", \"help\"],\n  \"audience\" =\u003e [\"post\", \"help\", \"event\"],\n  \"fellowship\" =\u003e [\"event\"],\n];\n```\n\n### Separators\n\nCall `new WP\\Admin\\Separators(22, 26)` with a list of indexes to insert separators into the menu. Matching indexes will insert the separator _after_ the CPT, so the following code will show **Articles** with a separator directly below it:\n\n```php\nnew WP\\Admin\\Separators(23);\nnew CPT\\Article(23);\n```\n\n### Admin CSS\n\nNew Post_Types and Taxonomies can add specific CSS Rules to the WordPress admin by defining a `$css` property. See the `styles` methods in the example files.\n\n### Generating Labels\n\nA default set of labels can be generated from `WP\\DataModel\\Labels::post_type()` and `WP\\DataModel\\Labels::taxonomy()`. These should be used to populate the `labels` value of the `$args` property when defining a new Post_Type or Taxonomy.\n\nArguments are:\n\n- **`$singular`** _String_\u003cbr\u003e\n  The singular basename of the label. Case will be normalized.\n- **`$plural`** _String_\u003cbr\u003e\n  The plural basename of the label.\n- **`$hierarchical`** _Boolean_, default: `true` \u003cbr\u003e\n  When true, labels will be created from Pages and Categories. When false, default labels will be created from Posts and Tags. (default [post_type labels](https://github.com/WordPress/wordpress-develop/blob/7d10dd7b0fde2a782395887c2d66439481440f9b/src/wp-includes/class-wp-post-type.php#L977-L1032), default [taxonomy labels](https://github.com/WordPress/wordpress-develop/blob/7d10dd7b0fde2a782395887c2d66439481440f9b/src/wp-includes/class-wp-taxonomy.php#L595-L651)).\n\nAny label overrides should be applied directly to the returned array:\n\n```php\n// Generate default labels\n$labels = WP\\DataModel\\Labels::taxonomy(__('audience', 'text_domain'), __('audiences', 'text_domain'));\n\n// Change \"All Audiences\" to \"Every Audience\"\n$labels['all_items'] = __('Every Audience', 'text_domain');\n```\n\n### Renaming Built-in Post Types and Taxonomies\n\nBuilt-in post_types and taxonomies can be easily renamed. The DataModel object adds a static function to the WP namespace which can be called like this:\n\n```php\n// rename Posts to Topics: (capitalization is internally normalized)\nWP\\Rename::post('Topic', 'topics');\n\n// Rename Categories to Colors with i18n translation:\nWP\\Rename::category(__('color', 'text_domain'), __('colors', , 'text_domain'));\n\n// Rename Tags to Flavors with an override label:\nWP\\Rename::tag('flavor', 'Flavors', ['popular_items' =\u003e 'Most delicious flavors']);\n```\n\nDataModel will normalize capitalization to match WordPress best practices. For non-standard labels, apply overrides.\n\n`tag` is an alias for `post_tag`, both can be used.\n\nRenaming only affects the labels of built in Types and Taxonomies, internal query_vars are unchanged.\n\n#### Why a static call?\n\nRenaming via a static call was a deliberate choice. Unlike creating a new CPT or Taxonomy, renaming does not create anything new, so a `new` invocation wouldn't make sense, even though it would parallel existing syntax.\n\nCalling `new CPT` or `new Taxonomy` makes sense because those commands _create_ something new. For renaming, the command acts on something which already exists, so the invocation syntax would be inconsistent with the performed action.\n\nThe syntax for renaming can often be achieved in a single line whereas creating new CPTs or Taxonomies requires defining additional actions and filters.\n\n### Nav-Menu Visibility\n\nThe plugin will automatically set all custom Taxonomies and CPTs to be visible by default in the WordPress Nav-Menu Admin for new user accounts. Previously, users had to remember to open Screen Options and enable each component of the data model.\n\nIf you'd like to reset nav-menu visibility for all existing user accounts, run this to clear previous entries from the user_meta table.\n\n```sql\nDELETE FROM `wp_usermeta` WHERE `meta_key` = \"metaboxhidden_nav-menus\";\n```\n\n## Automatic plugin updates and AWS\n\nWhenever a new version is pushed, a GitHub Action runs which compiles and packages the project, then pushes the versioned asset to one of our AWS S3 buckets. A lambda microservice handles queries from the plugin and enables native WordPress plugin updates for new versions.\n\n### AWS Lambda\n\nThe **aws** directory contains the lambda function which handles update requests from the WordPress Admin Plugins page for each installed plugin.\n\nLambda function updates must be manually triggered by calling `npm run lambda:deploy`.\n\n### WordPress Compatibility\n\nWordPress reports a `tested` value describing the latest version the plugin was developed and tested on. This value is auto-generated before the package.json **version** script runs. Tested values are collected from the [WordPress Stable-Check API](http://api.wordpress.org/core/stable-check/1.0) and stored in **assets/tested.json**.\n\n### Changelog, Description and Banners\n\nAssets should be stored in the project and be uploaded to a directory matching the plugin basename\n\nThe **README.md** and **CHANGELOG.md** files will be used to populate details in the WordPress plugin admin interface. The Changelog is auto-generated.\n\n#### Notes\n\n- The **wp-update-handler** lambda function lives in AWS region **`us-east-2`**.\n- AWS lambda functions run on node v14, run `nvm use 14` before `npm install` to ensure package compatibility.\n- AWS Credentials should be created from **IAM \u003e Users \u003e Security Credentials** for user **iop-cams**. Duplicate **.env.sample** and update the values in that file\n- AWS API-Gateway now references the lambda using `$LATEST` instead of a published version to make deploying updates simpler. The **Lambda Function** setting is found in the API Gateway Resource's POST - Integration Request options\n- Use this link to switch to the correct organization account role: [AWS Login](https://signin.aws.amazon.com/switchrole?roleName=OrganizationAccountAccessRole\u0026account=iop003\u0026displayName=IOP\u0026color=B7CA9D)\n\n### Composer Updates\n\nThe Docker Compose Composer service will mount and use local auth credentials if they exist in **~/.composer/auth.json**. If those credentials don't exist and Composer hits an API rate limit, pasting a token will create a new auth.json file in the mount which with persist on the host system.\n\n### GitHub Actions\n\nThe **publish-to-aws** GitHub Action (part of the example plugin) will build the plugin and push the artifact to our S3 updates bucket. Each project will need to define two GitHub Secrets for accessing AWS, these names are found in the `env` section at the top of the [example/.github/workflows/publish-to-aws.yml](https://github.com/ideasonpurpose/wp-data-model/blob/master/example/.github/workflows/publish-to-aws.yml#L9-L10) file:\n\n```yaml\nenv:\n  S3_URI: s3://ideasonpurpose-wp-updates\n  AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}\n  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}\n```\n\n### Testing plugin updates\n\nOur [WordPress Updates AWS endpoint][wp-update] can be tested by sending a POST request with a raw JSON body which looks something like this:\n\n```json\n{\n  \"version\": \"0.0.1\",\n  \"slug\": \"example-data-model\",\n  \"plugin\": \"example-data-model/main.php\"\n}\n```\n\n### PHPUnit Tests\n\nThe [PHPUnit](https://phpunit.de/) test suite can be run from Docker or Herd. Docker uses our [PHPUnit Watch Docker Image](https://hub.docker.com/r/ideasonpurpose/phpunit-watch). Herd users will need to enable xDebug. Here are [Herd's instructions for enabling XDebug](https://herd.laravel.com/docs/macos/debugging/xdebug).\n\n## Default Post_Type and Taxonomy Labels\n\nEvery available default label can be found in [`WP_Post_Type::get_default_labels`](https://github.com/WordPress/wordpress-develop/blob/b5b4e3ada690e86ada210760f0300471d8d48a4e/src/wp-includes/class-wp-post-type.php#L977-L1032) or [`WP_Taxonomy::get_default_labels`](https://github.com/WordPress/wordpress-develop/blob/b5b4e3ada690e86ada210760f0300471d8d48a4e/src/wp-includes/class-wp-taxonomy.php#L595-L651), where each value is an array with the hierarchical option first (Pages \u0026 Categories). Default labels can also be found by dumping the `$wp_post_types[$type]-\u003elabels` and `$wp_taxonomies[$taxonomy]-\u003elabels` objects.\n\nWordPress defines labels as an Array, then sometimes stores them as an Object, but always [casts back to an Array](https://github.com/WordPress/wordpress-develop/blob/b5b4e3ada690e86ada210760f0300471d8d48a4e/src/wp-includes/taxonomy.php#L708) before applying them. Posts and Pages overlap cleanly, Tags and Categories include special-cases for hierarchical display.\n\n```php\n// Default Page labels\n[\n  \"name\" =\u003e \"Pages\",\n  \"singular_name\" =\u003e \"Page\",\n  \"add_new\" =\u003e \"Add New\",\n  \"add_new_item\" =\u003e \"Add New Page\",\n  \"edit_item\" =\u003e \"Edit Page\",\n  \"new_item\" =\u003e \"New Page\",\n  \"view_item\" =\u003e \"View Page\",\n  \"view_items\" =\u003e \"View Pages\",\n  \"search_items\" =\u003e \"Search Pages\",\n  \"not_found\" =\u003e \"No pages found.\",\n  \"not_found_in_trash\" =\u003e \"No pages found in Trash.\",\n  \"parent_item_colon\" =\u003e \"Parent Page:\",\n  \"all_items\" =\u003e \"All Pages\",\n  \"archives\" =\u003e \"Page Archives\",\n  \"attributes\" =\u003e \"Page Attributes\",\n  \"insert_into_item\" =\u003e \"Insert into page\",\n  \"uploaded_to_this_item\" =\u003e \"Uploaded to this page\",\n  \"featured_image\" =\u003e \"Featured image\",\n  \"set_featured_image\" =\u003e \"Set featured image\",\n  \"remove_featured_image\" =\u003e \"Remove featured image\",\n  \"use_featured_image\" =\u003e \"Use as featured image\",\n  \"filter_items_list\" =\u003e \"Filter pages list\",\n  \"filter_by_date\" =\u003e \"Filter by date\",\n  \"items_list_navigation\" =\u003e \"Pages list navigation\",\n  \"items_list\" =\u003e \"Pages list\",\n  \"item_published\" =\u003e \"Page published.\",\n  \"item_published_privately\" =\u003e \"Page published privately.\",\n  \"item_reverted_to_draft\" =\u003e \"Page reverted to draft.\",\n  \"item_scheduled\" =\u003e \"Page scheduled.\",\n  \"item_updated\" =\u003e \"Page updated.\",\n  \"menu_name\" =\u003e \"Pages\",\n  \"name_admin_bar\" =\u003e \"Page\",\n];\n```\n\n```php\n// Default Category labels\n[\n  \"name\" =\u003e \"Categories\",\n  \"singular_name\" =\u003e \"Category\",\n  \"search_items\" =\u003e \"Search Categories\",\n  \"popular_items\" =\u003e null,\n  \"all_items\" =\u003e \"All Categories\",\n  \"parent_item\" =\u003e \"Parent Category\",\n  \"parent_item_colon\" =\u003e \"Parent Category:\",\n  \"edit_item\" =\u003e \"Edit Category\",\n  \"view_item\" =\u003e \"View Category\",\n  \"update_item\" =\u003e \"Update Category\",\n  \"add_new_item\" =\u003e \"Add New Category\",\n  \"new_item_name\" =\u003e \"New Category Name\",\n  \"separate_items_with_commas\" =\u003e null,\n  \"add_or_remove_items\" =\u003e null,\n  \"choose_from_most_used\" =\u003e null,\n  \"not_found\" =\u003e \"No categories found.\",\n  \"no_terms\" =\u003e \"No categories\",\n  \"filter_by_item\" =\u003e \"Filter by category\",\n  \"items_list_navigation\" =\u003e \"Categories list navigation\",\n  \"items_list\" =\u003e \"Categories list\",\n  \"most_used\" =\u003e \"Most Used\",\n  \"back_to_items\" =\u003e \"\u0026larr; Go to Categories\",\n  \"menu_name\" =\u003e \"Categories\",\n  \"name_admin_bar\" =\u003e \"category\",\n];\n```\n\n```php\n// Default Tag (post_tag) labels\n[\n  \"name\" =\u003e \"Tags\",\n  \"singular_name\" =\u003e \"Tag\",\n  \"search_items\" =\u003e \"Search Tags\",\n  \"popular_items\" =\u003e \"Popular Tags\",\n  \"all_items\" =\u003e \"All Tags\",\n  \"parent_item\" =\u003e null,\n  \"parent_item_colon\" =\u003e null,\n  \"edit_item\" =\u003e \"Edit Tag\",\n  \"view_item\" =\u003e \"View Tag\",\n  \"update_item\" =\u003e \"Update Tag\",\n  \"add_new_item\" =\u003e \"Add New Tag\",\n  \"new_item_name\" =\u003e \"New Tag Name\",\n  \"separate_items_with_commas\" =\u003e \"Separate tags with commas\",\n  \"add_or_remove_items\" =\u003e \"Add or remove tags\",\n  \"choose_from_most_used\" =\u003e \"Choose from the most used tags\",\n  \"not_found\" =\u003e \"No tags found.\",\n  \"no_terms\" =\u003e \"No tags\",\n  \"filter_by_item\" =\u003e null,\n  \"items_list_navigation\" =\u003e \"Tags list navigation\",\n  \"items_list\" =\u003e \"Tags list\",\n  \"most_used\" =\u003e \"Most Used\",\n  \"back_to_items\" =\u003e \"\u0026larr; Go to Tags\",\n  \"menu_name\" =\u003e \"Tags\",\n  \"name_admin_bar\" =\u003e \"post_tag\",\n];\n```\n\n\u003c!-- START IOP CREDIT BLURB --\u003e\n\n## \u0026nbsp;\n\n#### Brought to you by IOP\n\n\u003ca href=\"https://www.ideasonpurpose.com\"\u003e\u003cimg src=\"https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/iop-logo-white-on-black-88px.png\" height=\"44\" align=\"top\" alt=\"IOP Logo\"\u003e\u003c/a\u003e\u003cimg src=\"https://raw.githubusercontent.com/ideasonpurpose/ideasonpurpose/master/spacer.png\" align=\"middle\" width=\"4\" height=\"54\"\u003e This project is actively developed and used in production at \u003ca href=\"https://www.ideasonpurpose.com\"\u003eIdeas On Purpose\u003c/a\u003e.\n\n\u003c!-- END IOP CREDIT BLURB --\u003e\n\n[iop]: https://www.ideasonpurpose.com\n[wp-update]: https://1q32dgotuh.execute-api.us-east-2.amazonaws.com/production\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fideasonpurpose%2Fwp-data-model","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fideasonpurpose%2Fwp-data-model","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fideasonpurpose%2Fwp-data-model/lists"}