select2 search of json results not working - json

I've been searching for an answer to this issue for several hours now, but have not found anything that relates to my case.
My setup:
I've create a custom TinyMCE button with a popup for a WordPress theme that allows the user to select a custom post type post from a dropdown (in this case a "Customer Review") and then have its ID inserted into the shortcode. Since the sites that this theme will run on have well over 1,000 reviews, i thought it would be better to json encode the data for the dropdown and then use select2 to search through a list of limited results (paged results) and keep the whole thing from blowing up. All of that is working successfully, except for 2 items:
The json encoded data shows up, but when i enter a search term, the select2 dropdown shows me a list of all reviews with the first selected. It does not find a result, even though the searched text is in the list
from above, once i enter a search term, select2 shows me all the results instead of just 10, or whatever limit i set.
Here is how i'm json encoding the data (in a file called bpl_content.php):
$args = array('post_type' => 'customer_reviews', 'posts_per_page' => -1, 'fields' => 'ids');
$posts = get_posts($args);
if( $posts ) :
$reviews = array();
foreach( $posts as $post ) : setup_postdata( $post );
$title = get_the_title();
$the_ID = get_the_ID();
$reviews[] = array (
'id' => $the_ID,
'text' => $title
);
endforeach;
endif;
echo json_encode( $reviews )
which returns
[{"id":12286,"text":"John Doe"},{"id":12285,"text":"Jane Doe"},...]
(There are over 800 items returned, so the above just shows 2, but you get the idea)
Here is the javascript i'm using to get my <select> menu populated with the json data:
$(".js-data-example-ajax").select2({
placeholder: "Select a Review",
ajax: {
url: "_bpl_content/bpl_content.php",
type: 'POST',
params: {
contentType: 'application/json; charset=utf-8'
},
dataType: 'json',
delay: 250,
data: function (term, page) {
return JSON.stringify({ q: term, page_limit: 10 });
},
processResults: function (data) {
return {
results: data
};
},
cache: true
},
minimumInputLength: 3
});
I can't for the life of me figure out why everything is working, except for the search and pagination. Any ideas?

You do not appear to have any code in your server side code for filtering and paginating results. Select2 realizes that it's more efficient for this to be done on the server side and expects developers to implement it there. The search term will be passed in as q and the page will be passed in as page (if it's available).
If you do not want to implement searching and pagination on the server side, or only have an endpoint which returns all results, you can still work around it. You just need to initialize Select2 with the JSON results as data instead of using the AJAX functionality.
$.ajax({
url: "_bpl_content/bpl_content.php",
type: 'POST',
contentType: 'application/json; charset=utf-8'
}).then(function (response) {
$(".js-data-example-ajax").select2({
placeholder: "Select a Review",
minimumInputLength: 3,
data: response
});
});

Related

ACF acf_form on admin page works, but when it is loaded in via ajax it breaks

I'm using acf_form() to embed a form in a metabox on the admin page using this code (simplified):
$submitted_user_id = 'user_3';
$form_settings = array(
'fields' => ['field_625008b509dca'],
'form_attributes' => array(
'method' => 'POST',
'action' => admin_url("admin-post.php"),
),
'post_id' => $submitted_user_id,
);
acf_form( $form_settings );
..and calling the requisite acf_form_head() in admin_init as such:
function append_acf_form_head() {
acf_form_head();
}
add_action( 'admin_init', 'append_acf_form_head', 1 );
This works fine, and updates the values on submit. However I want to pull this form in via ajax, in order to pass the user_id from a select filter above.
The ajax is also working perfectly, and pulling in the form with the passed user_id, however on submission the form does not save the data, and redirects to 'wp-admin/admin-post.php'.
This is the php code for the ajax functionality in functions.php:
add_action( 'wp_ajax_test_action', 'test_action' );
function test_action() {
// Same acf_form() function as above
wp_die()
}
And finally the JS:
$('button#support-ticket-user-filter').on('click', function(e) {
e.preventDefault();
var data = {
'action': 'test_action',
'user_id': $('select#get_user_id').val()
};
$.post(ajaxurl, data, function(response) {
$('#listings-result').append( response );
});
});
Any ideas why it would work perfectly in the first case, but not in the second? Perhaps it's related to the ajaxurl?
Many thanks!
For anyone looking for the solution; I've found a slightly hacky approach which does the trick for now. The ACF Support team mentioned this wasn't out the box functionality but has now been included as a feature request. For those trying to achieve this the trick is to permanently embed a 'dummy form' which loads the necessary stuff to make it all work - then kind of hook this into the form pulled in via ajax:
So, in the meta box a permanent dummy form is embedded:
// This dummy form is required to load all the acf stuff for the ajax version
$dummy_form_settings = array(
// Do not declare any fields
'post_id' => $submitted_user_id, // Make sure the ID corresponds ( for users in my case )
'form' => true,
);
acf_form( $dummy_form_settings );
All we want from this dummy form is the 'update' button - which is the one that actually works, as opposed to the one in the form pulled in via ajax. So that function will remove the submit button like this:
$form_settings = array(
'fields' => ['field_625008b509dca'],
'form_attributes' => array(
'method' => 'POST',
),
'html_before_fields' => sprintf(
'<input type="hidden" name="user_id" value="' . $submitted_user_id . '">',
),
'post_id' => $submitted_user_id,
'form' => true,
'html_submit_button' => '', // The important bit
);
acf_form( $form_settings );
wp_die();
Now your essentially left with a single form that submits properly.

