Building WordPress Plugins with Object-Oriented Concept
Home » BLOG » WordPress » Building WordPress Plugins with Object-Oriented Concept

Building WordPress Plugins with Object-Oriented Concept

category:  WordPress

When I develop the custom plugin, I prefer to build the custom plugin with Object-Oriented Programming(OOP) to avoid the conflict from other plugins or the themes. Plus my code is cleaner and more organized. Today I will share how to create the report plugin with OOP.

Here what the plugin does:

The plugin will display the data regarding the criteria. I use the datatable plugin for the data table with search, pagination and column sort features. The datatable plugin has more features you can use. The datepicker plugin I use the jquery-ui plugin.

Reports plugin structure

mct-reports.php

This is a main plugin file. We define the constants and include our classes.

<?php
/*
  Plugin Name: Reports
  Description: 
  Version: 1.0
  Author: <a  href="http://applerinquest.com" target="_BLANK">AppleRinquest</a>
  Text Domain: mct-reports
  Domain Path: /languages/  
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

//  declare the constant
define('MCT_REPORTS_PLUGIN_PATH', dirname(__FILE__));  // this constant uses in the class files
define('MCT_REPORTS_PLUGIN_URL' , WP_PLUGIN_URL . '/mct-reports/' ); // this constant uses in enqueue file and style
define('MCT_REPORTS_LANG_PATH', basename( dirname( __FILE__ ) )); 

// our custom classes
require_once( dirname(__FILE__) . '/classes/class-translation.php' );
require_once( dirname(__FILE__) . '/classes/class-reports.php' );

class-reports.php

<?php
    // If this file is called directly, abort.
    if (!defined('ABSPATH')) die("No cheating!");

    if(!class_exists('Mtc_Reports')) {
        
        class Mtc_Reports
        {
        
            /**
             * Start up
             */
            public function __construct() {
              // add admin menu
              add_action( 'admin_menu', array( $this, 'reports_menu' ) );
              
              // add scripts and style on the backend
              add_action( 'admin_enqueue_scripts', array( $this,'reports_admin_scripts') );  
              add_action( 'wp_ajax_get_job_applications', array( $this, 'get_job_applications') );
            }
        
        
            /**
             * add reports menu
             */
            public function reports_menu() {
              // add_menu_page( $page_title, $menu_title, $capability, $menu_slug, $function, $icon_url, $position );
              add_menu_page(__('Reports','mct-reports'), __('Reports','mct-reports'), 'edit_others_posts', 'mct-reports', array( $this, 'reports_section') );        
            }
        
        
            /**
             * add jquery ui datepicker widget
             */
            public function reports_admin_scripts() 
            {
              // add the script and style on our plugin only
              if (isset($_GET['page']) && $_GET['page'] === 'mct-reports') {
            
                // # load style
                // load datepicker plugin style
                // @link https://gist.github.com/miwahall/7028640
                wp_enqueue_style('mct-reports-bootstrap-style', MCT_REPORTS_PLUGIN_URL . 'css/datepicker-bootstrap3.css' );
                
                // load datatable plugin style
                wp_enqueue_style('mct-reports-datatable-style', MCT_REPORTS_PLUGIN_URL . 'lib/DataTables/datatables.min.css' );
            
                // load custom style
                wp_enqueue_style('mct-reports-style', MCT_REPORTS_PLUGIN_URL . 'css/mct-reports.css' );
            
            
            
                // # load scripts
                // datepicker plugin
                wp_enqueue_script( 'mct-reports-datepicker' , MCT_REPORTS_PLUGIN_URL . 'js/jquery-ui.min.js', array('jquery') ,true);
                wp_enqueue_script( 'mct-reports' , MCT_REPORTS_PLUGIN_URL . 'js/mct-reports.js', array('jquery','mct-reports-datepicker') ,true);
                wp_localize_script( 'mct-reports', 'mctReportsAjax', array(
                    'ajaxurl' => admin_url('admin-ajax.php')
                ));
            
                // datatable plugin
                wp_enqueue_script( 'mct-reports-datatable' , MCT_REPORTS_PLUGIN_URL . 'lib/DataTables/datatables.min.js', array('jquery') ,true);
              }
            }
        
        
            /**
             * display the reports
             */
            public function reports_section()
            {
                // make sure the users have the access permision
                if (!current_user_can('edit_others_posts')) {
                  wp_die('Unauthorized user');
                }
                ?>
            
            
                <div class="wrap">
                    <!-- Title -->
                    <h1><?php echo __('Reports', 'mct-reports') ?></h1>
                    <br>
            
                    <div>
                        <!-- render a criteria -->
                        <table id="report_criteria" class="form-table">
                          <tr>
                            <th>
                              <?php echo __('Time period', 'mct-reports') ?>
                            </th>
                            <td id="report_date">
                              <!-- default date is last 7 days until today -->
                              <input type="text" id="txtStartDate" name="txtStartDate" value="<?php echo date('d/m/Y', strtotime('-7 days')); ?>">      
                              <?php echo __(' to ','mct-reports'); ?>              
                              <input type="text" id="txtEndDate" name="txtEndDate" value="<?php echo date('d/m/Y'); ?>"> 
                              <div class="help-block with-errors" style="display:none;"><?php echo __('Please choose the time period','mct-reports') ?></div>                     
                            </td>
                          </tr>       
                          <tr>
                            <th>
                              <?php echo __('Select a report','mct-reports') ?>
                            </th>
                            <td>
                              <select id="reports_selector" name="reports_selector">
                                <option value="total" selected><?php echo __('Total job applications','mct-reports') ?></option>
                                <option value="total_by_location"><?php echo __('Total job applications by locations','mct-reports') ?></option>
                                <option value="total_by_positionType"><?php echo __('Total job applications by position type','mct-reports') ?></option>
                                <option value="total_by_specialty"><?php echo __('Total job applications by specialty','mct-reports') ?></option>
                              </select>                  
                            </td>
                          </tr> 
                          <tr>
                            <td style="padding-left:0px;">
                              <button class="btn-add button-primary" id="btn_report" name="btn_report"><?php echo __('Show report','mct-reports') ?></button>
                            </td>
                          </tr>                 
                        </table>
                      </div>
                </div> 
                <!-- end div.wrap -->
            
                <br>
            
            
                <div class="wrap">
                    <!-- render a table -->
                    <!-- @link https://datatables.net/examples/data_sources/dom.html -->
                    <table id="mct_report" class="table table-striped table-bordered" style="width:100%; display:none;">
                        <thead>
                            <tr>
                                <th>#</th>
                                <th>Name</th>
                                <th>Position Applied</th>
                                <th>Salary Expectation</th>
                                <th>Email</th>
                                <th>Date</th>
                            </tr>
                        </thead>
            
                        <!-- the body section will inject by ajax  -->
            
                        <tfoot>
                            <tr>
                                <th>#</th>
                                <th>Name</th>
                                <th>Position Applied</th>
                                <th>Salary Expectation</th>
                                <th>Email</th>
                                <th>Date</th>
                            </tr>
                        </tfoot>
                    </table>
                </div> 
                <!-- end div.wrap -->
                
            
            
                <div class="wrap">
                    <!-- @link https://datatables.net/examples/advanced_init/row_grouping.html -->
                    <table id="mct_report_grouping" class="table table-striped table-bordered" style="width:100%; display:none;">
                      <thead>
                          <tr>
                            <th>#</th>
                            <th>Name</th>
                            <th>Position Applied</th>
                            <th>Salary Expectation</th>
                            <th>Email</th>
                            <th>Date</th>
                            <th>Localtion</th>
                            <th>Position Type</th>
                            <th>Specialty</th>
                          </tr>
                      </thead>
            
                             
                        <!-- the body section will inject by ajax  -->
            
                      <tfoot>
                          <tr>
                            <th>#</th>
                            <th>Name</th>
                            <th>Position Applied</th>
                            <th>Salary Expectation</th>
                            <th>Email</th>
                            <th>Date</th>
                            <th>Localtion</th>
                            <th>Position Type</th>
                            <th>Specialty</th>
                          </tr>
                      </tfoot>
                    </table>        
                </div>
                <!-- end div.wrap -->
            
                <?php
            }
        
        
            /**
             * get data from table
             */
            public function get_job_applications() 
            {
                global $wpdb;
                $table_name = $wpdb->prefix . 'job_applications'; // do not forget about tables prefix
                $status = 'active';
                $orderby = 'date_created, firstname';
                $order = 'asc';
            
                // # the parameters from ajax are $_POST['start_date'] and $_POST['end_date']
                // set the query condition and convert date format for using in query
                $start_date = strtr( $_POST['start_date'], '/', '-' );
                $start_date = strtotime($start_date);
                $start_date = date('Y-m-d',$start_date);  
            
                $end_date = strtr( $_POST['end_date'], '/', '-' );
                $end_date = strtotime($end_date);
                $end_date = date('Y-m-d',$end_date);
            
                // get current user id
                $user_id = get_current_user_id();        
                
                // get user role
                $user_meta = get_userdata( $user_id );
                $user_roles = $user_meta->roles;
    
                // query data from the custom table
                $query_result = $wpdb->get_results(
                  $wpdb->prepare("SELECT * FROM $table_name WHERE status = '$status' AND date_created BETWEEN '$start_date' AND '$end_date' ORDER BY %s %s", array( $orderby, $order ) )
                  , OBJECT);
                
                
                // add post meta into the query result so we can use these values in ajax call
                foreach( $query_result  as $value ) {
                    // get post meta
                    $job_localtion_id = get_post_meta( $value->id_job_opp, 'job_location', true );
                    $position_type_id = get_post_meta( $value->id_job_opp, 'job_type_position', true );
                    $specialty_id = get_post_meta( $value->id_job_opp, 'job_specialty', true );    
            
                    // get post title from custom post type
                    $job_location = get_the_title( $job_localtion_id );
            
                    // get term name by ID
                    $position_type = get_term_by( 'id' , $position_type_id, 'job_types_position' )->name;
                    $specialty = get_term_by( 'id' , $specialty_id, 'job_specialty' )->name;
            
                    // add 3 additional columns for row grouping table
                    $value->job_location = ( trim($job_location)==='' ? 'No Location' : $job_location );
                    $value->position_type = ( trim($position_type)==='' ? 'No Position Type' : $position_type );
                    $value->specialty = ( trim($specialty)==='' ? 'No Specialty' : $specialty );
                }
            
                // echo as json object to ajax calling
                echo json_encode($query_result);
            
                // # MUST add wp_die() here to avoid the 0 number that appends to the result json object and send back to ajax calling
                wp_die();
            }

        } // class ends
    
    
        // # initiate class on in the WP backend
        if( is_admin() ) {
            $mtc_reports = new Mtc_Reports;
        }
     
    } // class_exists() ends

