7

Searching for around two days to find an answer to my question, I finally registered for this StackExchange. :)

My Question is not simple, so I need to start at the very beginning. But to give you an idea of what this Post is about: Meanwhile my Plugin is working, but I do not like the code. I think there are better ways to solve this and so I am searching for wordpress ninjas who can help me with good hints and best practices. I really try to get into the deeps of wordpress, but it is kind of hard. Thank you in advance!


What my Plugin should do

The User of this Plugin should be able to use a shortcode to show a search form for specific post types. The Plugin is only needed on the pages containing this shortcode.

The post types are created via the plugin settings page. Every post of this type has a title, content and several custom fields from the acf plugin. There is one shortcode for every post type. The search form has one field to search all the post fields of the specified post type. The other two can be used to limit the results by two keywords (for example the location, which is an acf field).

The results are loaded via ajax and shown below the search form.


What I did already

I try to keep this question not too big. So I do not specifiy every single aspect. The following should work fine:

  • Create on post type in the php file of the plugin. For the moment one static post type is enaugh.
  • Create a shortcode which prints the search form and an empty container for the results.
  • The Ajax request is working via javascript and gives the expected results.

Search Custom fields

This was quite hard, but I found a working code snippet and I understand what it is doing. The problem here is, that I just want to search acf fields with my specific search form. I do not want to touch the search of the existing site.

First I tried to check which site the user is on and use an if-statement with is_page() before I modify the search query. But as I use ajax, this seems not to work for me here...

My workaround is to set a global variable at the beginning of every ajax search query. Now I check if this variable is set before I modify the search query. At the end of my ajax function, I set this variable back to false. Yes, I think there is a better way to solve this, but I do no know how...

The functions which modify my search query are the following:

/**
 * Extend WordPress search to include custom fields
 *
 * http://adambalee.com
 */

/**
 * Join posts and postmeta tables
 *
 * http://codex.wordpress.org/Plugin_API/Filter_Reference/posts_join
 */
function cf_search_join( $join ) {
    global $wpdb;
    //$cwlistpage=(is_page('list'));

    global $testcheck;
    $cwlistpage=$testcheck;

    if ( $cwlistpage ) {    
        $join .=' LEFT JOIN '.$wpdb->postmeta. ' ON '. $wpdb->posts . '.ID = ' . $wpdb->postmeta . '.post_id ';
    }

    return $join;
}
add_filter('posts_join', 'cf_search_join' );

/**
 * Modify the search query with posts_where
 *
 * http://codex.wordpress.org/Plugin_API/Filter_Reference/posts_where
 */
function cf_search_where( $where ) {
    global $pagenow, $wpdb;
    //$cwlistpage=(is_page('list'));

    global $testcheck;
    $cwlistpage=$testcheck;

    if ( $cwlistpage ) {
        $where = preg_replace(
            "/\(\s*".$wpdb->posts.".post_title\s+LIKE\s*(\'[^\']+\')\s*\)/",
            "(".$wpdb->posts.".post_title LIKE $1) OR (".$wpdb->postmeta.".meta_value LIKE $1)", $where );
    }

    return $where;
}
add_filter( 'posts_where', 'cf_search_where' );

/**
 * Prevent duplicates
 *
 * http://codex.wordpress.org/Plugin_API/Filter_Reference/posts_distinct
 */
function cf_search_distinct( $where ) {
    global $wpdb;
    //$cwlistpage=(is_page('list'));

    global $testcheck;
    $cwlistpage=$testcheck;

    if ( $cwlistpage ) {
        return "DISTINCT";
    }

    return $where;
}
add_filter( 'posts_distinct', 'cf_search_distinct' );

Limit the results by custom fields

All fields of the form are optional. An empty form will return all posts of this post type. The first field should search every field of the posts for the keyword. The second and third field should limit the results to the one containing this keyword. I solved this with if-statements and I know there must be a better solution.

