the-seo-framework icon indicating copy to clipboard operation
the-seo-framework copied to clipboard

Supports Bricks Builder Dynamic Data (DD).

Open datgausaigon opened this issue 1 year ago • 7 comments

I currently use Bricks Builder and The SEO Framework (TSF) for many of my Websites. Bricks Builder, TFS both have very good api support and documentation.

Therefore, I have no difficulty creating Dynamic Data (DD) links between Bricks Builder and TFS.

One of the DDs I find very necessary and use is to call The social image URL (_social_image_url), The social image ID (_social_image_id). My Post/Page/CPT will have the Featured Image as a square and the Social Image as a horizontal rectangle. It will be very convenient for me to reuse Social Image for display on PC, Desktop, Featured Image for Mobile, or somewhere it will be Social Image or Featured Image in a flexible way.

I created this Feature Request to ask for your feedback on Bricks Builder Dynamic Data (DD) for TSF? It is very useful. Like my Social Image DD as an example.

https://kb.theseoframework.com/kb/data-stored-in-your-database/

https://academy.bricksbuilder.io/article/dynamic-data/

https://academy.bricksbuilder.io/article/create-your-own-dynamic-data-tag/

Thanks 🐻

datgausaigon avatar Jul 21 '24 12:07 datgausaigon

We currently have no intention to expose TSF's data for display other than aimed at search engines. The breadcrumbs shortcode was postponed by a few years for this reason.

Bricks's Dynamic Data could interpret existing meta fields, for example, via {term_meta:my_term_meta_key}. However, because we use a key-value store, the data we store is an array. So, you cannot call {term_meta:autodescription-term-settings}, for you'll get an array-to-string conversion error.

For now, the same doesn't apply to TSF's post metadata (their documentation doesn't say how to access post meta). But I'm planning to convert this data to a key-value store as well: https://github.com/sybrew/the-seo-framework/issues/185. Bricks does have something to access array values; however, it is unclear to me if this could be applied to what we currently offer.

I think if we were to implement this, we'd indeed need to create custom dynamic data. Here's an example (untested, based on their documentation):

add_filter(
	'bricks/dynamic_tags_list',
	/**
	 * @param array $tags
	 */
	function ( $tags ) {

		$tags[] = [
			'name'  => '{tsf_post_meta}',
			'label' => 'The SEO Framework post metadata',
			'group' => 'The SEO Framework',
		];

		return $tags;
	},
);

add_filter(
	'bricks/dynamic_data/render_tag',
	/**
	 * @param string $tag
	 * @param WP_Post $post
	 */
	function ( $tag, $post ) {

		if ( ! str_starts_with( $tag, 'tsf_post_meta:' ) )
			return $tag;

		if ( ! function_exists( 'tsf' ) )
			return '';

		$tag_parts = explode( ':', $tag );

		switch ( $tag_parts[1] ?? '' ) {
			case 'title':
				return tsf()->get_title( [ 'id' => $tag_parts[2] ?? $post->ID ] );
			case 'description':
				return tsf()->get_description( [ 'id' => $tag_parts[2] ?? $post->ID ] );
			case 'canonical_url':
				return tsf()->get_canonical_url( [ 'id' => $tag_parts[2] ?? $post->ID ] );
		}

		return '';
	},
	10,
	2,
);

(Where do I place filters?)

With that, here's how you get the title for post ID 42:

{tsf_post_meta:title:42}

You can also omit the :42 part to get it for the current page. For example, here's how to get the description for the current page:

{tsf_post_meta:description}

I hope this helps :)

sybrew avatar Jul 21 '24 14:07 sybrew

Hi @sybrew ,

Thank you very much for your attention to my request. ❤️

I'm currently very interested in the fact that TSF has Bricks Dynamic Data that are single data fields stored in _postmeta. Like "Social Image" is one of the things I'm most interested in today. Could you please prioritize Bricks DD development for the single data fields (not arrays) of the TSF stored in _postmeta first?

I'm trying to create a plugin to make calling TFS's Social Image via Bricks DD easy and in accordance with Bricks' code standards. I followed the Bricks instructions and documentation but I'm having problems. This is my code.

<?php
/**
 * Plugin Name: @Dev - TSF and Bricks DD
 * Version: 1.0.1
 */