Below is a filter form when you call reports_section function.

report filter

class-translation.php

This class is used for translation.

<?php
    // If this file is called directly, abort.
    if (!defined('ABSPATH')) die("No cheating!");

    if(!class_exists('Mtc_Reports_Translation')) 
    {
        
     class Mtc_Reports_Translation
     {
      public function __construct() {
        // translation
        add_action( 'init', array( $this, 'mct_reports_load_textdomain' ) );   
      }
    
      public function mct_reports_load_textdomain() {
        load_plugin_textdomain( 'mct-reports', false, MCT_REPORTS_LANG_PATH . '/languages' ); 
      } 
     }
    
      // initialize class
      if( is_admin() ) {
        $mtc_reports_translation = new Mtc_Reports_Translation;
      }
    }

mct-reports.js

This JavaScript file, it handles the click event from the filter form and then fetches the data following the filter form. Fetching data by Ajax. Once Ajax gets the response back, we will destroy the Datatable object and then we reinitiate the Datatable by mct_reports and mct_reports_grouping functions.

jQuery(document).ready(function ($) {

  // ------- START Report button is triggered --------- //
  $('#btn_report').on('click', function (e) {

    var $period = $('#report_date');
    var $txtStartDate = $('#txtStartDate');
    var $txtEndDate = $('#txtEndDate');
    var $reports_selector = $('#reports_selector');

    var $mct_report = $('#mct_report');
    var $mct_report_grouping = $('#mct_report_grouping');


    // # getting db via PHP function
    // we pass the javascript value to data attribute. this way, get_job_application function(PHP) can use these value. In PHP, you can access the value by $_POST.
    $.ajax({
      method: 'post',
      url: mctReportsAjax.ajaxurl,
      cache: false,
      dataType: 'json',
      data: {
        action: 'get_job_applications',
        start_date: $txtStartDate.val().trim(),
        end_date: $txtEndDate.val().trim(),
      },
      beforeSend: function () {
        // # hide the datatable tables
        $mct_report.hide();
        $mct_report_grouping.hide();

        // # destroy the database tables object then we can initialize when the criteria changes without loading new page
        $mct_report.dataTable().fnDestroy();
        $mct_report_grouping.dataTable().fnDestroy();
      },
      success: function (response) {
        // # check which report users want to view
        switch ($reports_selector.val()) {
          case 'total_by_location':
            // console.log('total_by_location');

            // show the report table
            $mct_report_grouping.show();
            // initial the report row grouping
            mct_reports_grouping(6, response);
            break;

          case 'total_by_positionType':
            // console.log('total_by_positionType');

            // show the report table
            $mct_report_grouping.show();
            // initial the report row grouping
            mct_reports_grouping(7, response);
            break;

          case 'total_by_specialty':
            // console.log('total_by_specialty');

            // show the report table
            $mct_report_grouping.show();
            // initial the report row grouping
            mct_reports_grouping(8, response);
            break;

          default:
            // show the report table
            $mct_report.show();
            // initial the report
            mct_reports(response);
            break;
        }
      },
      error: function (xhr, status, error) {
        var err = eval("(" + xhr.responseText + ")");
        alert(err.Message);
      }
    });

  });
  // ------- END Report button is triggered --------- //

});

