flyo / nitro-yii2
Flyo Nitro Yii2 Framework Module
Requires
- php: >=8.3
- flyo/nitro-php: ^3.0
- flyo/nitro-php-bridge: ^1.1
- yiisoft/yii2: ^2.0.54
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.2
- phpstan/phpstan: ^2.2
- phpunit/phpunit: ^11.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
composer require flyo/nitro-yii2
add the module to your config
'modules' => [ 'flyo' => [ 'class' => \Flyo\Yii\Module::class, 'token' => 'YOUR_TOKEN', 'liveEdit' => !YII_ENV_PROD, // this is the default behavior, set to `true` to enable live edit in production as well ] ]
add the cms page resolve to your views in the folder /views/nitro.php, all the routes from flyo nitro will now be resolved into this view file:
<?php use Flyo\Yii\Widgets\PageWidget; /** @var \Flyo\Model\Page $page */ ?> <h1><?= $page->getTitle(); ?> <?= PageWidget::widget(['page' => $page]); ?>
In order to render those blocks use the Flyo\Yii\Widgets\PageWidget which will lookup all blocks inside the folder /views/flyo/*, so for instance you have a HeroTeaser component defined in flyo the view file is stored in /views/flyo/HeroTeaser.php with example content:
/** @var \Flyo\Model\Block $block */ print_r($block->getContent()); print_r($block->getConfig()); print_r($block->getItems()); print_r($block->getSlots());
Layout
Generate a navigation in the layout file, use the NavWidget:
<?php $nav = ContainerWidget::begin(['identifier' => 'main']) ?> <ul> <?php foreach ($nav->getItems() as $item): ?> <li><?= Html::a($item->getLabel(), $item->getHref()); ?></li> <?php endforeach; ?> </ul> <?php $nav::end(); ?>
Layout blocks with children
An example where a block contains child blocks, defined in the slot content:
<?php use Flyo\Yii\Widgets\BlockWidget; /** @var \Flyo\Model\Block $block */ $config = $block->getConfig(); ?> <div class="container"> <?php foreach ($block->getSlots()['content']->getContent() as $childBlock): ?> <div class="w-full"> <?= BlockWidget::widget([ 'block' => $childBlock, ]); ?> </div> <?php endforeach; ?> </div> <?php SectionWidget::end(); ?>
Extend existing Routes
Its possible to extend the routing system for existing pages. This can help when building dynamic sub pages which need to ensure that you are still on the same CMS page (not not entity detail), in order to do add the following url rule in the UrlManager section:
'<path:(the-requested-slug)>/<slug:[a-z\-]+>' => 'flyo/nitro/index',
In order to link to extended route, its not possible to use Url::toRoute, since this is a fixed rule in routes anyhow you have to use:
<a href="/the-requested-slug/<?= ...; ?>">Detail</a>
Live Edit
As long as the liveEdit module property is enabled, the module registers the Nitro JS Bridge together with its
boot script (page refresh, scroll to block, editor handshake and the click handlers for all elements with a
data-flyo-uid attribute) whenever a page is rendered by a web application. This happens independently of the
Editable widget, therefore live edit also works in projects which mark their blocks by hand or do not mark them
at all, and it is switched on and off in one single place.
Yii2 Widget: Editable
This widget makes Flyo blocks editable inside the Flyo preview iframe by rendering the data-flyo-uid marker,
which is picked up by the bridge registered through Live Edit.
Usage
Wrap content
<?php use Flyo\Yii\Widgets\Editable; ?> <?php Editable::begin(['block' => $block]); ?> <h2><?= $block->getTitle(); ?></h2> <p><?= $block->getText(); ?></p> <?php Editable::end(); ?>
Renders:
<div data-flyo-uid="block-uid-here">
<h2>…</h2>
<p>…</p>
</div>
Attribute only
If you already have a wrapper element, use the static helper:
<section <?= Editable::attr($block) ?>> <h1><?= $block->getTitle(); ?></h1> </section>
Renders:
<section data-flyo-uid="block-uid-here">
<h1>…</h1>
</section>
Notes
Highlighting/click-to-edit works only inside Flyo’s preview iframe.
Outside preview, the page behaves normally.
Entity Detail and Draft Links
An entity detail is rendered with the EntityAction, the finder callable resolves the entity from the api:
public function actions() { return [ 'detail' => [ 'class' => \Flyo\Yii\Actions\EntityAction::class, 'finder' => fn (\Flyo\Api\EntitiesApi $api) => $api->entityBySlug(Yii::$app->request->get('slug')), ], ]; }
Flyo can hand out a draft link for an entity which is still offline: an expiring snapshot addressed by a token
which takes the place of the unique id or the slug, so it is resolved through the very same call. Two things to keep
in mind: a draft token does not look like a normal slug or unique id, therefore an url rule which validates the
parameter against a pattern has to let it through, and typeId does not apply to a draft token.
Whenever the api answers with a draft, the action turns every cache layer off for that request — server page
cache, cdn cache and client cache — and the response is sent with Cache-Control: no-store (plus no-store for the
cdn headers and without the Last-Modified / Etag validators). A draft is deliberately never stored anywhere, so
there is no copy left which could outlive the draft or hide a change made in the meantime.
Because that decision can only be made after the entity has been resolved, controllers which serve an EntityAction
have to use Flyo\Yii\Filters\NitroPageCache instead of yii\filters\PageCache. It is the same filter, but it
throws the recorded output away when the cache has been disabled during the request:
public function behaviors() { return [ [ 'class' => \Flyo\Yii\Filters\NitroPageCache::class, 'only' => ['detail'], 'enabled' => YII_ENV_PROD && \Flyo\Yii\Module::getInstance()->serverPageCache, 'duration' => \Flyo\Yii\Module::getInstance()->serverPageCacheDuration, 'dependency' => new \Flyo\Yii\Cache\VersionCacheDependency(), 'variations' => [Yii::$app->request->getQueryParam('slug')], ], ]; }
In the view the draft state is available on the entity, use it to render a hint for the editor:
<?php if ($entity->getIsDraft()): ?> <p>Draft preview, this entity is not online yet. <?php if ($expiresAt = $entity->getDraftExpiresAt()): ?> The link expires on <?= Yii::$app->formatter->asDatetime((int) $expiresAt); ?>. <?php endif; ?> </p> <?php endif; ?>
Any action which must not be cached can use the same switch: \Flyo\Yii\Module::getInstance()->disableCache();.