add_filter( 'bricks/dynamic_tags_list', 'tsf_dynamic_data_tags_list', 10, 1 );
add_filter( 'bricks/dynamic_data/render_tag', 'tsf_social_image_render_tag', 10, 3 );
add_filter( 'bricks/dynamic_data/render_content', 'tsf_social_image_render_content', 10, 3 );
add_filter( 'bricks/frontend/render_data', 'tsf_social_image_render_content', 10, 2 );


function tsf_dynamic_data_tags_list( $tags ) {

	$tags[] = [
		'name'  => '{tsf_social_image}',
		'label' => 'Social Image',
		'group' => 'The SEO Framework',
	];

	return $tags;
}

function tsf_social_image_render_tag( $tag, $post, $context = 'text' ) {
	if ( $tag !== 'tsf_social_image' ) {
		return $tag;
	}

	$post_id = $post->ID;

	$social_image_data = tsf_social_image_tag_logic( $post_id, $context );

	return $social_image_data;
}

function tsf_social_image_render_content( $content, $post, $context = 'text' ) {

	if ( ! str_contains( $content, '{tsf_social_image}' ) ) {
		return $content;
	}

	$post_id = $post->ID;

	$social_image_data = tsf_social_image_tag_logic( $post_id, $context );

	$content = str_replace( '{tsf_social_image}', $social_image_data, $content );

	return $content;
}

function tsf_social_image_tag_logic( $post_id, $context ) {

	$social_image_id = get_post_meta( $post_id, '_social_image_id', true );

	$social_image_id = ! empty( $social_image_id ) ? $social_image_id : '';

	$social_image_data = $social_image_id;

	if ( $context === 'image' ) {
		$social_image_data = ( ! empty( $social_image_id ) ) ? [ $social_image_id ] : [];
	}

	return $social_image_data;
}

Bricks DD displays fine in Builder like my Screenshot:

Screenshot 2024-07-25 at 12 39 13

If I call Dynamic Data Tag {tsf_social_image} in "Heading Element" I get "_social_image_id" as I expected. But if I call {tsf_social_image} on an "IMAGE" Element I get an error message. According to Bricks' instructions, when we want to Render Image Dynamic Data for Image Element, we need to return an array of image IDs. Here is a link related to that issue.

https://forum.bricksbuilder.io/t/dynamic-data-not-rendered-correctly-on-image-element/22641/

I have tried many ways but still stuck here. This is the error message.

Fatal error: Uncaught TypeError: strpos(): Argument #1 ($haystack) must be of type string, array given in /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/integrations/dynamic-data/providers.php:305 Stack trace: #0 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/integrations/dynamic-data/providers.php(305): strpos() #1 /usr/local/lsws/domain.dev/html/wp-includes/class-wp-hook.php(324): Bricks\Integrations\Dynamic_Data\Providers->get_tag_value() #2 /usr/local/lsws/domain.dev/html/wp-includes/plugin.php(205): WP_Hook->apply_filters() #3 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/integrations/dynamic-data/providers.php(362): apply_filters() #4 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/elements/base.php(3573): Bricks\Integrations\Dynamic_Data\Providers::render_tag() #5 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/elements/image.php(625): Bricks\Element->render_dynamic_data_tag() #6 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/elements/image.php(658): Bricks\Element_Image->get_normalized_image_settings() #7 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/elements/base.php(2324): Bricks\Element_Image->render() #8 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/frontend.php(557): Bricks\Element->init() #9 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/frontend.php(656): Bricks\Frontend::render_element() #10 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/frontend.php(906): Bricks\Frontend::render_data() #11 /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/page.php(7): Bricks\Frontend::render_content() #12 /usr/local/lsws/domain.dev/html/wp-includes/template-loader.php(106): include('...') #13 /usr/local/lsws/domain.dev/html/wp-blog-header.php(19): require_once('...') #14 /usr/local/lsws/domain.dev/html/index.php(17): require('...') #15 {main} thrown in /usr/local/lsws/domain.dev/html/wp-content/themes/bricks/includes/integrations/dynamic-data/providers.php on line 305

There has been a critical error on this website.

Can you assist me in correcting this issue? If you need a dev site for this issue with Bricks License, please message me.

Looking forward to your help

🐻

datgausaigon avatar Jul 25 '24 05:07 datgausaigon

I'm also very interested about using dynamic data in TSF but I'd primarily like to use dynamic data from custom fields (for example price or driving_distance) in the TSF meta description.