The sample code above shows you how to write the plugin in an OOP way. Basically, it is the same PHP class. One thing you have to do in order to call the functions within the class is passing $this in the array() just like the code below.

<?php
   // call the function in a class
    add_action( 'init', array( $this, 'mct_reports_load_textdomain' ) );
?>

<?php
   // call the function outside a class
    add_action( 'init', 'mct_reports_load_textdomain' );
?>

If you don’t know how to write the code in OOP. You may start with something like the video tutorial.

mct_reports function

  /**
   * The datatables in bootstrap style. Create the data set array for datatable source data and initialize the datatable plugin
   * 
   * @link https://datatables.net/examples/styling/bootstrap.html
   * 
   * @param array object data from WP query function
   */
  function mct_reports(response) {

    // # create data set for datatable plugin
    var dataReport = []; // array type
    $.each(response, function (idx, item) {
      // add each data array into the dataReport variable
      dataReport.push([(idx + 1).toString(), item.firstname + ' ' + item.middlename + ' ' + item.lastname, item.position_appiled, item.salary_expect, item.email, item.date_created]);
    }); // loop ends    


    // # initial datatable plugin
    $('#mct_report').DataTable({
      data: dataReport,
    });
  }

mct_reports_grouping function

  /**
   * Row grouping
   * @link https://datatables.net/examples/advanced_init/row_grouping.html
   */
  function mct_reports_grouping(groupColumnParam, response) {

    // # which column(<td>) you want to group. start index with 0 for the first column
    var groupColumn = groupColumnParam;

    // # create data set for datatable plugin
    var dataReport = []; // array type
    $.each(response, function (idx, item) {
      // add each data array into the dataReport variable
      dataReport.push(['',
        item.firstname + ' ' + item.middlename + ' ' + item.lastname,
        item.position_appiled,
        item.salary_expect,
        item.email,
        item.date_created,
        item.job_location,
        item.position_type,
        item.specialty
      ]);
    }); // loop ends    


    var table = $('#mct_report_grouping').DataTable({
      "data": dataReport,
      "columnDefs": [{
        "visible": false,
        "targets": groupColumn
      }],
      "order": [
        [groupColumn, 'asc']
      ],
      "displayLength": 25,
      "drawCallback": function (settings) {
        var api = this.api();
        var rows = api.rows({
          page: 'current'
        }).nodes();
        var last = null;

        api.column(groupColumn, {
          page: 'current'
        }).data().each(function (group, i) {
          if (last !== group) {
            $(rows).eq(i).before(
              // colspan with total columns in the table except group column
              '<tr class="group"><td colspan="8">' + group + '</td></tr>'
            );

            last = group;
          }
        });
      }
    });

Yep, that’s it for today. I hope you enjoy!