baks-dev/files-res
Laravel/PHP пакет для управления файловыми ресурсами: загрузка и хранение в public/upload, настройка прав доступа, асинхронная обработка через очередь Messenger (async_files_resources). Поддерживает пережатие и конвертацию изображений в WebP через отдельный CDN-сервер.
Installation:
composer require baks-dev/files-res
Ensure PHP 8.4+ and Laravel/Symfony compatibility.
Directory Setup:
mkdir -p public/upload
chown -R www-data:www-data public/upload # Adjust user/group as needed
Basic Upload Usage:
use BaksDev\FilesRes\Facades\FileResource;
$file = FileResource::upload('file.jpg', 'public/upload');
$url = asset($file->getPath());
Queue Setup (for async processing):
php artisan queue:work --queue=resources
Or use Symfony Messenger:
php bin/console messenger:consume async_files_resources
CDN Integration (recommended):
baks-dev/files-cdn separately.Leverage Facades:
use BaksDev\FilesRes\Facades\FileResource;
// Upload with validation
$file = FileResource::upload('image.jpg', 'public/upload', [
'max_size' => '10MB',
'allowed_types' => ['jpg', 'png', 'webp'],
]);
// Store metadata (e.g., MIME type, size)
$file->getMetadata();
Custom Storage Adapters:
Extend BaksDev\FilesRes\Contracts\StorageAdapter for S3, GCS, etc.:
class CustomS3Adapter implements StorageAdapter {
public function store($path, $content) { ... }
public function url($path) { ... }
}
Dispatch Jobs:
use BaksDev\FilesRes\Message\ProcessFileMessage;
$message = new ProcessFileMessage($file->getPath(), 'webp');
app('messenger')->dispatch($message);
Handle Messages:
namespace App\MessageHandler;
use BaksDev\FilesRes\Message\ProcessFileMessage;
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
#[AsMessageHandler]
class ProcessFileHandler {
public function __invoke(ProcessFileMessage $message) {
// Convert to WebP, store, etc.
}
}
Offload Static Assets:
Configure baks-dev/files-cdn to:
Laravel Proxy: Use middleware to redirect requests:
Route::get('/cdn/{path}', function ($path) {
return redirect("https://cdn.yourdomain.com/{$path}");
});
Dynamic Paths:
$tenantId = auth()->user()->tenant_id;
$file = FileResource::upload('file.jpg', "public/upload/{$tenantId}");
Policy Integration:
Extend BaksDev\FilesRes\Policies\FilePolicy for custom rules.
Laravel-Specific Adjustments:
ContainerInterface with Laravel’s Container:
$container = app();
Storage facade for fallback logic:
use Illuminate\Support\Facades\Storage;
if (!FileResource::exists($path)) {
Storage::disk('public')->exists($path);
}
Queue System:
// In a Laravel Job
public function handle() {
$message = new ProcessFileMessage(...);
app('messenger')->dispatch($message);
}
WebP Conversion:
spatie/image-optimizer as a fallback:
use Spatie\ImageOptimizer\OptimizerChainFactory;
$optimizer = OptimizerChainFactory::create();
$optimizer->optimize($filePath)->save();
Testing:
StorageAdapter and Messenger in PHPUnit:
$this->partialMock(FileResource::class, ['store'])
->shouldReceive('store')
->once();
Symfony vs. Laravel Mismatches:
MessageBus won’t work directly with Laravel’s queues. Use a wrapper:
class LaravelMessengerBridge {
public function dispatch($message) {
ProcessFileJob::dispatch($message);
}
}
new calls; use Laravel’s app() or make().CDN Dependency:
baks-dev/files-cdn for WebP conversion. Without it, you’ll need to:
Intervention\Image).browser-image-compression).Path Handling:
public/upload structure. For Laravel’s storage/app/public, adjust:
$adapter = new LocalStorageAdapter(storage_path('app/public'));
Async Processing Quirks:
serialize(). Laravel’s queues may fail with complex objects. Use ShouldQueue with serialize: false:
public $connection = 'database';
public $queue = 'resources';
public $serialize = false;
Permissions:
chown may fail on shared hosting. Use:
chmod -R 775 public/upload
Queue Issues:
php bin/console messenger:failed:list
php bin/console messenger:failed:remove <id>
php artisan queue:failed
File Not Found:
StorageAdapter paths:
FileResource::adapter()->getRootPath(); // Debug root
WebP Conversion Failures:
imagemagick or gd is installed:
sudo apt-get install imagemagick
convert input.jpg output.webp
Custom Validators:
Extend BaksDev\FilesRes\Validator\FileValidator:
class CustomValidator extends FileValidator {
protected function rules() {
return array_merge(parent::rules(), [
'custom_rule' => 'required|custom_validation',
]);
}
}
Event Listeners: Listen for file upload events:
use BaksDev\FilesRes\Events\FileUploaded;
FileUploaded::listen(function ($event) {
Log::info("File uploaded: {$event->file->getPath()}");
});
Middleware: Add custom logic to uploads:
FileResource::extend(function ($resource) {
$resource->onUpload(function ($file) {
// Pre-process file
});
});
Testing Helpers:
Use FileResourceTestCase for unit tests:
use BaksDev\FilesRes\Tests\FileResourceTestCase;
class MyTest extends FileResourceTestCase {
public function testUpload() { ... }
}
Environment Variables:
Override defaults in .env:
FILES_RES_UPLOAD_DIR=storage/app/public/uploads
FILES_RES_CDN_URL=https://cdn.yourdomain.com
Messenger Transport: Configure Symfony Messenger to use Laravel’s database transport:
# config/packages/messenger.yaml
framework:
messenger:
transports:
async: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
'BaksDev\FilesRes\Message\ProcessFileMessage': async
Fallback Storage:
Set a default adapter in config/files-res.php:
'storage' => [
'default' => 'local',
'adapters' => [
'local' => \BaksDev\FilesRes\Storage\LocalStorageAdapter::class,
's3' => \BaksDev\FilesRes\Storage\S3StorageAdapter::class,
],
],
// webpack.mix.js
mix.webpackConfig({
module: {
rules: [
{
test:
How can I help you explore Laravel packages today?