与Joomla网络服务(API)玩耍 - 第4部分
[本文是系列文章的一部分]
8. 使用Joomla API直接从Google Sheet导入和更新文章
通过API仅用几行代码即可程序化地POST或PATCH单个文章,如上所示,这很棒……但在此阶段,实际应用中的增值相当有限。
确实,需要准备1个脚本只创建1篇文章似乎相当繁琐。但一旦你能做到这一点,你开始想“关于循环创建多篇文章怎么办”:)
当我第一次与Alexandre谈论这件事时,我就会很高兴只用CSV文件来做这件事
- 从工作表中导出CSV格式
- 然后在一个导入按钮上选择该文件
但Alexandre更进一步
- 为什么麻烦导出/导入CSV文件:如果你使用Google Sheet等,你可以通过一个简单的链接共享它……及其CSV版本
- 为什么只将文章导入到1个网站上:让我们在我们的Google Sheet中添加一列,以选择我们想在哪个网站上创建相应的文章
- 为什么限制导入到原生字段:让我们让它更容易导入(任意数量的)自定义字段
- 为什么不允许单元格中有json:因为以下字段以json格式存储在数据库中
- 简介图片 & 完整图片链接,以及它们的配置(描述、类、…)
- Urla, Urlb, Urlc
- 自定义字段类型为子表单
- 为什么要在Google Sheet中遵守某些列的顺序:让我们允许用户有任意顺序
8.1. 准备Google Sheets
8.1.1. 创建Google Sheets
创建一个Google Sheets
- 转到 https://drive.google.com/
- 点击新建按钮
- 选择Google Sheets
- 为新创建的Google Sheets命名
现在我们需要一些具有正确结构的样本数据来填充我们的Google Sheets。我们不想重新发明轮子,所以让我们看看以下脚本
特别是我注意到以下这一行 $csvUrl = 'https://docs.google.com/spreadsheets/d/1IJXrctQPnykw4NVcvthsJsf9GflYvb6W_-lEsavA6xE/pub?output=csv';
太好了,让我们把这个网址复制粘贴到浏览器中
https://docs.google.com/spreadsheets/d/1IJXrctQPnykw4NVcvthsJsf9GflYvb6W_-lEsavA6xE/pub?output=csv
这样我的浏览器就会下载一个CSV文件。我可以从这个文件开始……但更简单的方法是直接在浏览器中显示Google电子表格,只需将 ?output=csv
从URL的末尾删除。
换句话说,我打开
https://docs.google.com/spreadsheets/d/1IJXrctQPnykw4NVcvthsJsf9GflYvb6W_-lEsavA6xE/pub
然后
- 我把这个表的内容复制粘贴到我的空Google电子表格中
- 如果这些列还在那里,我会删除它们(它们实际上是在某个时候创建的,只是为了测试脚本)
- alias-seed
- seed
- article-subform-field(如果你想要导入存储在数据库中以JSON格式存储的自定义字段类型子表,这会很有用)
- 对于这里的演示,我只保留内容的前3行
8.1.2. 共享Google电子表格
在Google电子表格中
- 选择菜单文件 > 共享 > 在网络上发布
- 在弹出的窗口中
- 选择包含您的数据的表
- 选择CSV(而不是网页、PDF或其他)
- 点击发布按钮
- 复制现在在弹出的窗口中显示的URL(并将其粘贴到某处,我们稍后会用到它)
- 关闭窗口
在我们的情况下,URL是
因此,如果您想在浏览器中直接预览它,请拿走随后的内容 不 包括/pub
,即
屏幕截图(我的电脑显然是法语,但它非常直观)
8.2. 基本示例
8.2.1. 准备Google电子表格脚本
在您的网站上创建您的 Google电子表格 脚本
- 在您的网站上创建一个新文件(例如
api-google-sheet.php
在根目录下) - 访问 https://github.com/alexandreelise/j4x-api-examples/blob/master/using-raw-php/smart-add-edit-to-multiple-sites-from-csv-url.php
- 转到“使用PHP cURL函数”部分(因为我们这里需要一个独立的脚本,所以我们不使用Joomla框架版本)
- 复制“定义一些变量”的代码并将其粘贴到您的文件中
- 复制“Google电子表格”的代码并将其粘贴到文件末尾
自定义您的网站上的 Google电子表格 脚本。在您的文件中调整以下变量
$csvUrl
与我们从Google电子表格中获得的URL$baseUrl
。在我的情况下- 我进行了替换
'app-001' => 'https://app-001.example.org',
替换为'app-001' => 'https://api.joomlacustomfields.org',
(注意,不要有尾随斜杠!)- 我删除了包含
'app-002'
和'app-003'
的行,因为我只想导入一个网站,而不是脚本允许的3个(或更多) $token
。在我的情况下,我将'app-001' => 'yourapp001token',
替换为'app-001' => 'c2hhMjU2OjI3NDpmOGMyNGIyMmI4ZTI0YzhlN2VhMGI1YmI2MTk5ODdiODViZWIwYjBhMTkwYzYxMmZkMDEzYWUxNzg5MmE2YzNm',
- 我删除了包含
'app-002'
和'app-003'
的行,因为我只想导入一个网站,而不是脚本允许的3个(或更多)
8.2.2. 执行你的脚本
简单按照上面解释的方法执行你的脚本。
通常这个脚本不会显示任何内容,只是在后台运行。
但脚本还是显示了“原始结果”:这显然不是必要的,但它允许你直观地检查确实发生了某些操作(例如,一旦我可以看到某些行没有被处理,我就可以在Google Sheets中识别原因)。
注意:我已经注意到Google Sheets有时需要1分钟或更长时间才能实际更新CSV版本(即使其他Google Sheets的用户可以看到你的更改“实时”)。所以如果你执行脚本后没有看到任何变化,请稍等1分钟,然后重新启动它
8.2.3. 返回或刷新文章管理器
当打开文章管理器时,我们可以确实检查到
- 已经创建了新的文章。太好了!
当你打开其中的任何一个时,你还可以看到所有内容都存在
- 文章的简介文本
- 简介图片/完整图片的链接/描述/类/标题
- urla, urlb, urlc
注意
- 在这种情况下,对于简介图片和完整图片,我们只是在Google Sheets中放置了一个外部图片的链接
- 在现实生活中,人们可能希望
- 使用Joomla的媒体管理器来选择图片
- 或者使用比Google Sheets更强大的工具,比如AirTable,它允许在“单元格”中放置图片(请参阅AirTable脚本,该脚本甚至能够将Airtable图片物理复制到本地网站)
8.2.4. 如果你想稍后更新文章
你可能已经注意到Google Sheets的第一列是 id
,当然是对每个文章的ID。
实际上我们希望有一个既能创建又能更新文章的单个脚本。
但如何使脚本足够“智能”以实现这一点呢?
- 如果
id
为0,则脚本将创建一篇文章 - 如果
id
是其他任何数字,它将更新具有相应ID的文章
因此,如果将来我还有机会再次执行此脚本,我不想重新创建已经存在的文章。
因此,在我的Google表格中
- 我将每个已创建文章的
id
手动输入 - 并且如果我希望在下次运行脚本时创建新文章,我也可以通过给它们分配
id
0来实现 - 每次运行脚本时都这样做
以下是我的修改后的Google表格的示例,其中
- 我更改了前3篇文章的ID、标题和别名
- 添加了一行以创建新文章
再次运行脚本后的文章管理器中的结果
显然,在你的Google表格中,你可以更改任何字段的值。例如:语言设置为 *
(这意味着所有语言,因为它是以数据库中的方式编码的),但你也可以指定一种语言,如 fr-FR
8.2.5. 两个循环处理
脚本运行两个循环
- 首先创建所有要求创建的文章
- 但如果所需的别名已被占用,则无法创建文章
- 然后记住这一点
- 一旦第一个循环完成,它就会进行第二个循环,以便通过在所需别名中添加随机数来创建这些文章(在代码中查看//处理错误和重试部分)
8.2.6. 其他智能功能
请注意,脚本也被尽可能地制作成“智能”的,以适应不同的方面。例如
- 如果给定的行出现错误,脚本将简单地跳过该行,但会继续执行下一行。例如
- 存在一个类型为链接的自定义字段,但用户在相应的单元格中输入文本或输入电子邮件地址
但忘记了提及协议此电子邮件地址正在被保护免受垃圾邮件机器人攻击。您需要启用JavaScript才能查看它。 mailto:
) - 某些所需数据丢失
- 存在一个类型为链接的自定义字段,但用户在相应的单元格中输入文本或输入电子邮件地址
- 正如你所知道的那样,在给定的类别中,别名只能给一次
- 所以如果某个别名已经被指定,脚本将给建议的别名添加随机数,以便它仍然可以处理/创建。说明
8.3. 高级示例
以下步骤当然不是必要的:只有在您打算使用该脚本的情况下才适用,例如
- 导入其他本地字段,例如
publish_up
、publish_down
、featured_up
、featured_down
(为了找到这些字段的正确名称,只需查看数据库列...) - 导入自定义字段
8.3.1. 在Joomla中创建自定义字段
假设我们要为一支摇滚乐队创建一个网站。对于乐手来说,在Google Sheets中简单地添加他们的新音乐会比学习如何使用CMS要容易得多。
然后这些音乐会就可以轻松导入和/或更新到网站上。
因此,让我们创建一个新的类别 Concerts
并将其分配给以下3个自定义字段
- 日期和时间:类型为日历的CF,名称为
date-and-time
- 地点:类型为文本的CF,名称为
venue
- 事件链接:类型为URL的CF,名称为
link-to-event
8.3.2. 调整Google Sheets
由于我们创建了一个新的类别(ID为8的“Concert”),因此让我们将Google Sheets中 catid
列的值从2更改为8。
我们还在Google Sheets中创建以下列
publish_up
、publish_down
、featured_up
、featured_down
date-and-time
,venue
,link-to-event
注意:当您输入日期时,应采用以下格式:2022-11-23 20:00:00
(并且显然它导入的日期是您网站的真正时区,就像您从Joomla的后端输入它们一样)
这里有一些值的示例
8.3.3. 修改脚本
在您的脚本中
- 通过更改
$customFieldKeys = [];
到$customFieldKeys = ['date-and-time','venue','link-to-event'];
- 并在
$defaultKeys
数组内添加以下代码块
'publish_up',
'publish_down',
'featured_up',
'featured_down',
8.3.4. 返回或刷新文章管理器
搞定!
我们的分类确实已经更新了
我们的自定义字段确实已经更新了
我们的其他原生字段确实已经更新了
8.4. 特殊字段,如图片、自定义字段或子表单类型或URL
一些特殊字段包含“多个值”。那么如何处理这种情况呢?
假设您想导入一个图片或子表单类型的自定义字段。
如您所知,在数据库中这些字段以 json 格式 保存。
那么我们应该在Google表格单元格中输入什么?最简单的方法是保存一个真实的文章在网站上,然后查看数据库中相应的json。
请注意,在Google表格中我们不需要“转义 /
字符”。换句话说,我们只需输入 /
,而不是 \/
图片 的示例(包括简介图片和全文图片,以及它们的ALT、标题等)
{"image_intro":"images/test.jpg","image_intro_alt":"","float_intro":"","image_intro_caption":"","image_fulltext":"images/test.jpg","image_fulltext_alt":"","float_fulltext":"", "image_fulltext_caption":""}
一个 子表单类型的自定义字段 的示例,在这种情况下有2个自定义字段(field2 和 field7)和3个值(row0、row1、row2)- 注意:它也接受HTML标签,但我无法在这里的演示中粘贴它们
{"row0":{"field2":1410,"field1":"这是一篇关于这个甜点的摘要1。简短而甜美!”,“field7":"What’s up Super Joomlers! Alex here…自豪地成为Joomler。如今专注于Joomla! 4.x Web Services Apis。"}, “row1”:{"field2":1410,"field1":"这是一篇关于这个甜点的摘要2。简短而甜美!”,“field7":"What’s up Super Joomlers! Alex here…自豪地成为Joomler。如今专注于Joomla! 4.x Web Services Apis。"}, “row2”:{"field2":1410,"field1":"这是一篇关于这个甜点的摘要3。简短而甜美!”,“field7":"What’s up Super Joomlers! Alex here…自豪地成为Joomler。如今专注于Joomla! 4.x Web Services Apis。"}}}
以下是关于 urls 字段 的示例(这些URL A、URL B 和 URL C 是Joomla内置的)
{"urla":"https://alexandree.io","urlatext":"网站","targeta":"","urlb":"https://github.com/alexandreelise", "urlbtext":"Github","targetb":"","urlc":"https://twitter.com/mralexandrelise","urlctext":"Twitter", "targetc":""}
注意:我的编辑器在这里调整了我的代码片段。当然,所有 " 都是普通的双引号(不是斜体)。GitHub图标也是由编辑器添加的
8.5. 具有潜在多个值的字段,如列表或复选框类型的自定义字段
假设您有一个列表或复选框类型的自定义字段。对于这些CF
- 通常有一个选项允许或禁止多个值
- 当您创建您的建议选项时,对于每个选项,您需要指定
- 文本(即将在前端和后端显示的内容)
- 值(即写入数据库的内容)
在这种情况下
- 如果没有多个值
- 在Google表格中,您只需输入唯一的值(例如:巧克力)
- 如果您有多个值
- 在Google表格中,您应该输入 {"0":"薯条", "1":"巧克力", "2":"啤酒"},如果我想要的3个值是薯条、巧克力和啤酒
注意:目前脚本可能会抛出以下消息不能将stdClass类型的对象用作数组
,但显然这不会阻止代码执行和内容导入
- 在Google表格中,您应该输入 {"0":"薯条", "1":"巧克力", "2":"啤酒"},如果我想要的3个值是薯条、巧克力和啤酒
8.6. 具有潜在多个值的字段,如Regular Labs的自定义字段文章字段
Regular Labs的文章字段是一种强大的自定义字段类型,允许选择其他文章:https://regularlabs.com/articlesfield
一个很好的例子:对于一个图书馆网站,通常会有一个“书籍”类别和一个“作者”类别。然后您可以将每篇文章链接到一个或多个作者文章。
如果您有多个值,我已经成功地测试了以下2种方法(假设我们想链接到ID为12和13的文章)
- 在Google表格中,输入 12,13,然后同步。之后在后台编辑文章(这两个值确实出现了,但尚未正确保存到数据库中)并保存它
- 在Google表格中,直接以JSON格式输入值,即 {"0":"12", "1":"13"},然后同步
8.7. 性能
当我第一次开始使用这个脚本时,我正在导入100到200篇文章,一切正常。
然后我决定为了测试目的提高标准,尝试导入大约2600篇文章。
我认为脚本成功地创建了大约500篇文章(每篇文章有10个自定义字段)。确实,几分钟后脚本抛出了以下错误:请求超时
此请求处理时间过长,已被服务器超时。如果不应超时,请联系此网站管理员增加 '连接超时'。
因此,在我的托管服务上,我增加了以下设置
- 最大执行时间
- 最大输入时间
- 默认套接字超时
- 内存限制
这对我来说似乎解决了问题,因为现在
- 脚本在5分钟后仍然显示一个
请求超时
- 但实际上,过程仍在继续,我看到我的文章正在进一步更新
实际上,更新我所有2600篇文章大约需要13分钟(意味着平均每分钟200篇文章)。
如果需要导入更多文章,以下操作可能会有所帮助
- 禁用智能搜索(更准确地说,是禁用
智能搜索内容
和内容智能搜索
。可能其中之一就足够了,但我没有检查它们的确切作用) - 禁用版本控制
实际上,每次创建或更新文章都会触发智能搜索和版本控制,这可能会在处理下一篇文章之前消耗额外资源。
8.8. 使用API或直接在数据库中执行SQL查询的区别
有人可能会考虑直接使用SQL查询(基于某种方式与Google表格中的相同数据)来导入/更新他们的文章。
这样做确实会直接修改数据库…但不会触发Joomla(通常是 onContentAfterSave
)预定的所有操作,例如
- 智能搜索
- 版本控制
因此,如果您认为这些操作有意义,那么API会更好,因为它真的就像在后台手动编辑每篇文章一样。
但是,对于 纯性能 来说,直接写入数据库只能更加高效。
9. 故障排除
如果在启动脚本时没有创建或更新任何文章,您可以这样做
- 检查脚本是否一切正常(您的API密钥、Google表格的链接、Google表格的结构)
- 检查日志
- 转到系统 > 全局配置 > 标签日志 > 启用
记录几乎一切
+ 确保将日志优先级
设置为所有
- 重新启动脚本
- 查看 /administrator/logs/everything.php
- 转到系统 > 全局配置 > 标签日志 > 启用
注意:如果您想使生活更轻松,请安装Yannick Gaultier制作的这个小免费插件,它允许您直接从后端查看/下载/删除Joomla生成的所有日志文件:https://weeblr.com/joomla-seo/4logs-simple-free-logs-viewer
以下是我自己遇到的两个实际问题的两个例子。
9.1. 示例 1 – 需要某些自定义字段
有一天我在/administrator/logs/everything.php中遇到了以下错误消息
字段:datetime priority clientip category message 2023-01-18T16:11:36+00:00 CRITICAL 错误 未捕获的异常类型 Tobscure\JsonApi\Exception\InvalidParameterException 抛出错误信息 "必需字段:事件类别"。堆栈跟踪:#0 [ROOT]/libraries/src/MVC/Controller/ApiController.php(362): Joomla\CMS\MVC\Controller\ApiController->save()
实际上,自定义字段“事件类别”是必需的,但在我的Google Sheets中该字段是空的。
因此,在这种情况下,您既不能通过界面保存文章,也不能通过API进行操作...
9.2. 示例 2 – 由第三方扩展引起的冲突
我已遇到过扩展(在我案例中是CFI插件)阻止API工作的情况,这我也是通过日志文件发现的。
我的解决方案是将插件限制为超级用户,这样它就不会干扰。
9.3. 示例 3 – Google Sheets中的特殊字符
以下情况相当棘手:某些文章可以通过脚本正常导入...但有些其他文章会抛出这样的错误
致命错误:未捕获的 ValueError: array_combine(): 第1个参数($keys)和第2个参数($values)必须具有相同数量的元素 in /home/my-sync-file.php:151 堆栈跟踪:#0 /home/my-sync-file.php(151): array_combine(Array, Array) #1 /home/my-sync-file.php(194): {closure}('https://docs.go...', Array, Object(Closure)) #2 {main} 抛出在 /home/my-sync-file.php 行 151
过了一段时间,我意识到用户使用了一种“花括号单引号”而不是“常规单引号”...而“花括号单引号”会被解释为字段的结尾,导致差异
l’oasis
而不是 l'oasis
解决这个问题的唯一方法是在整个Google Sheets中进行搜索和替换
9.4. 示例 4 – Google Sheets中的换行符
当您在Google Sheets的某个单元格中键入文本时,如果您想在该单元格中创建新行,您只需按下 CTRL+ENTER
。
显然(我应该进一步测试以100%确定),如果像标题这样的纯文本字段以换行符结尾,这会导致导入/同步相关文章失败。
对于类型为编辑器的自定义字段,这不会成问题 - 我已经测试过 - 虽然您会在Joomla中丢失“换行”,文章将变成一个单一的、很长的段落(想解决这个问题?请看下一节的小贴士)。
10. 最终提示
如果您想在Google Sheets的单元格中拥有多个段落,这里有一个最后的提示
如果您想自动将用户在Google Sheets单元格中键入的所有“换行符”替换为适当的HTML标签,如 <p>
和 </p>
,您可以使用以下正则表达式(REGEX),其中“换行符”由 \n
标识
=if(A1="";"";"<p>"®EXREPLACE(A1;"\n";"</p><p>")&"</p>")
在Joomla社区杂志上发表的一些文章代表了作者对特定主题的个人观点或经验,可能不符合Joomla项目的官方立场。
通过接受,您将访问由 https://magazine.joomla.net.cn/ 外部的第三方提供的服务
评论 2
我需要以编程方式创建1500篇文章,这似乎正是我所需要的。然而,当我尝试遵循文章时,我发现8.1.1中提到的谷歌文档已经不存在。
嗨,
感谢您的反馈。
不幸的是,那份谷歌电子表格不是我的,其所有者似乎已经将其删除
但没关系,我已经重新创建了一个,并调整了演示。
此外,我发现图片链接已损坏,所以我刚刚修复了这个问题。
我们必须等待杂志团队批准新版本,但无论如何,您始终可以在这里查看完整最新版本
https://slides.woluweb.be/api/api.html