/**
* Search with AJAX
*/
function cwlist_click_search() {
global $testcheck;
$testcheck = true;

$searchterm = $_POST['query'];
$searchlocation = $_POST['location'];
$searchdegree = $_POST['degree'];

// WP_Query arguments
$args = array (
    'post_type' => 'offers',
    'post_status' => 'publish',
    's' => $searchterm
);

$query = new WP_Query( $args );

ob_start();

// The Loop
if ( $query->have_posts() ) : ?>
  <br><br><p>
   <?php while ( $query->have_posts() ) : $query->the_post();
        if( ($searchlocation == NULL) || in_array(trim(strtolower($searchlocation)), array_map('strtolower', get_field('offer_location')))):
            if( ($searchdegree == NULL) || (trim(strtolower($searchdegree)) === strtolower(get_field('offer_degree')))):?>

     Titel: <?php the_title(); ?> <br>
     Abschluss: <?php the_field('offer_degree'); ?> <br>
     Ort: <?php the_field('offer_location'); ?> <br>
     Anbieter: <?php the_field('offer_provider'); ?> <br>
     <?php if(get_field('offer_subtitle')): ?>
     - <?php the_field('offer_subtitle'); ?> <br>
     <?php endif; ?>
     <br>

    <?php endif; endif; endwhile; ?>
    </p>
<?php else: ?>
    <!-- no posts found -->
    Keine Angebote gefunden
<?php endif;

// Restore original Post Data
wp_reset_postdata();

$content = ob_get_clean();

echo $content;
die();

$testcheck = false;
}
add_action( 'wp_ajax_nopriv_cwlist_click_search', 'cwlist_click_search' );
add_action( 'wp_ajax_cwlist_click_search', 'cwlist_click_search' );

WP Debug

When I activate the plugin many lines like the following appear at the front page:

Notice: Trying to get property of non-object in C:\Users\CWalt\Documents\gitlab\steuerazubi\wordpress\wp-includes\query.php on line 4520 Notice: Trying to get property of non-object in C:\Users\CWalt\Documents\gitlab\steuerazubi\wordpress\wp-includes\query.php on line 4522 Notice: Trying to get property of non-object in C:\Users\CWalt\Documents\gitlab\steuerazubi\wordpress\wp-includes\query.php on line 4524 ...

I activated debugging to create a good plugin. What could I possibly did wrong?


More Post Types

Do you know a good approach to let the User specify more post types via the wordpress backend? The user should be able to create different search pages for different post types via shortcodes.


Thank you very very much for reading. I am thankful for every small hint.

1 Answer 1

0

I've read through your post in it's entirety, I certainly don't see anything particularly wrong with it, but it sounds to me like your more frustrated with how it's been laid out.

I personally have a big objected oriented programming background, it allows me to establish a structure and makes my code much more re-usable. My suggestion to you would be to attempt an object oriented approach for your next plugin, or the restructuring of this plugin you wrote about in the original question.

Wordpress Plugin Boiler Plate is what I whole heartily recommend. I usually start by heading over to wwpb.me to generate a plugin skeleton for myself that is functional out of the gate, simply fill out the form, download, and extract into your plugins directory of your wordpress install.

Once you have your running skeleton in place I'd suggest giving this tutorial a read, it's a great primer on how to build up the walking skeleton you just downloaded:

Speed up Development Using the WordPress Plugin Boilerplate pt1

The WordPress Plugin Boilerplate Part 2: Developing a Plugin

The WordPress Plugin Boilerplate Part 3: The Last Steps

After giving yourself a quick read in that, I usually like to create a data class that stores all my custom database queries such as:

<?php 

class example_Events_Data {
  private $wpdb;

  public function __construct() {
    global $wpdb;
    $this->wpdb = $wpdb;
  }

  public function get_events($start_date = null, $end_date = null) {
    $query = "SELECT wp_example_event.id, wp_example_event.name, wp_example_event.description, wp_example_event.date, wp_example_event.end_date, wp_example_event_type.id as event_type_id, wp_example_event_type.type as event_type
              FROM wp_example_event
              JOIN wp_example_event_type on wp_example_event.type_id = wp_example_event_type.id
              ORDER BY wp_example_event.name ASC";
    $events = $this->wpdb->get_results($query, ARRAY_A);
    return $events;
  }

Then to use this inside your admin / public facing controllers it's as simple as doing:

    <?php

class Example_Events_Admin {

/**
 * The ID of this plugin.
 *
 * @since    1.0.0
 * @access   private
 * @var      string    $plugin_name    The ID of this plugin.
 */
private $plugin_name;

/**
 * The version of this plugin.
 *
 * @since    1.0.0
 * @access   private
 * @var      string    $version    The current version of this plugin.
 */
private $version;

private $data;
private $utils;

/**
 * Initialize the class and set its properties.
 *
 * @since    1.0.0
 * @param      string    $plugin_name       The name of this plugin.
 * @param      string    $version    The version of this plugin.
 */
public function __construct( $plugin_name, $version ) {

    $this->plugin_name = $plugin_name;
    $this->version = $version;
    $this->data = new example_Events_Data();

}

I also like to create myself a utility class that houses very common functions throughout my plugin, such as form validation rules, implemented and used in exactly the same way.

You simply include them in your main plugin file with require_once and instantiate where needed.

Using the data class allows me to centralize all my data calls in one place, and use them throughout the plugin development.

Using these practices has reduced my development time significantly, and made my plugins a whole lot easier to maintain.

I hope you find this answer useful!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.