touchdowntech avatar Sep 17 '24 12:09 touchdowntech

I parked this issue for reconsideration, but I didn't see the part where you asked for help with the filters. Sorry about that, @datgausaigon. It appears your code is mixing arrays and strings. The return value to the filters should be a string. Basically, change all instances of [ $image_id ] to $image_id (without the square brackets), and the error should disappear.

@touchdowntech Your request is covered in https://github.com/sybrew/the-seo-framework/issues/140. At https://github.com/sybrew/the-seo-framework/issues/140#issuecomment-462597715, you can see an example filter. I just updated its syntax for TSF v5.0+.

sybrew avatar Sep 17 '24 14:09 sybrew

Hi @sybrew , After some time of research, I have found a way to do it. Here is the full source, you can refer to it and improve it further. In addition, can you tell me the name you will give to Bricks - TSF Dynamic Tag so that I can use it for my code, currently I am using {tsf_social_image}. This will be convenient when you update this feature to TSF.

<?php
/**
 * Plugin Name: Bricks + TFS
 * Author: Gấu
 * Version: 1.0.0
 */

namespace Bricks\Integrations\Dynamic_Data\Providers;

add_filter('bricks/dynamic_data/register_providers', function ($providers) {
	$providers[] = 'TSF';
	return $providers;
});

require_once get_theme_file_path('/includes/integrations/dynamic-data/providers/provider-interface.php');
require_once get_theme_file_path('/includes/integrations/dynamic-data/providers/base.php');

class Provider_TSF extends Base {
	protected $name = 'provider_tfs';
	protected $settings = [];

	public function __construct($name) {
		parent::__construct($name);
		$this->settings = $this->get_controls();
	}

	public function register_tags(): void {
		parent::register_tags();

		$tags = $this->get_tags_config();
		foreach ($tags as $key => $tag) {
			$this->tags[$key] = [
				'name'     => '{' . $key . '}',
				'label'    => $tag['label'],
				'group'    => $tag['group'],
				'provider' => $this->name,
				'fields'   => $tag['fields']
			];
		}
	}

	public function get_tags_config(): array {
		return [
			'tsf_social_image' => [
				'label'  => 'Social Image',
				'group'  => 'The SEO Framework',
				'fields' => $this->get_controls(),
				'render' => 'tsf_social_image'
			],
		];
	}

	public function get_controls(): array {
		return [
			'meta_key' => [
				'label'       => esc_html__('Meta Key', 'bricks'),
				'type'        => 'text',
				'default'     => '_social_image_id',
				'description' => esc_html__('Enter the custom field name storing image ID', 'bricks')
			]
		];
	}

	public function get_tag_value($tag, $post, $args, $context) {
		$post_id = $post->ID ?? get_the_ID();
		$meta_key = !empty($this->settings['meta_key']['default'])
			? $this->settings['meta_key']['default']
			: '_social_image_id';

		$image_id = get_post_meta($post_id, $meta_key, true);

		if (!$image_id || !wp_attachment_is_image($image_id)) {
			return '';
		}

		$context = 'image';
		$filters = [
			'object_type' => 'media',
			'image'       => true
		];

		return $this->format_value_for_context($image_id, $tag, $post_id, $filters, $context);
	}
}

Thanks 🐻

datgausaigon avatar Mar 06 '25 05:03 datgausaigon

Thanks for that! I don't see a pressing issue in the code.

If there's any deviation or new way to implement this after I've dealt with it, I'll post it in this thread.

I will likely lean toward the example I provided in my first comment. I'll consider granting access to individual data points of TSF, but then via its API, rather than WordPress's, offering more flexibility in forward compatibility.

sybrew avatar Mar 10 '25 23:03 sybrew

Hi @sybrew , Thanks for your reply.

I also really like your approach in the first comment. It is an organized approach. But with Image, we have something special and more complicated.

  1. I want to take advantage of and optimize Image production. Website, designer, data entry person only need to care about 2 aspect ratios: 1:1 and 1200:630. These two image ratios will be utilized in a website. For example, displaying for PC and Mobile.
  2. Bricks currently does not support a simple way to get Image for Dynamics Tag. Therefore, we have to inherit the Base Class of Bricks like I did.

Thanks 🐻

datgausaigon avatar Mar 11 '25 05:03 datgausaigon