How to access /posts WordPress JSON REST API and filter by post_type?

I have a number of posts in the table wp_3_posts with the post_type "people". This post_type is created using the Admin Columns plugin.
How do I retrieve these?
The documentation for Posts offers filtering by categories or tags but post_type is neither of these.
https://developer.wordpress.org/rest-api/reference/posts/
Thank you :]
If I got your question the right way, you want to get posts of a custom post type with REST API.
You have to set show_in_rest and public in the arguments when you create the custom post type in wordpress.
add_action( 'init', 'my_cpt' );
function my_cpt() {
$args = array(
'public' => true,
'show_in_rest' => true,
'label' => 'My Custom Post'
);
register_post_type( 'mycustompost', $args );
}
More about this: https://developer.wordpress.org/rest-api/extending-the-rest-api/adding-rest-api-support-for-custom-content-types/
With that having set, you can get the posts of post type with using the right parameters in the url.
So if you just want to get posts, you can use:
https://yoursite.com/wp-json/wp/v2/mycustompost?per_page=10
I would suggest setting the per_page to have control if you are getting a lot of posts.
You can also have access to more data without additional HTTP requests using _embed
https://yoursite.com/wp-json/wp/v2/mycustompost?per_page=10&_embed=wp:term,wp:featuredmedia
For example, with this you get taxonomy terms and urls of different featured image sizes.
So you do not need to get all posts (and post types) of your website and then filter by post type, but just get posts of this post type instead. You can than do more filtering using global parameters:
https://developer.wordpress.org/rest-api/using-the-rest-api/global-parameters/
With VueJS (in my opinion better performance) this would look something like:
fetch("https://yoursite.com/wp-json/wp/v2/mycustompost?per_page=10")
.then(response => response.json())
.then((data => {
this.mycustompost = data;
}))
Or if using standard javascript something like:
let state = {
posts: [],
baseUrl: 'https://yoursite.com/wp-json/wp/v2/mycustompost',
perPage: '?per_page=10',
wpFetchHeaders: {
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Expose-Headers': 'x-wp-total'
}
}
}

WP API Filter By Post Schema

