You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+19Lines changed: 19 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,25 @@
2
2
3
3
All notable changes to `laravel-cloudinary` will be documented in this file.
4
4
5
+
## 13.0.0 - 2026-04-03
6
+
7
+
- Laravel 13 support
8
+
- PHP 8.3, 8.4, and 8.5 support (PHP 8.2 dropped)
9
+
- Development tooling: Orchestra Testbench 11, Pest 4, Larastan 3.9
10
+
- Packagist `dev-main` branch alias `13.x-dev` for Composer until `v13.0.0` is tagged
11
+
-**Flysystem 3:**`listContents()` now returns `FileAttributes` / `DirectoryAttributes` so `Storage::files()`, `Storage::directories()`, and related APIs work (fixes [#64](https://github.com/codebar-ag/laravel-flysystem-cloudinary/issues/64), [#80](https://github.com/codebar-ag/laravel-flysystem-cloudinary/issues/80))
12
+
-**Flysystem 3:**`read()` / `readStream()` throw `UnableToReadFile` on failure; `readStream()` returns a `resource`; `copy()` throws `UnableToCopyFile`; `delete()` throws `UnableToDeleteFile` when destroy fails; `createDirectory()` / `deleteDirectory()` throw the corresponding Flysystem exceptions
13
+
- Apply configured folder prefix to `move()`, `createDirectory()`, `deleteDirectory()`, and fix `directoryExists()` for root-level paths
14
+
- Fix string uploads: rewind temp stream after `fwrite()` before Cloudinary `upload()`
15
+
-`createDir()` catches `ApiError` as well as `RateLimited`
16
+
- Composer scripts: `analyse` (PHPStan), `test` / `test-coverage` via Pest with `--exclude-group=integration`; `format` uses Pint
17
+
- Integration tests: `integration` group, skip when placeholder Cloudinary env is used
18
+
-**Adapter internals:** Extracted `CloudinaryPathNormalizer`, `CloudinaryDiskOptions`, `CloudinaryResponseMapper`, `CloudinaryResourceOperations`, and `CloudinaryResponseLogger`; disk options (folder, upload preset, merge options, secure URL preference) are resolved from the Laravel disk config in `FlysystemCloudinaryServiceProvider` with fallback to `config('flysystem-cloudinary.*')` when the adapter is constructed without disk options
19
+
-**API:** Added `lastUploadMetadata()`, `lastCopySucceeded()`, and `lastDeleteSucceeded()`; public `$meta`, `$copied`, and `$deleted` remain for backward compatibility
20
+
-**Adapter slimming:**`CloudinaryStringUploadSource`, `CloudinaryAdminFolderLocator`, `CloudinaryUrlBuilder`, `CloudinaryListResponseAssembler`, `CloudinaryDiskOptions::uploadOptionsFor()`, and `Concerns\InteractsWithCloudinaryMetadata` further shrink `FlysystemCloudinaryAdapter` and move branching out of the adapter file
21
+
-**Security:**`phpunit.xml` no longer ships real Cloudinary API secrets; default env is placeholder-only. Live API integration tests run only when real `CLOUDINARY_*` values are exported (see `tests/cloudinary_integration.php`)
22
+
-**Tests:** Dedicated Pest files per collaborator (`CloudinaryResourceOperations`, `CloudinaryResponseMapper`, `CloudinaryResponseLogger`, `CloudinaryUrlBuilder`, `CloudinaryListResponseAssembler`, `CloudinaryAdminFolderLocator`, `CloudinaryDiskOptions`, `FlysystemCloudinaryResponseLog`, `FlysystemCloudinaryServiceProvider`); extra integration cases for missing files, directories, and rename
-**`deleteDirectory`:** Cloudinary’s Admin API only deletes **empty** folders. The adapter first destroys **shallow-listed files** under the logical path, then calls `delete_folder`—aligned with the legacy `deleteDir()` behaviour. Listing is shallow; deeply nested trees may need extra steps depending on how assets are organised.
78
+
-**`listContents`:****Shallow** listing only; the `$deep` argument is ignored. Each Admin API `assets` call uses `max_results` => 500 **without**`next_cursor` pagination, so very large prefixes may not return a complete list.
79
+
-**`write` / `writeStream` vs `update` / `updateStream`:** Only `write` and `writeStream` set `lastUploadMetadata()` and the public `$meta` property. `update` and `updateStream` return the normalized metadata `array` from the upload (or `false` on failure) but **do not** update `lastUploadMetadata()`—it keeps the value from the last `write` / `writeStream`. Use the return value of `update` / `updateStream` when you need fresh metadata.
80
+
-**Other helpers:**`lastCopySucceeded()` and `lastDeleteSucceeded()` (and legacy public `$copied` / `$deleted`) reflect the outcome of the latest `copy` / `delete` calls on this adapter instance.
81
+
82
+
### Cloudinary folder modes
83
+
84
+
This adapter lists assets with the Admin API using **public ID `prefix`** and manages folders with **`subFolders` / `create_folder` / `delete_folder`**, which matches **legacy fixed folder mode** and typical public-ID paths. If your Cloudinary product environment uses **dynamic folder mode** only, some behaviours may differ; see [Folder modes](https://cloudinary.com/documentation/folder_modes) and the [Admin API](https://cloudinary.com/documentation/admin_api#folders).
85
+
86
+
### Continuous integration and integration tests
87
+
88
+
The test suite includes optional **integration** tests that call the live Cloudinary API. They run only when `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` are set to real values (for example via GitHub Actions secrets). The default `composer test` command excludes the `integration` group; run `vendor/bin/pest` without `--exclude-group` to include them locally.
89
+
69
90
## 🏗 File extension problem
70
91
71
92
Let's look at the following example:
@@ -143,8 +164,8 @@ use Illuminate\Support\Facades\Storage;
Default test run (Pest, **excludes**the `integration` group that calls the live Cloudinary API):
266
287
267
288
```shell
268
289
composer test
269
290
```
270
291
292
+
PHPStan with Larastan:
293
+
294
+
```shell
295
+
composer analyse
296
+
```
297
+
298
+
Pest with code coverage (also excludes `integration`):
299
+
300
+
```shell
301
+
composer test-coverage
302
+
```
303
+
304
+
Apply the project code style (Laravel Pint):
305
+
306
+
```shell
307
+
composer format
308
+
```
309
+
310
+
To run **all** tests including integration tests, use real `CLOUDINARY_CLOUD_NAME`, `CLOUDINARY_API_KEY`, and `CLOUDINARY_API_SECRET` in the environment, then:
311
+
312
+
```shell
313
+
vendor/bin/pest
314
+
```
315
+
316
+
The same credentials can be supplied as GitHub Actions secrets for CI (see [.github/workflows/run-tests.yml](.github/workflows/run-tests.yml)).
317
+
271
318
## 📝 Changelog
272
319
273
320
Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
@@ -278,7 +325,7 @@ Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.
278
325
279
326
## 🧑💻 Security Vulnerabilities
280
327
281
-
Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
328
+
Please see [.github/SECURITY.md](.github/SECURITY.md) for how to report security vulnerabilities.
0 commit comments