{"id":23009897,"url":"https://github.com/openactive/models-php","last_synced_at":"2025-08-11T02:10:55.525Z","repository":{"id":37432197,"uuid":"210607493","full_name":"openactive/models-php","owner":"openactive","description":"PHP Models for the OpenActive Opportunity and Booking Specifications","archived":false,"fork":false,"pushed_at":"2025-05-12T09:29:23.000Z","size":2562,"stargazers_count":6,"open_issues_count":8,"forks_count":7,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-07-20T08:24:00.312Z","etag":null,"topics":["implementation-support","php","pre-release"],"latest_commit_sha":null,"homepage":null,"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/openactive.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":"2019-09-24T13:17:09.000Z","updated_at":"2025-05-12T09:29:25.000Z","dependencies_parsed_at":"2024-03-27T18:33:03.558Z","dependency_job_id":"a7e712f7-2ac2-43c2-9c9b-23c66bf13682","html_url":"https://github.com/openactive/models-php","commit_stats":{"total_commits":349,"total_committers":14,"mean_commits":"24.928571428571427","dds":"0.28939828080229224","last_synced_commit":"ce8ab4b7d2641aa60863993b265c2ee4dabb7696"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/openactive/models-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openactive%2Fmodels-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openactive%2Fmodels-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openactive%2Fmodels-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openactive%2Fmodels-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openactive","download_url":"https://codeload.github.com/openactive/models-php/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openactive%2Fmodels-php/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269819032,"owners_count":24480087,"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","status":"online","status_checked_at":"2025-08-11T02:00:10.019Z","response_time":75,"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":["implementation-support","php","pre-release"],"created_at":"2024-12-15T09:16:26.712Z","updated_at":"2025-08-11T02:10:55.457Z","avatar_url":"https://github.com/openactive.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# models-php [![Latest Stable Version](https://poser.pugx.org/openactive/models/v/stable)](https://packagist.org/packages/openactive/models) [![Total Downloads](https://poser.pugx.org/openactive/models/downloads)](https://packagist.org/packages/openactive/models) [![Tests](https://github.com/openactive/models-php/actions/workflows/tests.yaml/badge.svg?branch=master)](https://github.com/openactive/models-php/actions/workflows/tests.yaml)\nPHP Models for the OpenActive Opportunity and Booking Specifications\n\nOpenActive aims to provide model files for all classes defined in its Opportunity and Booking specifications across the PHP, Ruby, and .NET languages. This repository is intended for the PHP files; see also the [Ruby](https://github.com/openactive/models-ruby/) and [.NET](https://github.com/openactive/OpenActive.NET) implementations.\n\n## Table of Contents\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Usage](#usage)\n    - [Models](#models)\n        - [OpenActive](#openactive)\n        - [Schema.org](#schemaorg)\n        \n    - [RPDE](#rpde)\n    - [Enums](#enums)\n    - [Serialization](#serialization)\n        - [`serialize($obj, $prettyPrint = false)`](#serializeobj-prettyprint--false)\n        - [`deserialize($data)`](#deserializedata)\n        - [Modifiers](#modifiers)\n- [Contributing](#contributing)\n\n## Requirements\nThis project requires PHP \u003e=5.6.\n\n## Installation\nTo install via Composer, from terminal, run:\n```bash\ncomposer require openactive/models\n```\n\n## Usage\n\nThis package provides PHP models for the OpenActive specifications.\n\nIt also provide a set of models for the [schema.org](https://schema.org) specifications.\n\nFinally it provides a set of classes to handle OpenActive's [RPDE](https://developer.openactive.io/publishing-data/data-feeds/how-a-data-feed-works) data feeds.\n\n### Models\n\nThe models are included under the `\\OpenActive\\Models` namespace.\n\nYou can instantiate a new model, passing an associative array, where the key is the attribute name, and the value is the attribute value.\n\nFor example, from your PHP application, run:\n\n```php\n// Make sure you use the right namespace for your models\nuse OpenActive\\Models\\OA\\SessionSeries;\nuse OpenActive\\Models\\OA\\Place;\nuse OpenActive\\Models\\OA\\GeoCoordinates;\nuse OpenActive\\Models\\OA\\Concept;\nuse OpenActive\\Models\\OA\\Organization;\nuse OpenActive\\Models\\OA\\Offer;\n\n$sessionSeries = new SessionSeries([\n    \"name\" =\u003e \"Virtual BODYPUMP\",\n    \"description\" =\u003e \"This is the virtual version of the original barbell class, which will help you get lean, toned and fit - fast\",\n    \"startDate\" =\u003e \"2017-04-24T19:30:00-08:00\",\n    \"endDate\" =\u003e \"2017-04-24T23:00:00-08:00\",\n    \"location\" =\u003e new Place([\n        \"name\" =\u003e \"Raynes Park High School, 46A West Barnes Lane\",\n        \"geo\" =\u003e new GeoCoordinates([\n            \"latitude\" =\u003e 51.4034423828125,\n            \"longitude\" =\u003e -0.2369088977575302,\n        ])\n    ]),\n    \"activity\" =\u003e [new Concept([\n        \"id\" =\u003e \"https://openactive.io/activity-list#5e78bcbe-36db-425a-9064-bf96d09cc351\",\n        \"prefLabel\" =\u003e \"Bodypump™\",\n        \"inScheme\" =\u003e \"https://openactive.io/activity-list\"\n    ])],\n    \"organizer\" =\u003e new Organization([\n        \"name\" =\u003e \"Central Speedball Association\",\n        \"url\" =\u003e \"http://www.speedball-world.com\"\n    ]),\n    \"offers\" =\u003e [new Offer([\n        \"identifier\" =\u003e \"OX-AD\",\n        \"name\" =\u003e \"Adult\",\n        \"price\" =\u003e 3.3,\n        \"priceCurrency\" =\u003e \"GBP\",\n        \"url\" =\u003e \"https://profile.everyoneactive.com/booking?Site=0140\u0026Activities=1402CBP20150217\u0026Culture=en-GB\"\n    ])],\n]);\n```\n\nPlease note that type enforcement is in place whenever creating a new model.\n\nFor example, providing a `string` to the `target` attribute in the example above will result in an `\\OpenActive\\Exception\\InvalidArgumentException` being thrown.\n\nA set of getters and setters for all the attributes is provided. Type enforcement is in place for setters too.\n\n#### OpenActive\n\nThe OpenActive models are included under the `\\OpenActive\\Models\\OA` namespace.\n\nTo instantiate a new one, see the [models](#models) section, making sure you are using the right namespace from your model.\n\n#### Schema.org\n\nThe Schema.org models are included under the `\\OpenActive\\Models\\SchemaOrg` namespace.\n\nTo instantiate a new one, see the [models](#models) section, making sure you are using the right namespace from your model.\n\n### RPDE\n\nRpdeItem \u0026 RpdeBody are the main classes to use when generating a page for an RPDE feed.\n\n#### Feed items\n\nRpdeItem is used to create each individual item for a page. It includes a data attribute to which should be an instance of an OA Model along with metadata (id, modified, state and kind). It is left to each application developer to generate these models and metadata.\n\ne.g. a session series collection\n```php\n\nuse OpenActive\\Rpde\\RpdeItem;\n\n$feedItems = [\n    new RpdeItem([\n        \"id\" =\u003e \"2\",\n        \"modified\" =\u003e 4,\n        \"state\" =\u003e RpdeState::UPDATED,\n        \"kind\" =\u003e RpdeKind::SESSION_SERIES,\n        \"data\" =\u003e $sessionSeries2,\n    ]),\n    new RpdeItem([\n        \"id\" =\u003e \"1\",\n        \"modified\" =\u003e 5,\n        \"state\" =\u003e RpdeState::DELETED,\n        \"kind\" =\u003e RpdeKind::SESSION_SERIES,\n    ]),\n];\n```\n\n#### Feed page\n\nRpdeBody is then used to wrap a collection of items and provide the licence and next entries expected from an RPDE page. To help keep pages valid and create an correct next link, use `RpdeBody::createFromNextChangeNumber` or `RpdeBody::createFromModifiedId` to create an RPDE page feed out of an array of `RpdeItem`s (the constructor has been made private).\n\n`RpdeBody::createFromNextChangeNumber` will check that all feed items do indeed come after the `$changeNumber` argument provided. It will contruct the next link based on the modified value of the newest feed item and the provided `$feedBaseUrl` argument.\n\ne.g.\n```php\nuse OpenActive\\Rpde\\RpdeBody;\n\n$feedPage = RpdeBody::createFromNextChangeNumber(\n    'https://www.example.com/rpde-feeds/session-series', # $feedBaseUrl\n    0, # $changeNumber,\n    $feedItems\n);\n\n$feedPage-\u003egetNext(); # 'https://www.example.com/rpde-feeds/session-series?afterTimestamp=5\u0026afterId=2'\n```\n\n`RpdeBody::createFromModifiedId` will check that all feed items do indeed come after the `$id` and `$modified` arguments provided. It will contruct the next link based on the id and modified value of the newest feed item and the provided `$feedBaseUrl` argument.\n\ne.g.\n```php\nuse OpenActive\\Rpde\\RpdeBody;\n\n$feedPage = RpdeBody::createFromModifiedId(\n    'https://www.example.com/rpde-feeds/session-series', # $feedBaseUrl\n    0, # $modified,\n    0, # $id\n    $feedItems\n);\n\n$feedPage-\u003egetNext(); # 'https://www.example.com/rpde-feeds/session-series?afterChangeNumber=5'\n```\n\nTo override the default licence:\n```php\n$feedPage-\u003esetLicense('https://www.example.com/my-licence/v2.0');\n```\n\n#### Serializing the feed page\n\nFinally the feed page can then be serialized with `Serialize RpdeBody::serialize($feedPage)` which will also take care of serializing the each feed item's data attribute as JSON-LD.\n\n```php\n$jsonFeedPage = RpdeBody::serialize($feedPage);\n```\n\nSee the [OpenActive's guide to publishing data](https://developer.openactive.io/publishing-data/data-feeds) and [the RPDE specification](https://www.openactive.io/realtime-paged-data-exchange/) for more details about RPDE feeds.\n\n### Enums\n\nEach enum is represented by a class which contains a constant for each available value.\n\ne.g. Adding days of the week to a Schedule:\n\n```php\nuse OpenActive\\Models\\OA\\Schedule;\nuse OpenActive\\Enums\\SchemaOrg\\DayOfWeek;\n\nnew Schedule([\n    \"scheduledEventType\" =\u003e \"Event\",\n    \"startTime\" =\u003e \"12:00:00\",\n    \"endTime\" =\u003e \"14:00:00\",\n    \"byDay\" =\u003e [\n        new DayOfWeek\\Monday,\n        new DayOfWeek\\Wednesday,\n        new DayOfWeek\\Friday\n    ],\n    ...\n]);\n```\n\n### Serialization\n\nThis package provides support for JSON-LD serialization/deserialization of [models](#models) and and for the `\\OpenActive\\Rpde\\RpdeBody` object.\n\n#### `serialize($obj, $prettyPrint = false, $schema = false, $modifiers = [])`\n\nReturns the JSON-LD string representation of the given object `$obj`.\n\nAn additional parameter `$prettyPrint` is available to return a JSON-LD string in a human-readable format.\n\nAn example, using the `\\OpenActive\\Models\\OA\\SessionSeries` defined above:\n```php\nuse OpenActive\\Models\\OA\\SessionSeries;\n\necho SessionSeries::serialize($sessionSeries, true);\n```\n\nWill output:\n```json\n{\n    \"@context\": [\n        \"https:\\/\\/openactive.io\\/\",\n        \"https:\\/\\/openactive.io\\/ns-beta\"\n    ],\n    \"type\": \"SessionSeries\",\n    \"name\": \"Virtual BODYPUMP\",\n    \"description\": \"This is the virtual version of the original barbell class, which will help you get lean, toned and fit - fast.\",\n    \"startDate\": \"2017-04-24T19:30:00-08:00\",\n    \"endDate\": \"2017-04-24T23:00:00-08:00\",\n    \"location\": {...},\n    \"activity\": {...},\n    \"organizer\": {...},\n    \"offers\": [...]\n}\n```\n\n**Please note:** at the moment by default, only the OpenActive `@context` is rendered in the serialization output. You may supply the `$schema` flag to add the Schema.org `@context`.\n\n#### `deserialize($data)`\n\nReturns an object from a given JSON-LD representation.\n\nThe `$data` argument can be a JSON-LD string, or an associative array, for example as a result of `json_encode($string, true)`.\n\nFor example:\n\n```php\nuse OpenActive\\Models\\OA\\Action;\n\n$jsonLd = '{\"@context\": [\"https:\\/\\/openactive.io\\/\",\"https:\\/\\/openactive.io\\/ns-beta\"],\"type\": \"Action\",\"name\": \"Book\",\"target\": {\"type\": \"EntryPoint\",\"encodingType\": \"application\\/vnd.openactive.v1.0+json\",\"httpMethod\": \"POST\",\"type\": \"EntryPoint\",\"url\": \"https:\\/\\/example.com\\/orders\"}}';\n\n$action = Action::deserialize($jsonLd);\n\nvar_dump($action);\n```\n\nWill result in:\n```\nobject(OpenActive\\Models\\OA\\Action)#3 (24) {\n  [\"name\":protected]=\u003e\n  string(4) \"Book\"\n  [\"target\":protected]=\u003e\n  object(OpenActive\\Models\\OA\\EntryPoint)#2 (20) {\n    [\"encodingType\":protected]=\u003e\n    string(36) \"application/vnd.openactive.v1.0+json\"\n    [\"httpMethod\":protected]=\u003e\n    string(4) \"POST\"\n    [\"urlTemplate\":protected]=\u003e\n    NULL\n    [\"actionApplication\":protected]=\u003e\n    NULL\n    [\"application\":protected]=\u003e\n    NULL\n    [\"actionPlatform\":protected]=\u003e\n    NULL\n    [\"contentType\":protected]=\u003e\n    NULL\n    [\"identifier\":protected]=\u003e\n    NULL\n    [\"name\":protected]=\u003e\n    NULL\n    [\"description\":protected]=\u003e\n    NULL\n    [\"sameAs\":protected]=\u003e\n    NULL\n    [\"url\":protected]=\u003e\n    string(26) \"https://example.com/orders\"\n    [\"image\":protected]=\u003e\n    NULL\n    [\"additionalType\":protected]=\u003e\n    NULL\n    [\"subjectOf\":protected]=\u003e\n    NULL\n    [\"mainEntityOfPage\":protected]=\u003e\n    NULL\n    [\"potentialAction\":protected]=\u003e\n    NULL\n    [\"disambiguatingDescription\":protected]=\u003e\n    NULL\n    [\"alternateName\":protected]=\u003e\n    NULL\n    [\"id\":protected]=\u003e\n    NULL\n  }\n  [\"result\":protected]=\u003e\n  NULL\n  [\"startTime\":protected]=\u003e\n  NULL\n  [\"actionStatus\":protected]=\u003e\n  NULL\n  [\"agent\":protected]=\u003e\n  NULL\n  [\"endTime\":protected]=\u003e\n  NULL\n  [\"instrument\":protected]=\u003e\n  NULL\n  [\"participant\":protected]=\u003e\n  NULL\n  [\"object\":protected]=\u003e\n  NULL\n  [\"error\":protected]=\u003e\n  NULL\n  [\"location\":protected]=\u003e\n  NULL\n  [\"identifier\":protected]=\u003e\n  NULL\n  [\"description\":protected]=\u003e\n  NULL\n  [\"sameAs\":protected]=\u003e\n  NULL\n  [\"url\":protected]=\u003e\n  NULL\n  [\"image\":protected]=\u003e\n  NULL\n  [\"additionalType\":protected]=\u003e\n  NULL\n  [\"subjectOf\":protected]=\u003e\n  NULL\n  [\"mainEntityOfPage\":protected]=\u003e\n  NULL\n  [\"potentialAction\":protected]=\u003e\n  NULL\n  [\"disambiguatingDescription\":protected]=\u003e\n  NULL\n  [\"alternateName\":protected]=\u003e\n  NULL\n  [\"id\":protected]=\u003e\n  NULL\n}\n```\n\n#### Modifiers\n\nModifiers allow you to change the serialized context *after* it has been normalized by the library. An example use-case\nfor this is if you are working with a third party who does not completely adhere to the specification, or has other\nimplementation details which require non-standard serialization. For example, if the third party requires a different\ndate format you could do this:\n\n```\nuse OpenActive\\Models\\OA\\SessionSeries;\n\necho SessionSeries::serialize($sessionSeries, true, false, [\n    function ($class, $key, $value, $object) {\n      if (!in_array($key, ['startDate', 'endDate'])) {\n        return $value;\n      }\n      return (new \\DateTime($value))-\u003eformat('Y.m.d.H:i:s');\n    }\n]);\n```\n\nThe given modifiers **MUST** adhere to this method signature:\n\n```\nfunction (string $class, string $key, mixed $value, mixed $object): mixed\n```\n\nEach modifier **MUST** always respond with a value, as the modifiers are always applied. If it does not return a value\nall the data in your object will be wiped during serialization. You **SHOULD** use the `$class` and `$key` parameters to\ndetermine if the modifier should run for the parameter, and simply return the `$value` if it is not necessary. The\n`$object` parameter is also available for modifiers which require further information from the \nparent object.\n\nModifiers can also be added to the Deserialization process, and are applied *before* the property is set. This can be\nuseful when having to handle migration paths as changes happen in the library, or to fix data coming from an\nincompatible seller. Building on the previous example, if the third party in this case is sending dates in an odd format\nyou could do this:\n\n```\nuse OpenActive\\Models\\OA\\SessionSeries;\n\n$session = SessionSeries::deserialize('{...}', [\n    function ($class, $key, $value, $object) {\n      if (!in_array($key, ['startDate', 'endDate'])) {\n        return $value;\n      }\n      return \\DateTime::createFromFormat('Y.m.d.H:i:s', $value, new \\DateTimeZone('Europe/London'));\n    }\n]);\n```\n\nExample \u0026 helper modifiers can be found in the `src/Modifiers` directory.\n\n## Contributing\n\n### Installation\n**Please note:** [Composer](https://getcomposer.org/doc/00-intro.md#installation-linux-unix-macos) is required for dependency management.\n\n```bash\ngit clone git@github.com:openactive/models-php.git\ncd models-php\ncomposer install\n```\n\n### Running Tests\nPHPUnit is used to run tests.\n\nTo run the whole suite:\n```bash\n./vendor/bin/phpunit\n```\n\nIf you want to run the whole suite in verbose mode:\n```bash\n./vendor/bin/phpunit --verbose\n```\n\nYou can also run a section of the suite by specifying the class's relative path on which you want to perform tests:\n```bash\n./vendor/bin/phpunit --verbose tests/Unit/RpdeTest.php\n```\n\nFor additional information on the commands available for PHPUnit,\nconsult [their documentation](https://phpunit.readthedocs.io/en/8.5/textui.html#command-line-options)\n\n\n### Updating models\nA guide is provided in [UPDATING.md](UPDATING.md)\n\n### Implementing APIs\nThese models can be used in the context of implementing an data API. A guide is provided in [IMPLEMENTING_APIS.md](IMPLEMENTING_APIS.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenactive%2Fmodels-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenactive%2Fmodels-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenactive%2Fmodels-php/lists"}