Is it possible to return a list of posts based from the Wordpress Rest API v2 based on their schema:
For List of Schemas:
http://v2.wp-api.org/reference/posts/
I want to filter by sticky field, but the same would go for the rest of the fields.
So far I have:
/wp-json/wp/v2/posts?filter[sticky]=true
/wp-json/wp/v2/posts?filter[sticky]=1
Both return the same response as the standard endpoint:
/wp-json/wp/v2/posts
I have read other material such detailing how to sort by meta or custom taxonomies but I don't believe that's the same as this.
After going through the documentation and looking and posting issues on the WP-API Github repo, it's become clear that the filter[ignore_sticky_posts] should toggle the expected sorting behaviour, so that sticky posts are either always first (default) or ignored (by using filter[ignore_sticky_posts]=true).
However, there's currently a bug in WP API that makes the filter[ignore_sticky_posts] flag unusable.
The best way to fix it now would be to create you own custom endpoint to get the data or IDs of all the sticky posts in your database. By looking at the code discussed in this thread and in the WP-API documentation, I think adding the following code to your functions.php should do the trick:
// Sticky posts in REST - https://github.com/WP-API/WP-API/issues/2210
function get_sticky_posts() {
$posts = get_posts(
array(
'post__in' => get_option('sticky_posts')
)
);
if (empty($posts)) {
return null;
}
return $posts;
}
add_action( 'rest_api_init', function () {
register_rest_route( 'THEME_NAME/v1', '/sticky', array(
'methods' => 'GET',
'callback' => 'get_sticky_posts',
));
});
If you GET /wp-json/THEME_NAME/v1/sticky, you should get an array of all your sticky posts.
I hope this helps.
In addition to Laust Deleuran's answer (thanks Laust!), i've created an altered version of his script which allows you to use the embedded feature of the REST-api.
Although this might not be 'the cleanest' solution, it does allow you to fully use the wp-json's functionality.
function get_sticky_posts(WP_REST_Request $request) {
$request['filter'] = [
'post__in' => get_option('sticky_posts')
];
$response = new WP_REST_Posts_Controller('post');
$posts = $response->get_items($request);
return $posts;
}
add_action( 'rest_api_init', function () {
register_rest_route( 'THEME_NAME/v1', '/sticky', array(
'methods' => 'GET',
'callback' => 'get_sticky_posts',
));
});
This will output the sticky posts in the same schema as a normal /wp-json/wp/v2/posts query would respond.

select2 filter local JSON data results with query term

I am implementing select2 version 3.5.0. I am using the following jQuery inside my document ready function:
jQuery.getJSON('/rest/call/to/retrieve/JSON').done(
function( data ) {
jQuery('#byProductName').select2({
placeholder: 'Type any portion of a single product name...',
allowClear: true,
minimumInputLength: 0,
multiple: true,
id: function(data){ return data.product; },
data: data,
formatResult: function(data){ return data.product; },
formatSelection: function(data){ return data.product; },
});
}
);
HTML hidden input element:
<div id='freeForm'>
<input name='Search Products' type='hidden' id='byProductName'>
</div>
JSON result:
[{"product":""},{"product":" windows"},{"product":" mac"},
{"product":" linux"},{"product":" RHEL"},{"product":"Test product list"}]
The drop down populates my values correctly and I can select multiple items and remove them successfully. However, when I type a string in the input field to filter the result set, it does not filter.
I have tried altering data: to the following:
data: function (data, term){
return {
results: data,
query: term }
},
but get this error once I click the drop down:
Uncaught TypeError: Cannot read property 'length' of undefined - select2 line 1784
How do I filter the result list successfully with a query term?
From the Select2 documentation for the data option:
Options [sic] for the built in query function that works with arrays.
If this element contains an array, each element in the array must
contain id and text keys.
Alternatively, this element can be specified as an object in which
results key must contain the data as an array and a text key can
either be the name of the key in data items that contains text or a
function that retrieves the text given a data element from the array.
This means you have two options:
(1) Alter your data array to be an array of objects with id and text properties before giving it to .select2(). You can then get rid of the id, formatResult and formatSelection options.
jQuery.getJSON('/rest/call/to/retrieve/JSON').done(
function( data ) {
data = $.map(data, function(item) {
return { id: item.product, text: item.product };
});
jQuery('#byProductName').select2({
placeholder: 'Type any portion of a single product name...',
allowClear: true,
minimumInputLength: 0,
multiple: true,
data: data
});
}
);
jsfiddle
(2) Supply an object with results and text properties for the data option. In this case, you still need to supply the id option, but you can get rid of the formatResult and formatSelection options.
jQuery.getJSON('/rest/call/to/retrieve/JSON').done(
function( data ) {
jQuery('#byProductName').select2({
placeholder: 'Type any portion of a single product name...',
allowClear: true,
minimumInputLength: 0,
multiple: true,
data: { results: data, text: 'product' },
id: function(item) { return item.product; }
});
}
);
jsfiddle

Multi-select Filter Search in Laravel 4

