{"id":16347714,"url":"https://github.com/oscarotero/folk","last_synced_at":"2025-03-23T00:32:58.331Z","repository":{"id":57033319,"uuid":"48906835","full_name":"oscarotero/folk","owner":"oscarotero","description":"Universal CMS to use with any web","archived":false,"fork":false,"pushed_at":"2021-12-16T18:01:53.000Z","size":9342,"stargazers_count":10,"open_issues_count":1,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-03-18T16:04:13.318Z","etag":null,"topics":["agnostic-to-frameworks","cms","content-management"],"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/oscarotero.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-01-02T10:55:46.000Z","updated_at":"2023-08-29T22:05:35.000Z","dependencies_parsed_at":"2022-08-23T20:50:28.842Z","dependency_job_id":null,"html_url":"https://github.com/oscarotero/folk","commit_stats":null,"previous_names":[],"tags_count":71,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oscarotero%2Ffolk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oscarotero%2Ffolk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oscarotero%2Ffolk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oscarotero%2Ffolk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oscarotero","download_url":"https://codeload.github.com/oscarotero/folk/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245040235,"owners_count":20551297,"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":["agnostic-to-frameworks","cms","content-management"],"created_at":"2024-10-11T00:45:07.014Z","updated_at":"2025-03-23T00:32:54.788Z","avatar_url":"https://github.com/oscarotero.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FOLK\n\nThe universal CMS\n\n[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/oscarotero/folk/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/oscarotero/folk/?branch=master)\n\nThis is a framework-agnostic CMS that you can use to edit the content of your site. It works with any kind of websites, no matter if the content is stored in a database, yml files, json, etc.\n\nDemo: https://oscarotero.com/folk/demo/\n\n![List view](https://raw.githubusercontent.com/oscarotero/folk/master/list-screenshoot.png)\n![Edit view](https://raw.githubusercontent.com/oscarotero/folk/master/edit-screenshoot.png)\n\n## Requirements\n\n* PHP \u003e= 7\n* Composer\n\n## Installation\n\nThis package is installable and autoloadable via Composer as [oscarotero/folk](https://packagist.org/packages/oscarotero/folk).\n\n```\ncomposer require oscarotero/folk\n```\n\n## Entities\n\nThe entities are classes to manage \"things\". It can be a database table, a file, a directory with files, etc. They must implement `Folk\\Entities\\EntityInterface` (or extend `Folk\\Entities\\AbstractEntity`). Let's see an example of an entity using a database table.\n\n```php\nnamespace MyEntities;\n\nuse Folk\\SearchQuery;\nuse Folk\\Formats\\FormatFactory;\nuse Folk\\Formats\\Group;\nuse Folk\\Entities\\AbstractEntity;\n\n/**\n * Entity to manage the posts\n */\nclass Posts extends AbstractEntity\n{\n    public $title = 'Posts';\n    public $description = 'These are the posts of the blog';\n\n    /**\n     * List the posts\n     *\n     * @return array [id =\u003e data, ...]\n     */\n    public function search(SearchQuery $search): array\n    {\n        $query = 'SELECT * FROM posts';\n\n        if ($search-\u003egetPage() !== null) {\n            $limit = $search-\u003egetLimit();\n            $offset = ($search-\u003egetPage() * $limit) - $limit;\n            $query .= \" LIMIT {$offset}, {$limit}\";\n        }\n\n        $pdo = $this-\u003eadmin-\u003eget('pdo');\n        $result = $pdo-\u003equery($query);\n\n        $data = [];\n\n        while ($row = $result-\u003efetch(PDO::FETCH_ASSOC)) {\n            $data[$row['id']] = $row;\n        }\n\n        return $data;\n    }\n\n    /**\n     * Create a new post\n     *\n     * @return mixed The post id\n     */\n    public function create(array $data)\n    {\n        $pdo = $this-\u003eadmin-\u003eget('pdo');\n\n        $statement = $pdo-\u003eprepare('INSERT INTO posts (title, text) VALUES (:title, :text)');\n        $statement-\u003eexecute([\n            ':title' =\u003e $data['title'],\n            ':text' =\u003e $data['text'],\n        ]);\n\n        return $pdo-\u003elastInsertId();\n    }\n\n    /**\n     * Read a post\n     *\n     * @return array\n     */\n    public function read($id): array\n    {\n        $pdo = $this-\u003eadmin-\u003eget('pdo');\n\n        $statement = $pdo-\u003eprepare('SELECT * FROM posts WHERE id = ? LIMIT 1');\n        $statement-\u003eexecute([$id]);\n\n        return $statement-\u003efetch(PDO::FETCH_ASSOC);\n    }\n\n    /**\n     * Update a post\n     */\n    public function update($id, array $data)\n    {\n        $pdo = $this-\u003eadmin-\u003eget('pdo');\n\n        $statement = $pdo-\u003eprepare('UPDATE posts SET title = :title, text = :text WHERE id = :id LIMIT 1');\n        $statement-\u003eexecute([\n            ':title' =\u003e $data['title'],\n            ':text' =\u003e $data['text'],\n            ':id' =\u003e $data['id'],\n        ]);\n    }\n\n    /**\n     * Delete a post\n     */\n    public function delete($id)\n    {\n        $pdo = $this-\u003eadmin-\u003eget('pdo');\n\n        $statement = $pdo-\u003eprepare('DELETE FROM posts WHERE id = ? LIMIT 1');\n        $statement-\u003eexecute([$id]);\n    }\n\n    /**\n     * Returns the data scheme used by the posts.\n     */\n    public function getScheme(FormatFactory $factory): Group\n    {\n        return $factory-\u003egroup([\n            'title' =\u003e $factory-\u003etext()\n                -\u003emaxlength(200)\n                -\u003elabel('The post title'),\n\n            'text' =\u003e $factory-\u003ehtml()\n                -\u003elabel('The body'),\n        ]);\n    }\n\n    /**\n     * Returns the label of a row.\n     * (used in autocomplete searches, select, etc)\n     */\n    public function getLabel($id, array $data): string\n    {\n        return sprintf('%s (%d)', $data['title'], $id);\n    }\n}\n```\n\n## Getting started\n\nThere's some predefined entities that you can extend and configure, for example to use with [simplecrud](https://github.com/oscarotero/simple-crud), or to save the content in yaml or json files, etc.\n\nOnce your entities are created, let's make them to run:\n\n```php\nuse Folk\\Admin;\n\nuse Entities\\Posts;\n\n//Create a Admin instance passing the root path and the http uri:\n$uri = new Zend\\Diactoros\\Uri('http://my-site.com/admin');\n$admin = new Admin(__DIR__, $uri);\n\n//Set the pdo instance:\n$admin['pdo'] = new PDO('mysql:dbname=database;charset=UTF8');\n\n//Add set your entities classes\n$admin-\u003esetEntities([\n    Posts::class\n]);\n\n//Run the web (using PSR-7 request/responses)\n$request = Zend\\Diactoros\\ServerRequestFactory::fromGlobals();\n$emitter = new Zend\\Diactoros\\Response\\SapiEmitter();\n\n$response = $admin($request);\n$emitter-\u003eemit($response);\n```\n\nAs you can see, this is a simple example with a simple mysql table. But the interface is flexible enought to work with any kind of data.\n\nTo know how to work with the scheme, visit [form-manager](https://github.com/oscarotero/form-manager/) project.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foscarotero%2Ffolk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foscarotero%2Ffolk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foscarotero%2Ffolk/lists"}