{"id":19988792,"url":"https://github.com/cps-it/cache-bags","last_synced_at":"2025-05-04T08:32:08.099Z","repository":{"id":246163377,"uuid":"819359027","full_name":"CPS-IT/cache-bags","owner":"CPS-IT","description":"TYPO3 CMS extension to build and register cache bags for enhanced cache control","archived":false,"fork":false,"pushed_at":"2024-11-07T07:31:03.000Z","size":114,"stargazers_count":2,"open_issues_count":5,"forks_count":0,"subscribers_count":6,"default_branch":"main","last_synced_at":"2024-11-07T07:35:15.819Z","etag":null,"topics":["cache","cms","expiration-date","extension","typo3"],"latest_commit_sha":null,"homepage":"https://extensions.typo3.org/extension/cache_bags","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CPS-IT.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","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":"2024-06-24T10:51:44.000Z","updated_at":"2024-11-07T06:56:20.000Z","dependencies_parsed_at":"2024-06-26T08:34:33.049Z","dependency_job_id":"cd72eb23-2681-4a30-be3e-3df6213f4ba0","html_url":"https://github.com/CPS-IT/cache-bags","commit_stats":null,"previous_names":["cps-it/cache-bags"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CPS-IT%2Fcache-bags","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CPS-IT%2Fcache-bags/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CPS-IT%2Fcache-bags/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CPS-IT%2Fcache-bags/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CPS-IT","download_url":"https://codeload.github.com/CPS-IT/cache-bags/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224391420,"owners_count":17303609,"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":["cache","cms","expiration-date","extension","typo3"],"created_at":"2024-11-13T04:44:11.118Z","updated_at":"2025-05-04T08:32:08.092Z","avatar_url":"https://github.com/CPS-IT.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n![Extension icon](Resources/Public/Icons/Extension.svg)\n\n# TYPO3 extension `cache_bags`\n\n[![Coverage](https://img.shields.io/coverallsCoverage/github/CPS-IT/cache-bags?logo=coveralls)](https://coveralls.io/github/CPS-IT/cache-bags)\n[![Maintainability](https://img.shields.io/codeclimate/maintainability/CPS-IT/cache-bags?logo=codeclimate)](https://codeclimate.com/github/CPS-IT/cache-bags/maintainability)\n[![CGL](https://github.com/CPS-IT/cache-bags/actions/workflows/cgl.yaml/badge.svg)](https://github.com/CPS-IT/cache-bags/actions/workflows/cgl.yaml)\n[![Release](https://github.com/CPS-IT/cache-bags/actions/workflows/release.yaml/badge.svg)](https://github.com/CPS-IT/cache-bags/actions/workflows/release.yaml)\n[![License](http://poser.pugx.org/cpsit/typo3-cache-bags/license)](LICENSE.md)\\\n[![Version](https://shields.io/endpoint?url=https://typo3-badges.dev/badge/cache_bags/version/shields)](https://extensions.typo3.org/extension/cache_bags)\n[![Downloads](https://shields.io/endpoint?url=https://typo3-badges.dev/badge/cache_bags/downloads/shields)](https://extensions.typo3.org/extension/cache_bags)\n[![Supported TYPO3 versions](https://shields.io/endpoint?url=https://typo3-badges.dev/badge/cache_bags/typo3/shields)](https://extensions.typo3.org/extension/cache_bags)\n[![Extension stability](https://shields.io/endpoint?url=https://typo3-badges.dev/badge/cache_bags/stability/shields)](https://extensions.typo3.org/extension/cache_bags)\n\n📦\u0026nbsp;[Packagist](https://packagist.org/packages/cpsit/typo3-cache-bags) |\n🐥\u0026nbsp;[TYPO3 extension repository](https://extensions.typo3.org/extension/cache_bags) |\n💾\u0026nbsp;[Repository](https://github.com/CPS-IT/cache-bags) |\n🐛\u0026nbsp;[Issue tracker](https://github.com/CPS-IT/cache-bags/issues)\n\n\u003c/div\u003e\n\n---\n\nAn extension for TYPO3 CMS to build and register _cache bags_ for enhanced cache\ncontrol. Cache bags are built during runtime on uncached contents and can be used\nto define cache metadata like cache tags. In addition, they are used to calculate\nexpiration dates for specific cache entries. This allows are fine-grained cache\ncontrol, depending on the contents and their explicit dependencies like specific\ndatabase contents or short-living API requests.\n\n## 🚀 Features\n\n* Interface for cache bags with different cache scopes (e.g. pages)\n* Cache bag registry to handle generated cache bags\n* Cache expiration calculator for various use cases (query builder, query result etc.)\n* Event listener to override page cache expiration, based on registered page cache bags\n* Compatible with TYPO3 11.5 LTS, 12.4 LTS and 13.4 LTS\n\n## 🔥 Installation\n\n### Composer\n\n```bash\ncomposer require cpsit/typo3-cache-bags\n```\n\n### TER\n\nAlternatively, you can download the extension via the\n[TYPO3 extension repository (TER)][1].\n\n## ⚡ Usage\n\n### Cache bags\n\nA _cache bag_ can be seen as some type of metadata collection for a specific\ncache scope, e.g. for the current page (Frontend-related cache). It is used\nto control the cache behavior within a given scope, for example by defining\ncustom cache tags or an explicit expiration date.\n\nAt the moment the following cache bags are supported:\n\n| Cache bag                                                      | Scope                                                   |\n|----------------------------------------------------------------|---------------------------------------------------------|\n| [`Cache\\Bag\\PageCacheBag`](Classes/Cache/Bag/PageCacheBag.php) | [`Enum\\CacheScope::Pages`](Classes/Enum/CacheScope.php) |\n\n\u003e [!TIP]\n\u003e You can also add your own cache bags by implementing [`Cache\\Bag\\CacheBag`](Classes/Cache/Bag/CacheBag.php).\n\nHere is an example about how to generate a new `PageCacheBag` for a given page:\n\n```php\nuse CPSIT\\Typo3CacheBags;\n\n$pageId = 72;\n$expirationDate = new DateTimeImmutable('tomorrow midnight');\n\n$cacheBag = Typo3CacheBags\\Cache\\Bag\\PageCacheBag::forPage($pageId, $expirationDate);\n```\n\n### Cache bag registry\n\nIn order to actually use a generated cache bag, each bag must be registered in\nthe global [`Cache\\Bag\\CacheBagRegistry`](Classes/Cache/Bag/CacheBagRegistry.php).\nThis registry is defined as singleton instance and stores all registered cache\nbags during runtime.\n\nThe `CacheBagRegistry` should be injected using dependency injection or, if\nDI is not possible, by using `GeneralUtility::makeInstance()`:\n\n```php\nuse CPSIT\\Typo3CacheBags;\nuse TYPO3\\CMS\\Core;\n\n$cacheBag = Typo3CacheBags\\Cache\\Bag\\PageCacheBag::forPage(72);\n$cacheBagRegistry = Core\\Utility\\GeneralUtility::makeInstance(Typo3CacheBags\\Cache\\Bag\\CacheBagRegistry::class);\n$cacheBagRegistry-\u003eadd($cacheBag);\n```\n\n#### Dispatched events\n\nWhen adding a new `CacheBag` to the registry, a\n[`CacheBagRegisteredEvent`](Classes/Event/CacheBagRegisteredEvent.php) is\ndispatched. It is used, for example, to apply cache tags to the current\nFrontend request (using the shipped\n[`PageCacheBagRegisteredEventListener`](Classes/EventListener/PageCacheBagRegisteredEventListener.php)).\n\n#### Get closest expiration date\n\nBased on all registered cache bags, the registry is able to calculate the\nclosest expiration date (if any cache bag provides an expiration date) for a\ngiven cache scope:\n\n```php\nuse CPSIT\\Typo3CacheBags;\nuse TYPO3\\CMS\\Core;\n\n$cacheBagRegistry = Core\\Utility\\GeneralUtility::makeInstance(Typo3CacheBags\\Cache\\Bag\\CacheBagRegistry::class);\n$expirationDate = $cacheBagRegistry-\u003egetExpirationDate(Typo3CacheBags\\Enum\\CacheScope::Pages);\n```\n\nThis is used, for example, to apply the expiration date to the current Frontend\npage cache (using the shipped [`PageCacheLifetimeEventListener`](Classes/EventListener/PageCacheLifetimeEventListener.php)\nfor TYPO3 ≥ v12 and [`PageCacheTimeoutHook`](Classes/Hooks/PageCacheTimeoutHook.php)\nfor TYPO3 v11).\n\n### Cache expiration calculator\n\nAs already mentioned, cache bags may also store the expiration date of a\ntargeted cache entry. The extension ships with a\n[`Cache\\CacheExpirationCalculator`](Classes/Cache/Expiration/CacheExpirationCalculator.php)\nthat can be used to calculate an expiration date. The calculation is based\non various input methods. At the moment, the following methods are available:\n\n* Calculation based on an **Extbase query or query result**\n* Calculation based on a **Query Builder** instance\n* Calculation based on an initialized **Relation Handler**\n\n```php\nuse CPSIT\\Typo3CacheBags;\nuse TYPO3\\CMS\\Core;\n\n// Use DI instead, calculator is *NOT* publicly available in the service container!\n$cacheExpirationCalculator = Core\\Utility\\GeneralUtility::makeInstance(Typo3CacheBags\\Cache\\Expiration\\CacheExpirationCalculator::class);\n$connectionPool = Core\\Utility\\GeneralUtility::makeInstance(Core\\Database\\ConnectionPool::class);\n\n$queryBuilder = $connectionPool-\u003egetQueryBuilderForTable('pages');\n$queryBuilder-\u003eselect('*')\n    -\u003efrom('pages')\n    -\u003ewhere(\n        $queryBuilder-\u003eexpr()-\u003eor(\n            $queryBuilder-\u003eexpr()-\u003eeq('uid', $queryBuilder-\u003ecreateNamedParameter(72, Core\\Database\\Connection::PARAM_INT)),\n            $queryBuilder-\u003eexpr()-\u003eeq('pid', $queryBuilder-\u003ecreateNamedParameter(72, Core\\Database\\Connection::PARAM_INT)),\n        ),\n    )\n;\n\n$expirationDate = $cacheExpirationCalculator-\u003eforQueryBuilder('pages', $queryBuilder);\n$cacheBag = Typo3CacheBags\\Cache\\Bag\\PageCacheBag::forPage(72, $expirationDate);\n```\n\n### Full example\n\nTypical use cases of cache bags are list and detail views of custom records\nin Frontend scope. Here is a full example about how to use the `PageCacheBag`\nin list and detail views of a custom table:\n\n```php\nuse CPSIT\\Typo3CacheBags;\nuse Psr\\Http\\Message;\nuse TYPO3\\CMS\\Core;\nuse TYPO3\\CMS\\Extbase;\n\nfinal class BlogController extends Extbase\\Mvc\\Controller\\ActionController\n{\n    public function __construct(\n        private readonly BlogRepository $blogRepository,\n        private readonly Typo3CacheBags\\Cache\\Bag\\CacheBagRegistry $cacheBagRegistry,\n        private readonly Typo3CacheBags\\Cache\\Expiration\\CacheExpirationCalculator $cacheExpirationCalculator,\n    ) {}\n\n    public function listAction(): Message\\ResponseInterface\n    {\n        /** @var Extbase\\Persistence\\QueryResultInterface\u003cBlog\u003e $blogArticles */\n        $blogArticles = $this-\u003eblogRepository-\u003efindAll();\n\n        // Create cache bag with reference to the queried table\n        // and apply the calculated expiration date of all queried blog articles\n        $cacheBag = Typo3CacheBags\\Cache\\Bag\\PageCacheBag::forTable(\n            Blog::TABLE_NAME,\n            $this-\u003ecacheExpirationCalculator-\u003eforQueryResult(Blog::TABLE_NAME, $blogArticles),\n        );\n\n        // Add cache bag to registry\n        $this-\u003ecacheBagRegistry-\u003eadd($cacheBag);\n\n        $this-\u003eview-\u003eassign('articles', $blogArticles);\n\n        return $this-\u003ehtmlResponse();\n    }\n\n    public function detailAction(Blog $article): Message\\ResponseInterface\n    {\n        // Create cache bag with reference to the current article\n        // and apply the article's endtime as cache expiration date\n        $cacheBag = Typo3CacheBags\\Cache\\Bag\\PageCacheBag::forRecord(\n            Blog::TABLE_NAME,\n            $article-\u003egetUid(),\n            $article-\u003egetEndtime(),\n        );\n\n        // Add cache bag to registry\n        $this-\u003ecacheBagRegistry-\u003eadd($cacheBag);\n\n        $this-\u003eview-\u003eassign('article', $article);\n\n        return $this-\u003ehtmlResponse();\n    }\n}\n```\n\n## 🧑‍💻 Contributing\n\nPlease have a look at [`CONTRIBUTING.md`](CONTRIBUTING.md).\n\n## 💎 Credits\n\nThe extension icon (\"container\") is a modified version of the original\n[`actions-container`][2] icon from TYPO3 core which is originally licensed\nunder [MIT License][3].\n\n## ⭐ License\n\nThis project is licensed under [GNU General Public License 2.0 (or later)](LICENSE.md).\n\n[1]: https://extensions.typo3.org/extension/cache_bags\n[2]: https://typo3.github.io/TYPO3.Icons/icons/actions/actions-container.html\n[3]: https://github.com/TYPO3/TYPO3.Icons/blob/main/LICENSE\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcps-it%2Fcache-bags","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcps-it%2Fcache-bags","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcps-it%2Fcache-bags/lists"}