Nette Assets
************
<div class=perex>
Устали вручную управлять статическими файлами в своих веб-приложениях? Забудьте о жёстко прописанных путях, возне со сбросом кеша и заботах о версиях файлов. Nette Assets меняет то, как вы работаете с изображениями, таблицами стилей, скриптами и другими статическими ресурсами.
- **Умное версионирование** обеспечивает, что браузеры всегда загружают свежие файлы
- **Автоматическое определение** типов файлов и размеров
- **Бесшовная интеграция с Latte** через интуитивные теги
- **Гибкая архитектура** с поддержкой файловых систем, CDN и Vite
- **Ленивая загрузка** ради максимальной производительности
</div>
Зачем нужен Nette Assets?
=========================
Работа со статическими файлами часто означает повторяющийся код, в котором легко ошибиться. Вы вручную составляете URL, добавляете параметры версии для сброса кеша и по-разному обрабатываете разные типы файлов. Это приводит к коду вроде такого:
```latte
<img src="/images/logo.png?v=1699123456" width="200" height="100" alt="Logo">
<link rel="stylesheet" href="/css/style.css?v=2">
```
С Nette Assets вся эта сложность исчезает:
```latte
{* Всё автоматизировано - URL, версионирование, размеры *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">
{* Или просто *}
{asset 'css/style.css'}
```
Вот и всё! Библиотека автоматически:
- добавляет параметры версии по времени изменения файла
- определяет размеры изображений и включает их в HTML
- порождает правильный HTML-элемент для каждого типа файла
- справляется и со средой разработки, и с продакшном
Установка
=========
Установите Nette Assets с помощью [Composer|best-practices:composer]:
```shell
composer require nette/assets
```
Он требует PHP 8.1 или новее и прекрасно работает с Nette Framework, но может использоваться и самостоятельно.
Первые шаги
===========
Nette Assets работает сразу, без всякой настройки. Поместите свои статические файлы в каталог `www/assets/` и начинайте их использовать:
```latte
{* Показываем изображение с автоматическими размерами *}
{asset 'logo.png'}
{* Подключаем таблицу стилей с версионированием *}
{asset 'style.css'}
{* Загружаем скрипт *}
{asset 'app.js'}
```
Для большего контроля над порождаемым HTML используйте атрибут `n:asset` или функцию `asset()`.
Как это работает
================
Nette Assets построен вокруг трёх основных понятий, которые делают его мощным и при этом простым в использовании:
Ресурсы: ваши файлы становятся умнее
------------------------------------
**Ресурс** (asset) представляет любой статический файл в вашем приложении. Каждый файл становится объектом с полезными свойствами только для чтения:
```php
$image = $assets->getAsset('photo.jpg');
echo $image->url; // '/assets/photo.jpg?v=1699123456'
echo $image->file; // '/var/www/assets/photo.jpg' (локальный путь либо null)
echo $image->width; // 1920
echo $image->height; // 1080
echo $image->mimeType; // 'image/jpeg'
```
Разные типы файлов дают разные свойства:
- **Изображения**: ширина, высота, альтернативный текст, ленивая загрузка
- **Скрипты**: тип модуля, хеши целостности, crossorigin
- **Таблицы стилей**: медиазапросы, целостность
- **Аудио и видео**: длительность, размеры (только видео)
- **Шрифты**: правильная предзагрузка с CORS
Библиотека автоматически определяет типы файлов и создаёт подходящий класс ресурса.
Мапперы: откуда берутся файлы
-----------------------------
**Маппер** знает, как найти файлы и создать для них URL. У вас может быть несколько мапперов для разных задач: локальные файлы, CDN, облачное хранилище или инструменты сборки (у каждого из них есть имя). Встроенный `FilesystemMapper` занимается локальными файлами, а `ViteMapper` интегрируется с современными инструментами сборки.
Мапперы определяются в [конфигурации |configuration].
Реестр: ваш основной интерфейс
------------------------------
**Реестр** управляет всеми мапперами и предоставляет основной API:
```php
// Внедряем реестр в свой сервис
public function __construct(
private Nette\Assets\Registry $assets
) {}
```
```php
// Получаем ресурсы из разных мапперов
$logo = $this->assets->getAsset('images:logo.png'); // маппер 'images'
$app = $this->assets->getAsset('app:main.js'); // маппер 'app'
$style = $this->assets->getAsset('style.css'); // использует маппер по умолчанию
```
Реестр автоматически выбирает нужный маппер и кеширует результаты ради производительности.
Работа с ресурсами в PHP
========================
Реестр предоставляет два метода получения ресурсов:
```php
// Выбрасывает Nette\Assets\AssetNotFoundException, если файла нет
$logo = $assets->getAsset('logo.png');
// Возвращает null, если файла нет
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
echo $banner->url;
}
```
Указание мапперов
-----------------
Вы можете явно выбрать, какой маппер использовать:
```php
// Использовать маппер по умолчанию
$file = $assets->getAsset('document.pdf');
// Использовать конкретный маппер через приставку
$image = $assets->getAsset('images:photo.jpg');
// Использовать конкретный маппер через запись массивом
$script = $assets->getAsset(['scripts', 'app.js']);
```
Свойства и типы ресурсов
------------------------
Каждый тип ресурса даёт подходящие свойства только для чтения:
```php
// Свойства изображения
$image = $assets->getAsset('photo.jpg');
echo $image->width; // 1920
echo $image->height; // 1080
echo $image->mimeType; // 'image/jpeg'
// Свойства скрипта
$script = $assets->getAsset('app.js');
echo $script->type; // null ('module' для точек входа Vite)
// Свойства аудио
$audio = $assets->getAsset('song.mp3');
echo $audio->duration; // длительность в секундах
// Все ресурсы можно привести к строке (вернётся URL)
$url = (string) $assets->getAsset('document.pdf');
```
.[note]
Свойства вроде размеров или длительности загружаются лениво, только при обращении, благодаря чему библиотека остаётся быстрой.
.[tip]
Для точного статического анализа установите расширение [nette/phpstan-rules |tools:phpstan-rules#Assets]. PHPStan тогда знает конкретный тип каждого ресурса, так что `getAsset('photo.jpg')` понимается как `ImageAsset`, а обращение к `->width` не вызывает ошибки.
Использование ресурсов в шаблонах Latte
=======================================
Nette Assets предлагает интуитивную интеграцию с [Latte|latte:] через теги и функции.
`{asset}`
---------
Тег `{asset}` отрисовывает целые HTML-элементы:
```latte
{* Отрисует: <img src="/assets/hero.jpg?v=123" width="1920" height="1080"> *}
{asset 'hero.jpg'}
{* Отрисует: <script src="/assets/app.js?v=456"></script> *}
{asset 'app.js'}
{* Отрисует: <link rel="stylesheet" href="/assets/style.css?v=789"> *}
{asset 'style.css'}
```
Тег автоматически:
- определяет тип ресурса и порождает подходящий HTML
- добавляет версионирование для сброса кеша
- добавляет размеры для изображений
- задаёт правильные атрибуты (type, media и т. д.)
Внутри HTML-атрибутов и внутри элементов `<style>` и `<script>` он выводит только URL:
```latte
<div style="background-image: url({asset 'bg.jpg'})">
<img srcset="{asset 'logo@2x.png'} 2x">
```
`n:asset`
---------
Для полного контроля над HTML-атрибутами:
```latte
{* Атрибут n:asset заполняет src, размеры и т. д. *}
<img n:asset="product.jpg" alt="Product" class="rounded">
{* Работает с любым подходящим элементом *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print">
<audio n:asset="podcast.mp3" controls></audio>
```
Используйте переменные и мапперы:
```latte
{* Переменные работают естественно *}
<img n:asset="$product->image">
{* Указываем маппер фигурными скобками *}
<img n:asset="images:{$product->image}">
{* Указываем маппер записью массивом *}
<img n:asset="[images, $product->image]">
```
`n:asset` работает и на `<a>`, где заполняет `href`, и на `<link>`, где создаёт подсказку предзагрузки:
```latte
<a n:asset="hero.jpg">Скачать изображение</a>
```
Обратите внимание, что варианты `<a>` и `<link>` работают только для отрисовываемых ресурсов (изображение, скрипт и так далее), но никогда для `GenericAsset`, например PDF.
Для изображений достаточно задать только `width` (или только `height`), и второй размер вычислится автоматически с сохранением соотношения сторон:
```latte
{* height дополняется из соотношения сторон *}
<img n:asset="product.jpg" width="200">
```
`asset()`
---------
Для максимальной гибкости используйте функцию `asset()`:
```latte
{var $logo = asset('logo.png')}
<img src={$logo} width={$logo->width} height={$logo->height}>
{* Или напрямую *}
<img src={asset('logo.png')} alt="Logo">
```
Необязательные ресурсы
----------------------
Изящно справляйтесь с отсутствующими ресурсами через `{asset?}`, `n:asset?` и `tryAsset()`:
```latte
{* Необязательный тег: если ресурса нет, не отрисуется ничего *}
{asset? 'optional-banner.jpg'}
{* Необязательный атрибут: пропускается, если ресурса нет *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">
{* С запасным вариантом *}
{var $avatar = tryAsset('user-avatar.jpg') ?? asset('default-avatar.jpg')}
<img n:asset=$avatar alt="Avatar">
```
`{preload}`
-----------
Улучшайте скорость загрузки страницы:
```latte
{* В секции <head> *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}
```
Порождает подходящие ссылки предзагрузки:
```latte
<link rel="preload" href="/assets/critical.css?v=123" as="style">
<link rel="preload" href="/assets/important-font.woff2?v=456" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/hero-image.jpg?v=789" as="image" type="image/jpeg">
```
.[note]
Когда ваш ответ задаёт заголовок `Content-Security-Policy` с `nonce`, Nette автоматически добавляет соответствующий атрибут `nonce` каждому порождённому элементу `<script>`, `<link>` и `<style>`, чтобы политика безопасности браузера их не заблокировала.
Продвинутые возможности
=======================
Автоопределение расширения
--------------------------
Автоматически справляйтесь с несколькими форматами:
```neon
assets:
mapping:
images:
path: img
extension: [webp, jpg, png] # Пробовать по порядку
```
Теперь можно запрашивать без расширения:
```latte
{* Автоматически найдёт logo.webp, logo.jpg или logo.png *}
{asset 'images:logo'}
```
Отлично подходит для постепенного улучшения с современными форматами.
Умное версионирование
---------------------
Файлы автоматически версионируются по времени изменения:
```latte
{asset 'style.css'}
{* Вывод: <link rel="stylesheet" href="/assets/style.css?v=1699123456"> *}
```
Когда вы обновляете файл, метка времени меняется и вынуждает браузер обновить кеш.
Управляйте версионированием для каждого ресурса:
```php
// Отключаем версионирование для конкретного ресурса
$asset = $assets->getAsset('style.css', ['version' => false]);
```
```latte
{* В Latte *}
{asset 'style.css', version: false}
```
Тот же синтаксис `ссылка, ключ: значение` передаёт параметры и в `n:asset`, и в `{preload}`:
```latte
<img n:asset="photo.jpg, version: false">
{preload 'style.css', version: false}
```
Ресурсы-шрифты
--------------
Шрифты получают особое обращение с правильным CORS:
```latte
{* Правильная предзагрузка с crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}
{* Использование в CSS *}
<style>
@font-face {
font-family: 'Open Sans';
src: url('{asset 'fonts:OpenSans-Regular.woff2'}') format('woff2');
font-display: swap;
}
</style>
```
Собственные мапперы
===================
Создавайте собственные мапперы для особых нужд вроде облачного хранилища или динамического порождения:
```php
use Nette\Assets\Mapper;
use Nette\Assets\Asset;
use Nette\Assets\Helpers;
class CloudStorageMapper implements Mapper
{
public function __construct(
private CloudClient $client,
private string $bucket,
) {}
public function getAsset(string $reference, array $options = []): Asset
{
if (!$this->client->exists($this->bucket, $reference)) {
throw new Nette\Assets\AssetNotFoundException("Asset '$reference' not found");
}
$url = $this->client->getPublicUrl($this->bucket, $reference);
return Helpers::createAssetFromUrl($url);
}
}
```
Регистрируем в конфигурации:
```neon
assets:
mapping:
cloud: CloudStorageMapper(@cloudClient, 'my-bucket')
```
Используем как любой другой маппер:
```latte
{asset 'cloud:user-uploads/photo.jpg'}
```
Метод `Helpers::createAssetFromUrl()` автоматически создаёт правильный тип ресурса по расширению файла.
Типы ресурсов, реализующие интерфейс `Nette\Assets\HtmlRenderable` (изображения, скрипты, стили и так далее), может отрисовать `{asset}` как целый HTML-элемент. Остальные типы файлов становятся `GenericAsset` (например, PDF), который нельзя отрисовать как HTML-элемент, но он всё равно предоставляет URL (и другие метаданные). Попытка отрисовать такой ресурс как HTML-элемент выбрасывает `Nette\InvalidArgumentException`; его URL внутри атрибута использовать по-прежнему можно.
Дополнительные материалы
========================
- [Nette Assets: наконец-то единый API для всего, от изображений до Vite |https://blog.nette.org/en/introducing-nette-assets]
Nette Assets
Устали вручную управлять статическими файлами в своих
веб-приложениях? Забудьте о жёстко прописанных путях, возне со сбросом
кеша и заботах о версиях файлов. Nette Assets меняет то, как вы работаете с
изображениями, таблицами стилей, скриптами и другими статическими
ресурсами.
- Умное версионирование обеспечивает, что браузеры всегда
загружают свежие файлы
- Автоматическое определение типов файлов и размеров
- Бесшовная интеграция с Latte через интуитивные теги
- Гибкая архитектура с поддержкой файловых систем, CDN и Vite
- Ленивая загрузка ради максимальной производительности
Зачем нужен Nette Assets?
Работа со статическими файлами часто означает повторяющийся код, в
котором легко ошибиться. Вы вручную составляете URL, добавляете
параметры версии для сброса кеша и по-разному обрабатываете разные
типы файлов. Это приводит к коду вроде такого:
<img src="/images/logo.png?v=1699123456" width="200" height="100" alt="Logo">
<link rel="stylesheet" href="/css/style.css?v=2">
С Nette Assets вся эта сложность исчезает:
{* Всё автоматизировано - URL, версионирование, размеры *}
<img n:asset="images/logo.png">
<link n:asset="css/style.css">
{* Или просто *}
{asset 'css/style.css'}
Вот и всё! Библиотека автоматически:
- добавляет параметры версии по времени изменения файла
- определяет размеры изображений и включает их в HTML
- порождает правильный HTML-элемент для каждого типа файла
- справляется и со средой разработки, и с продакшном
Установка
Установите Nette Assets с помощью Composer:
composer require nette/assets
Он требует PHP 8.1 или новее и прекрасно работает с Nette Framework, но может
использоваться и самостоятельно.
Первые шаги
Nette Assets работает сразу, без всякой настройки. Поместите свои
статические файлы в каталог www/assets/ и начинайте их
использовать:
{* Показываем изображение с автоматическими размерами *}
{asset 'logo.png'}
{* Подключаем таблицу стилей с версионированием *}
{asset 'style.css'}
{* Загружаем скрипт *}
{asset 'app.js'}
Для большего контроля над порождаемым HTML используйте атрибут
n:asset или функцию asset().
Как это работает
Nette Assets построен вокруг трёх основных понятий, которые делают его
мощным и при этом простым в использовании:
Ресурсы: ваши файлы становятся умнее
Ресурс (asset) представляет любой статический файл в вашем
приложении. Каждый файл становится объектом с полезными свойствами
только для чтения:
$image = $assets->getAsset('photo.jpg');
echo $image->url; // '/assets/photo.jpg?v=1699123456'
echo $image->file; // '/var/www/assets/photo.jpg' (локальный путь либо null)
echo $image->width; // 1920
echo $image->height; // 1080
echo $image->mimeType; // 'image/jpeg'
Разные типы файлов дают разные свойства:
- Изображения: ширина, высота, альтернативный текст, ленивая
загрузка
- Скрипты: тип модуля, хеши целостности, crossorigin
- Таблицы стилей: медиазапросы, целостность
- Аудио и видео: длительность, размеры (только видео)
- Шрифты: правильная предзагрузка с CORS
Библиотека автоматически определяет типы файлов и создаёт
подходящий класс ресурса.
Мапперы: откуда берутся файлы
Маппер знает, как найти файлы и создать для них URL. У вас может быть
несколько мапперов для разных задач: локальные файлы, CDN, облачное
хранилище или инструменты сборки (у каждого из них есть имя).
Встроенный FilesystemMapper занимается локальными файлами, а
ViteMapper интегрируется с современными инструментами сборки.
Мапперы определяются в конфигурации.
Реестр: ваш основной интерфейс
Реестр управляет всеми мапперами и предоставляет основной API:
// Внедряем реестр в свой сервис
public function __construct(
private Nette\Assets\Registry $assets
) {}
// Получаем ресурсы из разных мапперов
$logo = $this->assets->getAsset('images:logo.png'); // маппер 'images'
$app = $this->assets->getAsset('app:main.js'); // маппер 'app'
$style = $this->assets->getAsset('style.css'); // использует маппер по умолчанию
Реестр автоматически выбирает нужный маппер и кеширует результаты
ради производительности.
Работа с ресурсами в PHP
Реестр предоставляет два метода получения ресурсов:
// Выбрасывает Nette\Assets\AssetNotFoundException, если файла нет
$logo = $assets->getAsset('logo.png');
// Возвращает null, если файла нет
$banner = $assets->tryGetAsset('banner.jpg');
if ($banner) {
echo $banner->url;
}
Указание мапперов
Вы можете явно выбрать, какой маппер использовать:
// Использовать маппер по умолчанию
$file = $assets->getAsset('document.pdf');
// Использовать конкретный маппер через приставку
$image = $assets->getAsset('images:photo.jpg');
// Использовать конкретный маппер через запись массивом
$script = $assets->getAsset(['scripts', 'app.js']);
Свойства и типы ресурсов
Каждый тип ресурса даёт подходящие свойства только для чтения:
// Свойства изображения
$image = $assets->getAsset('photo.jpg');
echo $image->width; // 1920
echo $image->height; // 1080
echo $image->mimeType; // 'image/jpeg'
// Свойства скрипта
$script = $assets->getAsset('app.js');
echo $script->type; // null ('module' для точек входа Vite)
// Свойства аудио
$audio = $assets->getAsset('song.mp3');
echo $audio->duration; // длительность в секундах
// Все ресурсы можно привести к строке (вернётся URL)
$url = (string) $assets->getAsset('document.pdf');
Свойства вроде размеров или длительности загружаются лениво,
только при обращении, благодаря чему библиотека остаётся быстрой.
Для точного статического анализа установите расширение nette/phpstan-rules. PHPStan тогда знает
конкретный тип каждого ресурса, так что getAsset('photo.jpg') понимается
как ImageAsset, а обращение к ->width не вызывает ошибки.
Использование ресурсов в шаблонах Latte
Nette Assets предлагает интуитивную интеграцию с Latte через теги и функции.
{asset}
Тег {asset} отрисовывает целые HTML-элементы:
{* Отрисует: <img src="/assets/hero.jpg?v=123" width="1920" height="1080"> *}
{asset 'hero.jpg'}
{* Отрисует: <script src="/assets/app.js?v=456"></script> *}
{asset 'app.js'}
{* Отрисует: <link rel="stylesheet" href="/assets/style.css?v=789"> *}
{asset 'style.css'}
Тег автоматически:
- определяет тип ресурса и порождает подходящий HTML
- добавляет версионирование для сброса кеша
- добавляет размеры для изображений
- задаёт правильные атрибуты (type, media и т. д.)
Внутри HTML-атрибутов и внутри элементов <style> и
<script> он выводит только URL:
<div style="background-image: url({asset 'bg.jpg'})">
<img srcset="{asset 'logo@2x.png'} 2x">
n:asset
Для полного контроля над HTML-атрибутами:
{* Атрибут n:asset заполняет src, размеры и т. д. *}
<img n:asset="product.jpg" alt="Product" class="rounded">
{* Работает с любым подходящим элементом *}
<script n:asset="analytics.js" defer></script>
<link n:asset="print.css" media="print">
<audio n:asset="podcast.mp3" controls></audio>
Используйте переменные и мапперы:
{* Переменные работают естественно *}
<img n:asset="$product->image">
{* Указываем маппер фигурными скобками *}
<img n:asset="images:{$product->image}">
{* Указываем маппер записью массивом *}
<img n:asset="[images, $product->image]">
n:asset работает и на <a>, где заполняет href, и на
<link>, где создаёт подсказку предзагрузки:
<a n:asset="hero.jpg">Скачать изображение</a>
Обратите внимание, что варианты <a> и <link> работают
только для отрисовываемых ресурсов (изображение, скрипт и так далее),
но никогда для GenericAsset, например PDF.
Для изображений достаточно задать только width (или только
height), и второй размер вычислится автоматически с сохранением
соотношения сторон:
{* height дополняется из соотношения сторон *}
<img n:asset="product.jpg" width="200">
asset()
Для максимальной гибкости используйте функцию asset():
{var $logo = asset('logo.png')}
<img src={$logo} width={$logo->width} height={$logo->height}>
{* Или напрямую *}
<img src={asset('logo.png')} alt="Logo">
Необязательные ресурсы
Изящно справляйтесь с отсутствующими ресурсами через {asset?},
n:asset? и tryAsset():
{* Необязательный тег: если ресурса нет, не отрисуется ничего *}
{asset? 'optional-banner.jpg'}
{* Необязательный атрибут: пропускается, если ресурса нет *}
<img n:asset?="user-avatar.jpg" alt="Avatar" class="avatar">
{* С запасным вариантом *}
{var $avatar = tryAsset('user-avatar.jpg') ?? asset('default-avatar.jpg')}
<img n:asset=$avatar alt="Avatar">
{preload}
Улучшайте скорость загрузки страницы:
{* В секции <head> *}
{preload 'critical.css'}
{preload 'important-font.woff2'}
{preload 'hero-image.jpg'}
Порождает подходящие ссылки предзагрузки:
<link rel="preload" href="/assets/critical.css?v=123" as="style">
<link rel="preload" href="/assets/important-font.woff2?v=456" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/assets/hero-image.jpg?v=789" as="image" type="image/jpeg">
Когда ваш ответ задаёт заголовок Content-Security-Policy с
nonce, Nette автоматически добавляет соответствующий атрибут
nonce каждому порождённому элементу <script>, <link>
и <style>, чтобы политика безопасности браузера их не
заблокировала.
Продвинутые возможности
Автоопределение расширения
Автоматически справляйтесь с несколькими форматами:
assets:
mapping:
images:
path: img
extension: [webp, jpg, png] # Пробовать по порядку
Теперь можно запрашивать без расширения:
{* Автоматически найдёт logo.webp, logo.jpg или logo.png *}
{asset 'images:logo'}
Отлично подходит для постепенного улучшения с современными
форматами.
Умное версионирование
Файлы автоматически версионируются по времени изменения:
{asset 'style.css'}
{* Вывод: <link rel="stylesheet" href="/assets/style.css?v=1699123456"> *}
Когда вы обновляете файл, метка времени меняется и вынуждает браузер
обновить кеш.
Управляйте версионированием для каждого ресурса:
// Отключаем версионирование для конкретного ресурса
$asset = $assets->getAsset('style.css', ['version' => false]);
{* В Latte *}
{asset 'style.css', version: false}
Тот же синтаксис ссылка, ключ: значение передаёт параметры и в
n:asset, и в {preload}:
<img n:asset="photo.jpg, version: false">
{preload 'style.css', version: false}
Ресурсы-шрифты
Шрифты получают особое обращение с правильным CORS:
{* Правильная предзагрузка с crossorigin *}
{preload 'fonts:OpenSans-Regular.woff2'}
{* Использование в CSS *}
<style>
@font-face {
font-family: 'Open Sans';
src: url('{asset 'fonts:OpenSans-Regular.woff2'}') format('woff2');
font-display: swap;
}
</style>
Собственные мапперы
Создавайте собственные мапперы для особых нужд вроде облачного
хранилища или динамического порождения:
use Nette\Assets\Mapper;
use Nette\Assets\Asset;
use Nette\Assets\Helpers;
class CloudStorageMapper implements Mapper
{
public function __construct(
private CloudClient $client,
private string $bucket,
) {}
public function getAsset(string $reference, array $options = []): Asset
{
if (!$this->client->exists($this->bucket, $reference)) {
throw new Nette\Assets\AssetNotFoundException("Asset '$reference' not found");
}
$url = $this->client->getPublicUrl($this->bucket, $reference);
return Helpers::createAssetFromUrl($url);
}
}
Регистрируем в конфигурации:
assets:
mapping:
cloud: CloudStorageMapper(@cloudClient, 'my-bucket')
Используем как любой другой маппер:
{asset 'cloud:user-uploads/photo.jpg'}
Метод Helpers::createAssetFromUrl() автоматически создаёт правильный тип
ресурса по расширению файла.
Типы ресурсов, реализующие интерфейс Nette\Assets\HtmlRenderable
(изображения, скрипты, стили и так далее), может отрисовать {asset}
как целый HTML-элемент. Остальные типы файлов становятся GenericAsset
(например, PDF), который нельзя отрисовать как HTML-элемент, но он всё равно
предоставляет URL (и другие метаданные). Попытка отрисовать такой ресурс
как HTML-элемент выбрасывает Nette\InvalidArgumentException; его URL внутри
атрибута использовать по-прежнему можно.
Дополнительные материалы