I am using a headless Wordpress with ACF, WP-GraphQL and WPGraphQL for Advanced Custom Fields installed. In ACF I created a field group with some fields and set the condition to User Role == All. The Goal is to add some fields to the regular WP-User.
When I now want to use the GraphQL-IDE to click together a mutation to register a new user, the ACF fields I created aren't there as inputs. So I can't register a user using those fields. I wonder if I've done anything wrong because, it seems, that GraphQL does not update its schema. I still can see CPTs that I've created and deleted earlier.
Under registerUser I can find a node user and under that I can find the field group I have created. But not as inputs.
Here are some screenshots:
ACF field group
GraphQL Settings
GraphQL-IDE Query Composer
As I've learned, Wordpress isn't designed at all to receive data from the outside. Which make sense, since it is a content management system itself. Furthermore, Wordpress is quite protective when it comes to user data, as it should be. Which means, you can't fetch user data without being logged in ans have the rights to do so. Those two things were leading to my problem.
My solution was to extend the WPGraphQL for Advanced Custom Fields plugin with my own mutations, which then do the user registering for me.
I simply followed this video by the creator of WPGraphQL for Advanced Custom Fields https://www.youtube.com/watch?v=0plIW5hf6lM
Here is my code
namespace Weooo\Graphql;
use function json_encode;
use function strtolower;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class WeoooGraphqlExtended {
public function __construct() {
$this->register_hooks();
}
private function register_hooks() {
add_action( 'graphql_register_types', array( $this, 'nfc_user_address_object' ) );
add_action( 'graphql_register_types', array( $this, 'nfc_user_contact_object' ) );
add_action( 'graphql_register_types', array( $this, 'nfc_user_link_object' ) );
add_action( 'graphql_register_types', array( $this, 'register_nfc_user_registration' ) );
add_action( 'init', array( $this, 'add_theme_support' ) );
}
public function add_theme_support() {
add_theme_support( 'post-thumbnails', array( 'post', 'nfc_user' ) );
}
public function nfc_user_address_object() {
register_graphql_input_type(
'NFCUserAddress',
array(
'description' => __( 'Input for the address of the user', 'weoo-graphql-extended' ),
'fields' => array(
'nfc_user_address_description' => array(
'type' => 'String',
'description' => __( 'The Description of the address', 'weoo-graphql-extended' ),
),
'nfc_user_address_street' => array(
'type' => 'String',
'description' => __( 'The zip street of the user', 'weoo-graphql-extended' ),
),
'nfc_user_address_addition' => array(
'type' => 'String',
'description' => __( 'Additional information to the street', 'weoo-graphql-extended' ),
),
'nfc_user_address_hsn' => array(
'type' => 'String',
'description' => __( 'The house number of the address', 'weoo-graphql-extended' ),
),
'nfc_user_address_zip' => array(
'type' => 'String',
'description' => __( 'The zip code of th address', 'weoo-graphql-extended' ),
),
'nfc_user_address_city' => array(
'type' => 'String',
'description' => __( 'The city og the address', 'weoo-graphql-extended' ),
),
'nfc_user_address_country' => array(
'type' => 'String',
'description' => __( 'The country of the address', 'weoo-graphql-extended' ),
),
'nfc_user_address__navlink' => array(
'type' => 'Boolean',
'description' => __( 'Should the address be a link to google maps', 'weoo-graphql-extended' ),
),
),
)
);
}
public function nfc_user_contact_object() {
register_graphql_input_type(
'NFCUserContact',
array(
'description' => __( 'Input for a contact of the user', 'weoo-graphql-extended' ),
'fields' => array(
'nfc_user_contact_type' => array(
'type' => 'String',
'description' => __( 'The type of the contact', 'weoo-graphql-extended' ),
),
'nfc_user_contact_description' => array(
'type' => 'String',
'description' => __( 'The Description of the contact', 'weoo-graphql-extended' ),
),
'nfc_user_contact_value' => array(
'type' => 'String',
'description' => __( 'The value of the contact', 'weoo-graphql-extended' ),
),
),
)
);
}
public function nfc_user_link_object() {
register_graphql_input_type(
'NFCUserLink',
array(
'description' => __( 'Input for a link of the user', 'weoo-graphql-extended' ),
'fields' => array(
'nfc_user_link_type' => array(
'type' => 'String',
'description' => __( 'The type of the link', 'weoo-graphql-extended' ),
),
'nfc_user_link_description' => array(
'type' => 'String',
'description' => __( 'The Description of the link', 'weoo-graphql-extended' ),
),
'nfc_user_link_url' => array(
'type' => 'String',
'description' => __( 'The url of the link', 'weoo-graphql-extended' ),
),
),
)
);
}
public function register_nfc_user_registration() {
register_graphql_mutation(
'registerNFCUser',
array(
'inputFields' => array(
'first_name' => array(
'type' => 'String',
'description' => __( 'The first name of the user.', 'weoo-graphql-extended' ),
),
'last_name' => array(
'type' => 'String',
'description' => __( 'The last name of the user.', 'weoo-graphql-extended' ),
),
'username' => array(
'type' => 'String',
'description' => __( 'The username for the user.', 'weoo-graphql-extended' ),
),
'email' => array(
'type' => 'String',
'description' => __( 'The email for the user.', 'weoo-graphql-extended' ),
),
'password' => array(
'type' => 'String',
'description' => __( 'The password for the user.', 'weoo-graphql-extended' ),
),
'nfc_user_agb__accepted' => array(
'type' => 'Boolean',
'description' => __( 'If the policy was accepted', 'weooo-graphql-extended' ),
),
'nfc_user_title' => array(
'type' => 'String',
'description' => __( 'If the user has a title like Prof., Dr.', 'weoo-graphql-extended' ),
),
'nfc_user_company' => array(
'type' => 'String',
'description' => __( 'The Employer of the user', 'weoo-graphql-extended' ),
),
'nfc_user_position' => array(
'type' => 'String',
'description' => __( 'The position or function of the user', 'weoo-graphql-extended' ),
),
'nfc_user_company__public' => array(
'type' => 'Boolean',
'description' => __( 'Should the company be visible to the public?', 'weoo-graphql-extended' ),
),
'nfc_user_position__public' => array(
'type' => 'Boolean',
'description' => __( 'Should the position of the user be public', 'weoo-graphql-extended' ),
),
'nfc_user_addresses' => array(
'type' => array( 'list_of' => 'NFCUserAddress' ),
'description' => __( 'The addresses of the user', 'weoo-graphql-extended' ),
),
'nfc_user_contacts' => array(
'type' => array( 'list_of' => 'NFCUserContact' ),
'description' => __( 'The contacts of the user', 'weoo-graphql-extended' ),
),
'nfc_user_links' => array(
'type' => array( 'list_of' => 'NFCUserLink' ),
'description' => __( 'The links of the user', 'weoo-graphql-extended' ),
),
'nfc_user_avatar' => array(
'type' => 'String',
'description' => __( 'The avatar of the user', 'weoo-graphql-extended' ),
),
),
'outputFields' => array(
'registered' => array(
'type' => 'Boolean',
'description' => __( 'Was registering successful?', 'weoo-graphql-extended' ),
),
'error' => array(
'type' => 'String',
'description' => __( 'Discription of the error', 'weoo-graphql-extended' ),
),
'username' => array(
'type' => 'String',
'description' => __( 'The username for the user.', 'weoo-graphql-extended' ),
'resolve' => function( $payload, $args, $context, $info ) {
return isset( $payload['username'] ) ? $payload['username'] : null;
},
),
'user_id' => array(
'type' => 'String',
'description' => __( 'The users ID', 'weoo-graphql-extended' ),
),
'nfc_user_id' => array(
'type' => 'Integer',
'description' => __( 'The ID of the NFC User CPT', 'weoo-graphql-extended' ),
),
'status' => array(
'type' => 'Integer',
'description' => __( 'Status of the request', 'weoo-graphql-extended' ),
),
),
'mutateAndGetPayload' => function( $input, $context, $info ) {
$password = wp_generate_password( 12, false );
$verifacation_code = wp_generate_password( 30, false );
$user = wp_insert_user(
array(
'user_login' => $input['username'],
'user_pass' => $password,
'user_email' => $input['email'],
'first_name' => $input['first_name'],
'last_name' => $input['last_name'],
'role' => 'subscriber',
'user_registered' => date( 'Y-m-d H:i:s' ),
'user_nice_name' => strtolower( $input['first_name'] . '-' . $input['last_name'] ),
'display_name' => $input['first_name'] . ' ' . $input['last_name'],
'user_activation_key' => $verifacation_code,
)
);
if ( is_wp_error( $user ) ) {
return array(
'status' => 400,
'registered' => false,
'error' => $user->get_error_message() . json_encode( $input ),
);
}
try {
$user_info = get_userdata( $user );
$the_nfc_user_id = wp_insert_post(
array(
'post_type' => 'nfc_user',
'post_status' => 'publish',
'post_author' => $user,
'post_title' => $user_info->display_name,
)
);
$acf_fields = array(
'nfc_user_agb__accepted' => $input['nfc_user_agb__accepted'],
'nfc_user_title' => $input['nfc_user_title'],
'nfc_user_company' => $input['nfc_user_company'],
'nfc_user_position' => $input['nfc_user_position'],
'nfc_user_company__public' => $input['nfc_user_company__public'],
'nfc_user_position__public' => $input['nfc_user_position__public'],
'nfc_user_id' => $user,
'nfc_user_email_verifiacation_code' => $verifacation_code,
'nfc_user_avatar' => $input['nfc_user_avatar'],
);
foreach ( $acf_fields as $key => $value ) {
update_field( $key, $value, $the_nfc_user_id );
}
$this->add_to_repeater_field( 'nfc_user_addresses', $input['nfc_user_addresses'], $the_nfc_user_id );
$this->add_to_repeater_field( 'nfc_user_contacts', $input['nfc_user_contacts'], $the_nfc_user_id );
$this->add_to_repeater_field( 'nfc_user_links', $input['nfc_user_links'], $the_nfc_user_id );
$this->send_verification_mail( $user_info, $verifacation_code );
return array(
'status' => 200,
'registered' => true,
'username' => json_encode( $input ),
'user_id' => $user,
'nfc_user_id' => $the_nfc_user_id,
);
} catch ( WP_Error $e ) {
return array(
'status' => 400,
'registered' => false,
'error' => $e->get_error_message(),
);
}
},
)
);
}
public function add_to_repeater_field( $field_name, $content, $nfc_user_id ) {
foreach ( $content as $item ) {
add_row( $field_name, $this->sort_field_content( $item ), $nfc_user_id );
}
}
public function sort_field_content( $item ) {
$new_item = array();
foreach ( $item as $k => $v ) {
$new_item[ $k ] = $v;
}
return $new_item;
}
public function send_verification_mail( $user_info, $verification_code ) {
$to = $user_info->user_email;
$subject = 'Email Verifikation';
$body = "<p>Bitte klicken Sie auf folgende URL um Ihre Emailadresse zu bestätigen:</p><p>" . get_site_url
() .
"/verify-email?code=$verification_code </p><p> Vielen Dank!</p>Ihr NFC-Team</p>";
$headers = array( 'Content-Type: text/html; charset=UTF-8' );
try {
wp_mail( $to, $subject, $body, $headers );
wp_send_json_success( 'Email sent' );
} catch( Exception $e ) {
wp_send_json_error( $e->getMessage() );
}
}
}
$weooo_graphql_extended = new WeoooGraphqlExtended();
I'm tyring to register_meta as a array with oneOf keyword.
The wp rest schema docs says that we have to use the keyword inside the items array like so:
array(
'type' => 'array',
'items' => array(
'oneOf' => array(
...
),
),
)
But this throws an error saying that we should register with the show_in_rest property, as so:
array(
'type' => 'array',
'show_in_rest' => array(
'schema' => array(
'items' => array(
'oneOf' => array(
...
),
),
),
),
);
But then again another error saying that we have to define the type of the items:
array(
'type' => 'array',
'show_in_rest' => array(
'schema' => array(
'items' => array(
'type' => 'object',
'oneOf' => array(
...
),
),
),
),
);
After that I registered two different objects inside oneOf:
array(
'type' => 'object',
'properties' => array(
'dia' => array(
'type' => 'string',
'format' => 'date',
'name' => 'dia',
'label' => 'Dia',
),
'hora_inicio' => array(
'type' => 'string',
'format' => 'time',
'name' => 'hora_inicio',
'label' => 'Hora inicial',
),
'hora_fim' => array(
'type' => 'string',
'format' => 'time',
'name' => 'hora_fim',
'label' => 'Hora final',
),
),
'name' => 'data_unica',
'title' => 'Data única',
),
array(
'type' => 'object',
'properties' => array(
'periodicidade' => array(
'type' => 'string',
'name' => 'periodicidade',
'label' => 'Periodicidade',
'description' => 'Periodicidade do ciclo',
'enum' => array(
'DIARIO' => 'Diário',
'SEMANAL' => 'Semanal',
'QUINZENAL' => 'Quinzenal',
'MENSAL' => 'Mensal',
),
),
'dia_inicial' => array(
'type' => 'string',
'format' => 'date',
'name' => 'dia_inicial',
'label' => 'Dia inicial',
'description' => 'Dia de início do ciclo',
),
'dia_final' => array(
'type' => 'string',
'format' => 'date',
'name' => 'dia_final',
'label' => 'Dia final',
'description' => 'Dia final do ciclo',
),
'hora_inicial' => array(
'type' => 'string',
'format' => 'time',
'name' => 'hora_inicial',
'label' => 'Hora inicial',
),
'hora_final' => array(
'type' => 'string',
'format' => 'time',
'name' => 'hora_final',
'label' => 'Hora final',
),
),
'name' => 'data_corrente',
'title' => 'Data corrente',
),
when I save the post with this meta object (below) mounted with gutenberg blocks I get a error saying that the object matchs the both schema
Request:
{
"datas": [
{
"dia": "2022-07-14",
"hora_inicio": "20:20",
"hora_fim": "17:20"
}
],
}
Response
{
"code": "rest_one_of_multiple_matches",
"message": "meta.datas[0] matches Data única and Data corrente, but should match only one.",
"data": {
"status": 400
},
"additional_data": [{
"positions": [0, 1]
}]
}
Could someone help me with this?
I have a question, i know that i can use FromFactoryInterface to set Form name, but how i can do that with FormBuilder ? This get name of class and generate auto name from them, how i can change it to my specify name?
Form Type:
<?php
class ProfileAddPracownikType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', null, [
'label_attr' => array('class' => 'bmd-label-floating'),
'label' => 'Nazwa użytkownika',
'mapped' => false,
'attr' => ['class' => 'form-control']])
->add('imie', null, [
'label_attr' => array('class' => 'bmd-label-floating'),
'label' => 'Imię',
'mapped' => false,
'data' => $options['imie'],
'attr' => ['class' => 'form-control']])
->add('nazwisko', null, [
'label_attr' => array('class' => 'bmd-label-floating'),
'label' => 'Nazwisko',
'mapped' => false,
'data' => $options['nazwisko'],
'attr' => ['class' => 'form-control']])
->add('telefon', null, [
'label_attr' => array('class' => 'bmd-label-floating'),
'label' => 'Numer telefonu',
'mapped' => false,
'data' => $options['telefon'],
'attr' => ['class' => 'form-control']])
->add('email', null, [
'label_attr' => array('class' => 'bmd-label-floating'),
'label' => 'Adres e-mail',
'mapped' => false,
'data' => $options['email'],
'attr' => ['class' => 'form-control']])
->add('password', RepeatedType::class, array(
'type' => PasswordType::class,
'invalid_message' => 'Hasła muszą być takie same.',
'options' => array('attr' => array('class' => 'password-field')),
'required' => false,
'first_options' => array('label' => 'Hasło (jeżeli pozostawisz to pola puste hasło nie zmieni się)','attr' => ['class' => 'form-control'],'label_attr' => ['class' => 'bmd-label-floating']),
'second_options' => array('label' => 'Powtórz hasło','attr' => ['class' => 'form-control'],'label_attr' => ['class' => 'bmd-label-floating']),
'mapped' => false,
))
->add('avatar', FileType::class, [
'label_attr' => array('class' => 'bmd-label-floating'),
'label' => 'Wgraj lub aktualizuj avatar (jeżeli nie wgrasz pliku pozostanie standardowy obrazek)',
'mapped' => false,
'required' => false,
'data' => $options['avatar'],
'attr' => ['class' => 'form-control']])
->add('save', SubmitType::class, [
'label' => 'Zapisz profil',
'attr' => ['class' => 'btn btn-primary pull-right']])
;
}
}
Form building inside controller:
$form_profile = $this->createForm(ProfileAddPracownikType::class, $request, array());
have this ..
$builder->add('city', 'entity', array(
'class' => '..\Entity\City',
'translation_domain' => $this->translation_domain,
'required' => true,
'expanded' => true,
'invalid_message' => 'form.error.city',
'empty_value' => 'form.placeholder.city',
'query_builder' => function (...\Repository\CityRepository $repository) {
return $repository->get(array(
'enabled' => true,
'locale' => $this->container->get('poisk_raboty.helper.main')->getLocale(),
), true);
},
'attr' => array(
'data-search' => 2,
'data-placeholder' => $this->container->get('translator')->trans('form.placeholder.city', array(), $this->translation_domain),
),
))
;
How can I get cities in form builder, where one element must be like city?
->add('cities', 'collection', array(
'allow_add' => true,
'by_reference' => false,
'required' => true,
'type' => 'entity',
'options' => [
'class' => 'PoiskRaboty\MainBundle\Entity\City',
'translation_domain' => $this->translation_domain,
'invalid_message' => 'form.error.city',
'property' => 'title',
'expanded' => true,
'empty_value' => 'form.placeholder.city',
'query_builder' => function (\PoiskRaboty\MainBundle\Repository\CityRepository $repository) {
return $repository->get(array(
'enabled' => true,
'locale' => $this->container->get('poisk_raboty.helper.main')->getLocale(),
), true);
},
'attr' => array(
'data-search' => 2,
'data-placeholder' => $this->container->get('translator')->trans('form.placeholder.city', array(), $this->translation_domain),
),
]
))
i have a form for edit a user and in the class User i have
/**
* #Recaptcha\IsTrue
*/
public $recaptcha;
and my FormType :
$builder
->add('email', 'email', array('attr' => array('placeholder' => 'Email'), 'label' => false, 'translation_domain' => 'FOSUserBundle'))
->add('firstname', null, array('attr' => array('placeholder' => 'Nom'), 'label' => false, 'translation_domain' => 'FOSUserBundle'))
->add('lastname', null, array('attr' => array('placeholder' => 'Prénom'), 'label' => false, 'translation_domain' => 'FOSUserBundle'))
->add('telephone', 'number', array('attr' => array('placeholder' => 'Téléphone'), 'label' => false, 'translation_domain' => 'FOSUserBundle', 'constraints' => array(
new NotBlank(),
new Length(array('min' => 8, 'max' => 8)),
), ))
->add('roles', 'choice', array(
'choices' => array('m' => 'Moniteur', 's' => 'Secrétaire'),
'label' => false,
'multiple' => false,
'expanded' => true,
'mapped' => false
))
->add('adress', 'textarea', array('attr' => array('placeholder' => 'Adresse'), 'label' => false, 'translation_domain' => 'FOSUserBundle'))
->add('cin', null, array('attr' => array('placeholder' => 'CIN'), 'label' => false, 'translation_domain' => 'FOSUserBundle', 'constraints' => array(
new NotBlank(),
new Length(array('min' => 8, 'max' => 8)),
), ))
->remove('recaptcha', 'ewz_recaptcha')
;
}
i removed it but the error message still how to desactivate it ?
the error message returned when i add a new user : This value is not a valid captcha.