前回ご紹介したプラグイン。URLを入力すると美麗なバナーを制作してくれるというものでした。
今回はこれを機能拡張します。具体的にいうと、
・ 画像URLを入力するとそちらの画像パスを優先し、
・ 画像URLが空値なら、OGP画像を探しに行く
というものです。
OGP画像を準備していないサイト・ページがあったり、あるいは巧く取得できなかった場合に画像の絶対パスを指定することで必ずリカバリーができる、というものです。
実例をお見せしましょう。
今回用意したVer2の実装はこんな感じ。
記事のURLとは別に、画像のURLが入力できるようになっています。

そして、画像URLが入力されていない場合はOGP画像を探しに行くし、そうでなければURLどおりの画像を表示する、というバナーです。
そして、画像URLを入力した結果がこちら:
前回のコードより安定度も増し、一層盤石なプラグインとなりました。
画像の指定があればそれを優先、なければOGP画像を探す、それでもなければ画像なしのバナーとなります。
サンプルコードを以下に示します。どんなWordPressデザインでもそれなりに動くと思いますのでご活用されてください。
設置パスは前回同様:
–フォルダパス–
wordpress
┗wp-content
┗plugins
┗url-banner-block
┣ ★block.js
┗ ★url-banner-block.php
block.js
(function (blocks, element, components, apiFetch) {
var el = element.createElement;
var TextControl = components.TextControl;
blocks.registerBlockType('myplugin/url-banner', {
title: 'URLリンクバナー',
icon: 'smiley',
category: 'widgets',
attributes: {
url: { type: 'string', default: '' },
imageUrl: { type: 'string', default: '' },
preview: { type: 'object', default: null }
},
edit: function (props) {
function fetchPreview(value) {
if (!value) return;
apiFetch({
path: '/url-banner/v1/preview?url=' + encodeURIComponent(value)
}).then(function (data) {
props.setAttributes({
preview: data
});
}).catch(function () {
props.setAttributes({
preview: null
});
});
}
function getHostname(url) {
try {
return new URL(url).hostname;
} catch (e) {
return '';
}
}
var preview = props.attributes.preview;
var imageSrc =
props.attributes.imageUrl ||
(preview && preview.image);
return el('div', {},
// URL入力
el(TextControl, {
label: '記事URL',
value: props.attributes.url,
onChange: function (value) {
props.setAttributes({ url: value });
if (value) {
fetchPreview(value);
}
}
}),
// 画像URL入力
el(TextControl, {
label: '画像URL(任意・OGPより優先)',
value: props.attributes.imageUrl,
onChange: function (value) {
props.setAttributes({
imageUrl: value
});
}
}),
preview &&
el('a',
{
href: props.attributes.url,
target: '_blank',
rel: 'noopener noreferrer',
style: {
display: 'flex',
border: '1px solid #ddd',
borderRadius: '8px',
overflow: 'hidden',
textDecoration: 'none',
color: '#000',
marginTop: '12px',
backgroundColor: '#fff'
}
},
// 画像
imageSrc &&
el('div',
{
style: {
flex: '0 0 120px',
backgroundColor: '#f5f5f5'
}
},
el('img', {
src: imageSrc,
style: {
width: '120px',
height: '100%',
objectFit: 'cover',
display: 'block'
}
})
),
// テキスト
el('div',
{
style: {
padding: '12px',
flex: '1'
}
},
el('div',
{
style: {
fontWeight: 'bold',
marginBottom: '6px',
fontSize: '14px',
lineHeight: '1.4'
}
},
preview.title
),
preview.description &&
el('div',
{
style: {
fontSize: '12px',
color: '#555',
lineHeight: '1.4'
}
},
preview.description
),
el('div',
{
style: {
fontSize: '11px',
color: '#888',
marginTop: '6px'
}
},
getHostname(props.attributes.url)
)
)
)
);
},
save: function () {
return null;
}
});
})(
window.wp.blocks,
window.wp.element,
window.wp.components,
window.wp.apiFetch
);
url-banner-block.php
<?php
/*
Plugin Name: URL Banner Block
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/*
|--------------------------------------------------------------------------
| ブロック登録
|--------------------------------------------------------------------------
*/
function url_banner_block_init() {
wp_register_script(
'url-banner-block',
plugins_url( 'block.js', __FILE__ ),
array( 'wp-blocks', 'wp-element', 'wp-components', 'wp-api-fetch' ),
filemtime( plugin_dir_path( __FILE__ ) . 'block.js' )
);
register_block_type( 'myplugin/url-banner', array(
'editor_script' => 'url-banner-block',
'render_callback' => 'url_banner_render',
) );
}
add_action( 'init', 'url_banner_block_init' );
/*
|--------------------------------------------------------------------------
| REST API(プレビュー取得)
|--------------------------------------------------------------------------
*/
add_action( 'rest_api_init', function () {
register_rest_route( 'url-banner/v1', '/preview', array(
'methods' => 'GET',
'callback' => 'url_banner_preview_callback',
'permission_callback' => '__return_true',
) );
});
function url_banner_preview_callback( WP_REST_Request $request ) {
$url = esc_url_raw( $request->get_param( 'url' ) );
if ( empty( $url ) ) {
return array(
'title' => '',
'description' => '',
'image' => ''
);
}
$response = wp_remote_get( $url, array(
'timeout' => 15,
'redirection' => 5,
'user-agent' => 'WordPress URL Banner Preview'
) );
if ( is_wp_error( $response ) ) {
return array(
'title' => 'Error',
'description' => $response->get_error_message(),
'image' => ''
);
}
$body = wp_remote_retrieve_body( $response );
if ( empty( $body ) ) {
return array(
'title' => '',
'description' => '',
'image' => ''
);
}
libxml_use_internal_errors( true );
$dom = new DOMDocument();
$body = mb_convert_encoding( $body, 'HTML-ENTITIES', 'UTF-8' );
$dom->loadHTML( $body );
$xpath = new DOMXPath( $dom );
$title = '';
$description = '';
$image = '';
$meta_nodes = $xpath->query('//meta');
foreach ( $meta_nodes as $meta ) {
if ( $meta->hasAttribute('property') ) {
$property = strtolower( $meta->getAttribute('property') );
$content = $meta->getAttribute('content');
if ( $property === 'og:title' && empty( $title ) ) {
$title = $content;
}
if ( $property === 'og:description' && empty( $description ) ) {
$description = $content;
}
if ( $property === 'og:image' && empty( $image ) ) {
$image = $content;
}
}
if ( $meta->hasAttribute('name') ) {
$name = strtolower( $meta->getAttribute('name') );
$content = $meta->getAttribute('content');
if ( $name === 'description' && empty( $description ) ) {
$description = $content;
}
}
}
if ( empty( $title ) ) {
$title_nodes = $xpath->query('//title');
if ( $title_nodes->length > 0 ) {
$title = $title_nodes->item(0)->nodeValue;
}
}
return array(
'title' => wp_strip_all_tags( $title ),
'description' => wp_strip_all_tags( $description ),
'image' => esc_url_raw( $image ),
);
}
/*
|--------------------------------------------------------------------------
| フロント表示(動的レンダリング)
|--------------------------------------------------------------------------
*/
function url_banner_render( $attributes ) {
if ( empty( $attributes['url'] ) ) {
return '';
}
$url = esc_url_raw( $attributes['url'] );
$cache_key = 'url_banner_' . md5( $url . ( $attributes['imageUrl'] ?? '' ) );
$cached = get_transient( $cache_key );
if ( $cached !== false ) {
return $cached;
}
$request = new WP_REST_Request( 'GET' );
$request->set_param( 'url', $url );
$data = url_banner_preview_callback( $request );
if ( ! empty( $attributes['imageUrl'] ) ) {
$data['image'] = esc_url_raw( $attributes['imageUrl'] );
}
if ( empty( $data['title'] ) ) {
return '';
}
ob_start();
?>
<a href="<?php echo esc_url( $url ); ?>" target="_blank" rel="noopener noreferrer"
style="display:flex;border:1px solid #ddd;border-radius:8px;overflow:hidden;text-decoration:none;color:#000;background:#fff;margin:30px 0;">
<?php if ( ! empty( $data['image'] ) ) : ?>
<div style="flex:0 0 120px;background:#f5f5f5;">
<img src="<?php echo esc_url( $data['image'] ); ?>"
style="width:120px;height:100%;object-fit:cover;display:block;">
</div>
<?php endif; ?>
<div style="padding:12px;flex:1;">
<div style="font-weight:bold;margin-bottom:6px;font-size:16px;line-height:1.4;">
<?php echo esc_html( $data['title'] ); ?>
</div>
<?php if ( ! empty( $data['description'] ) ) : ?>
<div style="font-size:13px;color:#555;line-height:1.4;">
<?php echo esc_html( $data['description'] ); ?>
</div>
<?php endif; ?>
<div style="font-size:11px;color:#888;margin-top:6px;">
<?php echo esc_html( parse_url( $url, PHP_URL_HOST ) ); ?>
</div>
</div>
</a>
<?php
$html = ob_get_clean();
set_transient( $cache_key, $html, 12 * HOUR_IN_SECONDS );
return $html;
}

シリコンパワー ノートPC用メモリ DDR4-2400(PC4-19200) 8GB×1枚 260Pin 1.2V CL17 SP008GBSFU240B02
Synology NASを拡張した時に入れたメモリーがコレ!永久保証の上、レビューも高評価。もちろん正常に動作しており、速度余裕も生まれて快適です。

フィリップス 電動歯ブラシ ソニッケアー 3100シリーズ (軽量) HX3673/33 ホワイト 【Amazon.co.jp限定・2024年モデル】
歯の健康を考えるのならPhilipsの電動歯ブラシがお勧めです。歯科医の推奨も多いみたいです。高価なモデルも良いですが、最安価なモデルでも十分に良さを体感できる。