How to extend fields

From Osclass
Jump to: navigation, search

This page will show you how to create an attributes plugin to extend the fields of the ads in your Osclass installation. In most cases the built-in functionality custom fields of Osclass is enough to extend the fields of an ad, however, sometimes it's necessary to add a search functionality or have a deeper customization. In those cases, creating a plugin is the best option.


Requirements

  • You are required to have a very basic knowledge of PHP, HTML and MySQL
  • You are required to have read and understood How to create a plugin (basic knowledge of plugins, hooks and Osclass)


Base of our plugin

For this tutorial we're going to use products attributes plugin as a base. Search for the last release and download it to your PC.


Plugin structure

Files and folders you will find on the plugin:

  • languages: this folder has subfolders with the different locales your plugin is available
  • index.php: this is the main file of the plugin
  • item_detail.php: this is the HTML template that will show when users see the ad
  • item_edit.php: this is the HTML template that will show when publishing or editing and ad
  • search_form.php: this is the HTML template that will show on the search menu at the search page
  • struct.sql: .sql file that contains the structure for the tables of the plugins


You're free to rename those files as you want, as long as you change the names later in the code


Languages

Your plugin can be translated to other locales. To make it available, you need to use the functions __('text', 'name_of_your_plugin') and _e('text', 'name_of_your_plugin'), for every string on it. Later, you should generate .mo and .po files for different languages and place them in folders inside the language folder.


Main file (index.php)

First, we're going to declare the name and version of our plugin. Feel free to modify the next lines:

/*
Plugin Name: My Own attributes
Plugin URI: http://www.myownwebsite.org/myownplugin
Description: This plugin extends a category of items to my own attributes
Version: 1.0
Author: Me
Author URI: http://www.myownwebsite.org/
Short Name: myown_plugin
*/


At the end of index.php file, you will encounter the hooks. Hooks can be placed anywhere. To keep code clear, we put them at the end. These are the basic hooks needed to have a full functional attributes plugin:

// This is needed in order to be able to activate the plugin
osc_register_plugin(osc_plugin_path(__FILE__), 'myown_call_after_install');
// This is a hack to show a Configure link at plugins table (you could also use some other hook to show a custom option panel)
osc_add_hook(osc_plugin_path(__FILE__)."_configure", 'myown_admin_configuration');
// This is a hack to show a Uninstall link at plugins table (you could also use some other hook to show a custom option panel)
osc_add_hook(osc_plugin_path(__FILE__)."_uninstall", 'myown_call_after_uninstall');

// When publishing an item we show an extra form with more attributes
osc_add_hook('item_form', 'myown_form');
// To add that new information to our custom table
osc_add_hook('item_form_post', 'myown_form_post');

// When searching, display an extra form with our plugin's fields
osc_add_hook('search_form', 'myown_search_form');
// When searching, add some conditions
osc_add_hook('search_conditions', 'myown_search_conditions');

// Show an item special attributes
osc_add_hook('item_detail', 'myown_item_detail');

// Edit an item special attributes
osc_add_hook('item_edit', 'myown_item_edit');
// Edit an item special attributes POST
osc_add_hook('item_edit_post', 'myown_item_edit_post');

//Delete item
osc_add_hook('delete_item', 'myown_delete_item');

// previous to insert item
osc_add_hook('pre_item_post', 'myown_pre_item_post') ;


struct.sql

We are going to create a table to hold just one extra field (s_myfield, with a length of 50 characters), the struct.sql will be something like this example, but you're free to modify its structure and add more fields.

CREATE TABLE /*TABLE_PREFIX*/t_item_myown_attr (
    fk_i_item_id INT UNSIGNED NOT NULL,
    s_myfield VARCHAR(50),

        PRIMARY KEY (fk_i_item_id),
        FOREIGN KEY (fk_i_item_id) REFERENCES /*TABLE_PREFIX*/t_item (pk_i_id)
) ENGINE=InnoDB DEFAULT CHARACTER SET 'UTF8' COLLATE 'UTF8_GENERAL_CI';

Installing the plugin

We need to create a MySQL table to save the extra fields of your plugin. There're several ways to achieve this, but if you are going to use several tables, the best way is to create a .sql file with the definition of it. Later, on index.php, we call our install function, which basically will read struct.sql and execute its contents.