I need help/guidance in developing a multi-select filter search for my Laravel 4 app.
I have a table in my database called 'accounts'. This table is linked to other tables in the database via the following relationships:
'users' via belongs to (User model has a has many relationship to accounts)
'account_types' via belongs to (AccountType model has a has one relationship to accounts)
In my view I would like 3 multi-select boxes, company names (taken from the accounts table 'company_name' field), account managers (taken from the account_managers table, 'first_name' and 'last_name' fields) and account type (taken from the account_types table, 'type' field).
When a user selects values from these multi-select boxes and submits the form, I need to search the relevant tables and bring back the results. I don't want to use joins for this, as it is very slow. Especially, when bringing back values for the multi-select boxes.
If possible I would like to use Eloquent relationships in a way that brings back the results quickly.
I have this working with joins and query strings but it is very slow, up to 10 to 15 seconds.
I hope someone can help me out with this. Cheers.
OK, I have this working like a charm now by implementing select2, rather than simply loading all contacts in one go. Works much nicer too.
Here's my index method in AdminContactsController.php:
public function index()
{
$contact_names_value = explode(',', Input::get('contact_names_value'));
$accounts_value = explode(',', Input::get('accounts_value'));
$account_managers_value = explode(',', Input::get('account_managers_value'));
// In the view, there is a dropdown box, that allows the user to select the amount of records to show per page. Retrive that value or set a default.
$perPage = Input::get('perPage', 10);
// This code retrieves the order from that has been selected by the user by clicking on table ciolumn titles. The value is placed in the session and is used later in the Eloquent query and joins.
$order = Session::get('contact.order', 'cname.asc');
$order = explode('.', $order);
$message = Session::get('message');
$default = ($perPage === null ? 10 : $perPage);
$contacts_trash = Contact::contactsTrash($order)->get();
$this->layout->content = View::make('admin.contacts.index', array(
'contacts' => Contact::contacts($order, $contact_names_value, $accounts_value, $account_managers_value, $perPage)->paginate($perPage)->appends(array('accounts_value' => Input::get('accounts_value'), 'account_managers_value' => Input::get('account_managers_value'))),
'contacts_trash' => $contacts_trash,
'perPage' => $perPage,
'message' => $message,
'default' => $default
));
}
My scopeContacts method in my Contact.php model:
public function scopeContacts($query, $order, $contact_names_value, $accounts_value, $account_managers_value, $perPage)
{
$query->leftJoin('accounts', 'accounts.id', '=', 'contacts.account_id')
->leftJoin('users', 'users.id', '=', 'accounts.user_id')
->orderBy($order[0], $order[1])
->select(array('contacts.*', DB::raw('contacts.id as cid'), DB::raw('CONCAT(contacts.first_name," ",contacts.last_name) as cname'), DB::raw('CONCAT(users.first_name," ",users.last_name) as amname')));
if (empty($contact_names_value[0])) {
//
} else {
$query = $query->whereIn('contacts.id', $contact_names_value);
}
if (empty($accounts_value[0])) {
//
} else {
$query = $query->whereIn('accounts.id', $accounts_value);
}
if (empty($account_managers_value[0])) {
//
} else {
$query->whereIn('users.id', $account_managers_value);
}
}
Here's my JS code:
$('#contact_names_value').select2({
placeholder: 'Search contacts',
minimumInputLength: 3,
ajax: {
url: '/admin/get-contact',
dataType: 'json',
data: function (term, page) {
return {
contact_names_value: term
};
},
results: function (data, page) {
return {results: data};
}
},
tags: true
});
Here's my method getContactByName implemented in my AdminContactsController.php (similar methods implemented for users and accounts) code:
public function getContactByName()
{
$name = Input::get('contact_names_value');
return Contact::select(array('id', DB::raw('concat(first_name," ",last_name) as text')))->where(DB::raw('concat(first_name," ",last_name)'), 'like', "%$name%")->get();
}
Notice during my select statement, I do a DB::raw and set the 'first_name' and 'last_name' fields to be selected as 'text'. I think this was one of the major issues, as the plugin requires 'id' and 'text' to function.
My route was simply:
Route::get('admin/get-contact', 'AdminContactsController#getContactByName');