├── .styleci.yml ├── resources └── views │ ├── js.blade.php │ └── components │ ├── image.blade.php │ ├── video.blade.php │ └── button.blade.php ├── changelog.md ├── .gitignore ├── src ├── Support │ └── helpers.php ├── Facades │ └── Cloudinary.php ├── Commands │ ├── DeleteFilesCommand.php │ ├── FetchFilesCommand.php │ ├── RenameFilesCommand.php │ ├── UploadFileCommand.php │ ├── GenerateArchiveCommand.php │ └── BackupFilesCommand.php ├── Model │ └── Media.php ├── MediaAlly.php ├── CloudinaryServiceProvider.php ├── CloudinaryAdapter.php └── CloudinaryEngine.php ├── database └── migrations │ └── 2020_06_14_000001_create_media_table.php ├── phpunit.xml ├── license.md ├── config └── cloudinary.php ├── contributing.md ├── composer.json └── README.md /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: laravel -------------------------------------------------------------------------------- /resources/views/js.blade.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/views/components/image.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | echo cloudinary()->getImageTag($publicId ?? '')->scale($width ?? '', $height ?? '')->serialize(); 3 | @endphp -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `laravel-cloudinary` will be documented in this file. 4 | 5 | ## Version 1.0 6 | 7 | ### Added 8 | - Everything 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /storage/*.key 5 | /vendor 6 | .env 7 | .env.backup 8 | .phpunit.result.cache 9 | Homestead.json 10 | Homestead.yaml 11 | npm-debug.log 12 | yarn-error.log 13 | src/.php_cs.cache -------------------------------------------------------------------------------- /resources/views/components/video.blade.php: -------------------------------------------------------------------------------- 1 | @php 2 | echo cloudinary()->getVideoTag($publicId ?? '')->setAttributes(['controls', 'loop', 'preload'])->fallback('Your browser does not support HTML5 video tagsssss.')->scale($width ?? '', $height ?? ''); 3 | @endphp -------------------------------------------------------------------------------- /src/Support/helpers.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 13 | $table->morphs('medially'); 14 | $table->text('file_url'); 15 | $table->string('file_name'); 16 | $table->string('file_type')->nullable(); 17 | $table->unsignedBigInteger('size'); 18 | $table->timestamps(); 19 | }); 20 | } 21 | } -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | 13 | 14 | ./tests/ 15 | 16 | 17 | 18 | 19 | src/ 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /resources/views/components/button.blade.php: -------------------------------------------------------------------------------- 1 | 19 | 20 | 23 | 24 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | # The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Prosper Otemuyiwa 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 13 | > all 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 21 | > THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /config/cloudinary.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | return [ 13 | 14 | /* 15 | |-------------------------------------------------------------------------- 16 | | Cloudinary Configuration 17 | |-------------------------------------------------------------------------- 18 | | 19 | | An HTTP or HTTPS URL to notify your application (a webhook) when the process of uploads, deletes, and any API 20 | | that accepts notification_url has completed. 21 | | 22 | | 23 | */ 24 | 'notification_url' => env('CLOUDINARY_NOTIFICATION_URL'), 25 | 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Cloudinary Configuration 30 | |-------------------------------------------------------------------------- 31 | | 32 | | Here you may configure your Cloudinary settings. Cloudinary is a cloud hosted 33 | | media management service for all file uploads, storage, delivery and transformation needs. 34 | | 35 | | 36 | */ 37 | 'cloud_url' => env('CLOUDINARY_URL'), 38 | 39 | /** 40 | * Upload Preset From Cloudinary Dashboard 41 | * 42 | */ 43 | 'upload_preset' => env('CLOUDINARY_UPLOAD_PRESET') 44 | ]; 45 | -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/unicodeveloper/laravel-cloudinary). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please [squash them](http://www.git-scm.com/book/en/v2/Git-Tools-Rewriting-History#Changing-Multiple-Commit-Messages) before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ composer test 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | 34 | 35 | Daniel Alabuja -------------------------------------------------------------------------------- /src/Commands/DeleteFilesCommand.php: -------------------------------------------------------------------------------- 1 | warn('Please ensure your Cloudinary credentials are set before continuing.'); 38 | 39 | return; 40 | } 41 | 42 | $publicId = $this->argument('publicId'); 43 | 44 | $this->info("About to delete {$publicId} file on Cloudinary..."); 45 | 46 | try { 47 | $engine->destroy($publicId); 48 | 49 | $this->info('File deleted!'); 50 | } catch (Exception $exception) { 51 | $this->warn("Deletion of files on Cloudinary failed because: {$exception->getMessage()}."); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "unicodeveloper/laravel-cloudinary", 3 | "description": "A Laravel Cloudinary Package", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Prosper Otemuyiwa", 8 | "email": "prosperotemuyiwa@gmail.com", 9 | "homepage": "https://github.com/unicodeveloper" 10 | } 11 | ], 12 | "homepage": "https://github.com/unicodeveloper/laravel-cloudinary", 13 | "keywords": ["Laravel", "laravel-cloudinary"], 14 | "require": { 15 | "php": "^7.0", 16 | "illuminate/support": "~5|~6|~7", 17 | "cloudinary/cloudinary_php": "2.0.0-beta6", 18 | "ext-json": "*" 19 | }, 20 | "require-dev": { 21 | "phpunit/phpunit": "^8.0", 22 | "mockery/mockery": "^1.1", 23 | "orchestra/testbench": "~3|~4", 24 | "sempro/phpunit-pretty-print": "^1.0" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Unicodeveloper\\Cloudinary\\": "src/" 29 | }, 30 | "files": [ 31 | "src/Support/helpers.php" 32 | ] 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Unicodeveloper\\Cloudinary\\Tests\\": "tests" 37 | } 38 | }, 39 | "extra": { 40 | "laravel": { 41 | "providers": [ 42 | "Unicodeveloper\\Cloudinary\\CloudinaryServiceProvider" 43 | ], 44 | "aliases": { 45 | "Cloudinary": "Unicodeveloper\\Cloudinary\\Facades\\Cloudinary" 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/Model/Media.php: -------------------------------------------------------------------------------- 1 | morphTo(); 26 | } 27 | 28 | /** 29 | * Get the file url / path of a Media File 30 | * @return string 31 | */ 32 | public function getSecurePath() 33 | { 34 | return $this->file_url; 35 | } 36 | 37 | /** 38 | * Get the file name of a Media File 39 | * @return string 40 | */ 41 | public function getFileName() 42 | { 43 | return $this->file_name; 44 | } 45 | 46 | /** 47 | * Get the mime type of a Media File 48 | * @return string 49 | */ 50 | public function getFileType() 51 | { 52 | return $this->file_type; 53 | } 54 | 55 | /** 56 | * Get the Size of a Media File 57 | * @return Integer 58 | */ 59 | public function getSize() 60 | { 61 | return $this->size; 62 | } 63 | 64 | /** 65 | * Get the Readable Size of a Media File 66 | * @return string 67 | */ 68 | public function getReadableSize() 69 | { 70 | return resolve(CloudinaryEngine::class)->getHumanReadableSize($this->size); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Commands/FetchFilesCommand.php: -------------------------------------------------------------------------------- 1 | warn('Please ensure your Cloudinary credentials are set before continuing.'); 38 | 39 | return; 40 | } 41 | 42 | $publicId = $this->argument('publicId'); 43 | 44 | if (!is_string($publicId)) { 45 | $this->warn("Please ensure a valid public Id is passed as an argument."); 46 | 47 | return; 48 | } 49 | 50 | $this->info("Fetching file..."); 51 | 52 | try { 53 | $url = $engine->getImage($publicId)->toUrl(); 54 | $this->info("File: {$url}"); 55 | } catch (Exception $exception) { 56 | $this->warn("Renaming of file on Cloudinary failed because: {$exception->getMessage()}."); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/Commands/RenameFilesCommand.php: -------------------------------------------------------------------------------- 1 | warn('Please ensure your Cloudinary credentials are set before continuing.'); 38 | 39 | return; 40 | } 41 | 42 | $fromPublicId = $this->argument('fromPublicId'); 43 | $toPublicId = $this->argument('toPublicId'); 44 | 45 | if (!is_string($fromPublicId) || !is_string($toPublicId)) { 46 | $this->warn("Please ensure a valid public Id is passed as an argument."); 47 | 48 | return; 49 | } 50 | 51 | $this->info("About to rename {$fromPublicId} file to {$toPublicId} on Cloudinary..."); 52 | 53 | try { 54 | $engine->rename($fromPublicId, $toPublicId); 55 | 56 | $this->info('File renamed successfully on Cloudinary!'); 57 | } catch (Exception $exception) { 58 | $this->warn("Renaming of file on Cloudinary failed because: {$exception->getMessage()}."); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Commands/UploadFileCommand.php: -------------------------------------------------------------------------------- 1 | warn('Please ensure your Cloudinary credentials are set before continuing.'); 39 | 40 | return; 41 | } 42 | 43 | if (is_numeric($this->argument('remote-url'))) { 44 | $this->warn('This is a number, not a valid remote file url. Please try again with a valid URL.'); 45 | 46 | return; 47 | } 48 | 49 | if (!filter_var($this->argument('remote-url'), FILTER_VALIDATE_URL)) { 50 | $this->warn('Please add a valid remote file url as an argument.'); 51 | 52 | return; 53 | } 54 | 55 | $remoteUrl = $this->argument('remote-url'); 56 | 57 | $this->info('Extracting remote file...'); 58 | 59 | try { 60 | $engine->uploadFile($remoteUrl); 61 | $this->info('Uploading in progress...'); 62 | 63 | $this->info('Upload to Cloudinary completed!'); 64 | } catch (Exception $exception) { 65 | $this->warn("Backup of files to Cloudinary failed because: {$exception->getMessage()}."); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Commands/GenerateArchiveCommand.php: -------------------------------------------------------------------------------- 1 | warn('Please ensure your Cloudinary credentials are set before continuing.'); 40 | 41 | return; 42 | } 43 | 44 | if (!$this->option('tags') && !$this->option('public_ids')) { 45 | $this->warn( 46 | 'Please ensure you pass in at least a tag with --tags, or at least a public_id with --public_ids' 47 | ); 48 | 49 | return; 50 | } 51 | 52 | $this->info('Generating Archive...'); 53 | 54 | try { 55 | $response = $engine->createArchive( 56 | [ 57 | 'tags' => $this->option('tags') ?? null, 58 | 'public_ids' => $this->option('public_ids') ?? null 59 | ] 60 | )['secure_url']; 61 | 62 | $this->info("Archive: {$response}"); 63 | } catch (Exception $exception) { 64 | $this->warn("Backup of files to Cloudinary failed because: {$exception->getMessage()}."); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/Commands/BackupFilesCommand.php: -------------------------------------------------------------------------------- 1 | getFiles(); 40 | $folder = null; 41 | 42 | if (!$files) { 43 | $this->warn( 44 | 'There are no files in the storage/app/public directory. Use --location flag to specify the name of the directory (if there are files in there) within the storage/app directory.' 45 | ); 46 | 47 | return; 48 | } 49 | 50 | if (!config('cloudinary.cloud_url')) { 51 | $this->warn('Please ensure your Cloudinary credentials are set before continuing.'); 52 | 53 | return; 54 | } 55 | 56 | if ($this->option('folder') && is_string($this->option('folder'))) { 57 | $folder = $this->option('folder'); 58 | } 59 | 60 | if ($this->option('location') && is_string($this->option('location'))) { 61 | $files = $this->getFiles($this->option('location')); 62 | } 63 | 64 | $this->info('Starting backup to Cloudinary...'); 65 | 66 | try { 67 | foreach ($files as $file) { 68 | $engine->uploadFile($file->getRealPath(), $folder ? ['folder' => $folder] : []); 69 | $this->info('Uploading in progress...'); 70 | } 71 | 72 | $this->info('Backup to Cloudinary completed!'); 73 | } catch (Exception $exception) { 74 | $this->warn("Backup of files to Cloudinary failed because: {$exception->getMessage()}."); 75 | } 76 | } 77 | 78 | public function getFiles($location = 'public') 79 | { 80 | return File::allFiles(storage_path("app/{$location}")); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/MediaAlly.php: -------------------------------------------------------------------------------- 1 | morphMany(Media::class, 'medially'); 26 | } 27 | 28 | 29 | /** 30 | * Attach Media Files to a Model 31 | */ 32 | public function attachMedia($file) 33 | { 34 | if(! file_exists($file)) { 35 | throw new Exception('Please pass in a file that exists'); 36 | } 37 | 38 | $response = resolve(CloudinaryEngine::class)->uploadFile($file->getRealPath()); 39 | 40 | $media = new Media(); 41 | $media->file_name = $response->getFileName(); 42 | $media->file_url = $response->getSecurePath(); 43 | $media->size = $response->getSize(); 44 | $media->file_type = $response->getFileType(); 45 | 46 | $this->medially()->save($media); 47 | } 48 | 49 | /** 50 | * Attach Remote Media Files to a Model 51 | */ 52 | public function attachRemoteMedia($remoteFile) 53 | { 54 | $response = resolve(CloudinaryEngine::class)->uploadFile($remoteFile); 55 | 56 | $media = new Media(); 57 | $media->file_name = $response->getFileName(); 58 | $media->file_url = $response->getSecurePath(); 59 | $media->size = $response->getSize(); 60 | $media->file_type = $response->getFileType(); 61 | 62 | $this->medially()->save($media); 63 | } 64 | 65 | /** 66 | * Get all the Media files relating to a particular Model record 67 | */ 68 | public function fetchAllMedia() 69 | { 70 | return $this->medially()->get(); 71 | } 72 | 73 | /** 74 | * Get the first Media file relating to a particular Model record 75 | */ 76 | public function fetchFirstMedia() 77 | { 78 | return $this->medially()->first(); 79 | } 80 | 81 | /** 82 | * Delete all files associated with a particular Model record 83 | */ 84 | public function detachMedia() 85 | { 86 | 87 | $items = $this->medially()->get(); 88 | 89 | foreach($items as $item) { 90 | resolve(CloudinaryEngine::class)->destroy($item->getFileName()); 91 | } 92 | 93 | return $this->medially()->delete(); 94 | } 95 | 96 | /** 97 | * Get the last Media file relating to a particular Model record 98 | */ 99 | public function fetchLastMedia() 100 | { 101 | return $this->medially()->get()->last(); 102 | } 103 | 104 | /** 105 | * Update the Media files relating to a particular Model record 106 | */ 107 | public function updateMedia($file) 108 | { 109 | $this->detachMedia(); 110 | $this->attachMedia($file); 111 | } 112 | 113 | /** 114 | * Update the Media files relating to a particular Model record (Specificially existing remote files) 115 | */ 116 | public function updateRemoteMedia($file) 117 | { 118 | $this->detachMedia(); 119 | $this->attachRemoteMedia($file); 120 | } 121 | 122 | } -------------------------------------------------------------------------------- /src/CloudinaryServiceProvider.php: -------------------------------------------------------------------------------- 1 | bootMacros(); 32 | $this->bootResources(); 33 | $this->bootDirectives(); 34 | $this->bootComponents(); 35 | $this->bootCommands(); 36 | $this->bootPublishing(); 37 | $this->bootCloudinaryDriver(); 38 | } 39 | 40 | /** 41 | * Boot the package macros that extends Laravel Uploaded File API. 42 | * 43 | * @return void 44 | */ 45 | protected function bootMacros() 46 | { 47 | UploadedFile::macro( 48 | 'storeOnCloudinary', 49 | function ($folder = null) { 50 | return resolve(CloudinaryEngine::class)->uploadFile($this->getRealPath(), ['folder' => $folder]); 51 | } 52 | ); 53 | 54 | UploadedFile::macro( 55 | 'storeOnCloudinaryAs', 56 | function ($folder = null, $publicId = null) { 57 | return resolve(CloudinaryEngine::class)->uploadFile( 58 | $this->getRealPath(), 59 | ['folder' => $folder, 'public_id' => $publicId] 60 | ); 61 | } 62 | ); 63 | } 64 | 65 | /** 66 | * Boot the package resources. 67 | * 68 | * @return void 69 | */ 70 | protected function bootResources() 71 | { 72 | $this->loadViewsFrom(__DIR__ . '/../resources/views', 'cloudinary'); 73 | } 74 | 75 | /** 76 | * Boot the package directives. 77 | * 78 | * @return void 79 | */ 80 | protected function bootDirectives() 81 | { 82 | Blade::directive( 83 | 'cloudinaryJS', 84 | function () { 85 | return ""; 86 | } 87 | ); 88 | } 89 | 90 | /** 91 | * Boot the package components. 92 | * 93 | * @return void 94 | */ 95 | protected function bootComponents() 96 | { 97 | Blade::component('cloudinary::components.button', 'cld-upload-button'); 98 | Blade::component('cloudinary::components.image', 'cld-image'); 99 | Blade::component('cloudinary::components.video', 'cld-video'); 100 | } 101 | 102 | protected function bootCommands() 103 | { 104 | /** 105 | * Register Laravel Cloudinary Artisan commands 106 | */ 107 | if ($this->app->runningInConsole()) { 108 | $this->commands( 109 | [ 110 | BackupFilesCommand::class, 111 | UploadFileCommand::class, 112 | FetchFilesCommand::class, 113 | RenameFilesCommand::class, 114 | GenerateArchiveCommand::class, 115 | DeleteFilesCommand::class 116 | ] 117 | ); 118 | } 119 | } 120 | 121 | /** 122 | * Boot the package's publishable resources. 123 | * 124 | * @return void 125 | */ 126 | protected function bootPublishing() 127 | { 128 | if ($this->app->runningInConsole()) { 129 | $config = dirname(__DIR__) . '/config/cloudinary.php'; 130 | 131 | $this->publishes( 132 | [ 133 | $config => $this->app->configPath('cloudinary.php'), 134 | ], 135 | 'laravel-cloudinary-config' 136 | ); 137 | 138 | $this->publishes( 139 | [ 140 | __DIR__.'/../database/migrations' => $this->app->databasePath('migrations'), 141 | ], 142 | 'laravel-cloudinary-migration' 143 | ); 144 | } 145 | } 146 | 147 | protected function bootCloudinaryDriver() 148 | { 149 | $this->app['config']['filesystems.disks.cloudinary'] = ['driver' => 'cloudinary']; 150 | 151 | Storage::extend( 152 | 'cloudinary', 153 | function ($app, $config) { 154 | return new Filesystem(new CloudinaryAdapter(config('cloudinary.cloud_url'))); 155 | } 156 | ); 157 | } 158 | 159 | /** 160 | * Register any package services. 161 | * 162 | * @return void 163 | */ 164 | public function register() 165 | { 166 | // Register the service the package provides. 167 | $this->app->singleton( 168 | CloudinaryEngine::class, 169 | function ($app) { 170 | return new CloudinaryEngine(); 171 | } 172 | ); 173 | } 174 | } -------------------------------------------------------------------------------- /src/CloudinaryAdapter.php: -------------------------------------------------------------------------------- 1 | cloudinary = new Cloudinary($config); 31 | } 32 | 33 | /** 34 | * Update a file. 35 | * Cloudinary has no specific update method. Overwrite instead. 36 | * 37 | * @param string $path 38 | * @param string $contents 39 | * @param Config $options Config object 40 | * 41 | * @return array|false false on failure file meta data on success 42 | */ 43 | public function update($path, $contents, Config $options) 44 | { 45 | return $this->write($path, $contents, $options); 46 | } 47 | 48 | /** 49 | * Write a new file. 50 | * Create temporary stream with content. 51 | * Pass to writeStream. 52 | * 53 | * @param string $path 54 | * @param string $contents 55 | * @param Config $options Config object 56 | * 57 | * @return array|false false on failure file meta data on success 58 | */ 59 | public function write($path, $contents, Config $options) 60 | { 61 | $tempFile = tmpfile(); 62 | 63 | fwrite($tempFile, $contents); 64 | 65 | return $this->writeStream($path, $tempFile, $options); 66 | } 67 | 68 | /** 69 | * Write a new file using a stream. 70 | * 71 | * @param string $path 72 | * @param resource $resource 73 | * @param Config $options Config object 74 | * 75 | * @return array|false false on failure file meta data on success 76 | */ 77 | public function writeStream($path, $resource, Config $options) 78 | { 79 | $publicId = $options->has('public_id') ? $options->get('public_id') : $path; 80 | 81 | $resourceType = $options->has('resource_type') ? $options->get('resource_type') : 'auto'; 82 | 83 | $fileExtension = pathinfo($publicId, PATHINFO_EXTENSION); 84 | 85 | $newPublicId = $fileExtension ? substr($publicId, 0, - (strlen($fileExtension) + 1)) : $publicId; 86 | 87 | $uploadOptions = [ 88 | 'public_id' => $newPublicId, 89 | 'resource_type' => $resourceType 90 | ]; 91 | 92 | $resourceMetadata = stream_get_meta_data($resource); 93 | 94 | $result = resolve(CloudinaryEngine::class)->upload($resourceMetadata['uri'], $uploadOptions); 95 | 96 | return $result; 97 | } 98 | 99 | /** 100 | * Update a file using a stream. 101 | * Cloudinary has no specific update method. Overwrite instead. 102 | * 103 | * @param string $path 104 | * @param resource $resource 105 | * @param Config $options Config object 106 | * 107 | * @return array|false false on failure file meta data on success 108 | */ 109 | public function updateStream($path, $resource, Config $options) 110 | { 111 | return $this->writeStream($path, $resource, $options); 112 | } 113 | 114 | 115 | /** 116 | * Rename a file. 117 | * Paths without extensions. 118 | * 119 | * @param string $path 120 | * @param string $newpath 121 | * 122 | * @return bool 123 | */ 124 | public function rename($path, $newpath) 125 | { 126 | $pathInfo = pathinfo($path); 127 | $newPathInfo = pathinfo($newpath); 128 | 129 | $remotePath = ($pathInfo['dirname'] != '.') ? pathInfo['dirname'] . '/' . $pathInfo['filename'] : $pathInfo['filename']; 130 | 131 | $remoteNewPath = ($pathInfo['dirname'] != '.') ? $newPathInfo['dirname'] . '/' . $newPathInfo['filename'] : $newPathInfo['filename']; 132 | 133 | $result = $this->uploadApi()->rename($remotePath, $remoteNewPath); 134 | 135 | return $result['public_id'] == $newPathInfo['filename']; 136 | } 137 | 138 | /** 139 | * Expose the Cloudinary v2 Upload Functionality 140 | * 141 | */ 142 | protected function uploadApi() 143 | { 144 | return $this->cloudinary->uploadApi(); 145 | } 146 | 147 | /** 148 | * Copy a file. 149 | * Copy content from existing url. 150 | * 151 | * @param string $path 152 | * @param string $newpath 153 | * 154 | * @return bool 155 | */ 156 | public function copy($path, $newpath) 157 | { 158 | $result = $this->uploadApi()->upload($path, ['public_id' => $newpath]); 159 | 160 | return is_array($result) ? $result['public_id'] == $newpath : false; 161 | } 162 | 163 | /** 164 | * Delete a file. 165 | * 166 | * @param string $path 167 | * 168 | * @return bool 169 | */ 170 | public function delete($path) 171 | { 172 | $result = $this->uploadApi()->destroy($path); 173 | 174 | return is_array($result) ? $result['result'] == 'ok' : false; 175 | } 176 | 177 | /** 178 | * Delete a directory. 179 | * Delete Files using directory as a prefix. 180 | * 181 | * @param string $dirname 182 | * 183 | * @return bool 184 | * 185 | * @throws ApiError 186 | */ 187 | public function deleteDir($dirname) 188 | { 189 | $this->adminApi()->deleteResourcesByPrefix($dirname); 190 | 191 | return true; 192 | } 193 | 194 | /** 195 | * Expose the Cloudinary v2 Upload Functionality 196 | * 197 | */ 198 | protected function adminApi() 199 | { 200 | return $this->cloudinary->adminApi(); 201 | } 202 | 203 | /** 204 | * Create a directory. 205 | * 206 | * @param string $dirname directory name 207 | * @param Config $options 208 | * 209 | * @return bool 210 | * 211 | * @throws ApiError 212 | */ 213 | public function createDir($dirname, Config $options) 214 | { 215 | $this->adminApi()->createFolder($dirname); 216 | 217 | return true; 218 | } 219 | 220 | /** 221 | * Check whether a file exists. 222 | * 223 | * @param string $path 224 | * 225 | * @return array|bool|null 226 | */ 227 | public function has($path) 228 | { 229 | return file_exists($path); 230 | } 231 | 232 | /** 233 | * Read a file. 234 | * 235 | * @param string $path 236 | * 237 | * @return array|false 238 | */ 239 | public function read($path) 240 | { 241 | $resource = (array)$this->adminApi()->resource($path); 242 | $contents = file_get_contents($resource['secure_url']); 243 | 244 | return compact('contents', 'path'); 245 | } 246 | 247 | /** 248 | * Read a file as a stream. 249 | * 250 | * @param string $path 251 | * 252 | * @return array|false 253 | */ 254 | public function readStream($path) 255 | { 256 | $resource = (array)$this->adminApi()->resource($path); 257 | 258 | $stream = fopen($resource['secure_url'], 'rb'); 259 | 260 | return compact('stream', 'path'); 261 | } 262 | 263 | /** 264 | * List contents of a directory. 265 | * 266 | * @param string $directory 267 | * @param bool $hasR ecursive 268 | * 269 | * @return array 270 | */ 271 | public function listContents($directory = '', $hasRecursive = false) 272 | { 273 | $resources = []; 274 | 275 | // get resources array 276 | $response = null; 277 | do { 278 | $response = (array)$this->adminApi()->resources( 279 | [ 280 | 'type' => 'upload', 281 | 'prefix' => $directory, 282 | 'max_results' => 500, 283 | 'next_cursor' => isset($response['next_cursor']) ? $response['next_cursor'] : null, 284 | ] 285 | ); 286 | $resources = array_merge($resources, $response['resources']); 287 | } while (array_key_exists('next_cursor', $response)); 288 | 289 | // parse resourses 290 | foreach ($resources as $i => $resource) { 291 | $resources[$i] = $this->prepareResourceMetadata($resource); 292 | } 293 | return $resources; 294 | } 295 | 296 | /** 297 | * Prepare apropriate metadata for resource metadata given from cloudinary. 298 | * @param array $resource 299 | * @return array 300 | */ 301 | protected function prepareResourceMetadata($resource) 302 | { 303 | $resource['type'] = 'file'; 304 | $resource['path'] = $resource['public_id']; 305 | $resource = array_merge($resource, $this->prepareSize($resource)); 306 | $resource = array_merge($resource, $this->prepareTimestamp($resource)); 307 | $resource = array_merge($resource, $this->prepareMimetype($resource)); 308 | return $resource; 309 | } 310 | 311 | /** 312 | * prepare size response 313 | * 314 | * @param array $resource 315 | * 316 | * @return array 317 | */ 318 | protected function prepareSize($resource) 319 | { 320 | $size = $resource['bytes']; 321 | return compact('size'); 322 | } 323 | 324 | /** 325 | * prepare timestamp response 326 | * 327 | * @param array $resource 328 | * 329 | * @return array 330 | */ 331 | protected function prepareTimestamp($resource) 332 | { 333 | $timestamp = strtotime($resource['created_at']); 334 | return compact('timestamp'); 335 | } 336 | 337 | /** 338 | * prepare mimetype response 339 | * 340 | * @param array $resource 341 | * 342 | * @return array 343 | */ 344 | protected function prepareMimetype($resource) 345 | { 346 | $mimetype = $resource['resource_type']; 347 | return compact('mimetype'); 348 | } 349 | 350 | /** 351 | * Get all the meta data of a file or directory. 352 | * 353 | * @param string $path 354 | * 355 | * @return array|false 356 | */ 357 | public function getMetadata($path) 358 | { 359 | return $this->prepareResourceMetadata($this->getResource($path)); 360 | } 361 | 362 | /** 363 | * Get Resource data 364 | * @param string $path 365 | * @return array 366 | */ 367 | public function getResource($path) 368 | { 369 | return (array)$this->adminApi()->resource($path); 370 | } 371 | 372 | /** 373 | * Get all the meta data of a file or directory. 374 | * 375 | * @param string $path 376 | * 377 | * @return array|false 378 | */ 379 | public function getSize($path) 380 | { 381 | return $this->prepareSize($this->getResource($path)); 382 | } 383 | 384 | /** 385 | * Get the mimetype of a file. 386 | * 387 | * @param string $path 388 | * 389 | * @return array|false 390 | */ 391 | public function getMimetype($path) 392 | { 393 | return $this->prepareMimetype($this->getResource($path)); 394 | } 395 | 396 | /** 397 | * Get the timestamp of a file. 398 | * 399 | * @param string $path 400 | * 401 | * @return array|false 402 | */ 403 | public function getTimestamp($path) 404 | { 405 | return $this->prepareTimestamp($this->getResource($path)); 406 | } 407 | } 408 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