function myown_call_after_install() {
    // Insert here the code you want to execute after the plugin installation
    // for example you might want to create a table or modify some values

    // In this case we'll create a table to store the Example attributes
    $conn = getConnection() ;
    $conn->autocommit(false);
    try {
        $path = osc_plugin_resource('myown_attributes/struct.sql');
        $sql = file_get_contents($path);
        $conn->osc_dbImportSQL($sql);
        $conn->commit();
    } catch (Exception $e) {
        $conn->rollback();
        echo $e->getMessage();
    }
    $conn->autocommit(true);
}


Uninstalling the plugin

When we installed the plugin, we created some tables. If the user wants to uninstall it, we should delete the tables and remove all other data we created.

function myown_call_after_uninstall() {
    // Insert here the code you want to execute after the plugin's uninstall
    // for example you might want to drop/remove a table or modify some values
	
    // In this case we'll remove the table we created to store Example attributes
    $conn = getConnection() ;
    $conn->autocommit(false);
    try {
        $conn->osc_dbExec("DELETE FROM %st_plugin_category WHERE s_plugin_name = 'myown_plugin'", DB_TABLE_PREFIX);
        $conn->osc_dbExec('DROP TABLE %st_item_myown_attr', DB_TABLE_PREFIX);
        $conn->commit();
    } catch (Exception $e) {
        $conn->rollback();
        echo $e->getMessage();
    }
    $conn->autocommit(true);
}


Make your plugin configurable

Your plugin can be installed and uninstalled correctly now. You can start testing it! The next step is to make it configurable,because we're doing a standard attributes plugin and we need to let admin chose which categories they want the plugin to be attached to. This is done with this small piece of code:

function myown_admin_configuration() {
    // Standard configuration page for plugin which extend item's attributes
    osc_plugin_configure_view(osc_plugin_path(__FILE__));
}


Publishing items

We need to create a template of a form to publish our new extra field, take a look at item_edit.php

Pro-tip: use unique name variables and fields, so they won't get mixed up with Osclass' core or someone else's plugin.


<h3><?php _e('MyOwn attributes', 'myown_attributes') ; ?></h3>
<table>
    <tr>
        <?php
            // This lines prevent to clear the field if the form is reloaded
            if( Session::newInstance()->_getForm('myown_myfield') != '' ) {
                $detail['myown_myfield'] = Session::newInstance()->_getForm('myown_myfield');
            }
        ?>
        <td><label for="make"><?php _e('MyField', 'myown_attributes'); ?></label></td>
    	<td><input type="text" name="myown_myfield" id="myown_myfield" value="<?php if(@$detail['s_myfield'] != ''){echo @$detail['s_myfield']; } ?>" size="20" /></td>
    </tr>
</table>


