Following the docs on the offical CraftCMS website i'm not able to get "Native Layout Elements" saved. My field body
is always empty. Any idea what i'm missing here?
Relevant Part of Plugin base class
<?php
namespace tkf\craftfirebasemessaging;
use Craft;
use craft\base\Model;
use craft\base\Plugin;
use craft\events\RegisterComponentTypesEvent;
use craft\events\RegisterUrlRulesEvent;
use craft\services\Elements;
use craft\web\UrlManager;
use craft\models\FieldLayout;
use craft\events\DefineFieldLayoutFieldsEvent;
use craft\fieldlayoutelements\TitleField;
use craft\fieldlayoutelements\TextareaField;
use tkf\craftfirebasemessaging\elements\Notification;
use tkf\craftfirebasemessaging\models\Settings;
use tkf\craftfirebasemessaging\services\Messaging;
use tkf\craftfirebasemessaging\services\Tokens;
use yii\base\Event;
class FirebaseMessaging extends Plugin
{
public string $schemaVersion = '1.0.0';
public bool $hasCpSettings = true;
public static function config(): array
{
return [
'components' => ['tokens' => Tokens::class, 'messaging' => Messaging::class],
];
}
public function init(): void
{
parent::init();
// Defer most setup tasks until Craft is fully initialized
Craft::$app->onInit(function() {
$this->attachEventHandlers();
// ...
});
}
protected function createSettingsModel(): ?Model
{
return Craft::createObject(Settings::class);
}
protected function settingsHtml(): ?string
{
return Craft::$app->view->renderTemplate('firebase-messaging/_settings.twig', [
'plugin' => $this,
'settings' => $this->getSettings(),
]);
}
private function attachEventHandlers(): void
{
Event::on(
UrlManager::class,
UrlManager::EVENT_REGISTER_SITE_URL_RULES,
function (RegisterUrlRulesEvent $event) {
$event->rules['api/v1/firebase-token'] = 'firebase-messaging/tokens/index';
}
);
Event::on(
Elements::class,
Elements::EVENT_REGISTER_ELEMENT_TYPES,
function (RegisterComponentTypesEvent $event) {
$event->types[] = Notification::class;
}
);
Event::on(
UrlManager::class,
UrlManager::EVENT_REGISTER_CP_URL_RULES,
function (RegisterUrlRulesEvent $event) {
$event->rules['notifications'] = ['template' => 'firebase-messaging/notifications/_index.twig'];
$event->rules['notifications/<elementId:\\d+>'] = 'elements/edit';
}
);
Event::on(
FieldLayout::class,
FieldLayout::EVENT_DEFINE_NATIVE_FIELDS,
function(DefineFieldLayoutFieldsEvent $event) {
/** @var FieldLayout $fieldLayout */
$fieldLayout = $event->sender;
$event->fields[] = new TextareaField([
'attribute' => 'body',
'required' => true,
'mandatory' => true,
'label' => 'Body',
'instructions' => 'Enter a description.',
]);
}
);
}
}
Relevant Part of my Element Class
<?php
namespace tkf\craftfirebasemessaging\elements;
use Craft;
use craft\base\Element;
use craft\elements\User;
use craft\elements\conditions\ElementConditionInterface;
use craft\elements\db\ElementQueryInterface;
use craft\helpers\UrlHelper;
use craft\helpers\Db;
use craft\web\CpScreenResponseBehavior;
use tkf\craftfirebasemessaging\elements\conditions\NotificationCondition;
use tkf\craftfirebasemessaging\elements\db\NotificationQuery;
use yii\web\Response;
use craft\models\FieldLayout;
/**
* Notification element type
*/
class Notification extends Element
{
public static function displayName(): string
{
return Craft::t('firebase-messaging', 'Notification');
}
public static function lowerDisplayName(): string
{
return Craft::t('firebase-messaging', 'notification');
}
public static function pluralDisplayName(): string
{
return Craft::t('firebase-messaging', 'Notifications');
}
public static function pluralLowerDisplayName(): string
{
return Craft::t('firebase-messaging', 'notifications');
}
public static function refHandle(): ?string
{
return 'notification';
}
public static function trackChanges(): bool
{
return false;
}
public static function hasContent(): bool
{
return false;
}
public static function hasTitles(): bool
{
return true;
}
public static function hasUris(): bool
{
return false;
}
public static function isLocalized(): bool
{
return false;
}
public static function hasStatuses(): bool
{
return true;
}
public ?string $body = null;
public function getFieldLayout(): FieldLayout
{
return \Craft::$app->getFields()->getLayoutByType(self::class);
}
public static function find(): ElementQueryInterface
{
return Craft::createObject(NotificationQuery::class, [static::class]);
}
public static function createCondition(): ElementConditionInterface
{
return Craft::createObject(NotificationCondition::class, [static::class]);
}
protected static function defineSources(string $context): array
{
return [
[
'key' => '*',
'label' => Craft::t('firebase-messaging', 'All notifications'),
],
];
}
protected static function defineActions(string $source): array
{
// List any bulk element actions here
return [];
}
protected static function includeSetStatusAction(): bool
{
return true;
}
protected static function defineSortOptions(): array
{
return [
'title' => Craft::t('app', 'Title'),
[
'label' => Craft::t('app', 'Date Created'),
'orderBy' => 'elements.dateCreated',
'attribute' => 'dateCreated',
'defaultDir' => 'desc',
],
[
'label' => Craft::t('app', 'Date Updated'),
'orderBy' => 'elements.dateUpdated',
'attribute' => 'dateUpdated',
'defaultDir' => 'desc',
],
[
'label' => Craft::t('app', 'ID'),
'orderBy' => 'elements.id',
'attribute' => 'id',
],
];
}
protected static function defineTableAttributes(): array
{
return [
'id' => ['label' => Craft::t('app', 'ID')],
'dateCreated' => ['label' => Craft::t('app', 'Date Created')],
'dateUpdated' => ['label' => Craft::t('app', 'Date Updated')],
];
}
protected static function defineDefaultTableAttributes(string $source): array
{
return [
'link'
];
}
protected function defineRules(): array
{
return array_merge(parent::defineRules(), [
// ...
]);
}
public function getUriFormat(): ?string
{
// If notifications should have URLs, define their URI format here
return null;
}
protected function previewTargets(): array
{
$previewTargets = [];
$url = $this->getUrl();
if ($url) {
$previewTargets[] = [
'label' => Craft::t('app', 'Primary {type} page', [
'type' => self::lowerDisplayName(),
]),
'url' => $url,
];
}
return $previewTargets;
}
protected function route(): array|string|null
{
// Define how notifications should be routed when their URLs are requested
return [
'templates/render',
[
'template' => 'site/template/path',
'variables' => ['notification' => $this],
]
];
}
public function canView(User $user): bool
{
if (parent::canView($user)) {
return true;
}
// todo: implement user permissions
return $user->can('viewNotifications');
}
public function canSave(User $user): bool
{
if (parent::canSave($user)) {
return true;
}
// todo: implement user permissions
return $user->can('saveNotifications');
}
public function canDuplicate(User $user): bool
{
if (parent::canDuplicate($user)) {
return true;
}
// todo: implement user permissions
return $user->can('saveNotifications');
}
public function canDelete(User $user): bool
{
if (parent::canSave($user)) {
return true;
}
// todo: implement user permissions
return $user->can('deleteNotifications');
}
public function canCreateDrafts(User $user): bool
{
return true;
}
protected function cpEditUrl(): ?string
{
return sprintf('notifications/%s', $this->getCanonicalId());
}
public function getPostEditUrl(): ?string
{
return UrlHelper::cpUrl('notifications');
}
public function prepareEditScreen(Response $response, string $containerId): void
{
/** @var Response|CpScreenResponseBehavior $response */
$response->crumbs([
[
'label' => self::pluralDisplayName(),
'url' => UrlHelper::cpUrl('notifications'),
]
]);
}
public function afterSave(bool $isNew): void
{
if (!$this->propagating) {
Db::upsert('{{%firebase-messaging_notifications}}', [
'id' => $this->id,
], [
'body' => $this->body,
]);
}
parent::afterSave($isNew);
}
}