自定义字段 - 第7集 第1部分:一个自定义字段统治所有
正如你可能已经注意到的,本集标题的灵感来自两部著名电影/书籍:
- "一个自定义字段统治所有"是对JRR托尔金的《指环王》的引用。这仅仅是因为我们在这里谈论Joomla 4中引入的新自定义字段类型:"子表单"......这是最强大的,实际上可以统治所有其他字段 :)
- "第7集 第1部分"是对JK罗琳著名故事的引用:就像哈利·波特一样,第七和最后一集有点太长,无法放入一篇文章中。所以好消息是:原本应该是在Joomla社区杂志中关于自定义字段的最后一集将分为两部分 ;)
Joomla 3中的“可重复”类型自定义字段
正如你可能已经注意到的,在某个时候Joomla 3引入了一种新的自定义字段类型:可重复字段。正如其名所示,它允许重复一个字段或——更确切地说——一个包含多个字段的块。
让我们举一个例子。假设你正在构建一个网站,其中包含食谱,并想使用自定义字段来列出成分
- 在可重复字段之前,唯一的办法是创建一串自定义字段,例如
- 成分1名称
- 成分1数量
- 成分2名称
- 成分2数量
- 成分3名称
- 成分3数量
- 等等
实际上,如果你有一个包含20个成分的食谱,你需要创建20个"成分X名称"和20个"成分X数量"……即使所有其他食谱的最大成分数不超过5。
有过这样的经历,也做过这样的事情 :)
- 多亏了这种名为"可重复字段"的新自定义字段类型,你只需要创建一个单独的自定义字段"成分"。在其配置中,你将指定它由两个字段组成:"名称"和"数量"。
一切就绪!无论有多少(最大)配料,你都能应对
在 Joomla 3 中的某个时刻引入的这种可重复的自定义字段具有以下特点:
- 一个巨大的优点:它确实开辟了一个新的可能性领域
- 一个缺点:只能使用以下(原生)类型:
- 编辑器
- 媒体
- 数字
- 文本
- 文本区域
所以实际上,它并不完全“通用”,因为你不能有
- 所有其他原生的自定义字段类型,如列表、单选按钮、颜色等
- 也没有任何第三方类型自定义字段
(请参阅关于自定义字段的上一集,了解可用的自定义字段列表,包括免费或付费的) 典型示例包括- 视频
- 地图
- 相关文章
- …
- 也没有为特定用例设计的自定义字段类型
(自定义字段实际上是非常小的插件:即使不是经验丰富的编码者,也很容易复制现有类型以定制以满足您的需求)
这就是为什么不同的人提出了拥有一个“真正可重复”的自定义字段的想法,其中您可以选择网站上可用的任何其他自定义字段,无论是原生的/第三方的/定制的。
尽管如此,由于 Joomla 4 已经在眼前,因此无法在 Joomla 3 中引入该新功能:
- Joomla 4 在那时已经(真的)在眼前
- 并且任何添加到 Joomla 3 中的新功能都需要一个新的 3.x 版本,从而间接延迟了 Joomla 4
所以简而言之:类型为“可重复”的自定义字段
- 在 Joomla 4 中已被删除
- 以替换其更通用的形式:类型为“子表”的自定义字段
Joomla 4 中的“子表”自定义字段
一般用例
类型为“子表”的自定义字段可以用作什么用例?
实际上,答案就像乐高©一样:您可以真正构建您所能想到的任何东西。
我的典型用例是一个短片节。不同的文化场馆组织会议,每个会议包括
- 一个日期/时间
- 一系列短片,我们通常展示
- 标题
- 时长
- 一个不同图片的相册
- 一个视频
- …
所以正如你所猜到的
- 一个给定会议的影片数量没有预定义(可以是1到15之间的任何东西)
- 与影片相关的某些字段可以是原生的自定义字段(例如标题和时长),但其他则是更高级的第三方自定义字段(例如相册和视频)
此演示用例:轮播图
为了这次演示,我们将构建一个轮播图。
有多个原因选择轮播图
- 对于这个例子,我们只需要原生的自定义字段。这意味着您可以立即复制此演示
- 它不需要任何不在 Joomla 4 中可用的第三方库
- 最后但同样重要的是,它还将是一个展示如何正确使用 Joomla 4 做某些事情的机会,例如
- 在覆盖/替代布局中添加 CSS
- 启用默认随 Joomla 4 一起提供的 Bootstrap 5 JavaScript 中的必要部分
当然,您可能不喜欢轮播图,或者您永远不会需要轮播图。我尊重这一点。不过,轮播图仍然是演示的一个很好的例子 :)
如果您有其他用例,请发表建设性的评论并解释您所取得的成果(如果可能,附上截图)。这可以为读者提供更多想法。
此外,下面的代码可能并不完美。当然,我欢迎所有改进的建议 ;-) 但毕竟,这也是当前文章的重点:即使像我这样的非程序员也能轻松地进行覆盖并构建强大的东西,这得益于Joomla的功能和灵活性。
逐步操作过程
步骤 1 - 创建基本的自定义字段
第一步将是创建用于我们的轮播图的所需自定义字段。
对于我们想要的轮播图的每一张幻灯片
- 幻灯片标题 => 文本类型自定义字段
- 幻灯片持续时间 => 整数类型自定义字段
- 幻灯片图片 => 媒体类型自定义字段
由于我是在一个新创建的Joomla 4网站上创建这些字段,这些自定义字段分别具有ID 1、2和3。如果你之前已经创建了自定义字段,它们将具有其他ID。当然,这不是问题。你只需要在下面的代码中调整相应的ID。
通常,对于每个自定义字段,你可以指定
- 字段组(如果你喜欢创建一个来组织你的网站)
- 访问权限(公共、访客、超级用户等)
- 语言
但在“常规”选项卡上,请注意名为“仅用于子表单”的新开关。在这个例子中,我想启用这个开关,因为这三个自定义字段将仅在子表单类型的自定义字段上下文中使用。请注意,启用此开关将隐藏分类分配字段,这是非常合乎逻辑的:无论如何,你不想直接在文章编辑表单上显示这些自定义字段,因为你的目的是显示“父”子表单类型的自定义字段。
步骤 2 - 创建子表单类型的自定义字段
现在是最激动人心的部分:让我们创建子表单类型的自定义字段。在屏幕底部的“字段”区域中,添加上面创建的不同基本自定义字段。
根据你的偏好,你可以将此自定义字段分配给所有分类或仅分配给所选分类。
最终,你将得到以下自定义字段列表(注意:在第一部分中,我们不使用“幻灯片自由文本(编辑器)”自定义字段。所以请忽略它,直到第二部分)
步骤 3 - 创建一些文章
创建一个新分类,并在其中创建一些文章。对于每一篇,至少填写以下内容
- 标题(以及别名)
- 子表单类型的自定义字段,其中包含多个标题/持续时间/图片值
步骤 4 - 创建菜单项
为了在前端显示我们的文章,让我们创建一个指向所需分类的博客类型菜单项。
步骤 5 - 创建自定义字段的替代布局
有两种方法可以实现这一点
- 手动:请参阅以下2021年5月发布的Joomla社区杂志文章中的“创建自定义字段的替代布局”部分:探索核心!通过自定义字段丰富你的内容或设计
- 通常通过Joomla的界面
- 转到系统 > 网站模板 > Cassiopeia Details and Files > 创建覆盖 > 布局 > com_fields
- 点击“field”
- 你将收到以下确认消息:“在 /templates/cassiopeia/html/layouts/com_fields/field 中创建覆盖”
无论你选择哪种方式,都有两种方法来重命名/编辑默认命名为“render.php”的新创建的文件
- 或者通过保持Joomla的界面 > 标签编辑器 > html > 布局 > com_fields > field
- 或者通过你的(s)FTP客户端或IDE(这是我首选的方法,因为它更容易进行和撤销更改)
如果您不重命名那个"render.php"文件,您对其所做的任何更改都将适用于所有情况下所有的自定义字段。这被称为“覆盖”。
我们想要的类似但略有不同:我们希望我们的更改不是默认应用,而只是在我们想要的时候应用。换句话说,我们希望能够在某些情况下分配我们的文件。这被称为“备用布局”(或“替代布局”)。
这是一个两步的过程
- 首先,将“render.php”文件重命名为某个明确的名称。例如:marc.php、carousel.php或在这个例子中rawvalue.php。注意,文件名中允许使用连字符(但据我所知不允许使用下划线)
- 然后编辑所选择的自定义字段,转到选项卡选项 > 渲染选项 > 布局。在那里,您将看到一个下拉菜单,列出所有可用的备用布局。选择新创建并重命名的文件
请参阅下面的相应截图
步骤6 - 备用布局 - 版本1 - 原始值
现在是我们编辑备用布局的时候了
<?php
/**
* @package Joomla.Site
* @subpackage com_fields
*
* @copyright (C) 2016 Open Source Matters, Inc. <https://www.joomla.net.cn>
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
defined('_JEXEC') or die;
use Joomla\CMS\Language\Text;
if (!array_key_exists('field', $displayData))
{
return;
}
$field = $displayData['field'];
$label = Text::_($field->label);
$value = $field->value;
$showLabel = $field->params->get('showlabel');
$prefix = Text::plural($field->params->get('prefix'), $value);
$suffix = Text::plural($field->params->get('suffix'), $value);
$labelClass = $field->params->get('label_render_class');
$renderClass = $field->params->get('render_class');
if ($value == '')
{
return;
}
?>
<?php
// the block ABOVE comes from the original render.php file. The block BELOW is custom-made and replaces the rest of the original render.php file.
// Get the rawvalue and json_decode it
$rawvalue = $field->rawvalue;
$items = json_decode($rawvalue, true);
// this would display the value of the Custom Field taking into account the potential render class of the Custom Field
echo '<h2>Displaying the <strong>value</strong> of the Custom Field</h2>';
echo '<span class="field-value ' . $renderClass . '">' . $value . '</span>';
// this would display the rawvalue (json) of the Custom Field
echo '<h2>Displaying the <strong>rawvalue (json)</strong> of the Custom Field</h2>';
echo '<small><pre>'.print_r($items, true).'</pre></small>';
// this would display the rawvalue (json) as unordered list
echo '<h2>Displaying the <strong>rawvalue (json)</strong> as unordered list</h2>';
echo '<ul>';
foreach ($items as $item) {
echo
'<li>Slide<ul>'.
'<li>' . ( $item['field1'] ?? '' ) . '</li>' .
'<li>' . ( $item['field2'] ?? '' ) . '</li>' .
'<li>' . ( $item['field3']['imagefile'] ?? '' ) . '</li>' .
'</ul></li>';
}
echo '</ul>';
// this would display all what $displayData contains and that we could potentially use in an override / alternate layout
echo '<h2>Displaying the <strong>$displayData</strong> variable</h2>';
echo '<small><pre>'.print_r($displayData, true).'</pre></small>';
?>
如您所注意到的,我们保留了原始render.php文件的第一块,但我们调整了其余部分,以便获取CF的“原始值”(即在技术上写入数据库的内容),而不是其“值”(在Joomla的上下文中意味着“渲染值”)。
访问您站点的前端并查看结果。它将分别显示
- 自定义字段的值
- 自定义字段的原始值(json)
- 原始值(json)的无序列表
- 所有$displayData包含的内容,我们可以在覆盖或备用布局中使用
请参阅下面的相应截图
步骤7 - 备用布局 - 版本2 - 文章视图中的轮播
按照上面解释的程序创建一个新的备用布局。
将文件重命名为my-carousel-article-view-only.php(例如)并在其中粘贴以下代码。
<?php
defined('_JEXEC') or die;
if (!array_key_exists('field', $displayData))
{
return;
}
$field = $displayData['field'];
// Get the rawvalue
$rawvalue = $field->rawvalue ?? '';
$carouselContainerId = 'carousel-field'. $field->id . '-'. md5($rawvalue, false); // giving a unique ID to the carousel container. NB: the ID cannot start with a digit
// getting he article ID by making an override of components/com_fields/layouts/fields/render.php
echo $displayData['itemid'];
if ($rawvalue == '')
{
return;
}
?>
<?php
use Joomla\CMS\Factory; // necessary bc we use herafter Factory::getApplication() and Factory::getDocument()
$app = Factory::getApplication(); // JFactory is indeed deprecated in J!4
$view = $app->input->getCMD('view', ''); // "view" would output "article" or "category" for example
// $id = $app->input->getCMD('id', ''); // "id" gives the id of the category if blog view, the id of the article if article view. So not useful here
if ('article' !== $view) {
// return; // comment this line if you want the carousel to also display on the blog view for example
}
?>
<?php // https://docs.joomla.org/J4.x:Using_Bootstrap_Components_in_Joomla_4
\Joomla\CMS\HTML\HTMLHelper::_('bootstrap.carousel', '#' . $carouselContainerId, ['interval' => 3000, 'pause' => 'false']); // selector is necessary for the potentials Options to work ?>
<?php $items = json_decode($rawvalue, true); ?>
<div id="<?php echo $carouselContainerId; ?>" class="carousel slide carousel-fade" data-bs-ride="carousel">
<div class="carousel-indicators">
<?php $first=true; $i=0; ?>
<?php foreach($items as $item): ?>
<button <?php if ($first) {echo "class=\"active\""; $first=false;} ?> type="button" data-bs-target="#<?php echo $carouselContainerId; ?>" data-bs-slide-to="<?php echo $i; ?>" aria-current="true" aria-label="<?php echo "Slide " . $i+1; ?>"></button>
<?php $i=$i+1; ?>
<?php endforeach; ?>
</div>
<div class="carousel-inner">
<?php $first=true; ?>
<?php foreach($items as $item): ?>
<?php
// Note: field1 in $item['field1'] refers to the Custom Field having ID 1 - nothing to do with the Order of fields within the Custom Field of Type SubForm
// If necessary adapt the next three lines according to the ID of the Custom Fields on *your* site
$slideTitle = $item['field1'] ?? '';
$slideDuration = $item['field2'] ?? '1';
$slideImage = $item['field3']['imagefile'] ?? '';
?>
<div class="carousel-item <?php if ($first) {echo "active"; $first=false;} ?>" data-bs-interval="<?php echo 1000*$slideDuration; ?>">
<img class="d-block w-100" src="/<?php echo $slideImage; ?>" />
<div class="carousel-caption d-none d-md-block">
<h5><?php echo $slideTitle; ?></h5>
</div>
</div>
<?php endforeach; ?>
</div>
<button class="carousel-control-prev" type="button" data-bs-target="#<?php echo $carouselContainerId; ?>" data-bs-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span> <span class="visually-hidden">Previous</span>
</button>
<button class="carousel-control-next" type="button" data-bs-target="#<?php echo $carouselContainerId; ?>" data-bs-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span> <span class="visually-hidden">Next</span>
</button>
</div>
<?php
$carouselCSS = <<<MYCSS
/* example of CSS for bootstrap.carousel - adding a background to the carousel-caption - see https://ui.glass/generator/ for glassmorphism CSS */
.carousel-caption {
bottom: 40%; /* otherwise with our background too close from Indicators with the default 1.25rem */
left: 25%; /* to make it narrower than with the default 15% */
right: 25%; /* to make it narrower than with the default 15% */
backdrop-filter: blur(16px) saturate(180%);
-webkit-backdrop-filter: blur(16px) saturate(180%);
background-color: rgba(0,0,0,0.5);
border-radius: 12px;
border: 1px solid rgba(255, 255, 255, 0.125);
}
/* by default the custom fields appear in a Unordered List. To make the carousel start on the left and hide the bullet point we use a negative margin as a first approach */
li.field-entry.mycarousel {list-style-type: none; margin-left: -2rem}
MYCSS;
// use Joomla\CMS\Factory; // is already called above so commented here bc can only be put once in the file
$doc = Factory::getDocument();
$doc->addStyleDeclaration($carouselCSS); // CSS will be injected only once even if this layout is called multiple times on a page
// Ununeeded stuff, just needed during testing/development. Uncomment the 'return' to execute the code hereafter
return;
echo 'test';
?>
编辑您的子表单类型自定义字段并按上述说明分配新的备用布局。
访问您站点的前端并查看结果。
- 在博客视图中,自定义字段“轮播”不再显示。
这就是我们要表达的意思:)
我们将在当前第7集关于自定义字段的第2部分中看到原因。当然,我们也会提供解决方案;) - 打开该类别的其中一篇文章。在那里就是:我们完全功能的轮播!
这里是结果的一个动画GIF(图像质量有意识地较差,以保持文件尽可能轻)
关于代码的解释
按照出现的顺序,这里有一些关于代码的注释。
这是什么 ?? ''
在下一行我们看到 ?? ''
$rawvalue = $field->rawvalue ?? '';
这是一个简记法——称为空值合并运算符——允许在变量为NULL时分配一个值。更多信息请参阅https://php.ac.cn/manual/en/language.operators.comparison.php
Factory
use Joomla\CMS\Factory;
这一行是必要的,因为我们稍后将在代码中使用Factory::getApplication()和Factory::getDocument()
JFactory
JFactory在J!4中已被弃用。现在的正确代码如下
$app = Factory::getApplication();
getCMD('view', '')
在这个第一个例子中,我们不想在博客视图中显示CF。
那么我们如何知道我们是否处于“文章”视图或“类别”视图等情况下呢?
多亏了这个变量
$view = $app->input->getCMD('view', '');
仅在文章视图中显示(不在博客视图中显示)
多亏了以下行,代码的其余部分将被忽略。
请注意“Yoda 条件”,这是一种良好的编程实践,可以避免因打字错误而导致代码中的错误或意外结果:[Yoda 条件](https://en.wikipedia.org/wiki/Yoda_conditions)
if ('article' !== $view) { return;}
在J!4中调用Bootstrap 5附带的必要javascript
Joomla 4附带了Bootstrap 5。但为了使您的网站等性能更佳,BS5的javascript默认不加载。相反,您可以在自己的覆盖/替代布局中自行决定只加载您需要的内容以及何时何地需要它。
这正是以下行所做的,它调用了一个轮播图正常工作所需的内容
\Joomla\CMS\HTML\HTMLHelper::_('bootstrap.carousel', '#' . $carouselContainerId, ['interval' => 3000, 'pause' => 'false']);
有关更多信息,请参阅官方文档:[使用Bootstrap组件的官方文档](https://docs.joomla.org/J4.x:Using_Bootstrap_Components_in_Joomla_4)
轮播图的HTML代码
对于轮播图代码,我简单地从官方BS网站取了一个例子,并在他们的HTML代码中将所有给定的标题/图片等替换为从我们的CF(子表单类型)中获取的相应原始值。
请参见[Bootstrap轮播图的示例](https://bootstrap.ac.cn/docs/5.1/components/carousel/).
注意:$item['field1']中的field1字段指的是ID为1的自定义字段。这与子表单类型自定义字段中的字段顺序无关。如有必要,请根据您网站上自定义字段的ID调整代码。
添加自定义CSS
以下两行允许将我们的CSS注入页面的Head部分
$doc = Factory::getDocument();
$doc->addStyleDeclaration($carouselCSS);
注意:即使此布局在页面上被多次调用,CSS也只会注入一次
使用变量来存储CSS
有时在多行上编写CSS可能会很繁琐,具体取决于您使用的符号。在这里,我们选择使用“heredoc语法”的变量$carouselCSS。非常方便!
有关更多信息,请参阅[PHP手册](https://php.ac.cn/manual/en/language.types.string.php#language.types.string.syntax.heredoc).
返回
在文件末尾之前,有一个返回命令。这在测试时很有用,因为任何在下面的代码都将被忽略。因此,当您需要进行测试时,只需注释掉此行即可执行下面的测试代码。
return;
一些发表在Joomla社区杂志上的文章代表了作者在特定主题上的个人观点或经验,可能与Joomla项目的官方立场不一致
通过接受,您将访问由 https://magazine.joomla.net.cn/ 外部的第三方提供的服务
评论