I'm developing a plugin which has its own table. I need to display the data from the table in the Wordpress frontend (for example: category page). I don't need to JOIN this table with posts table, I just need to display the data from the table, with pagination. I need a separate page/custom template from my plugin directory (talking in a context of MVC frameworks — controller), on which this data should be displayed and paginated.
Please give me an advice, what is the best practice to implement it?
Thanks.
If I understood your question then I think you need to add template_include hook to use Custom template/page from your plugin directory and you can do it like
add_filter('template_include', 'my_template', 1, 1);
function my_template($template) {
global $post;
if($post->post_content == '[myPluginPage]')
return dirname(__FILE__) . '/my_pligin_template.php';
return $template;
}
You should paste the code given above in your plugin file and also create a file in your plugin folder with name my_pligin_template.php or whatever you want but in this case in the first return statement you have to change the file name too.
Now you have to create a page in wordpress admin to show the page in the menu bar and just paste [myPluginPage] as the content. Notice if($post->post_content == '[myPluginPage]'), this will check whether it's your plugin page or not whenever you click on any menu item and if it finds the word [myPluginPage] in the content then it will return the custom template and you will be at that page.
Now you need to fetch your data from database and to do it you should write the code in this custom template file (my_pligin_template.php). To do it you can write
global $wpdb;
$pagenum = isset( $_GET['pagenum'] ) ? absint( $_GET['pagenum'] ) : 1;
$limit = 10;
$offset = ($pagenum-1) * $limit;
$total = $wpdb->get_var( "SELECT COUNT(*) FROM yourtable" );
$num_of_pages = ceil( $total / $limit );
$qry="select * from yourtable LIMIT $offset, $limit";
$result=$wpdb->get_results($qry);
if($result):
foreach($result as $row)
{
// code here
}
//Link for Pagination
$page_links = paginate_links( array(
'base' => add_query_arg( 'pagenum', '%#%' ),
'format' => '',
'prev_text' => __( '«', 'aag' ),
'next_text' => __( '»', 'aag' ),
'total' => $num_of_pages,
'current' => $pagenum
) );
if ( $page_links ) {
echo '<div class="tablenav"><div class="tablenav-pages" style="margin: 1em 0">' . $page_links . '</div></div>';
}
endif;
Related
I get all my products from an API and those who are variations to each other all share a custom meta key called "api_product_family". The products with the same api_product_family are variants to each other, so on the single page I have a hook where I display the other variants with image and anchor to it's variants.
My code:
function mv_variations() {
global $post;
global $wpdb;
$product_id = $post->ID;
$product_family = get_post_meta( $post->ID, 'api_product_family', true );
if(!empty($product_family)) {
$query = "
SELECT post_id
FROM " . $wpdb->prefix . "postmeta
WHERE meta_value = '" . $product_family . "'
";
$products = $wpdb->get_col($query);
if(count($products) > 0) {
for($i=0; $i<count($products); $i++) {
if($products[$i] == $product_id) {
unset($products[$i]);
}
}
if(count($products) > 0) {
print '<h3>Choose other variants: </h3>';
foreach($products as $product) {
$image = wp_get_attachment_image_src(get_post_thumbnail_id($product));
print '<img src="' . $image[0] . '" alt="img"/> ';
}
}
}
}
}
add_action( 'woocommerce_single_product_summary', 'mv_variations' );
The problem:
I have a LOT of posts, and a lot of post_meta's, so it's taking an eternity to load so I was thinking to move this whole function inside and AJAX call so it's doesn't slow down the initial load. The problem is that I have no idea how to do that with wordpress
Are you simply looking to run a WP function via AJAX?
1) Add ajax actions
This needs to run inside the main plugin file. If you run this only on the public code, it will not work. WP is a little weird and all ajax uses admin-ajax.php
if ( wp_doing_ajax() ){
add_action( 'wp_ajax_yourcustomfunction', array($this, 'yourcustomfunction') );
add_action( 'wp_ajax_nopriv_yourcustomfunction', array($this, 'yourcustomfunction') );
}
function yourcustomfunction(){
echo 'success';
exit();
}
2) In JavaScript
in the backend, you have the global: ajaxurl for the ajax url
BUT in the front end you need to pass this as a variable via wp_localize_script
$datatoBePassed = array(
'ajaxurl' => admin_url( 'admin-ajax.php' ),
);
wp_localize_script( 'your_javascript_script', 'plugin_display_settings', $datatoBePassed );
In JS:
var datavar = {
action: 'yourcustomfunction',
};
$.post(plugin_display_settings.ajaxurl, datavar, function(response){
//response received here. It will be 'success' which is echoed in PHP
});
3)
If you also want to run a security nonce check (to check the request truly originates from the WP website, prevents some attacks), it gets a little more complicated:
$datatoBePassed should also include 'security' => wp_create_nonce( 'yourplugin_security_nonce' ),
datavar in JS includes security: plugin_display_settings.security,
Finally, your PHP custom function begins with:
// Check security nonce.
if ( ! check_ajax_referer( 'yourplugin_security_nonce', 'security' ) ) {
wp_send_json_error( 'Invalid security token sent.' );
wp_die();
}
// If security check passed, run further
So I think you might get better performance using WP_Query. Below I converted what you have into a custom WP_Query. May need some slight adjustment but should be the right direction.
function mv_variations() {
global $post_id;
// get current post "product family"
$product_family = get_post_meta( $post_id, 'api_product_family', true );
// build related "product family" products query
$products_query_args = array(
'post_type' => 'product', // may need to update this for your case
'posts_per_page' => -1, // return all found
'post__not_in' => array($post_id), // exclude current post
'post_status' => 'publish',
// use a meta query to pull only posts with same "product family" as current post
'meta_query' => array(
array(
'key' => 'api_product_family',
'value' => $product_family,
'compare' => '='
)
)
);
$products_query = new WP_Query($products_query);
// use "the loop" to display your products
if ( $products_query->have_posts() ) :
print '<h3>Choose other variants: </h3>';
while ( $products_query->have_posts() ) : $products_query->the_post();
print ''. wp_get_attachment_image() .'';
endwhile;
// restore global post
wp_reset_postdata();
endif;
}
add_action( 'woocommerce_single_product_summary', 'mv_variations' );
Good afternoon devs, I develop a project that consists of users profiles with a record created with fields from the ACF, including the image gallery field, my problem is ... as I only use one page to display this profile, the image gallery is makes it shared even with the option of "attached to the post", as I only use one page for this, example "/profile?userid=userid".
Is there any other good practice for doing this?
I would like suggestions.
one part profile edit
function acf_profile_edit( $atts ) {
$a = shortcode_atts( array(
'field_group' => ''
), $atts );
$uid = get_current_user_id();
if ( ! empty ( $a['field_group'] ) && ! empty ( $uid ) ) {
$options = array(
'post_id' => 'user_'.$uid,
'field_groups' => array( intval( $a['field_group'] ) ),
'return' => add_query_arg( 'updated', 'true', get_permalink() )
);
ob_start();
acf_form( $options );
$form = ob_get_contents();
ob_end_clean();
}
return $form;
}
add_shortcode( 'profile_edit', 'acf_profile_edit' );
Edit...
This code resolved my problem
add_filter('ajax_query_attachments_args', 'restrict_images_to_user');
function restrict_images_to_user($query) {
$gallery_images = 'field_5e4d799b34145';
$gallery_videos = 'field_5e5597e37f2c7';
if ( isset($_POST['query']['_acfuploader'] )
&& ( isset($_POST['query']['_acfuploader']) == $gallery_images || isset($_POST['query']['_acfuploader']) == $gallery_videos ) ) {
$author = get_current_user_id();
$query['author'] = $author;
}
return $query;
}
If you are using one page, use a page template called page-profile.php, the bit after page- must match the name of the page you have assigned.
Then use the WordPress Post Loop:
if (have_posts()) :
while (have_posts()) :
the_post();
the_content();
endwhile;
endif;
You can assign a username to the posts and the posts loop will only return the stuff associate with that username.
Another way would be to get the current username, then can you make an extra acf field with the username in and then do a check.
This is an example and will be exactly correct, but may be able to help you with suggestions.
If(content && username == currentuser)
{
$content = acf content;
}
then later in your page you can echo the content where you need it and will only be the content for the current username, or you can also do it with the user id.
I am busy developing a web application using WordPress. I have created a custom post with a few custom fields. When I search for post using WordPress search box only post with title that match the search string get returned. I want to add custom fields on searching domain.
I there a to search by custom field values in WordPress?
following can do
$args=array(
'post_type'=>'custom post',
'order'=>'ASC',
'orderby'=>'menu_order',
'meta_query' => array (
array (
'key' => 'meta-key',
'value' => 'meta-value',
)
)
);
$the_query = new WP_Query( $args );
if ( $the_query->have_posts() ) {
while ( $the_query->have_posts() ) {
$the_query->the_post();
echo the_title();
}
}
wp_reset_postdata();
To query a bunch of custom fields I found it easier to use the search filters instead.
http://codex.wordpress.org/Custom_Queries Scroll down to the "Keyword Search in Plugin Table" section for an example.
Here's a quick snippet of code form my custom 'posts_where' filter so you can get an idea:
function custom_search_where($where) {
// put the custom fields into an array
$customs = array('custom_field1', 'custom_field2', 'custom_field3');
foreach($customs as $custom) {
$query .= " OR (";
$query .= "(m.meta_key = '$custom')";
$query .= " AND (m.meta_value LIKE '{$n}{$term}{$n}')";
$query .= ")";
}
$where = " AND ({$query}) AND ($wpdb->posts.post_status = 'publish') ";
return($where);
}
add_filter('posts_where', 'custom_search_where');
There's a lot more code but between the Codex example and the snippet above, it should give you a good idea.
All my post has a custom field created_by_alias, I am basically use the value in place of whereever author is used. I have customized the entry-meta function to display this value in plain text, I have also learned to build a query:
$query = new WP_Query( array('meta_key' => 'created_by_alias',
'meta_value' => 'somevalue' ));
Now for the final part: how to create a link for this piece of text, that brings a listing of all posts by this alias?
Do I need to write a template file or is there a quick way to reuse whatever code that generates links to a category, tag listing?
Not sure what you are looking for but seems to me that you want to show a link for the author posts in a specific page, if so, then you can use (using your query)
$query = new WP_Query( array('meta_key'=>'created_by_alias', 'meta_value'=>'somevalue' ));
If $query returns result and you have the id then you can use
echo get_author_posts_url( $query->posts[0]->ID ); // the `ID` from first row
Or for multiple rows, you can loop like
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
echo get_author_posts_url( get_the_ID() ) . '<br />';
}
}
Once this link is clicked, then posts will be retrieved and will be shown according to your page hierarchy, if no author template was found then finally the inedx.php will be used.
Also, if you need a custom template and want to query
$posts = get_posts(array($ID);
Now, to get the id you an use
get_user_by( $field, $value ); // 'id', 'slug', 'email', or 'login'
So, if you have the login name of the user, for example admin then you can use
$id = get_user_by( 'login', 'admin' )->ID; // php 5+
// or
$user = get_user_by( 'login', 'admin' );
$id = $user->ID;
Then get the link and echo it, whereever you want
echo get_author_posts_url( $id );
I have a page which has a slideshow at the top, and images inserted inline into the content area.
I need to exclude the images that have been inserted into the post from the slideshow.
Currently I am excluding the 'Featured Image', but this limits me to one image that can be inserted into the post.
Here is my existing code:
$thumbnail = get_post_thumbnail_id();
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
Previously I have used the description field of the image meta data to exclude images by entering 'exclude'. This isn't as nice for the end user as I'd like it to be.
Any suggestions, plugins or code based!
Update:
I've updated the code, so now I get any image URLs from the post_content and check them against the slideshow images.
$content = $post->post_content;
$inlineImages = array();
preg_match( '/src="([^"]*)"/i', $content, $inlineImages ) ;
$thumbnail = get_post_thumbnail_id($post->ID);
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
if ($images) {
echo '<div id="slideshow">';
foreach ( $images as $attachment_id => $attachment ) {
$image = wp_get_attachment_image_src( $attachment_id,array(900,265));
if (!in_array($image[0],$inlineImages)) {
echo '<img src="'.$image[0].'" width="'. $image[1] .'" height="'. $image[2].'">';
}
}
echo '</div>';
}
This is an OK solution, although the regex could be improved.
A nicer step would be to add the array of images to a custom field field, which
is updated on post / page update or publish.
Any suggestions on how to go about this?
Just needed to do the same thing. Your original approach is the way I wanted to do it -- simply exclude any images that had been inserted into the post from appearing in the slider. But I didn't want the client to have to do anything special to make it happen. Here's my code.
$args = array( 'post_type' => 'attachment', 'post_mime_type'=>'image','numberposts' => -1, 'post_status' => null, 'post_parent' => $post->ID );
$attachments = get_posts($args);
preg_match_all("/<img[^']*?src=\"([^']*?)\"[^']*?>/", $post->post_content, $matches, PREG_PATTERN_ORDER);
/* $matches[1] holds the urls as an array */
foreach ( $attachments as $attachment ) {
if(in_array($attachment->guid, $matches[1])){ continue;}
wp_get_attachment_image( $attachment->ID , 'slider_size');
}
First bit gets all of the images associated with the post. The $preg_match_all gets all of the images in the post body. Then as we loop through the images to display them in the slider the in_array checks the urls of the images that were inserted with the url of the image about to be added to the slider and skips on to the next one if there is a match.
Thanks for you post, got me thinking in the right direction.
I've updated the code, so now I get any image URLs from the post_content and check them against the slideshow images.
$content = $post->post_content;
$inlineImages = array();
preg_match( '/src="([^"]*)"/i', $content, $inlineImages ) ;
$thumbnail = get_post_thumbnail_id($post->ID);
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
if ($images) {
echo '<div id="slideshow">';
foreach ( $images as $attachment_id => $attachment ) {
$image = wp_get_attachment_image_src( $attachment_id,array(900,265));
if (!in_array($image[0],$inlineImages)) {
echo '<img src="'.$image[0].'" width="'. $image[1] .'" height="'. $image[2].'">';
}
}
echo '</div>';
}
I think the easiest thing to do would be using the Meteor Slideshow plugin to create a slideshow for each page, then insert the shortcode for the proper slideshow in the content area of the proper page. Yes, it means you'll have to edit each page "outside" the page editor, but it also gives you easy, complete control over which photos do and do not appear in each slideshow, and the shortcode is very easy to put in with the page editor.
The Media Custom Fields plugin lets you add custom data to media items exactly as you would with custom post fields, providing a user-friendly way of flagging slideshow-only images. From the plugin FAQ:
In addition to giving you the option to add custom fields, your custom fields will show up in all appropriate places. If you go to the media section to manage your media like you usually do, they will show up on the edit media form. The custom fields also show up when you are uploading an image to a post, and in all other expected locations.
One approach is to install this plugin and add a custom field such as Slide with value TRUE to your slide images via the Dashboard -> Media menu. Then your filter code might be:
$thumbnail = get_post_thumbnail_id();
$images = get_children( /* removed for brevity */ );
if ($images) {
// ..
foreach ( $images as $attachment_id => $attachment ) {
// skip images not marked for slideshow
if ( !get_post_meta($attachment_id, 'Slide') ) continue;
// otherwise do slideshow things
}
// ..
}
Note that with this approach the value for Slide can be set to anything except the empty string. You may want to define Slide as a class constant somewhere to avoid hard-coding.
Advantages
Doesn't require any work on the poster's part
Future-proof: the plugin appears to be based on good-old post functionality, and attachments are just one kind of post anyway, so there's nothing weird.
Here is another approach to this.
I would not prefer using urls, because the picture inserted inside the content area could be of different size like medium, thumbnail or full. So, the url only would not match.
Using the above code,
function printMeImages() {
$content = $post->post_content;
$inlineImages = array();
// populate ids of images from wp-image-id
preg_match_all( '/wp-image-([^"]*)"/i', $content, $inlineImages ) ;
$thumbnail = get_post_thumbnail_id($post->ID);
$images = get_children( 'post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail);
$out = "";
if ($images) {
$out .= '<ul id="slideshow">';
foreach ( $images as $attachment_id => $attachment ) {
$image = wp_get_attachment_image_src( $attachment_id,'desiredImageSize');
// $inlineImages[1] has ids to be discarded
if (!in_array($attachment_id,$inlineImages[1])) {
$out .= '<li><img src="'.$image[0].'" width="'. $image[1] .'" height="'. $image[2].'"></li>';
}
}
$out .= '</ul>';
}
return $out;
}
The images which are not set as featured and not used inside the post are retrieved.
For performance reasons I would not execute the code while the page is being rendered, but rather bind the code to the save_post hook. When the post content is edited and saved, the hook will be called and all attached images (or their ids) which are not used in the content are saved into a postmeta table. When the slider is rendered the image ids can be accessed via get_post_meta($post_id, 'image_ids_for_slider').
Like this I can avoid to do the preg_match_all operation on page rendering. This might not be a performance reason when the post content is small and when there's only one slider loaded, but generally I find this a approach a little cleaner for scaling reasons.
//hook this function to save post action
function save_ids_of_image_attachments_not_used_in_the_content( $post_id, $post ) {
$args = array( 'post_type' => 'attachment',
'post_mime_type'=>'image',
'numberposts' => -1,
'post_status' => null,
'post_parent' => $post_id
);
$attachments = get_posts($args);
preg_match_all("/<img[^']*?src=\"([^']*?)\"[^']*?>/", $post->post_content, $matches, PREG_PATTERN_ORDER);
$ids_of_attached_images_not_in_content = array();
/* $matches[1] holds the urls as an array */
foreach ( $attachments as $attachment ) {
if(in_array($attachment->guid, $matches[1])){
continue;
}
$ids_of_attached_images_not_in_content[] = $attachment->ID;
}
// save the image_ids as postmeta
update_post_meta($post_id, 'image_ids_for_slider', $ids_of_attached_images_not_in_content);
}
add_action( 'save_post', 'save_ids_of_image_attachments_not_used_in_the_content', 10, 2 );
I haven't fully understood your problem, but how about excluding the div ID within which the slideshow is present?
$thumbnail = get_post_thumbnail_id();
$images = get_children('post_type=attachment&post_mime_type=image&order=asc&orderby=menu_order&post_parent='.$post->ID .'&exclude='.$thumbnail. '&NAME');
Replace 'NAME' after thumbnail.' in the parenthesis.
Hope this helps.