{"id":13594000,"url":"https://github.com/moussaclarke/planar","last_synced_at":"2025-04-09T05:32:43.869Z","repository":{"id":57020278,"uuid":"67816540","full_name":"moussaclarke/planar","owner":"moussaclarke","description":"Super simple flat file json database / model in PHP","archived":true,"fork":false,"pushed_at":"2024-12-06T08:59:56.000Z","size":16,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-12-06T09:33:00.920Z","etag":null,"topics":["flat-file","json-database"],"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/moussaclarke.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","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":"2016-09-09T16:56:32.000Z","updated_at":"2024-12-06T09:00:36.000Z","dependencies_parsed_at":"2024-11-06T15:40:35.776Z","dependency_job_id":"c190ae55-49a6-4323-83fe-8609f5b19832","html_url":"https://github.com/moussaclarke/planar","commit_stats":{"total_commits":22,"total_committers":2,"mean_commits":11.0,"dds":"0.13636363636363635","last_synced_commit":"5852933eebcbe21abafc02ac14b6ff4a9c312043"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moussaclarke%2Fplanar","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moussaclarke%2Fplanar/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moussaclarke%2Fplanar/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moussaclarke%2Fplanar/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moussaclarke","download_url":"https://codeload.github.com/moussaclarke/planar/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247987056,"owners_count":21028891,"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":["flat-file","json-database"],"created_at":"2024-08-01T16:01:27.436Z","updated_at":"2025-04-09T05:32:43.859Z","avatar_url":"https://github.com/moussaclarke.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"# Planar\n\n## ABANDONED - DO NOT USE\nThis was a useful project for me a while ago and taught me a lot about certain data concepts. I used it in production for a couple of internal projects at the time, but I don't anymore and no longer have the bandwidth to maintain it.\n\nIf you need a similar nosql file based php db, you could take a look at [SleekDB](https://github.com/SleekDB/SleekDB) which seems to be actively maintained at the time of writing this, or just stick with the relational paradigm and use [Eloquent](https://github.com/illuminate/database) with [SQLite](https://github.com/sqlite/sqlite).\n\n## A super simple flat file json database / model\n\nThroughout this readme, 'document' and 'collection' are used in the MongoDb sense.\n\nPlanar is a very basic flat file/nosql json database solution.\n\nPlanar is simple, fast, super-flexible, and probably very brittle. It's useful for small projects, where you have a relatively simple data structure, you don't need 1000s of documents and you only have a small amount of users with edit permissions.\n\nIt probably won't scale well, there's no collision detection or record locking, and will most likely slow to a crawl once your collections get really large, but I've used it in production apps with 100s of documents and had zero issues so far.\n\nPlanar creates json collections on the fly as needed, everything gets json encoded and stored in flat files. It is ORM-like, even though that's pretty much an irrelevant term since this is all json anyway, but you can do CRUD and simple queries on a 'model'-like object.\n\nIt backs up every change using diffs to make undos possible.\n\n## Disclaimer\nIt's still pretty alpha, could break at any time, and I might make backwards incompatible changes without any warning. Don't use it for anything too business critical. You have been warned.\n\n## Install\n\n`composer require moussaclarke\\planar`\n\n## Usage\n\n### Instantiation\nAt its simplest, you just extend the class to create your model/collection. It will use the class name (plural makes sense here) as the json collection name.\n\n```\nuse MoussaClarke\\Planar;\n\nclass Widgets extends Planar\n{\n    \n}\n```\n\nYou can then instantiate passing in a data folder location. The json and backup diff files will be stored in the folder you specify. If the json file doesn't exist yet, it will be created.\n\n```\n$widgets  = new Widgets('path/to/data/folder');\n```\n\nYou can also set a data folder location by over-riding the datafolder property on the class.\n\n```\nprotected $datafolder='path/to/data/folder';\n```\n\nYou can then just do the following to instantiate:\n\n```\n$widgets = new Widgets;\n```\n\nYou could set the data folder on a base model class, for example, which the concrete models extend.\n\nAlternatively, if you need things to be slightly less tightly coupled, you can also inject both data folder and collection name without extending the Planar class.\n\n```\n$widgets = new Planar('path/to/data/folder', 'Widgets');\n```\n\n### Schema\n\nYou can add a schema for the document if you like - Planar won't do anything to enforce it, and each document could in fact have completely different elements - but it might be useful elsewhere in your app to get a default instance of the data if you're trying to maintain a more rigid model structure, i.e. as a kind of configuration info. To do this, just over-ride the `$schema` property at the top of your model class.\n\n```\nprotected $schema   = [\n    'name' =\u003e ''\n    'price' =\u003e '',\n    'weight' =\u003e '',\n    'subwidgets' =\u003e [],\n];\n```\n\nYour initial property defaults would usually be an empty string or array, but you could also specify defaults. You can then grab your schema like this:\n\n```\n$schema = $widgets-\u003egetSchema();\n```\n\nIt's up to you to then `add` or `set` it back to the collection once you've loaded the array with data.\n\nIf you need to use any runtime values to set defaults then over-ride the `getSchema` method instead.\n\n```\n\npublic function getSchema()\n{\n    return [\n        'name' =\u003e ''\n        'price' =\u003e '',\n        'weight' =\u003e '',\n        'subwidgets' =\u003e [],\n        'invoicedate' =\u003e date ('Y-m-d')\n    ];\n}\n```\n\n### Creating \u0026 updating\n\nYou can create a document with `add`. Just pass in an array of data (which simply gets json encoded), it will return the unique id of the new document.\n\n```\n$data = [\n    'name' =\u003e 'foobar',\n    'price' =\u003e 5,\n    'weight' =\u003e 10,\n    'subwidget' =\u003e ['foo' =\u003e 'bar', 'bar' =\u003e 'foo']\n];\n$id = $widgets-\u003eadd($data);\n```\n\nYou don't need to worry about adding unique id or timestamp fields, those will be created and updated automatically. `_id` is simply a `uniqid()`, and `_created` and `_modified` are unix timestamps. Those three property names, all prefixed with `_`, are therefore reserved, so try not to have those in your data/schema.\n\nIf you know the id, you can replace a whole document with `set`. You can also use this to create a document with a specific id, although Planar won't warn you if it's over-writing anything.\n\n```\n$widgets-\u003eset('57d1d2fc97aee', $data);\n```\n\n### Finding \u0026 Searching\n\nPlanar has various ways of finding and searching records, although doesn't really support any particularly sophisticated queries. `find` returns an array of documents where the named property has a particular value.\n\n```\n$result = $widgets-\u003efind('price', 5);\n```\n\n`first` returns the first document it finds where the named property has a particular value.\n\n```\n$result = $widgets-\u003efirst('_id', '57d1d2fc97aee');\n```\n\n`all` returns the whole collection as an array, so you could perform more complicated queries on that.\n\n```\n$result = $widgets-\u003eall();\n```\n\nYou can also sort the `all` results in ascending order by passing in a property name to sort by.\n\n```\n$result = $widgets-\u003eall('price');\n```\n\n`search` allows you to search for a term or phrase throughout the whole collection. It returns an array of documents where any property contains the value, and is case insensitive.\n\n```\n$result = $widgets-\u003esearch('foo');\n```\n\n### Deleting, Undoing \u0026 Restoring\n\nYou can `delete` a document if you know the id.\n\n```\n$widgets-\u003edelete('57d1d2fc97aee');\n```\n\nYou can easily retrieve the previous version of a document at the last save point, in order to, for example, perform an undo, as long as the collection is persistent.\n\n```\n// get the previous version\n$previous = $widgets-\u003ehistory('57d1d2fc97aee');\n// undo i.e. set document to previous version \n$widgets-\u003eset('57d1d2fc97aee', $previous);\n```\n\nYou can retrieve older versions by specifying the amount of steps to go back.\n\n```\n// get the version of the document three saves ago\n$historical = $widgets-\u003ehistory('57d1d2fc97aee', 3);\n```\n\nWhile you can retrieve a deleted document with `history`, you can't `set` a document if it's been deleted. However you can `restore` a deleted document to its original id.\n\n```\n// delete the document\n$widgets-\u003edelete('57d1d2fc97aee');\n// and undelete it\n$widgets-\u003erestore('57d1d2fc97aee');\n```\n\n### Failing\n\nMost of the methods above will return `false` on fail so you can check if your call was successful.\n\n```\nif ($widgets-\u003edelete('57d1d2fc97aee')) {\n    echo 'Document succesfully deleted';\n} else {\n    echo 'Document not found';\n}\n```\n\nOnly the `add` method never returns `false`. As repeatedly mentioned, it will essentially `json_encode` any array you feed it, so doesn't really 'fail' as such, unless you don't send it an array. This has its own risks, so make sure the data you feed it isn't too weird!\n\n### Persistence\n\nYou might sometimes want non-persistent collections, for example you might want to instantiate an embedded model to scaffold out your UI, but it doesn't make sense for any data to persist within its own collection since that ultimately gets saved to the parent model/collection. For this use case, just over-ride the class `$persists` property and it will garbage collect once a day.\n\n```\nprotected $persists = false;\n```\n\n### Todo\n* ~~Retrieve historical state/undo~~\n* ~~Undelete~~\n* Errors/Exceptions\n* More granular set method, i.e. just one property rather than the whole document.\n* Some better query/search options, e.g. Fuzzy search / regex\n* ~~Docblocks~~\n* Slightly more verbose documentation with Daux.io, with clearer explanations and some longer examples\n* Tests\n\n### Maintained\nBy [Moussa Clarke](https://github.com/moussaclarke/)\n\n### Contribute\nFeel free to submit bug reports, suggestions and pull requests. Alternatively just fork it and make your own thing.\n\n### License\nMIT\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoussaclarke%2Fplanar","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoussaclarke%2Fplanar","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoussaclarke%2Fplanar/lists"}