Laravel Cloudinary (DEPRECATED)

3 |
4 | 5 |
6 |

Please use the comprehensive => Cloudinary Laravel Package Now!

7 |
8 | 9 |

10 | 11 | Total Downloads 12 | 13 | 14 | Latest Stable Version 15 | 16 | 17 | License 18 | 19 |

20 | 21 | > A Laravel Package for uploading, optimizing, transforming and delivering media files with Cloudinary. Furthermore, it provides a fluent and expressive API to easily attach your media files to Eloquent models. 22 | 23 | 24 | ## Usage 25 | 26 | **Upload** a file (_Image_, _Video_ or any type of _File_) to **Cloudinary**: 27 | 28 | ```php 29 | 30 | /** 31 | * Using the Cloudinary Facade 32 | */ 33 | 34 | // Upload an Image File to Cloudinary with One line of Code 35 | $uploadedFileUrl = Cloudinary::upload($request->file('file')->getRealPath())->getSecurePath(); 36 | 37 | // Upload a Video File to Cloudinary with One line of Code 38 | $uploadedFileUrl = Cloudinary::uploadVideo($request->file('file')->getRealPath())->getSecurePath(); 39 | 40 | // Upload any File to Cloudinary with One line of Code 41 | $uploadedFileUrl = Cloudinary::uploadFile($request->file('file')->getRealPath())->getSecurePath(); 42 | 43 | /** 44 | * This package also exposes a helper function you can use if you are not a fan of Facades 45 | * Shorter, expressive, fluent using the 46 | * cloudinary() function 47 | */ 48 | 49 | // Upload an Image File to Cloudinary with One line of Code 50 | $uploadedFileUrl = cloudinary()->upload($request->file('file')->getRealPath())->getSecurePath(); 51 | 52 | // Upload a Video File to Cloudinary with One line of Code 53 | $uploadedFileUrl = cloudinary()->uploadVideo($request->file('file')->getRealPath())->getSecurePath(); 54 | 55 | // Upload any File to Cloudinary with One line of Code 56 | $uploadedFileUrl = cloudinary()->uploadFile($request->file('file')->getRealPath())->getSecurePath(); 57 | 58 | 59 | 60 | /** 61 | * You can also skip the Cloudinary Facade or helper method and laravel-ize your uploads by simply calling the 62 | * storeOnCloudinary() method on the file itself 63 | */ 64 | 65 | // Store the uploaded file in the "lambogini" directory on Cloudinary 66 | $result = $request->file('image')->store('lambogini', 'cloudinary'); 67 | 68 | // Store the uploaded file on Cloudinary 69 | $result = $request->file('file')->storeOnCloudinary(); 70 | 71 | // Store the uploaded file on Cloudinary 72 | $result = $request->file->storeOnCloudinary(); 73 | 74 | // Store the uploaded file in the "lambogini" directory on Cloudinary 75 | $result = $request->file->storeOnCloudinary('lambogini'); 76 | 77 | // Store the uploaded file in the "lambogini" directory on Cloudinary with the filename "prosper" 78 | $result = $request->file->storeOnCloudinaryAs('lambogini', 'prosper'); 79 | 80 | 81 | $result->getPath(); // Get the url of the uploaded file; http 82 | $result->getSecurePath(); // Get the url of the uploaded file; https 83 | $result->getSize(); // Get the size of the uploaded file in bytes 84 | $result->getReadableSize(); // Get the size of the uploaded file in bytes, megabytes, gigabytes or terabytes. E.g 1.8 MB 85 | $result->getFileType(); // Get the type of the uploaded file 86 | $result->getFileName(); // Get the file name of the uploaded file 87 | $result->getOriginalFileName(); // Get the file name of the file before it was uploaded to Cloudinary 88 | $result->getPublicId(); // Get the public_id of the uploaded file 89 | $result->getExtension(); // Get the extension of the uploaded file 90 | $result->getWidth(); // Get the width of the uploaded file 91 | $result->getHeight(); // Get the height of the uploaded file 92 | $result->getTimeUploaded(); // Get the time the file was uploaded 93 | ``` 94 | 95 | **Attach Files to Laravel Eloquent Models**: 96 | 97 | First, import the `Unicodeveloper\Cloudinary\MediaAlly` trait into your Model like so: 98 | 99 | ```php 100 | request->input()); 130 | $page->attachMedia($file); // Example of $file is $request->file('file'); 131 | 132 | /** 133 | * How to attach an existing remote file to a Model by model creation 134 | */ 135 | $page = Page::create($this->request->input()); 136 | $page->attachRemoteMedia($remoteFileUrl); // Example of $remoteFileUrl is https://miro.medium.com/max/4096/1*V1TmCz1GeAQ4T7EWRTWebA.jpeg 137 | 138 | /** 139 | * How to attach a file to a Model by retreiving model records 140 | */ 141 | $page = Page::find(2); 142 | $page->attachMedia($file); // Example of $file is $request->file('file'); 143 | 144 | /** 145 | * How to attach a remote file to a Model by retreiving model records 146 | */ 147 | $page = Page::find(2); 148 | $page->attachRemoteMedia($remoteFileUrl); // Example of $remoteFileUrl is https://miro.medium.com/max/4096/1*V1TmCz1GeAQ4T7EWRTWebA.jpeg 149 | 150 | /** 151 | * How to retrieve files that were attached to a Model 152 | */ 153 | $filesBelongingToSecondPage = Page::find(2)->fetchAllMedia(); 154 | 155 | /** 156 | * How to retrieve the first file that was attached to a Model 157 | */ 158 | $fileBelongingToSecondPage = Page::find(2)->fetchFirstMedia(); 159 | 160 | /** 161 | * How to retrieve the last file that was attached to a Model 162 | */ 163 | $fileBelongingToSecondPage = Page::find(2)->fetchLastMedia(); 164 | 165 | /** 166 | * How to replace/update files attached to a Model 167 | */ 168 | $page = Page::find(2); 169 | $page->updateMedia($file); // Example of $file is $request->file('file'); 170 | 171 | /** 172 | * How to detach a file from a Model 173 | */ 174 | $page = Page::find(2); 175 | $page->detachMedia($file) // Example of $file is $request->file('file'); 176 | ``` 177 | 178 | **Upload Files Via An Upload Widget**: 179 | 180 | Use the `x-cld-upload-button` Blade upload button component that ships with this Package like so: 181 | ``` 182 | 183 | 184 | 185 | ... 186 | @cloudinaryJS 187 | 188 | 189 | 190 | Upload Files 191 | 192 | 193 | 194 | ```` 195 | 196 | Other Blade components you can use are: 197 | 198 | ```php 199 | // Blade Image Component for displaying images 200 | 201 | // Blade Video Component for displaying videos 202 | ``` 203 | 204 | **Media Management via The Command Line**: 205 | 206 | ```bash 207 | /** 208 | * Back up Files on Cloudinary 209 | */ 210 | php artisan cloudinary:backup 211 | 212 | /** 213 | * Delete a File on Cloudinary 214 | */ 215 | php artisan cloudinary:delete 216 | 217 | /** 218 | * Fetch a File from Cloudinary 219 | */ 220 | php artisan cloudinary:fetch 221 | 222 | /** 223 | * Rename a File from Cloudinary 224 | */ 225 | php artisan cloudinary:rename 226 | 227 | /** 228 | * Upload a File to Cloudinary 229 | */ 230 | php artisan cloudinary:upload 231 | 232 | /** 233 | * Generate an archive of a group of files and get the zipped downloadable url 234 | */ 235 | php artisan cloudinary:archive 236 | ``` 237 | 238 | 239 | ## Installation 240 | 241 | [PHP](https://php.net) 7.0+, and [Composer](https://getcomposer.org) are required. 242 | 243 | To get the latest version of Laravel Cloudinary, simply require it: 244 | 245 | ```bash 246 | composer require unicodeveloper/laravel-cloudinary 247 | ``` 248 | 249 | Or add the following line to the require block of your `composer.json` file. 250 | 251 | ``` 252 | "unicodeveloper/laravel-cloudinary": "1.0.0-beta" 253 | ``` 254 | 255 | You'll then need to run `composer install` or `composer update` to download it and have the autoloader updated. 256 | 257 | 258 | Once Laravel Cloudinary is installed, you need to register the service provider. Open up `config/app.php` and add the following to the `providers` key. 259 | 260 | ```php 261 | 'providers' => [ 262 | ... 263 | Unicodeveloper\Cloudinary\CloudinaryServiceProvider::class, 264 | ... 265 | ] 266 | ``` 267 | 268 | > Note: If you use **Laravel >= 5.5** you can skip this step (adding the code above to the providers key) and go to [**`configuration`**](https://github.com/unicodeveloper/laravel-cloudinary#configuration) 269 | 270 | Also, register the Cloudinary Facade like so: 271 | 272 | ```php 273 | 'aliases' => [ 274 | ... 275 | 'Cloudinary' => Unicodeveloper\Cloudinary\Facades\Cloudinary::class, 276 | ... 277 | ] 278 | ``` 279 | 280 | ## Configuration 281 | 282 | You can publish the configuration file using this command: 283 | 284 | ```bash 285 | php artisan vendor:publish --provider="Unicodeveloper\Cloudinary\CloudinaryServiceProvider" --tag="laravel-cloudinary-config" 286 | ``` 287 | 288 | A configuration-file named `cloudinary.php` with some sensible defaults will be placed in your `config` directory: 289 | 290 | ```php 291 | env('CLOUDINARY_NOTIFICATION_URL'), 304 | 305 | 306 | /* 307 | |-------------------------------------------------------------------------- 308 | | Cloudinary Configuration 309 | |-------------------------------------------------------------------------- 310 | | 311 | | Here you may configure your Cloudinary settings. Cloudinary is a cloud hosted 312 | | media management service for all file uploads, storage, delivery and transformation needs. 313 | | 314 | | 315 | */ 316 | 'cloud_url' => env('CLOUDINARY_URL'), 317 | 318 | /** 319 | * Upload Preset From Cloudinary Dashboard 320 | * 321 | */ 322 | 'upload_preset' => env('CLOUDINARY_UPLOAD_PRESET') 323 | ]; 324 | ``` 325 | 326 | ### API Keys 327 | Open your `.env` file and add your API Environment variable, upload_preset (this is optional, until you need to use the widget) like so: 328 | 329 | ```php 330 | CLOUDINARY_URL=xxxxxxxxxxxxx 331 | CLOUDINARY_UPLOAD_PRESET=xxxxxxxxxxxxx 332 | CLOUDINARY_NOTIFICATION_URL= 333 | ``` 334 | 335 | ***Note:** You need to get these credentials from your [Cloudinary Dashboard](https://cloudinary.com/console)* 336 | 337 | *If you are using a hosting service like heroku,forge,digital ocean, etc, please ensure to add the above details to your configuration variables.* 338 | 339 | ### Cloudinary JS 340 | 341 | Cloudinary relies on its own JavaScript library to initiate the Cloudinary Upload Widget. You can load the JavaScript library by placing the @cloudinaryJS directive right before your application layout's closing tag: 342 | 343 | ```html 344 | 345 | ... 346 | 347 | @cloudinaryJS 348 | 349 | ``` 350 | 351 | ***Note:** ONLY LOAD THIS IF YOU HAVE DECIDED TO USE THE UPLOAD WIDGET. IF YOU ARE USING THIS PACKAGE FOR A LARAVEL API BACKEND, YOU DON'T NEED TO DO THIS!* 352 | 353 | ## License 354 | 355 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information. 356 | -------------------------------------------------------------------------------- /src/CloudinaryEngine.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Unicodeveloper\Cloudinary; 13 | 14 | use Cloudinary\Api\ApiResponse; 15 | use Cloudinary\Api\Exception\ApiError; 16 | use Cloudinary\Cloudinary; 17 | use Cloudinary\Tag\ImageTag; 18 | use Cloudinary\Tag\VideoTag; 19 | use GuzzleHttp\Promise\PromiseInterface; 20 | use Unicodeveloper\Cloudinary\Exceptions\IsNullException; 21 | 22 | /** 23 | * Class CloudinaryEngine 24 | * @package Unicodeveloper\Cloudinary 25 | */ 26 | class CloudinaryEngine 27 | { 28 | 29 | /** 30 | * 31 | */ 32 | public const ASSET_ID = 'asset_id'; 33 | public const PUBLIC_ID = 'public_id'; 34 | public const VERSION = 'version'; 35 | public const VERSION_ID = 'version_id'; 36 | public const SIGNATURE = 'signature'; 37 | public const WIDTH = 'width'; 38 | public const HEIGHT = 'height'; 39 | public const FORMAT = 'format'; 40 | public const RESOURCE_TYPE = 'resource_type'; 41 | public const CREATED_AT = 'created_at'; 42 | public const TAGS = 'tags'; 43 | public const PAGES = 'pages'; 44 | public const BYTES = 'bytes'; 45 | public const TYPE = 'type'; 46 | public const ETAG = 'etag'; 47 | public const PLACEHOLDER = 'placeholder'; 48 | public const URL = 'url'; 49 | public const SECURE_URL = 'secure_url'; 50 | public const PHASH = 'phash'; 51 | public const ORIGINAL_FILENAME = 'original_filename'; 52 | 53 | /** 54 | * Instance of Cloudinary 55 | * @var Cloudinary 56 | */ 57 | protected $cloudinary; 58 | 59 | /** 60 | * Instance of Cloudinary Config 61 | * @var Configuration 62 | */ 63 | protected $cloudinaryConfig; 64 | 65 | /** 66 | * Response from Cloudinary 67 | * @var Array 68 | */ 69 | protected $response; 70 | 71 | public function __construct() 72 | { 73 | $this->setCloudinaryConfig(); 74 | $this->bootCloudinary(); 75 | } 76 | 77 | /** 78 | * Create a Cloudinary Config Instance 79 | * 80 | */ 81 | public function setCloudinaryConfig() 82 | { 83 | $config = config('cloudinary.cloud_url'); 84 | $this->cloudinaryConfig = $config; 85 | } 86 | 87 | /** 88 | * Create a Cloudinary Instance 89 | * 90 | */ 91 | public function bootCloudinary() 92 | { 93 | $this->cloudinary = new Cloudinary($this->cloudinaryConfig); 94 | } 95 | 96 | /** 97 | * Expose the Cloudinary Admin Functionality 98 | * 99 | */ 100 | public function admin() 101 | { 102 | return $this->cloudinary->adminApi(); 103 | } 104 | 105 | /** 106 | * Expose the Cloudinary Search Functionality 107 | * 108 | */ 109 | public function search() 110 | { 111 | return $this->cloudinary->searchApi(); 112 | } 113 | 114 | /** 115 | * Uploads an asset to a Cloudinary account. 116 | * 117 | * The asset can be: 118 | * * a local file path 119 | * * the actual data (byte array buffer) 120 | * * the Data URI (Base64 encoded), max ~60 MB (62,910,000 chars) 121 | * * the remote FTP, HTTP or HTTPS URL address of an existing file 122 | * * a private storage bucket (S3 or Google Storage) URL of a whitelisted bucket 123 | * 124 | * @param string $file The asset to upload. 125 | * @param array $options The optional parameters. See the upload API documentation. 126 | * 127 | * @return ApiResponse 128 | * 129 | * @throws ApiError 130 | * 131 | * @see https://cloudinary.com/documentation/image_upload_api_reference#upload_method 132 | */ 133 | public function upload($file, $options = []) 134 | { 135 | $this->response = $this->uploadApi()->upload($file, $options); 136 | 137 | return $this; 138 | } 139 | 140 | /** 141 | * Expose the Cloudinary Upload Functionality 142 | * 143 | */ 144 | public function uploadApi() 145 | { 146 | return $this->cloudinary->uploadApi(); 147 | } 148 | 149 | /** 150 | * Uploads an asset to a Cloudinary account. 151 | * 152 | * The asset can be: 153 | * * a local file path 154 | * * the actual data (byte array buffer) 155 | * * the Data URI (Base64 encoded), max ~60 MB (62,910,000 chars) 156 | * * the remote FTP, HTTP or HTTPS URL address of an existing file 157 | * * a private storage bucket (S3 or Google Storage) URL of a whitelisted bucket 158 | * 159 | * This is asynchronous 160 | */ 161 | public function uploadAsync($file, $options = []) 162 | { 163 | return $this->uploadApi()->uploadAsync($file, $options); 164 | } 165 | 166 | /** 167 | * Uploads an asset to a Cloudinary account. 168 | * 169 | * The upload is not signed so an upload preset is required. 170 | * 171 | * @param string $file The asset to upload. 172 | * @param string $uploadPreset The name of an upload preset. 173 | * @param array $options The optional parameters. See the upload API documentation. 174 | * 175 | * @return ApiResponse 176 | * 177 | * @throws ApiError 178 | * 179 | * @see https://cloudinary.com/documentation/image_upload_api_reference#unsigned_upload_syntax 180 | */ 181 | public function unsignedUpload($file, $uploadPreset, $options = []) 182 | { 183 | $this->response = $this->uploadApi()->unsignedUpload($file, $uploadPreset, $options); 184 | 185 | return $this; 186 | } 187 | 188 | /** 189 | * Uploads an asset to a Cloudinary account. 190 | * 191 | * The upload is not signed so an upload preset is required. 192 | * 193 | * This is asynchronous 194 | */ 195 | public function unsignedUploadAsync($file, $uploadPreset, $options = []) 196 | { 197 | return $this->uploadApi()->unsignedUploadAsync($file, $uploadPreset, $options); 198 | } 199 | 200 | /** 201 | * @param $file 202 | * @param array $options 203 | * @return $this 204 | * @throws ApiError 205 | */ 206 | public function uploadFile($file, $options = []) 207 | { 208 | $uploadOptions = array_merge($options, ['resource_type' => 'auto']); 209 | 210 | $this->response = $this->uploadApi()->upload($file, $uploadOptions); 211 | 212 | return $this; 213 | } 214 | 215 | /** 216 | * @param $file 217 | * @param array $options 218 | * @return $this 219 | * @throws ApiError 220 | */ 221 | public function uploadVideo($file, $options = []) 222 | { 223 | $videoUploadOptions = array_merge($options, ['resource_type' => 'video']); 224 | 225 | $this->response = $this->uploadApi()->upload($file, $videoUploadOptions); 226 | 227 | return $this; 228 | } 229 | 230 | /** 231 | * @return Array 232 | */ 233 | public function getResponse() 234 | { 235 | return $this->response; 236 | } 237 | 238 | /** 239 | * @return mixed 240 | */ 241 | public function getAssetId() 242 | { 243 | return $this->response[self::ASSET_ID]; 244 | } 245 | 246 | /** 247 | * Get the name of the file after it has been uploaded to Cloudinary 248 | * @return string 249 | */ 250 | public function getFileName() 251 | { 252 | return $this->response[self::PUBLIC_ID]; 253 | } 254 | 255 | /** 256 | * Get the public id of the file (also known as the name of the file) after it has been uploaded to Cloudinary 257 | * @return string 258 | */ 259 | public function getPublicId() 260 | { 261 | return $this->response[self::PUBLIC_ID]; 262 | } 263 | 264 | /** 265 | * Get the name of the file before it was uploaded to Cloudinary 266 | * @return string 267 | */ 268 | public function getOriginalFileName() 269 | { 270 | return $this->response[self::ORIGINAL_FILENAME]; 271 | } 272 | 273 | /** 274 | * @return mixed 275 | */ 276 | public function getVersion() 277 | { 278 | return $this->response[self::VERSION]; 279 | } 280 | 281 | /** 282 | * @return mixed 283 | */ 284 | public function getVersionId() 285 | { 286 | return $this->response[self::VERSION_ID]; 287 | } 288 | 289 | /** 290 | * @return mixed 291 | */ 292 | public function getSignature() 293 | { 294 | return $this->response[self::SIGNATURE]; 295 | } 296 | 297 | /** 298 | * @return mixed 299 | */ 300 | public function getWidth() 301 | { 302 | return $this->response[self::WIDTH]; 303 | } 304 | 305 | /** 306 | * @return mixed 307 | */ 308 | public function getHeight() 309 | { 310 | return $this->response[self::HEIGHT]; 311 | } 312 | 313 | /** 314 | * @return mixed 315 | */ 316 | public function getExtension() 317 | { 318 | return $this->response[self::FORMAT]; 319 | } 320 | 321 | /** 322 | * @return mixed 323 | */ 324 | public function getFileType() 325 | { 326 | return $this->response[self::RESOURCE_TYPE]; 327 | } 328 | 329 | /** 330 | * @return mixed 331 | */ 332 | public function getTimeUploaded() 333 | { 334 | return $this->response[self::CREATED_AT]; 335 | } 336 | 337 | /** 338 | * @return mixed 339 | */ 340 | public function getTags() 341 | { 342 | return $this->response[self::TAGS]; 343 | } 344 | 345 | /** 346 | * @return mixed 347 | */ 348 | public function getPages() 349 | { 350 | return $this->response[self::PAGES]; 351 | } 352 | 353 | /** 354 | * @return string 355 | */ 356 | public function getReadableSize() 357 | { 358 | return $this->getHumanReadableSize($this->getSize()); 359 | } 360 | 361 | /** 362 | * Formats filesize in the way every human understands 363 | * 364 | * @param file $file 365 | * @return string Formatted Filesize, e.g. "113.24 MB". 366 | */ 367 | private function getHumanReadableSize($sizeInBytes) 368 | { 369 | if ($sizeInBytes >= 1073741824) { 370 | return number_format($bytes / 1073741824, 2) . ' GB'; 371 | } elseif ($sizeInBytes >= 1048576) { 372 | return number_format($bytes / 1048576, 2) . ' MB'; 373 | } elseif ($sizeInBytes >= 1024) { 374 | return number_format($bytes / 1024, 2) . ' KB'; 375 | } elseif ($sizeInBytes > 1) { 376 | return $sizeInBytes . ' bytes'; 377 | } elseif ($sizeInBytes == 1) { 378 | return '1 byte'; 379 | } else { 380 | return '0 bytes'; 381 | } 382 | } 383 | 384 | /** 385 | * @return mixed 386 | */ 387 | public function getSize() 388 | { 389 | return $this->response[self::BYTES]; 390 | } 391 | 392 | /** 393 | * @return mixed 394 | */ 395 | public function getPlaceHolder() 396 | { 397 | return $this->response[self::PLACEHOLDER]; 398 | } 399 | 400 | /** 401 | * @return mixed 402 | */ 403 | public function getPath() 404 | { 405 | return $this->response[self::URL]; 406 | } 407 | 408 | /** 409 | * @return mixed 410 | */ 411 | public function getSecurePath() 412 | { 413 | return $this->response[self::SECURE_URL]; 414 | } 415 | 416 | /** 417 | * @return mixed 418 | */ 419 | public function getPhash() 420 | { 421 | return $this->response[self::PHASH]; 422 | } 423 | 424 | /** 425 | * Fetches a new Image with current instance configuration. 426 | * 427 | * @param string $publicId The public ID of the image. 428 | * 429 | * @return Image 430 | */ 431 | public function getImage($publicId) 432 | { 433 | return $this->cloudinary->image($publicId); 434 | } 435 | 436 | /** 437 | * Fetches a new Video with current instance configuration. 438 | * 439 | * @param string|mixed $publicId The public ID of the video. 440 | * 441 | * @return Video 442 | */ 443 | public function getVideo($publicId) 444 | { 445 | return $this->cloudinary->video($publicId); 446 | } 447 | 448 | /** 449 | * Fetches a raw file with current instance configuration. 450 | * 451 | * @param string|mixed $publicId The public ID of the file. 452 | * 453 | * @return File 454 | */ 455 | public function getFile($publicId) 456 | { 457 | return $this->cloudinary->raw($publicId); 458 | } 459 | 460 | /** 461 | * @param $publicId 462 | * @return ImageTag 463 | */ 464 | public function getImageTag($publicId) 465 | { 466 | return $this->cloudinary->imageTag($publicId); 467 | } 468 | 469 | /** 470 | * @param $publicId 471 | * @return VideoTag 472 | */ 473 | public function getVideoTag($publicId) 474 | { 475 | return $this->cloudinary->videoTag($publicId); 476 | } 477 | 478 | /* 479 | |-------------------------------------------------------------------------- 480 | | Cloudinary Tags 481 | |-------------------------------------------------------------------------- 482 | */ 483 | 484 | /** 485 | * Adds a tag to the assets specified. 486 | * 487 | * @param string $tag The name of the tag to add. 488 | * @param array $publicIds The public IDs of the assets to add the tag to. 489 | * @param array $options The optional parameters. See the upload API documentation. 490 | * 491 | * @return ApiResponse 492 | * 493 | * @see https://cloudinary.com/documentation/image_upload_api_reference#tags_method 494 | */ 495 | public function addTag($tag, $publicIds = [], $options = []) 496 | { 497 | return $this->uploadApi()->addTag($tag, $publicIds, $options); 498 | } 499 | 500 | /** 501 | * Adds a tag to the assets specified. 502 | * 503 | * This is an asynchronous function. 504 | */ 505 | public function addTagAsync($tag, $publicIds = [], $options = []) 506 | { 507 | return $this->uploadApi()->addTagAsync($tag, $publicIds, $options); 508 | } 509 | 510 | /** 511 | * Removes a tag from the assets specified. 512 | * 513 | * @param string $tag The name of the tag to remove. 514 | * @param array|string $publicIds The public IDs of the assets to remove the tags from. 515 | * @param array $options The optional parameters. See the upload API documentation. 516 | * 517 | * @return ApiResponse 518 | * 519 | * @see https://cloudinary.com/documentation/image_upload_api_reference#tags_method 520 | */ 521 | public function removeTag($tag, $publicIds = [], $options = []) 522 | { 523 | return $this->uploadApi()->removeTag($tag, $publicIds, $options); 524 | } 525 | 526 | /** 527 | * Removes a tag from the assets specified. 528 | * 529 | * This is an asynchronous function. 530 | * 531 | */ 532 | public function removeTagAsync($tag, $publicIds = [], $options = []) 533 | { 534 | return $this->uploadApi()->removeTagAsync($tag, $publicIds, $options); 535 | } 536 | 537 | /** 538 | * Removes all tags from the assets specified. 539 | * 540 | * @param array $publicIds The public IDs of the assets to remove all tags from. 541 | * @param array $options The optional parameters. See the upload API documentation. 542 | * 543 | * @return ApiResponse 544 | * 545 | * @see https://cloudinary.com/documentation/image_upload_api_reference#tags_method 546 | */ 547 | public function removeAllTags($publicIds = [], $options = []) 548 | { 549 | return $this->uploadApi()->removeAllTags($publicIds, $options); 550 | } 551 | 552 | /** 553 | * Removes all tags from the assets specified. 554 | * 555 | * This is an asynchronous function. 556 | * 557 | */ 558 | public function removeAllTagsAsync($publicIds = [], $options = []) 559 | { 560 | return $this->uploadApi()->removeAllTagsAsync($publicIds, $options); 561 | } 562 | 563 | /** 564 | * Replaces all existing tags on the assets specified with the tag specified. 565 | * 566 | * @param string $tag The new tag with which to replace the existing tags. 567 | * @param array|string $publicIds The public IDs of the assets to replace the tags of. 568 | * @param array $options The optional parameters. See the upload API documentation. 569 | * 570 | * @return ApiResponse 571 | * 572 | * @see https://cloudinary.com/documentation/image_upload_api_reference#tags_method 573 | */ 574 | public function replaceTag($tag, $publicIds = [], $options = []) 575 | { 576 | return $this->uploadApi()->replaceTag($tag, $publicIds, $options); 577 | } 578 | 579 | /** 580 | * Replaces all existing tags on the assets specified with the tag specified. 581 | * 582 | * This is an asynchronous function. 583 | * 584 | */ 585 | public function replaceTagAsync($tag, $publicIds = [], $options = []) 586 | { 587 | return $this->uploadApi()->replaceTagAsync($tag, $publicIds, $options); 588 | } 589 | 590 | /* 591 | |-------------------------------------------------------------------------- 592 | | Cloudinary Sprite & Image Generation 593 | |-------------------------------------------------------------------------- 594 | */ 595 | 596 | /** 597 | * Creates a sprite from all images that have been assigned a specified tag. 598 | * 599 | * The process produces two files: 600 | * * A single image file containing all the images with the specified tag (PNG by default). 601 | * * A CSS file that includes the style class names and the location of the individual images in the sprite. 602 | * 603 | * @param string $tag The tag that indicates which images to include in the sprite. 604 | * @param array $options The optional parameters. See the upload API documentation. 605 | * 606 | * @return ApiResponse 607 | * 608 | * @see https://cloudinary.com/documentation/image_upload_api_reference#sprite_method 609 | */ 610 | public function generateSprite($tag, $options = []) 611 | { 612 | return $this->uploadApi()->generateSprite($tag, $options); 613 | } 614 | 615 | /** 616 | * Creates a sprite from all images that have been assigned a specified tag. 617 | * 618 | * This is an asynchronous function. 619 | */ 620 | public function generateSpriteAsync($tag, $options = []) 621 | { 622 | return $this->uploadApi()->generateSpriteAsync($tag, $options); 623 | } 624 | 625 | /** 626 | * Creates a PDF file from images in your media library that have been assigned a specific tag. 627 | * 628 | * Important note for free accounts: 629 | * By default, while you can use this method to generate PDF files, free Cloudinary accounts are blocked from delivering 630 | * files in PDF format for security reasons. 631 | *For details or to request that this limitation be removed for your free account, see Media delivery. 632 | * 633 | * @see https://cloudinary.com/documentation/paged_and_layered_media#creating_pdf_files_from_images 634 | */ 635 | public function generatePDF($tag, $options = []) 636 | { 637 | $pdfOptions = array_merge($options, ['async' => false, 'format' => 'pdf']); 638 | 639 | return $this->uploadApi()->multi($tag, $pdfOptions); 640 | } 641 | 642 | /** 643 | * @param $tag 644 | * @param array $options 645 | * @return ApiResponse 646 | */ 647 | public function generatePDFAsync($tag, $options = []) 648 | { 649 | $pdfOptions = array_merge($options, ['async' => true, 'format' => 'pdf']); 650 | 651 | return $this->uploadApi()->multi($tag, $pdfOptions); 652 | } 653 | 654 | /** 655 | * @param $tag 656 | * @param array $options 657 | * @return ApiResponse 658 | */ 659 | public function generateAnimatedGIF($tag, $options = []) 660 | { 661 | $gifOptions = array_merge($options, ['async' => false, 'format' => 'gif']); 662 | 663 | return $this->uploadApi()->multi($tag, $gifOptions); 664 | } 665 | 666 | /** 667 | * @param $tag 668 | * @param array $options 669 | * @return ApiResponse 670 | */ 671 | public function generateAnimatedPNG($tag, $options = []) 672 | { 673 | $pngOptions = array_merge($options, ['async' => false, 'format' => 'png']); 674 | 675 | return $this->uploadApi()->multi($tag, $pngOptions); 676 | } 677 | 678 | /** 679 | * @param $tag 680 | * @param array $options 681 | * @return ApiResponse 682 | */ 683 | public function generateAnimatedPNGAsync($tag, $options = []) 684 | { 685 | $pngOptions = array_merge($options, ['async' => true, 'format' => 'png']); 686 | 687 | return $this->uploadApi()->multi($tag, $pngOptions); 688 | } 689 | 690 | /** 691 | * @param $tag 692 | * @param array $options 693 | * @return ApiResponse 694 | */ 695 | public function generateAnimatedWEBP($tag, $options = []) 696 | { 697 | $webpOptions = array_merge($options, ['async' => false, 'format' => 'webp']); 698 | 699 | return $this->uploadApi()->multi($tag, $webpOptions); 700 | } 701 | 702 | /** 703 | * @param $tag 704 | * @param array $options 705 | * @return ApiResponse 706 | */ 707 | public function generateAnimatedWEBPAsync($tag, $options = []) 708 | { 709 | $webpOptions = array_merge($options, ['async' => true, 'format' => 'webp']); 710 | 711 | return $this->uploadApi()->multi($tag, $webpOptions); 712 | } 713 | 714 | /** 715 | * @param $tag 716 | * @param array $options 717 | * @return ApiResponse 718 | */ 719 | public function generateAnimatedMP4($tag, $options = []) 720 | { 721 | $mp4Options = array_merge($options, ['async' => false, 'format' => 'mp4']); 722 | 723 | return $this->uploadApi()->multi($tag, $mp4Options); 724 | } 725 | 726 | /** 727 | * @param $tag 728 | * @param array $options 729 | * @return ApiResponse 730 | */ 731 | public function generateAnimatedMP4Async($tag, $options = []) 732 | { 733 | $mp4Options = array_merge($options, ['async' => true, 'format' => 'mp4']); 734 | 735 | return $this->uploadApi()->multi($tag, $mp4Options); 736 | } 737 | 738 | /** 739 | * @param $tag 740 | * @param array $options 741 | * @return ApiResponse 742 | */ 743 | public function generateAnimatedWEBM($tag, $options = []) 744 | { 745 | $webmOptions = array_merge($options, ['async' => false, 'format' => 'webm']); 746 | 747 | return $this->uploadApi()->multi($tag, $webmOptions); 748 | } 749 | 750 | /** 751 | * @param $tag 752 | * @param array $options 753 | * @return ApiResponse 754 | */ 755 | public function generateAnimatedWEBMAsync($tag, $options = []) 756 | { 757 | $webmOptions = array_merge($options, ['async' => true, 'format' => 'webm']); 758 | 759 | return $this->uploadApi()->multi($tag, $webmOptions); 760 | } 761 | 762 | /** 763 | * @param $tag 764 | * @param array $options 765 | * @return ApiResponse 766 | */ 767 | public function multi($tag, $options = []) 768 | { 769 | return $this->uploadApi()->multi($tag, $options); 770 | } 771 | 772 | /** 773 | * @param $tag 774 | * @param array $options 775 | * @return PromiseInterface 776 | */ 777 | public function multiAsync($tag, $options = []) 778 | { 779 | return $this->uploadApi()->multiAsync($tag, $options); 780 | } 781 | 782 | /** 783 | * @param $publicId 784 | * @param array $options 785 | * @return ApiResponse 786 | */ 787 | public function explode($publicId, $options = []) 788 | { 789 | return $this->uploadApi()->explode($publicId, $options); 790 | } 791 | 792 | /** 793 | * @param $publicId 794 | * @param array $options 795 | * @return PromiseInterface 796 | */ 797 | public function explodeAsync($publicId, $options = []) 798 | { 799 | return $this->uploadApi()->explodeAsync($publicId, $options); 800 | } 801 | 802 | /** 803 | * Dynamically generates an image from a given textual string. 804 | * 805 | * @param string $text The text string to generate an image for. 806 | * @param array $options The optional parameters. See the upload API documentation. 807 | * 808 | * @return ApiResponse 809 | * 810 | * @see https://cloudinary.com/documentation/image_upload_api_reference#text_method 811 | */ 812 | public function generateImageFromText($text, $options = []) 813 | { 814 | $this->response = $this->uploadApi()->text($text, $options); 815 | 816 | return $this; 817 | } 818 | 819 | /** 820 | * @param $text 821 | * @param array $options 822 | * @return PromiseInterface 823 | */ 824 | public function generateImageFromTextAsync($text, $options = []) 825 | { 826 | return $this->uploadApi()->textAsync($text, $options); 827 | } 828 | 829 | /* 830 | |-------------------------------------------------------------------------- 831 | | Cloudinary Zip & Archives 832 | |-------------------------------------------------------------------------- 833 | */ 834 | 835 | /** 836 | * @param array $options 837 | * @param null $targetFormat 838 | * @return ApiResponse 839 | */ 840 | public function createArchive($options = [], $targetFormat = null) 841 | { 842 | return $this->uploadApi()->createArchive($options, $targetFormat); 843 | } 844 | 845 | /** 846 | * @param array $options 847 | * @param null $targetFormat 848 | * @return PromiseInterface 849 | */ 850 | public function createArchiveAsync($options = [], $targetFormat = null) 851 | { 852 | return $this->uploadApi()->createArchiveAsync($options, $targetFormat); 853 | } 854 | 855 | /** 856 | * @param array $options 857 | * @return ApiResponse 858 | */ 859 | public function createZip($options = []) 860 | { 861 | return $this->uploadApi()->createZip($options); 862 | } 863 | 864 | /** 865 | * @param array $options 866 | * @return PromiseInterface 867 | */ 868 | public function createZipAsync($options = []) 869 | { 870 | return $this->uploadApi()->createZipAsync($options); 871 | } 872 | 873 | /** 874 | * @param array $options 875 | * @return string 876 | */ 877 | public function downloadZipUrl($options = []) 878 | { 879 | return $this->uploadApi()->downloadZipUrl($options); 880 | } 881 | 882 | /** 883 | * @param array $options 884 | * @return string 885 | */ 886 | public function downloadArchiveUrl($options = []) 887 | { 888 | return $this->uploadApi()->downloadArchiveUrl($options); 889 | } 890 | 891 | /* 892 | |-------------------------------------------------------------------------- 893 | | Cloudinary Context 894 | |-------------------------------------------------------------------------- 895 | */ 896 | 897 | /** 898 | * @param $context 899 | * @param array $publicIds 900 | * @param array $options 901 | * @return ApiResponse 902 | */ 903 | public function addContext($context, $publicIds = [], $options = []) 904 | { 905 | return $this->uploadApi()->addContext($context, $publicIds, $options); 906 | } 907 | 908 | /** 909 | * @param $context 910 | * @param array $publicIds 911 | * @param array $options 912 | * @return PromiseInterface 913 | */ 914 | public function addContextAsync($context, $publicIds = [], $options = []) 915 | { 916 | return $this->uploadApi()->addContextAsync($context, $publicIds, $options); 917 | } 918 | 919 | /** 920 | * @param array $publicIds 921 | * @param array $options 922 | * @return ApiResponse 923 | */ 924 | public function removeAllContext($publicIds = [], $options = []) 925 | { 926 | return $this->uploadApi()->removeAllContext($publicIds, $options); 927 | } 928 | 929 | /** 930 | * @param array $publicIds 931 | * @param array $options 932 | * @return PromiseInterface 933 | */ 934 | public function removeAllContextAsync($publicIds = [], $options = []) 935 | { 936 | return $this->uploadApi()->removeAllContextAsync($publicIds, $options); 937 | } 938 | 939 | /* 940 | |-------------------------------------------------------------------------- 941 | | Cloudinary Delete & Rename Assets 942 | |-------------------------------------------------------------------------- 943 | */ 944 | 945 | /** 946 | * @param $publicId 947 | * @param array $options 948 | * @return ApiResponse 949 | */ 950 | public function destroy($publicId, $options = []) 951 | { 952 | return $this->uploadApi()->destroy($publicId, $options); 953 | } 954 | 955 | /** 956 | * @param $publicId 957 | * @param array $options 958 | * @return PromiseInterface 959 | */ 960 | public function destroyAsync($publicId, $options = []) 961 | { 962 | return $this->uploadApi()->destroyAsync($publicId, $options); 963 | } 964 | 965 | /** 966 | * @param $from 967 | * @param $to 968 | * @param array $options 969 | * @return mixed 970 | */ 971 | public function rename($from, $to, $options = []) 972 | { 973 | return $this->uploadApi()->rename($from, $to, $options); 974 | } 975 | 976 | /** 977 | * @param $from 978 | * @param $to 979 | * @param array $options 980 | * @return PromiseInterface 981 | */ 982 | public function renameAsync($from, $to, $options = []) 983 | { 984 | return $this->uploadApi()->renameAsync($from, $to, $options); 985 | } 986 | 987 | /** 988 | * @param $publicId 989 | * @param array $options 990 | * @return mixed 991 | */ 992 | public function explicit($publicId, $options = []) 993 | { 994 | return $this->uploadApi()->explicit($publicId, $options); 995 | } 996 | 997 | /** 998 | * @param $publicId 999 | * @param array $options 1000 | * @return PromiseInterface 1001 | */ 1002 | public function explicitAsync($publicId, $options = []) 1003 | { 1004 | return $this->uploadApi()->explicitAsync($publicId, $options); 1005 | } 1006 | } 1007 | --------------------------------------------------------------------------------