Is there custom validation of meta values in Wordpress REST API? - wordpress

WordPress allows registering meta values in the REST API through register_meta().
One can add a schema to the meta like so:
register_meta(
'user',
'my_meta_key',
array(
'type' => 'array',
'description' => 'This is my array meta key!',
'single' => true,
'show_in_rest' => array(
'schema' => array(
'type' => 'array',
'items' => array(
'type' => 'string',
'format' => 'date-time',
),
),
),
)
);
It took me some time to find out about the 'format' => 'date-time' option.
I would like to validate my meta fields myself (for a custom post-type). But it seems like the WP_REST_Posts_Controller uses get_endpoint_args_for_item_schema() to set up the schema for arguments in register_rest_route().
register_rest_route() would allow for a custom validate_callback, but rest_get_endpoint_args_for_schema() hard-codes it to rest_validate_request_arg(), which uses rest_validate_value_from_schema().
And there are the primitives and hard-coded formats hex-color, date-time, email, ip and uuid.
So there are two options I see:
Live with it. Just accept the validating against schema primitives, since the schema is only validated on REST requests.
Extend WP_REST_Posts_Controller just to sneak in my own get_endpoint_args_for_item_schema(), which adds an exception for my meta key.
Do you have another idea, how to solve this issue?
EDIT: There seems to be a pattern property for strings. So I could at least do something like (don't mind the regex itself):
'schema' => array(
'type' => 'string',
'pattern' => '[0-9]/[0-9]^$',
),

Related

How to make Titan Framework text field required

I am using Titan Framework with a custom plugin I wrote. There is a settings page that has a tab and on it there is a text field:
array(
'name' => 'Slug',
'id' => 'my_slug',
'type' => 'text',
'default' => 'my-slug',
'desc' => 'The slug.',
),
This text field allows the user to specify a slug for the plugin's frontend UI. The problem is that a user can clear this field out to nothing and then save settings which results in an error.
I understand that I can trap this condition in the PHP code when this setting option is used. I also understand this could be done using jQuery to enforce a value being specified, but it would seem like there should be a best practice solution.
Can anyone tell me the proper way to ensure this field has a value?
The user can definitely save it as empty and yes you can prevent that with JS. But because there is no "pre save" filter, one way to address this is to to the method you mentioned, add a placeholder attribute to the field to give the illusion of a value when empty (this is just for a better user experience).
array(
'name' => 'Slug',
'id' => 'my_slug',
'type' => 'text',
'default' => 'my-slug',
'desc' => 'The slug.',
'placeholder' => 'post',
),
Then check for empty when getting the value.
$slug = $titan->getOption( 'my_slug' );
if ( empty( $slug ) ) {
$slug = 'post';
}

Woocommerce and country array on checkout

I am using woocommerce plugin. Idea was to edit checkout page to create my own classes , placeholders etc for fields. With some googling I managed it all. Only thing that I can't change is country dropdown/select. For example having this code
$fields['shipping']['shipping_country'] = array(
'type' => 'select',
'label' => 'Country',
'placeholder' => '',
'maxlength' => false,
'required' => false,
'class' => array(),
'label_class' => array('col-md-6'),
'input_class' => array('col-md-6'),
'return' => false,
'options' => array( 'Germany' => 'Germany',
'Germany' => 'Germany'),
'custom_attributes' => array(),
'validate' => array(),
'default' => '',
);
I want to dynamically load all countries from woocommerce. I don't want to add country by country in array, dont think thats the right way also. So is there some object or whatever that will load in this 'options' array all available countries?
You aren't very specific - you haven't shared where this code exists in your system, or which countries you want, but the answer to your question is yes.
Check out these two functions:
WC()->countries->get_shipping_countries();
WC()->countries->get_allowed_countries();
NOTE:
You ALSO probably want to be using the woocommerce_form_field for this, since it does all the heavy lifting for you.

Form collection preset data not based on entity

I have a difficult problem to solve. I need to implement a functionality to create custom forms (that is declare what fields it should have) and then be able to save and retrieve the data. The form generation mechanism is based on EAV model - form template entity has form fields entity (which are form templates attributes). Each form field has type, name, label etc.
Then I dynamically display the form( foreach $fields as $field (...) $formBuilder->add($field->getType() etc.). After all that the data is saved in another entity called FormInstanceData which consist of field name:field value pairs.
The hard part is that I have to be able to create form templates with form field groups which behave like collections (new ones can be added with JS). The form (generated with the template) displays correctly, but I have problem with retrieving the data (as the final data is not an entity for obvious reasons). Simple fields can be successfully filled out with retrieved data (by passing data option with the field name as key), but I can't get the nested collection fields to work - even after passing the data, the collections simply don't display.
The part of the code responsible for that looks like this:
elseif ($fieldType === 'collection'){
$subfields = $field->getSubfields();
$formBuilder->add('subfields', 'collection', array(
'type' => new FormCollectionType($subfields),
'allow_add' => true,
'mapped' => false,
'allow_delete' => true,
'by_reference' => false,
'options' => array('required' => false, 'data' => array(
array('title' => 'lorem', 'subtitle' => 'ipsum'),
array('title' => 'lorem', 'subtitle' => 'ipsum')
The FormCollectionType is also generated dynamically with the $subfields parameter. In this case, each item in collection has two fields - title and subtitle. With the data I passed, two already filled out input groups should appear, but nothing does. You can still add new (empty) groups with JS.
Please advise.
Ok, turned out the data needs to be passed not as:
'options' => array('required' => false, 'data' => array(
array('title' => 'lorem', 'subtitle' => 'ipsum'),
array('title' => 'lorem', 'subtitle' => 'ipsum')
but:
'data' => array(
array('title' => 'lorem', 'subtitle' => 'ipsum'),
array('title' => 'lorem', 'subtitle' => 'ipsum')

Sonata Media Bundle : How to set the default value of a media field?

I'm currently Sonata Media Bundle for my project.
In one of my Entity Form Type that has some Media fields, I added these fields :
<?php
$builder
->add('image', 'sonata_media_type', array(
'provider' => 'sonata.media.provider.image',
'context' => 'projects',
'label' => 'Project image',
'required' => true
))
->add('video', 'sonata_media_type', array(
'provider' => 'sonata.media.provider.vimeo',
'context' => 'projects',
'label' => 'Vimeo video',
'required' => true
));
?>
As you can see, my two fields are required. However, when I add a media and save the entity, and then when I want to edit it, I don't have the related media as the "default" value as it should be set, generating my HTML5 required warning when trying to saving my edited entity with no new values in my media fields.
Is it the expected behavior? I tried to use a workaround with a PRE_SET_DATA FormEvent that would set the media fields as unrequired when there is existing data, but I don't think it's a good way to handle this
How to set the existing value of the media fields in entity edit forms?
Thank you for your help.

Managing several custom content types from one module(drupal)

Is it possible to declare and manage several custom content types inside one module? I'm creating a site that needs four custom content types and I'd like to manage them from one module instead of creating module for every content type. After some testing, I found out that it seems impossible. Because, unless hook_form and content type share the same name of module, drupal doesn't call hook_form.
Here's how I'd like to do -
function mycontent_node_info(){
return array(
'mycontent1' => array(
'name' => t('....'),
'module' => 'mycontent',
'description' => t('...),
'has_title' => TRUE,
'title_label' => t('Title'),
'has_body' => TRUE,
'body_label' => t('content body'),
),
'mycontent2' => array(
.......
),
'mycontent3' => array(
......
),
'mycontent4' => array(
......
),
);
}
function mycontent1_form(&$node){
$form['control1'] = array(
'#type' => 'select',
'#options' => array(
'0' => t('selection 1'),
'1' => t('selection 2'),
),
'#attributes' => array('id'=>'control1'),
);
$form['control2'] = array(
'#type' => 'select',
'#options' => array(
'0' => t('1'),
'1' => t('2'),
'2' => t('3'),
'3' => t('4'),
),
'#attributes' => array('id'=>'control2'),
);
return $form;
}
function mycontent2_form(&$node){
....
}
function mycontent3_form(&$node){
....
}
function mycontent4_form(&$node){
....
}
Am I doing something wrong here or is not possible and there's no alternative other than creating module for every content types. I appreciate much your help.
The prefix for all your hooks should be the name of your module, i.e. mycontent_node_info() and mycontent_form(&$node). I think the content type itself can be called whatever you want but by convention anything global that you define in a module should be prefixed with the name of the module to avoid namespace problems. So your content becomes mycontent_type1, mycontent_type2, etc... As for dealing with hook_form, I guess the way to do it is to check the type of the node passed in and act accordingly.
You might try using the Features module (http://drupal.org/project/features) to export your content types. It auto generates the code to make this work, and you can have a look at what is going wrong with your code.

Resources