{"id":18782398,"url":"https://github.com/atk4/filestore","last_synced_at":"2025-08-20T20:32:24.092Z","repository":{"id":41203568,"uuid":"120025106","full_name":"atk4/filestore","owner":"atk4","description":"Implements integration between ATK UI and FlySystem","archived":false,"fork":false,"pushed_at":"2025-01-21T14:58:35.000Z","size":2328,"stargazers_count":9,"open_issues_count":6,"forks_count":8,"subscribers_count":9,"default_branch":"develop","last_synced_at":"2025-08-11T22:47:38.149Z","etag":null,"topics":["agile","atk4","filestore","php","upload"],"latest_commit_sha":null,"homepage":"https://agiletoolkit.org/","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/atk4.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":"2018-02-02T19:57:48.000Z","updated_at":"2025-01-21T14:58:37.000Z","dependencies_parsed_at":"2024-12-19T15:19:51.327Z","dependency_job_id":null,"html_url":"https://github.com/atk4/filestore","commit_stats":{"total_commits":36,"total_committers":6,"mean_commits":6.0,"dds":0.6944444444444444,"last_synced_commit":"eef8a9379970b327590738f02c399b63f4804d92"},"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/atk4/filestore","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Ffilestore","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Ffilestore/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Ffilestore/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Ffilestore/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/atk4","download_url":"https://codeload.github.com/atk4/filestore/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/atk4%2Ffilestore/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271378680,"owners_count":24749192,"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-20T02:00:09.606Z","response_time":69,"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":["agile","atk4","filestore","php","upload"],"created_at":"2024-11-07T20:35:51.777Z","updated_at":"2025-08-20T20:32:23.727Z","avatar_url":"https://github.com/atk4.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Starting from Version 1.4 of ATK UI, it adds support for File and Image uploads. In it's raw form, developer is\nresponsible for taking care of the file once it's uploaded. In most cases, developer would want to store file either on\na local or remote filesystem, but he would need to perform that action himself.\n\n**Filestore** is a plugin for ATK Data and ATK UI which offers seamless integration with Flysystem.\n\n## Introduction\n\nAttaching files to your database records is super-simple with **Filestore**. The next example can be implemented and\nadded to YOUR php application in just a few lines:\n\n![upload1](docs/images/demo.gif)\n\nSince ATK Form submits using AJAX file must be sent to the server as soon as it is selected. File is instantly placed\ninside **flysystem** (in the demo I'm using Local storage) and record in the database is created containing all the\ninformation about the file including if it is an image and if it is - dimensions:\n\n![upload1](docs/images/table.png)\n\nTwo random identifiers are generated - one is used for the actual file name (to make sure you don't overwrite existing\nfile) and another one (token) will be used to reference the file. Actually you don't see a token at all, but it's used\nto keep things secure.\n\nFiles can be uploaded directly in your CRUD and you can have several file fields:\n\n![upload1](docs/images/crud.png)\n\nIn your main table you only need one `varchar` field to store file token. If you use SQL, you can have instant access to\nadditional fields such as name of original file or more:\n\n![upload1](docs/images/refs.png)\n\nThis is especially valuable if you need to incorporate all these fields on your custom template.\n\n# Installation and Code\n\nTo install run `composer require atk4\\filestore` and you will need to create `filestore_file` table. In the definition\nof your model, you simply need to declare a new field:\n\n``` php\n$this-\u003eaddField('file', [\\Atk4\\Filestore\\Field\\FileField::class, 'flysystem' =\u003e $this-\u003egetApp()-\u003efilesystem]);\n```\n\nThis pretty much takes care of everything! For full example see file `demos/basic.php`.\n\n# Features\n\nCurrently the following features are implemented:\n\n- Nice file upload UI and full integration with ATK UI Form. Upload tracking with a progress-bar.\n- Keep files clean, removing record also removes linked files. Files uploaded but not linked remain in status \"draft\"\n  you can clean them up periodically\n- Supports any storage through integration with flysystem.\n- Very flexible, use different table, map fields differently, use multiple locations, add custom actions and much more.\n- Files can be scoped under \"user_id\" for further security (user won't be able to access images of another user even if\n  he knows a token).\n\n# Roadmap\n\nWe have the following ideas for the up-coming version. If you would like to help us implement (or sponsor) any of the\nfeatures below, please contact @romaninsh (https://gitter.im/atk4/atk4):\n\n- Ability to specify which extensions and/or file types to accept.\n- Easy to download file, that was previously uploaded.\n- Add automatic thumbnail creation. Delete thumbnails together with original image.\n- Specify custom actions for Upload, Link, Unlink and Delete.\n- Display icon for file type or preview for image\n- Add interactive cropping.\n- Add method to verify file.\n- Add drag\u0026drop uploading.\n- Add new View for a big drop-target or use arbitrary view (upload without form).\n- Add a UI for \"file management\"\n- Add support for re-croping thumbnails from originals.\n- Integrate NoSQL persistence support.\n- Encrypt file before storing / decrypt when loading.\n- Add option for \"hashing\" file into subdirectories.\n\n---------------\n\nOLD README:\n\nevent, so file upload will take place as soon as the file is selected. There are however some conditions, when file\nuploaded/deleted and form is not submitted.\n\nOn successful completion, PHP-side callback is executed to generate **ID**. **Filestore** provides this callback and\nperforms the following actions:\n\n- Collect information about the file - original filename, size, mime type etc.\n- Store information inside \"File\" model generating ID.\n- Move file into Flysystem by using a randomized name, recording it also in \"File\" model.\n- Respond with File's ID which is then passed back into form' submission handler.\n\n## Example\n\nIf you have a Model such as `Friend` and you wish to upload friend's photo, Filestore offers a great way to integrate:\n\n```php\n// in Friend::init();\n$this-\u003eaddField('photo_file_token', [\\Atk4\\Filestore\\Field\\FileField::class]);\n```\n\nThis field will automatically appear on the form as an upload field, but in the database will be storing \"token\" from\nthe \"File\" model. You can even define multiple fields like that.\n\nBy default the local file storage will be used, but you can configure a different location simply by passing into your\nfield:\n\n``` php\n$this-\u003eaddField('photo_file_token', [\n    \\Atk4\\Filestore\\Field\\FileField::class,\n    // specify if you want to only accept certain types of files or extensions.\n    'onlyTypes' =\u003e ['image/*', 'application/x-pdf'],\n    'onlyExtensions' =\u003e ['img', 'png', 'pdf'], // safer to specify both\n\n    // where to store\n    'flysystem' =\u003e $flysystem,\n\n    // you can also define callback if you wish to do something more with the file\n    'onUpload' =\u003e function ($file) {\n        // do something with file\n    },\n\n    // this is called when form with the file is submitted\n    'onAttach' =\u003e function ($file) {\n        // $file is a model object\n    },\n\n    // when user detaches file from the related entity\n    'onDetach' =\u003e function ($file) {\n        // $file is a model object\n    },\n])\n```\n\nIf you open form for the `Friend` which already have a file attached, you will be able to remove the files. However\njust \"removing\" files may not mean they will be unassociated, user can still click \"Cancel\". When form is saved,\nhowever, if the file was removed, it will be also deleted from the storage and from `File` table.\n\n## Image\n\nFilestore also implements `\\Atk4\\Filestore\\Field\\Image` class which offers additional features by extending File to\nautomatically crop and store various thumbnails for an image.\n\n``` php\n$this-\u003eaddField('picture_id', [\n    \\Atk4\\Filestore\\Field\\Image::class,\n\n    // no need to specify types, will only accept valid images\n\n    // where to store\n    'flysystem' =\u003e $flysystem,\n\n    // you can still define this if you wish to pre-process your file, e.g. add watermark\n    'onUpload' =\u003e function ($file) {\n        // do something with file\n    },\n\n    // cropping table\n    'crop' =\u003e [\n        'medium' =\u003e [200, 300], // width, height\n        'small' =\u003e [50, 50],\n    ],\n])\n```\n\nThe cropping will maintain aspect ratio of original image, but will make sure that the image is filled. Will use\neither \"imagick\" or \"gd\" for the operation. Arguments are defined like that:\n\n- `0 =\u003e 200`, width\n- `1 =\u003e height`, height\n- `'type' =\u003e 'jpeg'`  optional. Defaults to 'png'\n\nIn order to store thumbnails, filestore will create additional file(s) inside `File` table.\n\n## File model\n\nFilestore comes with a custom Model `Atk4\\Filestore\\Model\\File`, which is designed to store file meta-information. For\neach uploaded file it stores:\n\n- file_token - generated randomly, used on a form to pass back to submission handler\n- location - (used to identify file in Flysystem. Generated randomly)\n- storage - (short info on the storage used)\n- source_file_id (specify ID of the original file. Used for thumbnails or other generated files)\n- status (defines where is file currently)\n\nSeveral \"meta\" fields are also defined which describe contents of the file\n\n- meta_filename\n- meta_extension\n- meta_md5\n- meta_mime_type\n- meta_size\n- meta_is_image\n- meta_image_width\n- meta_image_height\n\n### Methods\n\n``` php\n$file-\u003ecreateFromPath('path/to/file.txt', 'my_file.txt');\n```\n\nCreate a `File` entity, save the data to `flysystem` and save the entity.\n\n``` php\n$stream = $file-\u003egetStream();\n```\n\nReturns stream of file contents. You can call `$stream-\u003egetContents()` to fetch the file content as a string.\n\n## Roadmap\n\nSome of the possible future features:\n\n- Add \"temporary\" location for files\n- Allow option to initialize Flysystem details when loading random file through model\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatk4%2Ffilestore","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fatk4%2Ffilestore","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fatk4%2Ffilestore/lists"}