{"id":36970235,"url":"https://github.com/reshadman/file-secretary","last_synced_at":"2026-01-13T21:46:08.820Z","repository":{"id":62534946,"uuid":"100842902","full_name":"reshadman/file-secretary","owner":"reshadman","description":"Get rid of anything related to files in Laravel, This package handles all for you. Anything we mean.","archived":false,"fork":false,"pushed_at":"2021-08-23T20:08:31.000Z","size":174,"stargazers_count":113,"open_issues_count":0,"forks_count":7,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-12-10T03:53:15.642Z","etag":null,"topics":[],"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/reshadman.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}},"created_at":"2017-08-20T06:27:05.000Z","updated_at":"2025-07-11T20:09:30.000Z","dependencies_parsed_at":"2022-11-02T16:00:51.396Z","dependency_job_id":null,"html_url":"https://github.com/reshadman/file-secretary","commit_stats":null,"previous_names":[],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/reshadman/file-secretary","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshadman%2Ffile-secretary","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshadman%2Ffile-secretary/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshadman%2Ffile-secretary/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshadman%2Ffile-secretary/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reshadman","download_url":"https://codeload.github.com/reshadman/file-secretary/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reshadman%2Ffile-secretary/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28401046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2026-01-13T21:46:08.162Z","updated_at":"2026-01-13T21:46:08.812Z","avatar_url":"https://github.com/reshadman.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Notice Before Usage\nFor new versions of Laravel use [spatie/laravel-medialibrary](https://github.com/spatie/laravel-medialibrary). The funcionality is almost the same\nexcept that the same files are stored multiple times. As a recommendation if you want image resizing functionality use something like [thumbor](https://github.com/thumbor/thumbor) which is more stable, secure, configurable and fast.  \n\n# Laravel File Secretary\n![Build Status](http://img.shields.io/travis/reshadman/file-secretary/master.png?style=flat-square)\n![Build Status](https://www.versioneye.com/user/projects/59a41045368b08003f172a41/badge.png?style=flat-square)\n\nGet rid of anything related to files in Laravel, This package handles all for you.\n\n## What does this package do?\n 1. **Handles your public assets** (.css, .js, .etc) you can use your CDN provider to serve the static assets. with a simple function call. For instance you can serve your static files through Rackspace CDN. After each deploy a new unique id is assigned to the path so the cached assets would be purged.\n \n 2. **Image manipulation and storage**: Store all of your images in the cloud (based on Laravel Adapters, Rackspace, S3, minio etc.), The image resizing is handled with simple configuration. You define templates and then images are generated automatically. Once a new image template created, you can use the nginx directives included in the package to remove the participation of PHP in next calls. Read the documentation for more info. A simple, fast and reliable method to manipulate images that are stored in the cloud.\n 3. **Detects redundant files**, File names are generated based on the \n filesize + a hash function.\n so redundant files could not exist technically, \n You can implement your own file name generator, too.\n 4. **Storing files** with a simple method call. \n They can be served without the participation of PHP, and they can\n be addressed with the package's helper functions if they are public.\n 5. **Database Tracking** (Optional), \n Centerialized Eloquent model which tracks your stored files(and images), You can store files/resiable-images and attach them to your bussiness models. Then you can use them. If it is an Image the templates are accessible by simple getters in the model.\n 6. **Simple helper functions** for dealing with resizable image urls, file urls, \n asset urls etc.\n 7. **A Simple controller for serving private/public files** Serve both resizable images, and files.\n You can implement your own access control for restricting access on request.\n So for instance if the file should be only served to its uploader, you can implement an access controller which checks that the requested file is attached to the user model or not.\n\n## Getting Started\n\n - [Does this package fit my needs?](#does-this-package-fit-my-needs)\n - [Installation](#installation)\n - [Configuration](#configuration)\n - [Usage](#usage)\n    - [Terminology](#terminology) : read for faster understanding.\n    - [Defining Contexts](#defining-contexts)\n    - [Using the asset uploader](#using-the-asset-uploader)\n    - [Storing files](#storing-files)\n        - [File names](#file-names)\n        - [Storing Service(Command)](#storing-command)\n        - [Storable file with file path](#storable-file-with-file-path)\n        - [Storable file with file content](#storable-file-with-file-content)\n        - [Storable file with file instance](#storable-file-with-file-instance)\n        - [Storable file with file HTTP url](#storable-file-with-file-http-url)\n        - [Storable file with base64 encoded content](#storable-file-with-base64-encoded-content)\n        - [Stored File Resoponse](#stored-file-response)\n    - [Storing images](#storing-images)\n    - [Deleting files](#deleting-files)\n    - [Updating files](#updating-files)\n    - [Manipulating images](#manipulating-images)\n        - [Image Templates](#image-templates)\n        - [Using the dynamic generic template](#using-the-dynamic-generic-template)\n        - [Writing your own template](#writing-your-own-template)\n        - [Storing manipulated image](#storing-manipulated-image)\n    - [Storing Eloquent-tracked files](#storing-eloquent-tracked-files)\n        - [When to use tracked files](#when-to-use-tracked-files)\n        - [Storing simple files and manupulateable images](#storing-simple-files-and-manipulatable-images)\n        - [Storing manipulatable images](#storing-manipulatable-images)\n        - [Using your own model](#using-your-own-model)\n    - [Deleting Eloquent-tracked files](#deleting-eloquent-tracked-files)\n        - [Handle what happens on delete](#handle-what-happens-on-delete)\n    - [Serving files](#serving-files)\n        - [Default Routes](#default-routes)\n        - [Restricting access](#restricting-access)\n        - [Serving public files and manipulated images](#serving-public-files-and-manipulated-images)\n        - [Serving images without the participation of PHP](#serving-images-without-the-participation-of-php)\n    - [Helper functions](#helper-functions)\n    - [Nginx Directives And Production Notes](#nginx-directives-and-production-notes)\n\n\n\n## Does this package fit my needs?\n\u003chttps://12factor.net\u003e offers some practical specs for dealing with files, called *attached resources*.\nAs files are an important part of the most of the information systems, they should be stored\nin a reliable, fast third party service (Like Amazon S3, or Rackspace object storage).\n\nThis package is an implementation of an spec for making Laravel application 12factor compatible for attached resources.\nYou can follow the spec with your own implementation.\n\n![Attached Resources](https://12factor.net/images/attached-resources.png)\n\nSo if your application domain is not about files (You are not Dropbox or Amazon S3 itself :D).\nYou can follow the spec and use the features this package offers, There are typically \nsome main usecases for files:\n - Serving private/public files (Like PDF, Docs etc)\n - Serving manipulatable images (Images that should be re-sized, watermarked etc),\n which is computation/memory heavy.\n - Serving static assets (like css, js, svg) etc.\n - Attaching and tracking business domain files to their equivalent business model (Like the profile image of a user)\n\nLaravel file-secretary offers some simple solutions for the above needs.\nWe did not apart the package to individual ones to respect the simplicity. The \ninterface that this package offers could be much simpler and more performant as Laravel\nfile-secretary has been developed periodically it is not in its simplest shape. We will keep that\nin mind for next releases.\n\n## Installation\nRun the following command in your project directory, to add the package to `composer.json`:\n```bash\ncomposer require reshadman/file-secretary \"\u003e=1.0.0 \u003c1.1.0\"\n```\n\nAdd the Service Provider to your `config/app.php`\n```php\n\u003c?php\nreturn [\n    // other app.php config elements\n    'providers' =\u003e [\n        // Other providers\n        // ...\n        \\Reshadman\\FileSecretary\\Infrastructure\\FileSecretaryServiceProvider::class    \n    ]  \n];\n```\n\nThen publish the configuration file:\n```bash\nphp artisqan vendor:publish \\\n    --provider=Reshadman\\FileSecretary\\Infrastructure\\FileSecretaryServiceProvider \\\n    --tag=config\n```\n\nIf you want to use the eloquent model for attaching files to your models, export the migration:\n```bash\nphp artisqan vendor:publish \\\n    --provider=Reshadman\\FileSecretary\\Infrastructure\\FileSecretaryServiceProvider \\\n    --tag=migrations\n```\n\n## Configuration\nAlmost everything is handled by configuration, For understanding how this package works please read the documentation\nblocks in the default config file here:\n\n[config/file_secretary.php](https://github.com/reshadman/file-secretary/blob/master/fixtures/config/file_secretary.php)\n\n\n## Usage\nThe best way to see the usage is by reading the integration tests, however you may\nread the following doc to understand what it does.\n\n## Terminology\n**Contexts**: file-secretary uses contexts for detecting where to store the files based on laravel filesystem drivers,\nwe have four context categories `basic_file`, `image`, `manipulated_image` and `assets`,\nall contexts should have a laravel filesystem driver, and a folder name in the driver.\nWhen you command to store the file in a context, the equivalent, laravel disk driver is found by the config\nand the starting path(folder of the context) is considered as the directory. Also generating file URLs is handled by this config.\n\n**Context Category: `basic_file`**: a basic file is a simple file that does not need\nany manipulation, when defining contexts you can indicate a `basic_file` context,\nand when you command to store the file, they will be added in the context's laravel filesystem\ndriver and the given folder. Files can be served with or without the participation of PHP.\n\n\n**Context Category: `image`**: Images that should be manipulated and mutated based on your given config. \nStoring manipulateable images is not different from storing simple files, except that\ninstead of a unique file name, they are stored in a unique directory, \nso the main image and its manipulated children are always in that unique folder.\nYou can also have different context strategies for the main image and its manipulated children.\nYou can indicate that the manipulated images of the main image should not be stored at all or be stored\nbeside the main image, or be stored in a different context (as manipulated images are not critical they can \nbe stored in more cost effective storage like your own server).\n \n**Context Category `manipulated_image`**: This is a context that is used for `image` context as the place\nto store its manipulated images.\n\n**Context Category: `asset`**: If you want, you can upload your entire built asset directory to your cloud CDN provider,\nlike Rackspace's public CDN.\n\n**Asset Folders/Tags**: These are the folders that you want to upload to the cloud, in your blade templates\nby calling `fs_asset('assetFolderName', 'your_local_path_to.css')` you can address them, assets are purged on each call, so the\nbrowser won't serve the old versions.\n\n**Image templates**: Templates are objects that keep the responsibility for mutating and manipulating images\nYou may use the default generic template (which dynamically re-sizes, strips images with different config), or \nimplement your own one. They are defined in the config.\n\n**Database/Eloquent Tracked Files**: After storing a file in a context you may assign it to the centralized eloquent\nmodel, this model can be attached to other business models, like the profile image of a user.\n\n**File/Folder name**: in the context of this package, a file name is a unique id\nwhich is generated automatically, it needs to be unique in the context of your app.\nYou can implement your own file name generator. By default it is based on the `sha1(fContent) + filesize`, It guarantees\nThat the same file is not redundant in the context.\n\n**Serving Files/Images/Manipulated Images**: Simply you may serve files publicly or privately, There is an HTTP endpoint in this package\nwhich will serve the requested files/images, It retrieves the equivalent `context` and `file_name` from the requested URL,\nand downloads the file from the storage of the found context and serves it to the user.\nBefore downloading, It calls the privacy object of the found context, if it returns false, it will throw an HTTP\n400 Exception. You can define your privacy classes for the found context, which will be discussed in the documentation.\n\n\n\n\n\n\n## Defining Contexts\nYou can read the default `config('file_secretary.contexts')` element of the config file to see all the available\noptions for creating contexts.\n\n\u003e Context must be unique in terms of Laravel filesystem disks + the starting folder in the disk (the `context_folder`).\n\n\n\n\n## Using the asset uploader\nTo serve your static assets through a public CDN, like Rackspace public CDN, you create an asset context like below:\n```php\n\u003c?php return [\n    // Other file secretary config elements...\n    'contexts' =\u003e [\n            // Other contexts...\n            'assets_context' =\u003e [\n                'category' =\u003e \\Reshadman\\FileSecretary\\Application\\ContextCategoryTypes::TYPE_ASSET,\n                'driver' =\u003e 'rackspace_asset_disk',\n                'context_folder' =\u003e 'some_context_folder',\n                'driver_base_address' =\u003e 'some-unique-string.rackcdn.com/etc/',\n            ]\n    ],\n    \n    'asset_folders' =\u003e [\n        'backoffice' =\u003e [\n            'path' =\u003e public_path('backoffice-assets'),\n            'context' =\u003e 'assets_context',\n            // fills .env automatically like =\u003e BACKOFFICE_ASSET_ID=unique-id\n            // which causes to the browser to not serve old versions.\n            'env_key' =\u003e 'BACKOFFICE_ASSET_ID',\n        ],    \n    ]\n];\n```\nOther things will be handled automatically, in development environment, the assets will be served\nlocal and in production env they will be served from the given `driver_base_address`\n\nTo sync the latest assets run:\n```bash\nphp artisan file-secretary:upload-assets --tags=backoffice\n```\n\nTo address the assets in the template call:\n```php\n\u003c?php\n$url = fs_asset('backoffice', 'styles.dist.css');\n\ndump($url);\n// In development env: http://localhost:8000/backoffice/styles.dist.css\n// In production env: some-unique-id.rackcdn.com/[context_folder]/[latest-unique-id]/styles.dist.css\n```\n\nTo delete the old versions:\nBy default the last two version are kept, and other versions are deleted after a successful upload.\nFor a different strategy you may change the following event listener in `file_secretary.php`:\n\n```php\n\u003c?php\nreturn [\n    // Other config file_secretary config elements\n    'listeners' =\u003e [\n        \\Reshadman\\FileSecretary\\Application\\Events\\AfterAssetUpload::class =\u003e [\n            '\\YourListener\\Class',    \n        ]  \n    ],  \n];\n```\n\n\n## Storing Files\n\nfile-secretary takes care of storing files after they have been validated by you. Then they can be tracked and served.\n\nYou can pass different file targets to the store command. For storing a file you should create an instance of the \nfollowing class:\n\n```php\n\u003c?php\n\n\\Reshadman\\FileSecretary\\Application\\PresentedFile::class;\n```\n\nTo see the list of available file targets read the contents of the above class.\n\n\u003e The `PresentedFile` class support different file types inluding: *URL*, *File Path*, *File Content* and *File Instance*\n\u003e ,read the following docs.\n\n\n### File names\nYou can not control the file names, they are used for tracking files and images.\n\nFor the files of a `basic_file` context, the storing format would be something like this:\n\n```bash\nhttps://driver_base_address.mycdn.com/[context_folder]/[unique-id-xxxx-xxxxx].[file_extension]\n```\n\nFor the files of a `image` or `manipulated_image` context the file name would be some thing like this:\n```bash\n# Main file\nhttps://driver_base_address.mycdn.com/[context_folder]/[unique-id-xxxx-xxxx]/1_main.[image_extension]\n\n# Manipulated templates:\nhttps://driver_base_address.mycdn.com/[context_folder]/[unique-id-xxxx-xxxx]/[my_template_200x200.[image_extension_or_template_extension]\n```\n\nYou can define your own unique id generator in the config:\n```php\n\u003c?php \nreturn [\n    'file_name_generator' =\u003e \\OpensslRandomFileNameGenerator::class,\n      \n    // O1ther config elements...\n];\n?\u003e\n\n\u003c?php\n\nuse Reshadman\\FileSecretary\\Application\\FileUniqueIdGeneratorInterface;\nuse Reshadman\\FileSecretary\\Application\\PresentedFile;\n\nclass OpensslRandomFileNameGenerator implements FileUniqueIdGeneratorInterface {\n     public static function generate(PresentedFile $presentedFile)\n     {\n         $length = 128;\n         return bin2hex(openssl_random_pseudo_bytes($length / 2));\n     }\n}\n?\u003e\n\n```\n\n\u003ePlease note that your closure should always return a unique string.\n\n\u003eThe function prevents redundant files in the same context.\n\n\n### Storing command\nAfter you have created the `PresentedFile` instance you should pass it to the store command.\nFor knowing all the constructor parameters of the `PresentedFile` class read the class implementation.\n\nFor finalizing the storage read the following:\n\n```php\n\u003c?php\n\n$presentedFile = new \\Reshadman\\FileSecretary\\Application\\PresentedFile(\n    'file_manager_private', // context name\n    'SOME_TEXT_CONTENT_HERE_', // The mime type will be detected automatically.\n    \\Reshadman\\FileSecretary\\Application\\PresentedFile::FILE_TYPE_CONTENT\n);\n\n/** @var \\Reshadman\\FileSecretary\\Application\\Usecases\\StoreFile $storeCommand */\n$storeCommand = app(\\Reshadman\\FileSecretary\\Application\\Usecases\\StoreFile::class);\n\n$addressableRemoteFile = $storeCommand-\u003eexecute($presentedFile);\n\ndd($addressableRemoteFile-\u003efullRelative(), $addressableRemoteFile-\u003efullUrl());\n\n```\n\n### Storable file with file path\nIf you have the a local file path, you can create the `PresentedFile` instance like the following:\n\n```php\n\u003c?php\n\n$presentedFile = new \\Reshadman\\FileSecretary\\Application\\PresentedFile(\n    'file_manager_private', // context name\n    $path = '../path_to/my_file.png',\n    \\Reshadman\\FileSecretary\\Application\\PresentedFile::FILE_TYPE_PATH,\n    basename($path)  \n);\n```\n\n### Storable file with file content\nIf you have the file content, you can create the `PresentedFile` instance like the following:\n\n```php\n\u003c?php\n\n$presentedFile = new \\Reshadman\\FileSecretary\\Application\\PresentedFile(\n    'file_manager_private', // context name\n    file_get_contents($path = '../path_to/my_file.png'), // The mime type will be detected automatically.\n    \\Reshadman\\FileSecretary\\Application\\PresentedFile::FILE_TYPE_CONTENT\n);\n```\n\n### Storable file with file instance\nIf you want to store the file from an instance of request `UploadedFile` or a Symfony file instance\nread the following\n\n```php\n\u003c?php\n\n$presentedFile = new \\Reshadman\\FileSecretary\\Application\\PresentedFile(\n    'file_manager_private', // context name\n    request()-\u003efile('company_logo'), // The mime type will be detected automatically.\n    \\Reshadman\\FileSecretary\\Application\\PresentedFile::FILE_TYPE_INSTANCE\n);\n```\n\n\n### Storable file with file HTTP url\nIf you want to store a file from a URL you can read the following:\n\n```php\n\u003c?php\n\n$presentedFile = new \\Reshadman\\FileSecretary\\Application\\PresentedFile(\n    'file_manager_private', // context name\n    'https://logo_url.com/logo.png', // The mime type will be detected automatically.\n    \\Reshadman\\FileSecretary\\Application\\PresentedFile::FILE_TYPE_URL\n);\n```\n\n### Storable file with base64 encoded content\nIf you want to store a file from a base64 string read the following:\n\n```php\n\u003c?php\n\n$presentedFile = new \\Reshadman\\FileSecretary\\Application\\PresentedFile(\n    'file_manager_private', // context name\n    'base64encodecontent=', // The mime type will be detected automatically.\n    \\Reshadman\\FileSecretary\\Application\\PresentedFile::FILE_TYPE_BASE64\n);\n```\n\n\u003eNote that if other meta data is attached to the string it should be removed by you, the mime type\nwill be detected automatically.\n\n\n\n### Stored File Response\nAfter executing the store file command it will return an instance of:\n\n```php\n\u003c?php\n\\Reshadman\\FileSecretary\\Application\\AddressableRemoteFile::class;\n```\n\nfor knowing methods read the class implementation.\n\n## Storing Images\nStoring images is not different from storing files. You should only pass the proper\n`context_name` which has `image` category to the `PresentedFile` instance.\n\n\nnew PresentedFile(\n    'context_name',\n    '/path/to/file',\n    PresentedFile::FILE_TYPE_PATH,\n    \"optional_original_file_name.pdf\"\n);\n\n\n## Deleting files\nTo delete a file you should use the following service command:\n\n```php\n\u003c?php\n\n/** @var \\Reshadman\\FileSecretary\\Application\\Usecases\\DeleteFile $deleter */\n$deleter = app(\\Reshadman\\FileSecretary\\Application\\Usecases\\DeleteFile::class);\n\n$fileFullRelativePath = '/context_folder/unique-id.pdf';\n\n$deleter-\u003eexecute(\"context_name\", $fileFullRelativePath);\n\n```\n\n\u003eIf your deleting an image context and it stores templates, the manipulated images with be deleted, too.\n\n## Updating files\nWhen dealing with cloud file storages, which some of them offer CDN services, it is not a good idea\nto update a file, you can simply delete the old one and create a new one, that is because it takes some times\nthat the CDN provider fetch the new version for all edges. And because we do not focus on file names \nin our implementation it is better to leave the file in the storage, or add a `X-DELETE-AFTER` header to that,\nor delete it entirely and create a new file.\nIf you have some reason that this functionality is needed please create a PR or open an issue.\n\n\u003e Please not that if you implement your own update strategy, do not use the built in file name generator\nfunction, because the file name is based on file content, so if you change the file content with the same\nfile name, it will be overridden next time the old file is uploaded to the context.\n \n\n## Manipulating images\nAfter you have created a context with `category` , `image` \nfile-secretary allows to manipulate and mutate images based on image templates, \nyou define image templates in the config file, and when the image is requested through the url\nwith predefined parameters, the main parent image is fetched from the disk storage, the image content\nis passed to the template object and the template's result is served to the user.\n\nThe image templates are defined for all `image` contexts.\n\n\n### Image templates \nTo define your image templates you should add your config to the `available_image_templates` of the config file:\n\n```php\n\u003c?php return [\n  'companies_logo_200x200' =\u003e [\n    'class' =\u003e \\Reshadman\\FileSecretary\\Infrastructure\\Images\\Templates\\DynamicResizableTemplate::class,\n    'args' =\u003e [\n      'width' =\u003e 200,\n      'height' =\u003e 200,\n      'encodings' =\u003e null, // When null only parent file encoding is allowed.\n      'strip' =\u003e  false, // removes the ICC profile when imagick is used.\n    ],\n  ],  \n];\n```\n\n### Using the dynamic generic template\nBy using the following class as the ```class``` parameter of your image template config, you can control the \nbehaviour with args, this will cover most of your needs:\n```php\n\u003c?php \n\\Reshadman\\FileSecretary\\Infrastructure\\Images\\Templates\\DynamicResizableTemplate::class\n```\n\nThe `class` parameter is the template implementation, each template can have some arguments, \nthe `DynamicResiableTemplate` get some arguments which allow you to satisfy most of your needs.\n\nfor resizing:\n - Width: can be null or an integer, if null it will be automatically calculated\n - Height: can be nul or an integer, if null it will be automatically calculated\n - encodings: can be null or an array of images extensions like `['png', 'jpg', 'svg']`, if null\n  the same encoding as the parent image will be used.\n - quality: takes an integer from 1 to 100\n - strip: when true and using imagick the embedded ICC profile of the image can removed, this reduces the generated image\n size significantly, the ICC profile is used to generate same true colors on different displys.\n - mode: You can use different fit strategies take a look at template manager class (```TemplateManager``) for \n  list of available fit strategies.\n\n### Writing your own template\nfile-secretary uses intervention package for image manipulation.\nTo write your own template you should create a class that implements the following interface\n\n```php\n\u003c?php\n\\Reshadman\\FileSecretary\\Infrastructure\\Images\\TemplateInterface::class;\n```\nThen you can use it in the config, also you can check the `DynamicResiableTemplate` implementation for faster understanding.\n\n\n### Storing manipulated image\nManipulated images are requested through some URL, this URL is automatically generated by the \npackage if for instance your attaching the `profile_image` to `users` model in eloquent.\n\nThe URL carries the following info with itself:\n - The context name\n - The context folder\n - Main image folder\n - Requested image template\n - Requested image extension\n \nThe endpoint retrieves the above info from the request and creates the following object\n```php\n\u003c?php\n\n\\Reshadman\\FileSecretary\\Application\\PrivacyCheckNeeds::class;\n```\n\nThe object contains all the needed information for fetching the parent image,\nmanipulating it, storing it, and serving the manipulated image.\n\nAfter the image has been manipulated, the context config says to whether store the manipulated image\nin the cloud, or ignore it, or store it somewhere else, in a less expensive disk,\nall of these can be defined by setting the value of `store_manipulated` in the config arary of the context.\n\nThere are 3 available values for `store_manipuled`:\n 1. `false` : It won't store the manipulated image.\n 2. `true`: Stores it beside the manipulated image.\n 3. `another_context_name` : the target context should be of type `ContextCategoryTypes::TYPE_MANIPULATED_IMAGE`\n so the parent file for example is stored in Amazon S3, all the manipulated images are stored in your own FTP\n server which is wrapped around an Nginx proxy. This allows to serve manipulated images without the participation\n of PHP, once they are created, and as they are not the only data source, nothing happens when they are deleted \n accidentally.\n \nFor more info about this options read the `contexts` section of the default config file.\n\n\n## Storing Eloquent-tracked files\n\nIf your domain models (like your `User` model) has some blob files, for example the avatar of the user,\nfile-secretary offers a centralized, file model which keeps tracks of your cloud files.\n\nSo after you uploaded your file to the proper context you may create a tracked record for that and \nattach the record to your user's model through a foreign key.\n\nYou should also include the packages migration to create the database table.\n\n### When to use tracked files\nAnytime you need to attach a cloud file to an eloquent business model,\n - Your blog post has some image which need thumbnail resizing.\n - Your user has some documents \n - etc.\n\nThe eloquent model has some getter methods that generate the image template URLs and file URLs, automatically.\n\nFor more info view the exported migration of the package.\n\n### Storing simple files and manipulatable images\nWhen storing files the process is not different from storing the file in the cloud,\n\nYou create an instance of `PresentedFile` and pass it to the following service, the created record is returned instead:\n\n```php\n\u003c?php\n\n$presentedFile = new \\Reshadman\\FileSecretary\\Application\\PresentedFile(\n    'user_images_context',\n    request()-\u003efile('avatar_file'),\n    \\Reshadman\\FileSecretary\\Application\\PresentedFile::FILE_TYPE_INSTANCE,\n    basename(request()-\u003efile('avatar_file')) // Is stored in the table so you can show the client's name if needed.\n);\n\n/** @var \\Reshadman\\FileSecretary\\Application\\Usecases\\StoreTrackedFile $storeTrackedFile */\n$storeTrackedFile = app(\\Reshadman\\FileSecretary\\Application\\Usecases\\StoreTrackedFile::class);\n\n$trackedModel = $storeTrackedFile-\u003eexecute($presentedFile);\n\nauth()-\u003euser()-\u003eavatar()-\u003eattach($trackedModel);\n\ndump($trackedModel-\u003efull_url);\n\necho \"\u003cimg src='{$trackedModel-\u003eimage_templates['avatar_50x50']}' title='{$trackedModel-\u003eoriginal_name}'/\u003e\";\n\n```\n\n### Using your own model\nBy default the following implementation is used:\n```php\n\u003c?php\n\\Reshadman\\FileSecretary\\Application\\EloquentPersistedFile::class;\n```\n\nYou can extend the above class and create your own implementation:\n\n```php\n\u003c?php return [\n    // Other file_secretary.php config elements\n    'eloquent' =\u003e [\n        'model' =\u003e '\\YourOwnModel\\Class',\n        'table' =\u003e 'defined_table_in_model_or_this_string_in_fallback'\n    ]  \n];\n```\n\n## Deleting Eloquent-tracked files\nThe package does not delete the file from cloud when you delete the tracked record, this is for preventing\nunexpected behaviour.\n\nYou can delete the file manually by the following service:\n```php\n\u003c?php\n\n/** @var \\Reshadman\\FileSecretary\\Application\\EloquentPersistedFile $trackedFile */\n$trackedFile = auth()-\u003euser()-\u003eavatar;\n\n/** @var \\Reshadman\\FileSecretary\\Application\\Usecases\\DeleteTrackedFile $deleter */\n$deleter = app(\\Reshadman\\FileSecretary\\Application\\Usecases\\DeleteTrackedFile::class);\n\n$deleter-\u003eexecute($trackedFile);\n\n// You can also delete a file by uuid\n$deleter-\u003eexecute($trackedFile-\u003egetFileableUuid());\n```\n\n### Handle what happens on delete\n\nThere 3 strategies when calling the delete service:\n - Ignore deleting the remote file\n - Strict Delete of the remote file\n - Delete if the current unique id of the file does not exists in the same context\n\nTo indicate this you should execute the service like the following:\n```php\n\u003c?php\n\nuse Reshadman\\FileSecretary\\Application\\Usecases\\DeleteTrackedFile;\n\n/** @var \\Reshadman\\FileSecretary\\Application\\EloquentPersistedFile $trackedFile */\n$trackedFile = auth()-\u003euser()-\u003eavatar;\n\n/** @var \\Reshadman\\FileSecretary\\Application\\Usecases\\DeleteTrackedFile $deleter */\n$deleter = app(\\Reshadman\\FileSecretary\\Application\\Usecases\\DeleteTrackedFile::class);\n\n$deleter-\u003eexecute($trackedFile, DeleteTrackedFile::ON_DELETE_DELETE_REMOTE);\n\n$deleter-\u003eexecute($trackedFile, DeleteTrackedFile::ON_DELETE_IGNORE_REMOTE);\n\n$deleter-\u003eexecute($trackedFile, DeleteTrackedFile::ON_DELETE_DELETE_IF_NOT_IN_OTHERS);\n```\n  \n\n## Serving files\nThis package handles file serving for you based on the config you've given in the `contexts` section and the \n`available_image_templates` section.\n\nHowever you may serve the files the way you are satisfied with.\nFor instance if only admins are allowed to view some doc files you can create a route for that\ncheck that the current user is admin or not, then retrieve the file id from the request parameters and\nserve it to the user:\n```php\n\u003c?php\n\nuse Reshadman\\FileSecretary\\Application\\EloquentPersistedFile;\nuse Reshadman\\FileSecretary\\Infrastructure\\FileSecretaryManager;\n\nclass SomeController extends \\Illuminate\\Routing\\Controller {\n    \n    public function getDocument($fileId, FileSecretaryManager $fManager)\n    {\n        if (!auth()-\u003euser()-\u003eis_admin) {\n            abort(403);\n        }\n        \n        /** @var EloquentPersistedFile $trackedFile */\n        $trackedFile = EloquentPersistedFile::findOrFail($fileId);\n        \n        $diskDriver = $fManager-\u003egetContextDriver($trackedFile-\u003egetFileableContext());\n        \n        $path = $trackedFile-\u003efull_relative_path;\n        \n        $contents = $diskDriver-\u003eget($path);\n        \n        if ($contents === false) {\n            abort(404);\n        }\n        \n        $headers = ['Content-Type' =\u003e $diskDriver-\u003emimeType($path)];\n        \n        return response($contents, 200, $headers);\n    }\n    \n}\n\n```\n\nIf you provide a `driver_base_address` for contexts, the URLs will be generated with prepended base_address\nSo if your files are public you can simply define the `driver_base_address` of your disk, and then\nthey are served directly from the disk HTTP endpoint.\n \n\n### Default routes \n \nThere is a predefined route which file-secretary provides:\n\n```php\n\u003c?php\n\nroute('file-secretary.get.download_file', [\n    'context_name' =\u003e '',\n    'context_folder' =\u003e '',\n    'after_context_path'\n]);\n```\n\nThis route handles file serving for all contexts except the `asset` contexts.\n\nIf you don't want this routes to be include in your package, disable it with setting its key to `false` in config.\n```php\n\u003c?php return [\n    // Other config elements\n    'load_routes' =\u003e false  \n];\n```\n\nThere is a default controller which you can use to attach it to your desired routes:\n```php\n\u003c?php\n\n\\Reshadman\\FileSecretary\\Presentation\\Http\\Actions\\DownloadFileAction::class;\n```\n\nThere is a middleware included in the package which fetches the needed data to retrieve files from the cloud\nif you are using your own routes with the default controller, this middleware should be wrapped around\nthe route:\n\n```php\n\u003c?php\n\n\\Reshadman\\FileSecretary\\Presentation\\Http\\Middleware\\PrivacyCheckMiddleware::class;\n```\n\n### Restricting access\nYou can decide that the given user is allowed to view the requested file or not, with defining privacy classes\nin the config, read the config file as the documentation. \n\nThe file identifiers are passed to your privacy class and then you can decide that the user is allowed to\nget this file or not.\n\nAll of the privacy classes should implement the following interface:\n```php\n\u003c?php\n\n\\Reshadman\\FileSecretary\\Application\\Privacy\\PrivacyInterface::class;\n```\n\n\u003e The restriction works only if you use the built-in controller class for serving.\n\n\n### Manipulating Headers\nYou can use an array pipeline for manipulating response headers on serving.\nEach array item should be an implementation of:\n```php\n\u003c?php\n\\Reshadman\\FileSecretary\\Presentation\\Http\\HeadersMutatorInterface::class;\n```\nThe headers array is passed to each header mutator item defined in config.\n\nFor details on how to use please see the ```public_images``` section of the config file.\n\n### Serving public files and manipulated images\nIf you have public contexts, you can use the `driver_base_address` functionality to remove the participcation of PHP\n\nfor `basic_file` context category, the base address will be prepended to the relative path, and it will be served \ndirectly.\n\n### Serving images without the participation of PHP\n\nfor `image` context category, the main image is always served from the base address, however the image templates\nare still served from PHP, you can create an Nginx reverse proxy which does one of the following:\n\n - Set `store_manipulated` to `true`, Create an Nginx directive that will call the laravel endpoint\n on file not found of the base address, the laravel endpoint will store the file beside the main so the in the next call\n the image is served from your upstream base address.\n\n - Set `store_manipulated` to `false` Use a reverse proxy with caching, which calls the laravel endpoint,\n after a successful call  Nginx will cache the image, so the image will be served from Nginx's cache.\n \n - Set `store_manipulated` to another context which uses a local disk, set the disk path as root for Nginx and \n on 404,  call the laravel endpoint, or make the route path and disk path look the same, So it will be routed\n to the generator controller automatically when the file does not exist.\n \n \u003eNginx Directives will be added to the package soon. To make it much more simpler.\n \n## Helper functions\nWill be added soon.\n \n## Nginx Directives And Production Notes\nWill be added soon.\n\n## Running the Integration Tests\n There are integration tests written for this package. To run integration\ntests do as the following:\n\n 1. Create your `phpunit.xml` file based on the packages's `phpunit.dist.xml`: `cp phpunit.dist.xml phpunit.xml`\n \n 2. Fill the phpunit config with your environment variables.\n The package has been tested with **Rackspace** Object storage, to prove the \n functionality in cloud. You can change the `phpunit.xml` file and the configs in `fixtures/config/`\n to integrate them with your testing environment.\n 3. Run the tests with `vendor/bin/phpunit --debug`\n \n\u003e Currently there is no isolated object unit testing for this package. \n\u003e They will be added in next releases.\n\n## Package Roadmap\n 1. Writing more integration tests + isolated object unit tests.\n 2. Use more semantic names for features, class names and methods names.\n 3. Make the tracking, eloquent independent.\n 4. Refactor the code both for design and performance.\n 5. In a new release, use a polymorphic model for database tracked files which allows\n to indicate that whether a file has been used somewhere in the other models or not (by design).\n  Which in result we can delete unused tracked files. This also works only with SQL databases.\n 6. Adding Nginx Directives\n 7. Delegate some works to worker queue. \n\n## About the package\nThis package has been extracted from [*jobinja.ir - The leading job board and career platform in Iran*](https://jobinja.ir),\nThis is part of the work for making [jobinja.ir](https://jobinja.ir), [12factor.net](http://12factor.net) compatible.\n\n## License\nThe MIT License (MIT). Please see License File for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freshadman%2Ffile-secretary","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freshadman%2Ffile-secretary","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freshadman%2Ffile-secretary/lists"}