blackpig-creatif/chambre-noir
Composer 安装命令:
composer require blackpig-creatif/chambre-noir
包简介
Automatic image conversion and optimization for Filament FileUpload fields
README 文档
README
Automatic image conversion, responsive images, attribution, and gallery management for FilamentPHP v5.
ChambreNoir extends Filament's FileUpload with automatic image conversions on upload, responsive image rendering via <picture> and srcset, and built-in photographer attribution. The core upload feature stores JSON in any column with no additional tables. The optional Gallery system adds first-class entities for managing curated image collections.
Requirements
- PHP 8.2+
- Laravel 11+ / 12+
- FilamentPHP 4+ / 5+
- Imagick PHP extension
- Intervention Image 3.x
Installation
composer require blackpig-creatif/chambre-noir
Publish the config (optional):
php artisan vendor:publish --tag=chambre-noir-config
Quick Start
1. Create a Conversion Preset
php artisan chambre-noir:make-conversion Hero
This generates app/BlackpigCreatif/ChambreNoir/Conversions/HeroConversion.php:
<?php namespace App\BlackpigCreatif\ChambreNoir\Conversions; use BlackpigCreatif\ChambreNoir\Conversions\BaseConversion; class HeroConversion extends BaseConversion { protected int $defaultQuality = 85; protected function define(): array { return [ 'small' => ['width' => 600, 'height' => 600, 'fit' => 'max'], 'medium' => ['width' => 1200, 'height' => 1200, 'fit' => 'max'], 'large' => ['width' => 2400, 'height' => 2400, 'fit' => 'max'], ]; } }
2. Use in a Filament Form
use BlackpigCreatif\ChambreNoir\Forms\Components\RetouchMediaUpload; use App\BlackpigCreatif\ChambreNoir\Conversions\HeroConversion; RetouchMediaUpload::make('hero_image') ->preset(HeroConversion::class) ->disk('public') ->directory('pages/hero') ->required();
3. Add the Trait to Your Model
use BlackpigCreatif\ChambreNoir\Concerns\HasRetouchMedia; class Page extends Model { use HasRetouchMedia; protected $casts = [ 'hero_image' => 'array', ]; }
4. Render in Blade
{!! $page->getPicture('hero_image', ['alt' => $page->title, 'class' => 'w-full h-auto']) !!}
How It Works
When an image is uploaded through RetouchMediaUpload:
- Filament's
FileUploadsaves the original to disk - ChambreNoir creates conversions based on the preset
- A JSON structure is stored in the database column
{
"original": "pages/hero/image.jpg",
"conversions": {
"small": "pages/hero/conversions/image-small.jpg",
"medium": "pages/hero/conversions/image-medium.jpg",
"large": "pages/hero/conversions/image-large.jpg"
},
"attribution": {
"name": "Jane Smith",
"link": "https://janesmith.photography"
},
"preset": "App\\BlackpigCreatif\\ChambreNoir\\Conversions\\HeroConversion"
}
Old images are automatically cleaned up when replaced or removed.
RetouchMediaUpload
Drop-in replacement for Filament's FileUpload. Image-only by default.
Conversions via Preset (Recommended)
RetouchMediaUpload::make('image') ->preset(HeroConversion::class)
Inline Conversions
RetouchMediaUpload::make('image') ->conversions([ 'thumb' => ['width' => 200, 'height' => 200, 'fit' => 'crop'], 'medium' => ['width' => 800, 'height' => 600, 'fit' => 'contain'], 'large' => ['width' => 1920, 'height' => 1080, 'fit' => 'max'], ])
Config-based Presets (Legacy)
String keys reference config/chambre-noir.php presets. BaseConversion classes are preferred for new projects.
RetouchMediaUpload::make('image') ->preset('hero')
Disable Conversions
RetouchMediaUpload::make('document') ->withoutConversions()
All standard FileUpload methods (disk(), directory(), imageEditor(), maxSize(), etc.) work as expected.
Conversion Presets
Presets define the image sizes generated on upload. Extend BaseConversion and implement define():
use BlackpigCreatif\ChambreNoir\Conversions\BaseConversion; class ProductConversion extends BaseConversion { protected int $defaultQuality = 90; protected string $defaultFit = 'contain'; protected function define(): array { return [ 'thumb' => ['width' => 300, 'height' => 300, 'fit' => 'crop'], 'medium' => ['width' => 800, 'height' => 800], 'large' => ['width' => 1600, 'height' => 1600, 'quality' => 92], ]; } }
Each conversion accepts width, height, fit, and quality. Missing values inherit from class defaults.
Fit Methods
| Fit | Behaviour | Use case |
|---|---|---|
crop |
Exact dimensions, crops overflow | Thumbnails, avatars |
contain |
Fits within bounds, preserves ratio | General images |
max |
Scales down only, never up | Responsive images |
fill |
Fills dimensions, may distort | Background fills |
Composition
Presets can include other presets via includes():
class FullConversion extends BaseConversion { protected function includes(): array { return [SocialImageConversion::class]; } protected function define(): array { return [ 'thumb' => ['width' => 200, 'height' => 200, 'fit' => 'crop'], 'large' => ['width' => 1920, 'height' => 1080, 'fit' => 'max'], ]; } }
Included conversions are merged first; the defining class can override them.
Responsive Configuration
Override getResponsiveConfig() to control <picture> and srcset behaviour:
public function getResponsiveConfig(): array { return [ 'default' => 'medium', 'srcset' => [ 'small' => true, 'medium' => true, 'large' => true, ], 'picture' => [ 'large' => '(min-width: 1024px)', 'medium' => '(min-width: 768px)', 'small' => null, // fallback ], 'sizes' => '(min-width: 768px) 50vw, 100vw', ]; }
See Conversion Presets for the full reference.
Built-in Preset
SocialImageConversion ships with ChambreNoir, producing og (1200x630) and twitter (1200x600) crops at 90% quality.
HasRetouchMedia Trait
Add to any Eloquent model (or Atelier block) to access rendering helpers. Ensure the field is cast to array.
Rendering Methods
// <picture> with responsive sources (recommended) $model->getPicture('image', ['alt' => '...', 'class' => '...']) // <img> with srcset attribute $model->getImageWithSrcset('image', $sizes, ['alt' => '...']) // <figure> with optional <figcaption> (uses attribution if present) $model->getFigure('image', [ 'mode' => 'picture', // 'picture', 'srcset', or 'image' 'image' => ['alt' => '...', 'class' => '...'], 'figure' => ['class' => 'relative'], 'caption' => ['class' => 'text-sm text-gray-600'], ]) // Simple <img> for a specific conversion $model->getImage('image', 'thumb', ['alt' => '...'])
URL Access
$model->getMediaUrl('image', 'large') // URL string $model->getMediaUrl('image', 'original') // Original file URL $model->getMediaUrls('gallery', 'medium') // Array of URLs (multiple files) $model->getMediaPath('image', 'large') // Storage path (not URL)
Srcset Attributes (Manual)
<img {!! $model->getSrcset('image') !!} alt="..." class="...">
Returns srcset="..." sizes="..." src="..." attributes for manual <img> construction.
Graceful Degradation
All methods handle missing conversions, legacy string paths, and null data without throwing exceptions. getPicture() falls back to a simple <img>, getSrcset() returns an empty string, and getImageWithSrcset() falls back to the original URL.
See Responsive Images for detailed usage.
Attribution
ChambreNoir stores photographer credit and portfolio link alongside image data.
Enable in Form
RetouchMediaUpload::make('image') ->preset(HeroConversion::class) ->attribution() ->required();
This adds "Credit" and "Portfolio Link" fields below the upload component.
Requirement Control
// Inherits from component's required() state (default) ->attribution() // Force all attribution fields required ->attributionRequired(true) // Force all optional ->attributionRequired(false) // Granular control ->attributionRequired(['name' => true, 'link' => false]) // Dynamic ->attributionRequired(fn ($get) => $get('requires_credit'))
Display in Templates
{{-- Automatic: getFigure() includes attribution as figcaption --}} {!! $post->getFigure('image', [ 'image' => ['alt' => $post->title], 'caption' => ['class' => 'text-sm text-gray-600'], ]) !!} {{-- Manual --}} @if($post->hasAttribution('image')) <p> Photo by @if($link = $post->getAttributionLink('image')) <a href="{{ $link }}" target="_blank" rel="noopener noreferrer"> {{ $post->getAttributionName('image') }} </a> @else {{ $post->getAttributionName('image') }} @endif </p> @endif
Attribution Methods
$model->getAttribution('image') // ['name' => '...', 'link' => '...'] or null $model->getAttributionName('image') // string or null $model->getAttributionLink('image') // string or null $model->hasAttribution('image') // bool
Gallery System
ChambreNoir ships a full gallery management system built on top of three additional tables. Galleries are managed through a dedicated Filament resource, attached to any Eloquent model via a polymorphic pivot, and rendered on the frontend via either raw Blade helpers or the included Atelier block.
Migrations
php artisan vendor:publish --tag=chambre-noir-migrations php artisan migrate
This creates galleries, gallery_images, and galleryables tables.
Add the Relationship to Your Model
GalleryPickerField writes to a polymorphic galleryables pivot. Add the relationship to any model that will hold gallery attachments:
use BlackpigCreatif\ChambreNoir\Models\Gallery; class Page extends Model { public function galleries(): \Illuminate\Database\Eloquent\Relations\MorphToMany { return $this->morphToMany(Gallery::class, 'galleryable') ->withPivot('sort_order') ->orderBy('galleryables.sort_order'); } }
Attach Galleries in a Form
use BlackpigCreatif\ChambreNoir\Forms\Components\GalleryPickerField; GalleryPickerField::make('galleries') ->multiple()
By default only active (published) galleries are shown. Options:
// Include unpublished galleries GalleryPickerField::make('galleries') ->multiple() ->unpublished() // Restrict to a specific conversion preset GalleryPickerField::make('galleries') ->multiple() ->conversion('product')
Render in Blade
@foreach($page->galleries as $gallery) @foreach($gallery->images as $image) {!! $image->getPicture('image', ['alt' => $image->caption ?? '']) !!} @endforeach @endforeach
Atelier Block
If Atelier is installed, a GalleriesBlock is registered automatically. It supports grid and carousel display, combined or tabbed multi-gallery modes, and an optional lightbox — all driven by Alpine.js with no additional dependencies.
See Gallery System for the full reference.
Artisan Commands
Make Conversion
php artisan chambre-noir:make-conversion ProductImage
Generates app/BlackpigCreatif/ChambreNoir/Conversions/ProductImageConversion.php extending BaseConversion with a scaffolded define() method and responsive config.
Regenerate Conversions
Regenerate image conversions across models, Atelier blocks, and Sceau SEO data:
# All images everywhere php artisan chambre-noir:regenerate --all # Only model images php artisan chambre-noir:regenerate --models # Only Atelier block images php artisan chambre-noir:regenerate --blocks # Only Sceau SEO images php artisan chambre-noir:regenerate --seo # Target a specific model php artisan chambre-noir:regenerate --model="App\Models\Post" # Target a specific block type php artisan chambre-noir:regenerate --block-type="HeroBlock" # Filter by field name or record ID php artisan chambre-noir:regenerate --models --field=hero_image --id=42 # Filter by conversion class or disk php artisan chambre-noir:regenerate --models --conversion="App\Conversions\HeroConversion" php artisan chambre-noir:regenerate --all --disk=s3 # Preview without changes php artisan chambre-noir:regenerate --all --dry-run # Skip confirmation php artisan chambre-noir:regenerate --all --force # Backup old conversions before regenerating php artisan chambre-noir:regenerate --all --backup # JSON output for scripting php artisan chambre-noir:regenerate --all --json
Configuration
Published to config/chambre-noir.php.
| Key | Default | Description |
|---|---|---|
quality |
90 |
Global default quality (1-100). Override per-preset or per-conversion. |
disk |
'public' |
Default storage disk. |
conversions_directory |
'conversions' |
Subdirectory for conversion files within the upload directory. |
presets |
[...] |
Legacy array-based presets. Use BaseConversion classes instead. |
responsive.default_conversion |
'medium' |
Fallback conversion for <img src=""> in responsive elements. |
responsive.default_sizes |
null |
Default sizes attribute. null = auto-generate from widths. |
responsive.auto_generate_sizes |
true |
Auto-calculate sizes from conversion widths and breakpoints. |
responsive.breakpoints |
Tailwind defaults | Breakpoints for auto-generated media queries. |
Environment variables:
CHAMBRE_NOIR_QUALITY=85 CHAMBRE_NOIR_DISK=public
File Storage Structure
storage/app/public/pages/hero/
image.jpg # Original
conversions/
image-small.jpg # 600x600
image-medium.jpg # 1200x1200
image-large.jpg # 2400x2400
Integration with Atelier
ChambreNoir is designed as a companion to Atelier. Use RetouchMediaUpload in block schemas and HasRetouchMedia on blocks:
use BlackpigCreatif\ChambreNoir\Concerns\HasRetouchMedia; class HeroBlock extends BaseBlock { use HasRetouchMedia; // ... }
In block templates:
{!! $block->getPicture('background_image', [ 'alt' => $block->getTranslated('title'), 'class' => 'w-full h-full object-cover', ]) !!}
Image cleanup for block attributes is handled automatically by Atelier's BlockManager via ImageCleanupService.
Integration with Sceau
ChambreNoir's regeneration command supports Sceau SEO images. Use --seo to regenerate Open Graph and social sharing images managed by Sceau.
License
MIT. See LICENSE.md.
Credits
blackpig-creatif/chambre-noir 适用场景与选型建议
blackpig-creatif/chambre-noir 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 104 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 02 月 09 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「laravel」 「image-processing」 「filament」 「responsive-images」 「image-conversion」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 blackpig-creatif/chambre-noir 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 blackpig-creatif/chambre-noir 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 blackpig-creatif/chambre-noir 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Smarty plugin that adds some image related template syntax enchantments
Build imgproxy URLs in PHP with optional encryption and signing.
Build imgproxy URLs in Laravel with optional encryption and signing.
Symfony 8 bundle for on-demand image processing, resizing, cropping, AVIF/WebP conversion, and caching. Filter pipelines with fit, fill, optimize, strip, interlace processors and avifenc, cwebp, MozJPEG, pngquant post-processors. Filesystem, stream, and S3 loaders/resolvers. HMAC-signed runtime URLs
Production-ready file storage, upload, and image processing for the MonkeysLegion framework
统计信息
- 总下载量: 104
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 38
- 依赖项目数: 2
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-02-09