-
-
Save doubleedesign/2fe3034c658f3e62e2d841a420bb4bc0 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* The SEO Framework + ACF flexible content integration | |
* TSF will look at the excerpt and then the content to generate the default meta description. | |
* If both of those are empty, this code looks for ACF flexible modules to get it from. | |
* // TODO: Make this work with archives as well as posts | |
* @param $description | |
* @param $args | |
* | |
* @return mixed|string | |
*/ | |
function doublee_seo_framework_description($description, $args) { | |
if(empty($description) && get_the_id()) { | |
$value_to_use = ''; | |
// Find the first set of flexible modules, if there are any | |
$field_name = doublee_get_name_of_first_acf_field_of_type('flexible_content'); | |
$modules = get_field($field_name); | |
// If there's modules, find the first one with a WYSIWYG or textarea field and get its value | |
if($field_name && $modules) { | |
$value_to_use = doublee_get_first_acf_subfield_value_of_type($modules, array('wysiwyg', 'textarea'), $field_name); | |
} | |
// Tell TSF to use this value; it will take care of truncating it for us. | |
$description = strip_tags($value_to_use); | |
} | |
return $description; | |
} | |
add_filter('the_seo_framework_custom_field_description', 'doublee_seo_framework_description', 10, 2); | |
add_filter('the_seo_framework_generated_description', 'doublee_seo_framework_description', 10, 2); | |
add_filter('the_seo_framework_fetched_description_excerpt', 'doublee_seo_framework_description', 10, 2); |
<?php | |
/** | |
* Utility function to get data about sub-fields that we want to use in doublee_get_first_acf_subfield_value_of_type | |
* because you can't use get_sub_field_object outside of an ACF have_rows loop which was causing headaches with nested repeaters and whatnot | |
* | |
* @param $field_name | |
* @param $post_id | |
* | |
* @return array | |
*/ | |
function doublee_get_sub_field_data($field_name, $post_id) { | |
global $wpdb; | |
$data = array(); | |
// Query the database for the field content of this field's subfields. Starts with the field slug without an underscore. | |
// Returns an indexed array of meta ID, postt ID, meta key, and meta value sub-arrays. | |
$meta_key_search = "'" . $field_name . "%'"; | |
$postmeta = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE post_id = $post_id AND meta_key LIKE $meta_key_search ORDER BY meta_key ASC", ARRAY_A); | |
// Query the database for the field keys of this field's subfields. Starts with the field slug preceded by an underscore. | |
// Field keys are not unique - e.g. repeaters will have the same field key for each instance of a subfield. | |
$meta_key_search = "'_" . $field_name . "%'"; | |
$keymeta = $wpdb->get_results("SELECT * FROM $wpdb->postmeta WHERE post_id = $post_id AND meta_key LIKE $meta_key_search ORDER BY meta_key ASC", ARRAY_A); | |
// Merge the results | |
$merged = array(); | |
foreach($postmeta as $index => $result_data) { | |
$merged[] = array_merge_recursive($result_data, $keymeta[$index]); | |
} | |
// Because the keys are the same in the arrays we merged, this will cause the values to be a sub-array | |
// Let's fix that, and don't include data we don't need | |
$flattened = array(); | |
foreach($merged as $merged_array) { | |
$flattened[] = array( | |
'post_id' => $merged_array['post_id'][0], | |
'value' => $merged_array['meta_value'][0], | |
'key' => $merged_array['meta_value'][1] | |
); | |
} | |
// Use this and some more processing to build an array of all the data we need | |
$i = 0; | |
foreach($flattened as $index => $raw_data) { | |
if(is_array($raw_data)) { | |
$object = get_field_object($raw_data['key']); | |
$value = $raw_data['value']; | |
$parent_key = $object['parent']; | |
$parent_name = ''; | |
if(!empty($parent_key)) { | |
$parent_object = get_field_object($parent_key); | |
$parent_name = $parent_object['name']; | |
} | |
$data[$i]['name'] = $object['name']; | |
$data[$i]['value'] = $value; | |
$data[$i]['type'] = $object['type']; | |
$data[$i]['parent_name'] = $parent_name; | |
$i++; | |
} | |
} | |
return $data; | |
} |
<?php | |
/** | |
* Utility function to get the name of the first ACF field of the specified type. | |
* @param $field_type | |
* @param string $post_id | |
* | |
* @return int|string | |
*/ | |
function doublee_get_name_of_first_acf_field_name_of_type($field_type, $post_id = '') { | |
$field_name = ''; | |
if(!$post_id) { | |
$post_id = get_the_id(); | |
} | |
$acf_fields = get_fields($post_id, true); | |
if($acf_fields) { | |
foreach($acf_fields as $name => $value) { | |
$field_object = get_field_object($name); | |
if($field_object['type'] == $field_type) { | |
$field_name = $name; | |
break; | |
} | |
} | |
} | |
return $field_name; | |
} |
<?php | |
/** | |
* Utility function to get the first direct subfield in an ACF set (flexible content or repeater) that is of the given type(s) | |
* Recursively checks within nested sets for their first instance of the type when applicable | |
* Returns the field (or sub-field) value ready for use by the calling function. | |
* // TODO: Test this on grouped fields too. | |
* | |
* @param array $fields Array of ACF fields or subfields, as returned by get_field() on a flexible content or repeater field | |
* @param array $types The field types we want to look for | |
* @param string $parent_field_name The name of the top level field, e.g. the flexible content field. | |
* Optional because when looking at nested fields recursively, the original value needs to be passed again. | |
* | |
* @return string | |
*/ | |
function doublee_get_first_acf_subfield_value_of_type(array $fields, array $types, string $parent_field_name = '') { | |
$all_field_data = doublee_get_sub_field_data('content_modules', get_the_id()); | |
// If no fields were provided if they're not an array, bail early | |
// Brought this out on its own to keep the main loop's nesting as simple and shallow as possible | |
if(empty($fields) && !is_array($fields)) { | |
return false; | |
} | |
// Loop through the fields | |
foreach($fields as $index => $subfield) { | |
// If the subfield's value is an array, it's a nested fieldset so we need to go another level down | |
if(is_array($subfield)) { | |
return doublee_get_first_acf_subfield_value_of_type($fields[$index], $types, $parent_field_name); | |
} | |
// We've reached content fields and can now proceed to look for our desired field types | |
foreach($all_field_data as $data) { | |
if(($data['name'] == $index) && (in_array($data['type'],$types)) && (!empty($data['value']))) { | |
return $value_to_use = $data['value']; | |
} | |
} | |
} | |
// If a value hasn't been returned yet, there isn't one | |
return false; | |
} |
Please see https://gist.github.com/sybrew/b0bd829846fc1eda24025eb9f50eb86d for a proper format of using $args
so that the filters also work on the administrative screens.
P.S. I'm The SEO Framework's developer.
On https://gist.github.com/doubleedesign/2fe3034c658f3e62e2d841a420bb4bc0#file-acf-tsf-integration-php-L18 you call an undefined function.
This snippet is from quite some time ago and I don't use it anymore (it was for some particular projects I no longer work on) so can't be 100% sure without setting up a test site to check, but I think it's likely meant to be doublee_get_name_of_first_acf_field_name_of_type
in the data.php
file above. I probably just messed up the naming when de-identifying the client by renaming functions for this gist.
On https://gist.github.com/doubleedesign/2fe3034c658f3e62e2d841a420bb4bc0#file-acf-tsf-integration-php-L18 you call an undefined function.