We already have our template file item_edit.php (yeah, it's called item_edit, but it will work for both, edit and add!).Now we need a way to display it on the form. That's an easy task, take a look at this function:

function myown_form($catId = '') {
    // We received the categoryID
    if($catId!="") {
        // We check if the category is the same as our plugin
        if(osc_is_this_category('myown_plugin', $catId)) {
            include_once 'item_edit.php';
        }
    }
}


Test it, test it now. The form should appear if you select the correct categories. Time to save our field in our mighty table.

function myown_form_post($catId = null, $item_id = null) {
    // We received the categoryID and the Item ID
    if($catId!=null) {
        // We check if the category is the same as our plugin
        if(osc_is_this_category('myown_plugin', $catId)) {
                // Insert the data in our plugin's table
                $conn = getConnection() ;
                $conn->osc_dbExec("INSERT INTO %st_item_myown_attr (fk_i_item_id, s_myfield) VALUES (%d, '%s')", DB_TABLE_PREFIX, $item_id, Params::getParam('myown_myfield') );
        }
    }
}



Editing an ad is quite similar to publishing it. This is why we called our template item_edit.php. Since we're using the same file for publishing and editing, we only need to write functions to display the form (before) and manage the data (after).


// Self-explanatory
function myown_item_edit($catId = null, $item_id = null) {
    if(osc_is_this_category('myown_plugin', $catId)) {
        $conn = getConnection() ;
        // Gather the information related to the ad we're editing
        $detail = $conn->osc_dbFetchResult("SELECT * FROM %st_item_myown_attr WHERE fk_i_item_id = %d", DB_TABLE_PREFIX, $item_id);
        if(isset($detail['fk_i_item_id'])) {
            include_once 'item_edit.php';
        }
    }
}

function myown_item_edit_post($catId = null, $item_id = null) {
	// We received the categoryID and the Item ID
	if($catId!=null) {
		// We check if the category is the same as our plugin
		if(osc_is_this_category('myown_plugin', $catId)) {
                    $conn = getConnection() ;
                    $conn->osc_dbExec("REPLACE INTO %st_item_myown_attr (fk_i_item_id, s_myfield) VALUES (%d, '%s')", DB_TABLE_PREFIX, $item_id, Params::getParam('myown_myfield') );
		}
	}
}


Pro-tip: We use REPLACE instead of an UPDATE SQL query because, if we add the plugin after some ads have been published, the record of them in the table would not exists and an UPDATE query will fail. Instead, a REPLACE query will always work.


Search filters

At this point, we should have a full working plugin that adds an extra attribute (myfield) to our ads, but we want our users to be able to filter search results by our new field. We need three things, a template file (search_form.php) for the filter, a function to display the template and a function to add conditions to the search.

The template will be quite simple, an input text field. This template should change depending on your needs, but we're going to do the most basic condition: any ad which contains a certain text in the new field.

<h3><?php _e('MyOwn attributes', 'myown_attributes') ; ?></h3>
<div class="row one_input">
    <table>
        <tr>
            <td>
                <label for="myown_myfield"><?php _e('MyField', 'myown_attributes'); ?></label>
                <br/>
                <input type="text" id="myown_myfield" name="myown_myfield" />
            </td>
        </tr>
    </table>
</div>


To display the new search filter:

function myown_search_form($catId = null) {
    include_once 'search_form.php';
}


Ok, the filter should show up now in the search page, let's add some conditions to our search!

// Adds some plugin-specific search conditions
function myown_search_conditions($params) {
    // we need conditions and search tables (only if we're using our custom tables)
        $has_conditions = false;

        foreach($params as $key => $value) {
            // We may want to  have param-specific searches
            switch($key) {
                case 'myown_myfield':
                    Search::newInstance()->addConditions(sprintf("%st_item_myown_attr.s_myfield LIKE '%%%s%%'", DB_TABLE_PREFIX, $value));
                    $has_conditions = true;
                    break;
                default:
                    break;
            }
        }

        // Only if we have some values at the params we add our table and link with the ID of the item.
        if($has_conditions) {
            Search::newInstance()->addConditions(sprintf("%st_item.pk_i_id = %st_item_myown_attr.fk_i_item_id ", DB_TABLE_PREFIX, DB_TABLE_PREFIX));
            Search::newInstance()->addTable(sprintf("%st_item_myown_attr", DB_TABLE_PREFIX));
        }
}

This function can be scary at first, and it can be simplified, but we prefer to leave it that way so it can be easily for you to modify and add more fields and conditions.

Additional functions

Our plugin is almost finished, it lets you install, uninstall, add and edit ads and even perform some filtered searches! Let's add two additional functions to make an awesome plugin.


First, we should perform some actions when an ad is deleted. Basically, delete the data in our table related to that ad. If the ad no longer exists, there's no point to keep fields of that ad.

function myown_delete_item($item) {
    $conn = getConnection();
    $conn->osc_dbExec("DELETE FROM %st_item_myown_attr WHERE fk_i_item_id = '" . $item . "'", DB_TABLE_PREFIX);
}


What else can be added? Well, this is completely unnecessary but it's awesome! Imagine your user is publishing an ad, but for some reason it fails, maybe the title is too short, or description too long,... So the page is reloaded and your extra field is empty!! In our example plugin is not such a problem, a simple 50 characters length text input is not much, but imagine you ask the user to write a dozen of fields, if the publish fail and your fields are empty, the user will be annoyed. To avoid that, we use this function (it's ok if you don't understand it, it works, just need to write the variable and its value):

function products_pre_item_post() {
    Session::newInstance()->_setForm('myown_myfield' , Params::getParam('myown_myfield'));
    // keep values on session
    Session::newInstance()->_keepForm('myown_myfield');
}


Enjoy

You've just created your first Osclass plugin!! If you have any question or just want to share it with other Osclass' users, visit our forums