tobento / app-renderable
Renderable support for the app, wiring the renderable factory with media, html sanitizing and locales.
Requires
- php: >=8.4
- tobento/app: ^2.0
- tobento/app-file-storage: ^2.0
- tobento/app-html-sanitizer: ^2.0
- tobento/app-http: ^2.0
- tobento/app-language: ^2.0
- tobento/app-media: ^2.0
- tobento/app-migration: ^2.0
- tobento/app-view: ^2.0
- tobento/service-renderable: ^2.0
Requires (Dev)
- phpunit/phpunit: ^12.3
- tobento/app-testing: ^2.0
- vimeo/psalm: ^6.13
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
App Renderable wiring the Renderable Service with the app: file urls using the App Media file download features, pictures, HTML sanitizing and the current language.
Table of Contents
Getting Started
Add the latest version of the app renderable project running this command.
composer require tobento/app-renderable
Requirements
- PHP 8.4 or greater
Documentation
App
Check out the App Skeleton if you are using the skeleton.
You may also check out the App to learn more about the app in general.
Renderable Boot
The renderable boot does the following:
- installs and loads renderable config file
- implements renderable interfaces based on renderable config
use Tobento\App\AppFactory; use Tobento\Service\Renderable\CompositeRenderableFactoryInterface; use Tobento\Service\Renderable\RenderableFactoryInterface; // Create the app $app = new AppFactory()->createApp(); // Add directories: $app->dirs() ->dir(realpath(__DIR__.'/../'), 'root') ->dir(realpath(__DIR__.'/../app/'), 'app') ->dir($app->dir('app').'config', 'config', group: 'config') ->dir($app->dir('root').'public', 'public') ->dir($app->dir('root').'vendor', 'vendor'); // Adding boots: $app->boot(\Tobento\App\Renderable\Boot\Renderable::class); $app->booting(); // Implemented interfaces: $factory = $app->get(RenderableFactoryInterface::class); $factory = $app->get(CompositeRenderableFactoryInterface::class); // Run the app $app->run();
Both interfaces resolve to the same factory instance.
Renderable Config
The configuration for the renderable is located in the app/config/renderable.php file at the default App Skeleton config location where you can configure the renderable factory for your application, such as the file url generator, the html sanitizer or custom renderable types.
Basic Usage
Rendering Repository Entities
Use the entity factory with a Storage Repository to get renderable entities:
use Tobento\Service\Renderable\Repository\EntityFactory; use Tobento\Service\Repository\Storage\StorageRepository; class ArticleRepository extends StorageRepository { // } $repository = new ArticleRepository( storage: $storage, table: 'articles', entityFactory: new EntityFactory( // Attribute name to renderable type: mapping: [ 'title' => 'text', 'description' => 'html', 'image' => 'picture', 'downloads' => 'files', ], ), ); $article = $repository->findById(1);
Check out the Renderable Service to learn more about renderables, the available renderable types and the repository entity.
Rendering In Views
<?php /** @var \Tobento\Service\Renderable\Repository\Entity $article */ ?> <h1><?= $view->esc($article->get('title')) ?></h1> <?= $view->esc($article->get('image')) ?> <?= $view->esc($article->get('description')) ?> <?php if ($article->exists('downloads')) { ?> <?= $view->esc($article->get('downloads')) ?> <?php } ?>
The esc method does not escape renderables as they are already safe for HTML output.
File Urls
The File and Files renderables get their urls from the Tobento\App\Renderable\FileUrlGenerator, using the App Media file download features:
- files of public storages get a download url using the
media.file.downloadroute. - files of private storages get a signed download url using the
media.file.download.signedroute, only if the file has asigned_url_expiresattribute (in minutes).
Make sure the file download features are enabled and support the storages in the app/config/media.php file:
'features' => [ new Feature\FileDownload( // Supported public storages: supportedStorages: ['uploads-public', 'downloads'], ), new Feature\FileDownloadSigned( // Supported private storages: supportedStorages: ['uploads-private'], ), ],
If a feature is not enabled, files are rendered without link.
Custom route names
You may change the route names in the app/config/renderable.php file:
use Tobento\App\Renderable\FileUrlGenerator; fileUrlGenerator: new FileUrlGenerator( storages: $container->get(StoragesInterface::class), router: $container->get(RouterInterface::class), routeName: 'media.file.download', signedRouteName: 'media.file.download.signed', ),
Pictures
The Picture renderable uses the picture generator provided by the App Media picture feature. Make sure it is enabled in the app/config/media.php file:
'features' => [ new Feature\Picture(), ],
Picture Definitions
The renderable boot installs the default picture definitions in the views/picture-definitions/ directory, which you may adjust to your needs:
| Definition | Renderable | Used for |
|---|---|---|
renderable-image |
Picture | rendering the picture |
renderable-file |
File | image previews in custom views |
renderable-files |
Files | image previews in custom views |
Check out the App Media to learn more about picture definitions.
Generating Images In Background
By default, images are generated on the first request. If you have a running queue worker, you may generate them in background instead by setting generateImagesInBackground to true in the app/config/renderable.php file.
Html Sanitizing
The Html renderable sanitizes untrusted HTML using the default sanitizer of the App Html Sanitizer. You may configure the sanitizer in the app/config/html_sanitizer.php file, or change it in the app/config/renderable.php file:
use Tobento\App\HtmlSanitizer\HtmlSanitizersInterface; htmlSanitizer: static fn (string $html): string => $container->get(HtmlSanitizersInterface::class)->get('custom')->sanitize($html),
Locale
The renderable factory uses the current language key and fallbacks of the LanguagesInterface provided by the App Language, as repositories store translations by language key. If your repositories use other languages, such as area languages, adjust the locale and localeFallbacks in the app/config/renderable.php file accordingly:
use Tobento\App\Language\AreaLanguagesInterface; Renderable\RenderableFactoryInterface::class => static function ( ContainerInterface $container, AreaLanguagesInterface $areaLanguages, ): Renderable\RenderableFactoryInterface { $languages = $areaLanguages->get('resources'); return new Renderable\RenderableFactory( // ... locale: $languages->current()->key(), localeFallbacks: $languages->fallbacks('key'), ); },
Adding Renderable Types
Using The Config
You may add custom renderable types in the app/config/renderable.php file:
factories: [
'badge' => BadgeFactory::class,
// or with constructor parameters:
'price' => [PriceFactory::class, 'currency' => 'CHF'],
],
Check out the Renderable Service - Custom Renderable Types to learn more about creating renderable factories.
Using The App On Method
You may add custom renderable types from within a boot, for instance from an app bundle, using the app on method. The factory is only modified once it gets resolved:
use Tobento\App\Boot; use Tobento\Service\Renderable\CompositeRenderableFactoryInterface; use Tobento\Service\Renderable\RenderableFactoryInterface; class BadgeBoot extends Boot { public const BOOT = [ // you may ensure the renderable boot: \Tobento\App\Renderable\Boot\Renderable::class, ]; public function boot(): void { $this->app->on( RenderableFactoryInterface::class, static function (RenderableFactoryInterface $factory): void { if ($factory instanceof CompositeRenderableFactoryInterface) { $factory->addFactory(type: 'badge', factory: BadgeFactory::class); } } ); } }
Use the RenderableFactoryInterface with the on method, not the CompositeRenderableFactoryInterface, as renderables resolve the factory by the RenderableFactoryInterface.