├── LICENSE ├── composer.json ├── config └── filer.php ├── migrations ├── 2015_04_29_130341_create_table_local_files.php ├── 2015_04_29_130846_create_table_attachments.php ├── 2015_04_29_233700_create_table_urls.php ├── 2016_05_31_110633_make_attachments_user_id_nullable.php └── 2016_06_02_151938_add_hash_to_filer_local_files_table.php ├── readme.md ├── seeds └── LocalFilesHashSeeder.php └── src ├── Attachment.php ├── BaseItem.php ├── Controllers └── LocalFileController.php ├── Filer.php ├── FilerServiceProvider.php ├── HasAttachments.php ├── LocalFile.php ├── Type.php └── Url.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rick Mann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "teamteatime/laravel-filer", 3 | "description": "A simple Laravel ORM file attachment solution supporting multiple relationship types", 4 | "keywords": ["laravel", "laravel-5", "files", "attachments"], 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "Riari", 9 | "email": "hello@ricko.me" 10 | }, 11 | { 12 | "name": "NeeHi", 13 | "email": "nick.snape@hotmail.com" 14 | } 15 | ], 16 | "require": { 17 | "php": ">=5.4.0" 18 | }, 19 | "autoload": { 20 | "psr-4": { 21 | "TeamTeaTime\\Filer\\": "src/" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /config/filer.php: -------------------------------------------------------------------------------- 1 | true, 17 | 18 | /* 19 | |-------------------------------------------------------------------------- 20 | | Route Prefix 21 | |-------------------------------------------------------------------------- 22 | | 23 | | With routes enabled, you can specify the prefix to use here. 24 | | 25 | */ 26 | 27 | 'route_prefix' => 'files', 28 | 29 | /* 30 | |-------------------------------------------------------------------------- 31 | | Route hashing 32 | |-------------------------------------------------------------------------- 33 | | 34 | | Enables unique hashes for local files to obfuscate their IDs in routes. 35 | | 36 | */ 37 | 38 | 'hash_routes' => false, 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Hash length 43 | |-------------------------------------------------------------------------- 44 | | 45 | | The length to use when generating hashes for local files. 46 | | 47 | */ 48 | 49 | 'hash_length' => 40, 50 | 51 | /* 52 | |-------------------------------------------------------------------------- 53 | | Storage Path 54 | |-------------------------------------------------------------------------- 55 | | 56 | | The relative and absolute paths to the directory where your local 57 | | attachments are stored. 58 | | 59 | */ 60 | 61 | 'path' => [ 62 | 'relative' => 'uploads', 63 | 'absolute' => storage_path('uploads'), 64 | ], 65 | 66 | /* 67 | |-------------------------------------------------------------------------- 68 | | Attachment Query String 69 | |-------------------------------------------------------------------------- 70 | | 71 | | Enable this to append a query string to attachment URLs generated by this 72 | | package. This uses the attachment's updated_at timestamp in UNIX 73 | | timestamp format. 74 | | 75 | */ 76 | 77 | 'append_query_string' => true, 78 | 79 | /* 80 | |-------------------------------------------------------------------------- 81 | | Deletion Cleanup 82 | |-------------------------------------------------------------------------- 83 | | 84 | | Enable this to make Filer attempt deletion of local files when the 85 | | attachments they're referenced from are deleted from the database. 86 | | 87 | */ 88 | 89 | 'cleanup_on_delete' => true, 90 | 91 | /* 92 | |-------------------------------------------------------------------------- 93 | | Download attribute 94 | |-------------------------------------------------------------------------- 95 | | 96 | | The attribute to use when downloading a local file. 97 | | Can be one of: `key`, `title`, `description`, `filename`. 98 | */ 99 | 100 | 'download_attribute' => 'filename', 101 | 102 | ]; 103 | -------------------------------------------------------------------------------- /migrations/2015_04_29_130341_create_table_local_files.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('filename'); 19 | $table->string('path'); 20 | $table->string('mimetype'); 21 | $table->integer('size'); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | * 29 | * @return void 30 | */ 31 | public function down() 32 | { 33 | Schema::drop('filer_local_files'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /migrations/2015_04_29_130846_create_table_attachments.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('user_id')->unsigned(); 19 | $table->string('title')->nullable(); 20 | $table->string('description')->nullable(); 21 | $table->string('model_type')->nullable(); 22 | $table->integer('model_id')->unsigned()->nullable(); 23 | $table->string('key')->nullable(); 24 | $table->string('item_type')->nullable(); 25 | $table->integer('item_id')->unsigned()->nullable(); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::drop('filer_attachments'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /migrations/2015_04_29_233700_create_table_urls.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('url'); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::drop('filer_urls'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /migrations/2016_05_31_110633_make_attachments_user_id_nullable.php: -------------------------------------------------------------------------------- 1 | integer('user_id')->nullable()->change(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | * 24 | * @return void 25 | */ 26 | public function down() 27 | { 28 | Schema::table('filer_attachments', function(Blueprint $table) 29 | { 30 | $table->integer('user_id')->nullable(false)->change(); 31 | }); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /migrations/2016_06_02_151938_add_hash_to_filer_local_files_table.php: -------------------------------------------------------------------------------- 1 | string('hash')->nullable()->after('size')->unique(); 17 | }); 18 | } 19 | 20 | /** 21 | * Reverse the migrations. 22 | * 23 | * @return void 24 | */ 25 | public function down() 26 | { 27 | Schema::table('filer_local_files', function (Blueprint $table) { 28 | $table->dropColumn('hash'); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Please be aware that this package **is not** designed to handle uploading or image manipulation. It's simply designed to compliment other packages and tools that already exist in Laravel. If you're looking for a more feature-complete attachments solution, take a look at [CodeSleeve/stapler](https://github.com/CodeSleeve/stapler). 2 | 3 | ## Installation 4 | 5 | ### Step 1: Install the package 6 | 7 | Install the package via Composer: 8 | 9 | ``` 10 | composer require teamteatime/laravel-filer 11 | ``` 12 | 13 | Add the service provider to your `config/app.php`: 14 | 15 | ```php 16 | TeamTeaTime\Filer\FilerServiceProvider::class, 17 | ``` 18 | 19 | > If your app defines a catch-all route, make sure you load this service provider before your app service providers. 20 | 21 | ### Step 2: Publish the package files 22 | 23 | Run the vendor:publish command to publish Filer's migrations: 24 | 25 | `php artisan vendor:publish` 26 | 27 | ### Step 3: Update your database 28 | 29 | Run your migrations: 30 | 31 | `php artisan migrate` 32 | 33 | ### Step 4: Update your models 34 | 35 | Add attachment support to your models by using the HasAttachments trait: 36 | 37 | ```php 38 | class ... extends Eloquent { 39 | use \TeamTeaTime\Filer\HasAttachments; 40 | } 41 | ``` 42 | 43 | ## Configuration 44 | 45 | Filer requires no configuration out of the box in most cases, but the following options are available to you in `config/filer.php`: 46 | 47 | Option | Type | Description | Default 48 | ------ | ---- | ----------- | ------- 49 | routes | Boolean | Determines whether or not to automatically define filer's routes. If you set this to `false`, you can optionally use `\TeamTeaTime\Filer\Filer::routes($router, $namespace)` in your routes.php. | true 50 | route_prefix | string | If routes are enabled, this is used for all route prefixes. | files 51 | hash_routes | Boolean | Enables unique hashes for local files to obfuscate their IDs in routes. | false 52 | hash_length | string | The length to use when generating hashes for local files. | 40 53 | path | Array | Contains the relative and absolute paths to the directory where your attachment files are stored. | storage_path('uploads') 54 | append_querystring | Boolean | If enabled, attachment URLs include a querystring containing the attachment's updated_at timestamp. This prevents out of date attachments from being loaded by the browser. | true 55 | cleanup_on_delete | Boolean | If enabled, Filer will attempt to delete local files referenced by deleted attachments. | true 56 | 57 | ## Usage 58 | 59 | To attach a file or URL, use the `attach()` method on your model. This method will accept any of the following: 60 | 61 | ...a **local file path** 62 | ```php 63 | $user->attach('avatars/1.jpg'); // path relative to your configured storage directory 64 | ``` 65 | 66 | ...an instance of **SplFileInfo** 67 | ```php 68 | $photo = Request::file('photo')->move($destinationPath); 69 | $user->attach($photo); 70 | ``` 71 | > `Symfony\Component\HttpFoundation\File\File`, `Symfony\Component\HttpFoundation\File\UploadedFile` and `Illuminate\Http\UploadedFile` are extensions of `SplFileInfo` and Laravel Requests contain the latter by default. 72 | 73 | ...or a **URL** 74 | ```php 75 | $user->attach('http://www.analysis.im/uploads/seminar/pdf-sample.pdf'); 76 | ``` 77 | 78 | You can also specify a key (which uniquely identifies the attachment), a title, and/or a description using the options array: 79 | 80 | ```php 81 | $user->attach('uploads/avatars/1.jpg', ['key' => 'avatar']); 82 | ``` 83 | 84 | ```php 85 | $article->attach($pdf, ['title' => "Event 2015 Guide", 'description' => "The complete guide for this year's event."]); 86 | ``` 87 | 88 | By default, attachments are associated with user IDs using `Auth::id()`. You can override this at call time: 89 | 90 | ```php 91 | $user->attach($photo, ['user_id' => $user->id]); 92 | ``` 93 | 94 | ```php 95 | $article->attach($pdf, ['user_id' => null]); 96 | ``` 97 | 98 | Depending on what you pass to this method, the item will be stored as either a `TeamTeaTime\Filer\LocalFile` or a `TeamTeaTime\Filer\Url`. You can later call on attachments via the `attachments` relationship. Examples are provided below. 99 | 100 | ### Displaying a list of attachments in a view 101 | 102 | ``` 103 | @foreach ($article->attachments as $attachment) 104 | {{ $attachment->title }} 105 |
{{ $attachment->description }}
106 | @endforeach 107 | ``` 108 | 109 | ### Retrieving an attachment by ID or key 110 | 111 | ```php 112 | $user->attachments()->find($attachmentId); 113 | ``` 114 | ```php 115 | $user->findAttachmentByKey('avatar'); 116 | ``` 117 | 118 | ### Accessing an attachment's properties and type-specific properties 119 | 120 | ```php 121 | $avatar = $user->findAttachmentByKey('avatar'); 122 | $avatar->getUrl(); // the URL to the file (see below) 123 | $avatar->getDownloadUrl(); // the download URL to the file (see below) 124 | $avatar->title; // the attachment title, if any 125 | $avatar->description; // the attachment description, if any 126 | 127 | // If the attachment is a LocalFile... 128 | $avatar->item->filename; // the filename, with its extension 129 | $avatar->item->path; // the path to the directory where the file exists 130 | $avatar->item->mimetype; // the file's detected MIME type 131 | $avatar->item->size; // the file size, in bytes 132 | $avatar->item->getFile(); // the Symfony File representation of the file 133 | $avatar->item->hash; // the unique hash generated for the file (if filer.hash_routes is enabled) 134 | ``` 135 | 136 | ### Generating URLs 137 | 138 | The `getUrl()` and `getDownloadUrl()` methods above will return different values based on the attachment type; if it's a local file, they will return the 'view' and 'download' routes respectively, otherwise they'll return the URL that was attached. 139 | 140 | For local files, the provided routes can be generated with a file ID or hash: 141 | 142 | ```php 143 | route('filer.file.view', $fileId); 144 | ``` 145 | 146 | ```php 147 | route('filer.file.download', $fileId) 148 | ``` 149 | 150 | > Note that depending on the file's MIME type, the browser may begin a download with both of these routes. 151 | -------------------------------------------------------------------------------- /seeds/LocalFilesHashSeeder.php: -------------------------------------------------------------------------------- 1 | get(); 16 | foreach ($items as $item) { 17 | $item->hash = $item->generateHash(); 18 | $item->save(); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/Attachment.php: -------------------------------------------------------------------------------- 1 | item->delete(); 39 | }); 40 | } 41 | 42 | /** 43 | * Relationship: user 44 | * 45 | * @return \Illuminate\Database\Eloquent\Relations\BelongsTo 46 | */ 47 | public function user() 48 | { 49 | return $this->belongsTo(config('filer.user.model'), 'user_id'); 50 | } 51 | 52 | /** 53 | * Relationship: model 54 | * 55 | * @return \Illuminate\Database\Eloquent\Relations\MorphTo 56 | */ 57 | public function model() 58 | { 59 | return $this->morphTo(); 60 | } 61 | 62 | /** 63 | * Relationship: item 64 | * 65 | * @return \Illuminate\Database\Eloquent\Relations\MorphTo 66 | */ 67 | public function item() 68 | { 69 | return $this->morphTo(); 70 | } 71 | 72 | /** 73 | * Scope: key 74 | * 75 | * @param \Illuminate\Database\Query\Builder $query 76 | * @param string $key 77 | * @return \Illuminate\Database\Query\Builder 78 | */ 79 | public function scopeKey($query, $key) 80 | { 81 | return $query->where('key', $key); 82 | } 83 | 84 | /** 85 | * Get the attachment's URL. 86 | * 87 | * @return string 88 | */ 89 | public function getUrl() 90 | { 91 | if (config('filer.append_query_string')) { 92 | return "{$this->item->getUrl()}?v={$this->updated_at->timestamp}"; 93 | } 94 | 95 | return $this->item->getUrl(); 96 | } 97 | 98 | /** 99 | * Get the attachment's download URL. 100 | * 101 | * @return string 102 | */ 103 | public function getDownloadUrl() 104 | { 105 | return $this->item->getDownloadUrl(); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/BaseItem.php: -------------------------------------------------------------------------------- 1 | morphMany(Attachment::class, 'item'); 15 | } 16 | 17 | /** 18 | * Get the item's URL. 19 | * 20 | * @return string 21 | */ 22 | abstract public function getUrl(); 23 | 24 | /** 25 | * Get the item's download URL. 26 | * 27 | * @return string 28 | */ 29 | abstract public function getDownloadUrl(); 30 | } 31 | -------------------------------------------------------------------------------- /src/Controllers/LocalFileController.php: -------------------------------------------------------------------------------- 1 | getContents(), 200)->withHeaders([ 20 | 'Content-Type' => $file->getFile()->getMimeType(), 21 | 'Cache-Control' => 'max-age=86400, public', 22 | 'Expires' => Carbon::now()->addSeconds(86400)->format('D, d M Y H:i:s \G\M\T') 23 | ]); 24 | return $response; 25 | } 26 | 27 | /** 28 | * Return a download response for the specified file. 29 | * 30 | * @param int $id 31 | * @return \Illuminate\Http\Response 32 | */ 33 | public function download($id) 34 | { 35 | $file = LocalFile::getByIdentifier($id); 36 | return response()->download($file->getAbsolutePath(), $file->getDownloadName()); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Filer.php: -------------------------------------------------------------------------------- 1 | group(compact('namespace'), function ($router) { 18 | $router->get('{id}', [ 19 | 'as' => 'filer.file.view', 20 | 'uses' => 'LocalFileController@view' 21 | ]); 22 | $router->get('{id}/download', [ 23 | 'as' => 'filer.file.download', 24 | 'uses' => 'LocalFileController@download' 25 | ]); 26 | }); 27 | } 28 | 29 | /** 30 | * Attempts to determine what the given item is between a local filepath, a 31 | * local file object or a URL. 32 | * 33 | * @param mixed $item 34 | * @return string 35 | * @throws Exception 36 | */ 37 | public static function checkType($item) 38 | { 39 | if (is_string($item)) { 40 | // Item is a string; check to see if it's a URL or filepath 41 | if (filter_var($item, FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED)) { 42 | // Item is a URL 43 | return Type::URL; 44 | } elseif (is_file(config('filer.path.absolute') . "/{$item}")) { 45 | // Item is a filepath 46 | return Type::FILEPATH; 47 | } 48 | } elseif (is_a($item, 'SplFileInfo')) { 49 | // Item is a file object 50 | return Type::FILE; 51 | } 52 | 53 | // Throw an exception if item doesn't match any known types 54 | throw new Exception('Unknown item type'); 55 | } 56 | 57 | /** 58 | * Returns a file's relative path. 59 | * 60 | * @param mixed $file 61 | * @return string 62 | */ 63 | public static function getRelativeFilepath($file) 64 | { 65 | $storageDir = self::convertSlashes(config('filer.path.absolute')); 66 | $absolutePath = self::convertSlashes($file->getRealPath()); 67 | return dirname(str_replace($storageDir, '', $absolutePath)); 68 | } 69 | 70 | /** 71 | * Converts backslashes to forward slashes. 72 | * 73 | * @param string $string 74 | * @return string 75 | */ 76 | public static function convertSlashes($string) 77 | { 78 | return str_replace("\\", '/', $string); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/FilerServiceProvider.php: -------------------------------------------------------------------------------- 1 | mergeConfigFrom(__DIR__.'/../config/filer.php', 'filer'); 16 | } 17 | 18 | /** 19 | * Bootstrap the application events. 20 | * 21 | * @param Router $router 22 | * @return void 23 | */ 24 | public function boot(Router $router) 25 | { 26 | // Publish migrations, config and seeders 27 | $this->publishes([ 28 | __DIR__.'/../migrations/' => base_path('/database/migrations') 29 | ], 'migrations'); 30 | 31 | $this->publishes([ 32 | __DIR__.'/../config/filer.php' => config_path('filer.php') 33 | ], 'config'); 34 | 35 | $this->publishes([ 36 | __DIR__.'/../seeds/' => base_path('/database/seeds') 37 | ], 'seeds'); 38 | 39 | // Routes 40 | if (config('filer.routes')) { 41 | $router->group(['prefix' => config('filer.route_prefix'), 'middleware' => 'web'], function ($router) { 42 | Filer::routes($router); 43 | }); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/HasAttachments.php: -------------------------------------------------------------------------------- 1 | morphMany(Attachment::class, 'model'); 20 | } 21 | 22 | /** 23 | * Get an attachment by key. 24 | * 25 | * @param string $key 26 | * @return Attachment 27 | */ 28 | public function findAttachmentByKey($key) 29 | { 30 | return $this->attachments()->key($key)->first(); 31 | } 32 | 33 | /** 34 | * Attaches a file/link. $item can be a local file path (relative to config('filer.path.absolute')), 35 | * Symfony\Component\HttpFoundation\File\File or SplFileInfo instance or a file URL. 36 | * 37 | * @param string $item 38 | * @param array $options 39 | * @return Attachment|bool 40 | */ 41 | public function attach($item, array $options = []) 42 | { 43 | // Merge in default options 44 | $options += [ 45 | 'key' => '', 46 | 'title' => '', 47 | 'description' => '', 48 | 'user_id' => Auth::id() 49 | ]; 50 | 51 | // Determine the type 52 | $type = Filer::checkType($item); 53 | 54 | // Create the appropriate model for the item if it doesn't already exist 55 | $itemToAttach = null; 56 | switch ($type) { 57 | case Type::URL: 58 | $itemToAttach = Url::firstOrCreate(['url' => $item]); 59 | break; 60 | case Type::FILEPATH: 61 | $item = new File(config('filer.path.absolute') . "/{$item}"); 62 | case Type::FILE: 63 | $itemToAttach = LocalFile::firstOrNew([ 64 | 'filename' => $item->getFilename(), 65 | 'path' => Filer::getRelativeFilepath($item) 66 | ]); 67 | $itemToAttach->fill([ 68 | 'mimetype' => $item->getMimeType(), 69 | 'size' => $item->getSize() 70 | ])->save(); 71 | break; 72 | } 73 | 74 | if (is_null($itemToAttach)) { 75 | return false; 76 | } 77 | 78 | // Create/update and save the attachment 79 | $attributes = [ 80 | 'user_id' => $options['user_id'], 81 | 'model_id' => $this->id 82 | ]; 83 | 84 | if (!is_null($options['key'])) { 85 | $attributes['key'] = $options['key']; 86 | } 87 | 88 | $attachment = Attachment::firstOrNew($attributes); 89 | 90 | if (!is_null($options['title'])) { 91 | $attachment->title = $options['title']; 92 | } 93 | 94 | if (!is_null($options['description'])) { 95 | $attachment->description = $options['description']; 96 | } 97 | 98 | $attachment->save(); 99 | 100 | // Save the attachment to the model 101 | $this->attachments()->save($attachment); 102 | 103 | // Save the attachment to the item 104 | $itemToAttach->attachment()->save($attachment); 105 | 106 | return $attachment; 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/LocalFile.php: -------------------------------------------------------------------------------- 1 | path}/{$file->filename}"; 33 | 34 | if (is_file($path)) { 35 | unlink($path); 36 | } 37 | }); 38 | } 39 | 40 | // Whenever a new model is created in the database, we add a hash 41 | static::creating(function ($model) { 42 | $model->hash = $model->generateHash(); 43 | }); 44 | } 45 | 46 | /** 47 | * Return a Symfony File representation of the file. 48 | * 49 | * @return File 50 | */ 51 | public function getFile() 52 | { 53 | return new File($this->getAbsolutePath()); 54 | } 55 | 56 | /** 57 | * Get the file's absolute path. 58 | * 59 | * @return string 60 | */ 61 | public function getAbsolutePath() 62 | { 63 | return $this->getPath(config('filer.path.absolute')); 64 | } 65 | 66 | /** 67 | * Get the file's relative path. 68 | * 69 | * @return string 70 | */ 71 | public function getRelativePath() 72 | { 73 | return $this->getPath(config('filer.path.relative')); 74 | } 75 | 76 | /** 77 | * Get the file's contents. 78 | * 79 | * @return string 80 | */ 81 | public function getContents() 82 | { 83 | return file_get_contents($this->getAbsolutePath()); 84 | } 85 | 86 | /** 87 | * Get the item's URL. 88 | * 89 | * @return string 90 | */ 91 | public function getUrl() 92 | { 93 | return route('filer.file.view', $this->getIdentifier()); 94 | } 95 | 96 | /** 97 | * Get the item's download URL. 98 | * 99 | * @return string 100 | */ 101 | public function getDownloadUrl() 102 | { 103 | return route('filer.file.download', $this->getIdentifier()); 104 | } 105 | 106 | /** 107 | * Get the item's download name. 108 | * 109 | * @return string 110 | */ 111 | public function getDownloadName() 112 | { 113 | if (array_key_exists(config('filer.download_attribute'), $this->attributes)) { 114 | return $this->{config('filer.download_attribute')}; 115 | } 116 | 117 | return $this->filename; 118 | } 119 | 120 | /** 121 | * Construct a filepath. 122 | * 123 | * @param string $path 124 | * @return string 125 | */ 126 | private function getPath($path) 127 | { 128 | return "{$path}{$this->path}/{$this->filename}"; 129 | } 130 | 131 | /** 132 | * Get the unique identifier according to filer.hash_routes. 133 | * 134 | * @return integer|string 135 | */ 136 | public function getIdentifier() 137 | { 138 | return config('filer.hash_routes') ? $this->hash : $this->id; 139 | } 140 | 141 | /** 142 | * Get a model instance using a unique identifier according to filer.hash_routes. 143 | * 144 | * @param integer $id 145 | * @return LocalFIle 146 | */ 147 | public static function getByIdentifier($id) 148 | { 149 | $file = config('filer.hash_routes') ? static::whereHash($id)->first() : static::find($id); 150 | 151 | if (!$file) { 152 | throw (new ModelNotFoundException)->setModel(static::class); 153 | } 154 | 155 | return $file; 156 | } 157 | 158 | /** 159 | * Generate a unique hash for the file. 160 | * 161 | * @return string 162 | */ 163 | public function generateHash() 164 | { 165 | do { 166 | $hash = str_random(config('filer.hash_length', 40)); 167 | } while (static::whereHash($hash)->first() instanceof LocalFile); 168 | 169 | return $hash; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /src/Type.php: -------------------------------------------------------------------------------- 1 | url; 27 | } 28 | 29 | /** 30 | * Get the item's download URL. 31 | * 32 | * @return string 33 | */ 34 | public function getDownloadUrl() 35 | { 36 | return $this->url; 37 | } 38 | } 39 | --------------------------------------------------------------------------------