I added a custom meta field to my cpt taxonomy with "{$taxonomy}_add_form_fields".
So far it is working fine (add, edit and save) but I cant find this field in the API /wp-json/wp/v2/rest_base.
Is this a filter issue or do i "ADD" this field to the API?
... This answers a slightly different question than the one above ...
You need to enable the REST API where you defined the taxonomy.
Just add 'show_in_rest_api' => true
Something like this:
<?php
add_action( 'init', 'create_book_tax' );
function create_book_tax() {
register_taxonomy(
'genre',
'book',
array(
'label' => __( 'Genre' ),
'rewrite' => array( 'slug' => 'genre' ),
'hierarchical' => true,
'show_in_rest_api' => true // <-- Do This!
)
);
}
?>
You register the meta field calling register_term_meta, observe that using show_in_rest key not only set to true, but at least with an schema description, it is available both in queries to get the data, and to get the schema of that endpoint with OPTIONS method.
register_term_meta('replica', 'nice_field', [
'type' => 'string',
'description' => 'a nice description',
'single' => true,
'show_in_rest' => [
'schema' => [
'type' => 'string',
'format' => 'url',
'context' => ['view', 'edit'],
'readonly' => true,
]
],
]);
Using the schema key its currently documented only for non scalar types, but it's valid for scalar values too.
I had a similar issue. The answers here seem to point to the way to expose the taxonomy itself via the REST API, but not the custom fields created via ${taxonomy}_add_form_fields.
After some more research, I concluded that one should extend the REST API to modify the responses and explicitly add these custom taxonomy fields.
So, in my case, this was the solution:
// Add a custom field to the taxonomy form
function my_add_extra_fields_func()
{
$tpl = '
<div class="form-field form-required">
<label for="link">Field Label</label>
<input type="text" name="link" id="link" />
<p>Some help text</p>
</div>
';
echo $tpl;
}
add_action('taxonomySlug_add_form_fields', 'my_add_extra_fields_func');
// Save the custom field to database in the options table
function my_save_extra_fields_func($term_id)
{
$term_item = get_term($term_id, 'taxonomySlug');
$term_slug = $term_item->slug;
$link = sanitize_text_field($_POST['link']);
update_option('taxonomySlug_link_' . $term_slug, $link);
}
add_action('create_taxonomySlug', 'my_save_extra_fields_func');
/**
* This is the part that addresses the question
* of making the custom field visible in the API
*/
// Expose the taxonomy custom field via REST API
add_action( 'rest_api_init', function () {
register_rest_field( 'taxonomySlug', 'extra_fields', array(
'get_callback' => function( $term_as_arr ) {
$term_slug = $term_as_arr['slug'];
$link = get_option('taxonomySlug_link_' . $term_slug);
return $link;
})
);
});
* taxonomySlug = the slug of my taxonomy
Related
I've searched to find any solution but nothing worked.
Here what I want to do:
Add new field in checkout page called Shipping show as selectbox filled with Preorder and Non-preorder
If preorder then would show Location field show as selectbox
If non-preorder then would show Location field same as step 2 but different list option.
When Shipping and Location filled correctly, shipping cost would show with correct shipping cost.
Problem
When I filled all required field and Shipping, Location shipping cost wound calculate again. It do AJAX request, but shipping cost keep the same.
But, when I change Name or Street Address shipping cost updated correctly, which is bad behavior. When user click Place Order immediately would POST wrong shipping cost.
Here my Youtube Video to make clear what I ask for help.
What I did to create my own plugin:
Create custom field using hook something like this
public function woocommerce_checkout_fields( $fields )
{
// var_dump(have_rows('location', 'option'));
// die('test');
$options = [];
$options[''] = 'Select Shipping';
$options['pre-order'] = 'Pre Order';
if (!$this->hasPreorderItem()) {
$options['non-pre-order'] = 'Non Pre Order';
}
// Add custom billing shipping
$fields['billing']['billing_shipping'] = array (
'type' => 'select',
'label' => 'Shipping',
'placeholder' => 'Select shipping',
'class' => array ('address-field', 'update_totals_on_change' ),
'required' => true,
'options' => $options
// 'options' => $this->getLocations()
);
$fields['billing']['billing_location_pre_order'] = array (
'type' => 'select',
'label' => 'Location(Pre Order)',
'placeholder' => 'Select Preorder Location',
'class' => array ('address-field', 'update_totals_on_change' ),
'required' => false,
'options' => $this->preorderLocations->getLocations()
// 'options' => $this->getLocations()
);
// Add custom billing location
$fields['billing']['billing_location'] = array (
'type' => 'select',
'label' => 'Location',
'placeholder' => 'Select location',
'class' => array ('address-field', 'update_totals_on_change' ),
'required' => false,
'options' => $this->locations->getLocations()
// 'options' => $this->getLocations()
);
return $fields;
}
Create class to help calculate cost based on Shipping and Location.
What I have tried
Using this answer but no luck. Click here for the question.
Tried to make field to required, as I saw someone said it only update when all required fields filled.
Tried to disable cache in wp-config.php
I found solution from this link
I might help someone that has similar problem. You just need to clear the session. So, WooCommerce would re-calculate and recall your calculate_shipping() function.
To do that add woocommerce_checkout_update_order_review hook. It would look something like this
add_action('woocommerce_checkout_update_order_review', 'action_woocommerce_checkout_update_order_review', 10, 1);
function action_woocommerce_checkout_update_order_review( $posted_data )
{
global $woocommerce;
$packages = $woocommerce->cart->get_shipping_packages();
foreach( $packages as $package_key => $package ) {
$session_key = 'shipping_for_package_'.$package_key;
$stored_rates = WC()->session->__unset( $session_key );
}
}
I've been unable to get the single twig templates for a custom post type to work.
My developments.twig page correctly lists the posts in custom post type developments.
However, the single template defaults to single.twig, rather than single-development.twig.
In single-development.php I have:
$context = Timber::get_context();
$post = Timber::query_post();
$context['post'] = $post;
Timber::render('single-development.twig', $context);
Like some others suggested I resaved the permalinks but that didn't help.
In timber-functions.php I have:
public function register_post_types() {
// Use categories and tags with attachments
register_taxonomy_for_object_type( 'category', 'attachment' );
register_taxonomy_for_object_type( 'post_tag', 'attachment' );
register_post_type('developments', array(
'labels' =>
array(
'name' => __( 'Developments' ),
'singular_name' => __( 'development' )
),
'public' => true,
'supports' => array( 'title', 'author', 'excerpt', 'custom-fields', 'revisions', 'page-attributes' ),
'show_in_menu' => true,
'taxonomies' => array( 'category' ),
));
}
How is this structure supposed to work.. have I missed something?
Your post_type called developmentS (plural), so your single file should be named single-developments.php
Register a new custom post type has to be inside functions.php
Besides the way you wrote the script does not seem the right way to register a post type. There are no function named as register_post_types(), it's register_post_type();
Here is the developer handbook where you can get the details of custom post type.
https://developer.wordpress.org/reference/functions/register_post_type/
You can find an example at the bottom of the page. You can create a single for custom post type as single-developments.php where you simply can use wp_query to get the data for single post.
Reference: https://developer.wordpress.org/themes/template-files-section/custom-post-type-template-files/
I misunderstood the naming conventions around plural and singular.
The template in fact needed to be single-developments.twig, and was not using the 'singular_name' for single template as I had thought.
I also did not need the extra single-development/s.php.
This code in single.php works as expected:
else {
Timber::render( array( 'single-' . $timber_post->ID . '.twig', 'single-' . $timber_post->post_type . '.twig', 'single-' . $timber_post->slug . '.twig', 'single.twig' ), $context );
}
I have created a custom endpoint, that basically just grabs a few different posts from each category and returns it. This endpoint works fine, but the schema of each post being returned is not the same as when you just hit the default, built-in /posts endpoint. What do I have to do to keep the schemas consistent?
I have a feeling get_posts is the problem, but I have been doc crawling, and I cant seem to find anything that uses the same schema as /posts does.
// How the endpoint is built.
function anon_content_api_posts($category) {
$posts = get_posts(
array(
'posts_per_page' => 3,
'tax_query' => array(
array(
'taxonomy' => 'content_category',
'field' => 'term_id',
'terms' => $category->term_id,
)
)
)
);
$posts = array_map('get_extra_post_data', $posts); // just me appending more data to each post.
return $posts;
}
function anon_content_api_resources() {
$data = array();
$categories = get_categories(
array(
'taxonomy' => 'content_category',
)
);
foreach($categories as $category) {
$category->posts = anon_content_api_posts($category);
array_push($data, $category);
}
return $data;
}
Custom endpoint schema
ID:
author:
comment_count:
comment_status:
featured_image_url:
filter:
guid:
menu_order:
ping_status:
pinged:
post_author:
post_content:
post_content_filtered:
post_date:
post_date_gmt:
post_excerpt:
post_mime_type:
post_modified:
post_modified_gmt:
post_name:
post_parent:
post_password:
post_status:
post_title:
post_type:
to_ping:
Default /posts schema
_links:
author:
categories:
comment_status:
content:
date:
date_gmt:
excerpt:
featured_image_url:
featured_media:
format:
guid:
id:
link:
meta:
modified:
modified_gmt:
ping_status:
slug:
status:
sticky:
task_category:
template:
title:
type:
Any help would be appreciated!
Although this question is older, I had a difficult time finding the answer to getting the schema myself, so I wanted to share what I found.
Short Answer (to getting the schema information back): Use OPTIONS method on the route request
You are dealing with an endpoint that already exists /wp/v2/posts, so you probably want to modify the response of the existing route which you can do with register_rest_field() (this should keep the appropriate schema for all exposed post columns / fields, but allows you to modify the schema for the fields you are now exposing as well):
Something like this:
function demo_plugin_extend_route_rest_api()
{
register_rest_field(
'post',
'unexposed_column_in_wp_posts_table',
array(
'get_callback' => function( $post_arr ) {
$post_obj = get_post( $post_arr['id'] );
return $post_obj->unexposed_column_in_wp_posts_table;
},
'update_callback' => function( $unexposed_column_in_wp_posts_table, $post_obj ) {
$ret = wp_update_post(
array(
// ID is the name of the column in the posts table
// unexposed_column_in_wp_posts_table should be replaced throughout with the unexposed column in the posts table
'ID' => $post_obj->ID,
'unexposed_column_in_wp_posts_table' => $unexposed_column_in_wp_posts_table
)
);
if ( false === $ret )
{
return new WP_Error(
'rest_post_unexposed_column_in_wp_posts_table_failed',
__( 'Failed to update unexposed_column_in_wp_posts_table.' ),
array( 'status' => 500 )
);
}
return true;
},
'schema' => array(
'description' => __( 'Post unexposed_column_in_wp_posts_table' ),
'type' => 'integer'
),
)
);
}
// Not-in-class call (use only this add_action or the one below, but not both)
add_action( 'rest_api_init', 'demo_plugin_extend_route_rest_api' );
// In-class call
add_action( 'rest_api_init', array($this, 'demo_plugin_extend_route_rest_api') );
If what you are really wanting is to create a new route and endpoint with custom tables (or another endpoint to the posts table), something like this should work:
function demo_plugin_custom_rest_api()
{
// Adding Custom Endpoints (add tables and fields not currently exposed)
// register_rest_route()
// $namespace (string) (Required) The first URL segment after core prefix.
// Should be unique to your package/plugin.
// $route (string) (Required) The base URL for route you are adding.
// $args (array) (Optional) Either an array of options for the endpoint, or an
// array of arrays for multiple methods. Default value: array()
// array: If using schema element to define the schema, or multiple methods,
// then wrap the 'methods', 'args', and 'permission_callback' in an array,
// otherwise they do not need to be wrapped in an array. Best practice
// would be to wrap them in an array though
// 'methods' (array | string): GET, POST, DELETE, PUT, PATCH, etc.
// 'args' (array)
// '<schema property name>' (array) (ie. parameter name - if not including a schema
// then can include field names as valid parameters as a way to
// describe them):
// 'default': Used as the default value for the argument, if none is supplied
// Note: (if defined, then it will validate and sanitize regardless of if
// the parameter is passed in query).
// 'required': If defined as true, and no value is passed for that argument, an
// error will be returned. No effect if a default value is set, as the argument
// will always have a value.
// 'description': Field Description
// 'type': Data Type
// 'validate_callback' (function): Used to pass a function that will be passed the
// value of the argument. That function should return true if the value is valid,
// and false if not.
// 'sanitize_callback' (function): Used to pass a function that is used to sanitize
// the value of the argument before passing it to the main callback.
// 'permission_callback' (function): Checks if the user can perform the
// action (reading, updating, etc) before the real callback is called
// 'schema' (callback function) (optional): Defines the schema.
// NOTE: Can view this schema information by making OPTIONS method request.
// $override (bool) (Optional) If the route already exists, should we override it? True overrides,
// false merges (with newer overriding if duplicate keys exist). Default value: false
//
// View your describe page at: /wp-json/demo-plugin/v1
// View your JSON data at: /wp-json/demo-plugin/v1/demo-plugin_options
// View your schema at (with OPTIONS method) at: /wp-json/demo-plugin/v1/demo-plugin_options
// Note: For a browser method to see OPTIONS Request in Firefox:
// Inspect the JSON data endpoint (goto endpoint and click F12)
// > goto Network
// > find a GET request
// > click it
// > goto headers section
// > click Edit and Resend
// > change Method to OPTIONS
// > click Send
// > double click on last OPTIONS request
// > goto Response (the JSON data returned shows your schema)
register_rest_route(
'demo-plugin/v1',
'/demo-plugin_options/',
array(
// GET array options
array(
'methods' => array('GET'),
'callback' => function ( WP_REST_Request $request ){
// Get Data (here we are getting from options, but could be any data retrieval)
$options_data = get_option('demo_option_name');
// Set $param
$param = $request->get_params();
// Do Other things based upon Params
return $options_data;
},
'args' => array(
// Valid Parameters
'element_1' => array(
'description'=> 'Element text field',
'type'=> 'string',
),
'element_color' => array(
'description'=> 'Element color select box',
'type'=> 'string',
)
)
),
// POST array options
array(
'methods' => array('POST'),
'callback' => function ( WP_REST_Request $request ){
// Get Data (here we are getting from options, but could be any data retrieval)
$options_data = get_option('demo_option_name');
// Set $param
$param = $request->get_params();
// Do Other things based upon Params
if (is_array($param) && isset($param))
{
foreach ($param as $k=>$v)
{
// $param is in an array($key => array($key => $value), ...)
if (is_array($v) && array_key_exists($k, $options_data) && array_key_exists($k, $v))
{
$options_data[$k] = $v;
}
}
}
update_option('demo_option_name', $options_data);
return $options_data;
},
'args' => array(
'element_1' => array(
'default' => '',
'required' => false,
'description'=> 'Element text field',
'type'=> 'string',
'validate_callback' => function($param, $request, $key) { //validation function },
'sanitize_callback' => function($param, $request, $key) { //sanitization function }
),
'element_color' => array(
'default' => 'red',
'required' => true,
'description'=> 'Element color select box',
'type'=> 'integer',
'validate_callback' => function($param, $request, $key) {
$colors = array('red', 'blue');
return in_array($param, $colors);
},
'sanitize_callback' => function($param, $request, $key) {
// If it includes a default, and sanitize callback for other properties above are set, it seems to need it here as well
return true;
})
),
'permission_callback' => function () {
// See Capabilities here: https://wordpress.org/support/article/roles-and-capabilities/
$approved = current_user_can( 'activate_plugins' );
return $approved;
}
),
'schema' => function() {
$schema = array(
// This tells the spec of JSON Schema we are using which is draft 4.
'$schema' => 'http://json-schema.org/draft-04/schema#',
// The title property marks the identity of the resource.
'title' => 'demo-plugin_options',
'type' => 'object',
// In JSON Schema you can specify object properties in the properties attribute.
'properties' => array(
'element_1' => array(
'description' => esc_html__( 'Element text field', 'demo-plugin' ),
'type' => 'string',
'context' => array( 'view', 'edit', 'embed' ),
'readonly' => false,
),
'element_color' => array(
'description' => esc_html__( 'Element color select box', 'demo-plugin' ),
'type' => 'string',
'readonly' => false,
),
),
);
return $schema;
})
);
}
// Not-in-class call (use only this add_action or the one below, but not both)
add_action( 'rest_api_init', 'demo_plugin_custom_rest_api' );
// In-class call
add_action( 'rest_api_init', array($this, 'demo_plugin_custom_rest_api') );
Of course, this is just a basic outline. Here is a caveat:
According to the documentation listed below, it is best to use a Controller Pattern class extension method (rather than the method I outlined above): https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-custom-endpoints/#the-controller-pattern
These were very helpful links in finally putting this all together for myself:
REST API Handbook: https://developer.wordpress.org/rest-api/
See Sub-Pages:
REST API Modifying Responses,
REST API Adding Custom Endpoints,
REST API Schema
Register REST Field Function: https://developer.wordpress.org/reference/functions/register_rest_field/
Register REST Route Function: https://developer.wordpress.org/reference/functions/register_rest_route/
I have a custom post type and a taxonomy which allows the user to select which category the post is in.
Here is my custom taxonomy:
add_action( 'init', 'create_talcat_taxonomy', 0);
function create_Talcat_taxonomy()
{
register_taxonomy ( 'Talcat', 'artist', array( 'hierarchical' =>
true, 'label' => 'Categories', 'query_var' => true, 'rewrite' => true )
);
}
On my homepage I am querying the post_type=artist , which works fine and brings in my artist posts. However how can I print/display the name of the category that post belongs to and then link to that category page?
Similar to the_categories WordPress has function the_taxonomies to display custom taxonomies assigned to post type.
http://codex.wordpress.org/Function_Reference/the_taxonomies
Example:
<?php the_taxonomies('before=<ul>&after=</ul>'); ?>
<?php
$args = array(
//default to current post
'post' => 0,
'before' => '<p class=\"meta\">',
//this is the default
'sep' => ' ',
'after' => '<p class=\"meta\">',
//this is the default
'template' => '%s: %l.'
);
the_taxonomies($args);
?>
I have managed to find a clean and simple way to achieve printing the taxonomy term assigned to the post with the following:
<?php the_terms( $post->ID, 'TAXONOMY NAME', ' ', ' / ' ); ?>
Using term retrieves the terms associated with the given object(s), in the supplied taxonomies.
Here is a link to the codex with examples and further detail.
I am making front end admin for users to add/edit their posts. I already made post add form and it works, but edit form doesnt work.
functions.php
function add_new_post( $post_id )
{
if( $post_id == 'new' ) {
// Create a new post
$post = array(
'post_title' => $_POST["fields"]['field_52c810cb44c7a'],
'post_category' => array(4),
'post_status' => 'draft',
'post_type' => 'post'
);
// insert the post
$post_id = wp_insert_post( $post );
return $post_id;
}
else {
return $post_id;
}
}add_filter('acf/pre_save_post' , 'add_new_post' );
index.php
<div id="updateform-<?php the_ID(); ?>" class="collapse">
<?php
echo get_the_ID();
$args = array(
'post_id' => get_the_ID(), // post id to get field groups from and save data to
'field_groups' => array(31), // this will find the field groups for this post (post ID's of the acf post objects)
'form' => true, // set this to false to prevent the <form> tag from being created
'form_attributes' => array( // attributes will be added to the form element
'id' => 'post',
'class' => '',
'action' => get_permalink( get_the_ID() ),
'method' => 'post',
),
'return' => add_query_arg( 'updated', 'true', get_permalink() ), // return url
'html_before_fields' => '', // html inside form before fields
'html_after_fields' => '', // html inside form after fields
'submit_value' => 'Update', // value for submit field
'updated_message' => 'Post updated.', // default updated message. Can be false to show no message
);
acf_form( $args );
?>
</div>
For saving posts you should use save_post not pre_save_post.
Basically pre_save_post is used for new posts.
Use this below add_filter('save_post' , 'add_new_post');
I created plugins for that:
Forms actions
https://wordpress.org/plugins/forms-actions/
Add actions to yours ACF forms.
ACF Frontend display
https://wordpress.org/plugins/acf-frontend-display/
Display your ACF form on frontend
If by some chance you happen to be using Elementor Page Builder, you can do this by installing Advanced Widgets for Elementor. It comes with an ACF Form widget that you can just drag & drop anywhere into your site and configure your form through the UI (no coding required).