Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

3 Steps Simple PHP Tag System

INTRODUCTION
THE COMMON USEFUL FEATURE

Welcome to a tutorial on how to implement a PHP tag system. Tags are a common feature in systems these days, and you can pretty much see how it is used to describe anything – Posts, products, pictures, videos, audio, etc… If you are looking to put a similar tag system into your own project, this guide will walk you through the exact steps on how to create one. Read on to find out!

I have included a zip file with all the source code at the end of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.

 

CONFESSION
AN HONEST DISCLOSURE

Quick, hide your wallets! I am an affiliate partner of Google, eBay, Adobe, Bluehost, Clickbank, and more. There are affiliate links and advertisements throughout this website. Whenever you buy things from the evil links that I recommend, I will make a commission. Nah. These are just things to keep the blog going, and allows me to give more good stuff to you guys - for free. So thank you if you decide to pick up my recommendations!


 

NAVIGATION
TABLE OF CONTENTS

Prelude
Overview & Assumptions

Step 1
The Database

Step 2
Server-Side Scripts

Step 3
Client-Side Scripts

Extra
Download & More

Closing
What’s Next?

PRELUDE
OVERVIEW & ASSUMPTIONS

Before we hit the code sections, let us first explain the overview of the system, and some of the assumptions that I have made – So you know what to expect from this guide.

ASSUMPTIONS

Most of you guys here should already have an existing system, or a tag system will not make any sense as an independent system. So instead of reinventing the wheel, we shall not touch on how to create posts, products, images, or whatever you want to tag; This guide will purely touch on tags only.

Also, I am going to assume that you guys are comfortable enough with the basics – PHP, SQL, HTML, CSS, Javascript, and more. We will not go into tiny boring little details such as “what is AJAX and JSON”.

OVERVIEW

There will be 3 parts to this guide and system:

  • The Database – Where we store all the tags.
  • Server-side Scripts – PHP libraries that will do all the process. Save tags to the database, get the tags, etc…
  • Client-side Scripts – The user interface, HTML, CSS, and Javascripts.

STEP 1
THE DATABASE

Now that we are done with the overview of the system, let us start with the foundations by building the database tables.

TAGS TABLE

sql/tags.sql
CREATE TABLE `tags` (
  `post_id` int(11) NOT NULL,
  `tag` varchar(32) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

ALTER TABLE `tags`
  ADD PRIMARY KEY (`post_id`,`tag`);
COMMIT;
FieldDescription
post_idPrimary key. The post ID, or it could be your product ID, image ID, whatever you want to tag.
tagPrimary key. The tag itself, feel free to change the number of characters if you are expecting very long tags.

Yep, this simple table is actually all we need.

DEMO POSTS TABLE

For the sake of completeness for the beginners, this is an example posts table that we will be adding tags to – But please take note, we can actually tag anything with a proper unique ID.

sql/posts.sql
CREATE TABLE `posts` (
  `post_id` int(11) NOT NULL,
  `post_title` text NOT NULL,
  `post_txt` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `posts` (`post_id`, `post_title`, `post_txt`) VALUES
(1, 'Stream of Rainbow', 'Can the predecessor hope the diesel? The unknown bombs whatever buried manpower. The crucial boy tenders a developed blurb. A top law clicks before a release. Why does our employee monitor the many lawyer? An ear fumes.'),
(2, 'Misty in the Wings', 'When will a competing helmet react in a noise? A paragraph acts above the agenda! A kept delight repairs a controlling crush. Can the procedure vanish? The documented rectangle inconveniences a hysterical luggage. The learned tobacco screams.'),
(3, 'Lord of the Minks', 'The undone complaint collapses past an east estate. The insulting nurse flames the era. A willed hierarchy surfaces. A tentative wife bites the consenting fence.'),
(4, 'Ice in the Scent', 'A futile pump bangs against the cider. A night stomachs a wizard. How does the mania originate? Can a reject wreck a taking battle?');

ALTER TABLE `posts`
  ADD PRIMARY KEY (`post_id`);

ALTER TABLE `posts`
  MODIFY `post_id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
COMMIT;
FieldDescription
post_idThe post ID and primary key.
post_titleThe post title.
post_txtThe contents of the post.

STEP 2
SERVER-SIDE SCRIPTS

Next, we shall create the server-side scripts to deal with all the backend processes.

CONFIG FILE

lib/config.php

The first of the server-side scripts that we have to create is a config file to store all our settings and stuff. Please remember to change the database settings to your own.

DATABASE LIBRARY

lib/lib-db.php
pdo = new PDO(
        $str, DB_USER, DB_PASSWORD, [
          PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
          PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
          PDO::ATTR_EMULATE_PREPARES => false
        ]
      );
    }

    // ERROR - CRITICAL STOP - THROW ERROR MESSAGE
    catch (Exception $ex) {
      print_r($ex);
      die();
    }
  }

  function __destruct () {
  // __destruct() : close connection when done

    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }

  function exec ($sql, $data=null) {
  // exec() : run insert, replace, update, delete query
  // PARAM $sql : SQL query
  //       $data : array of data

    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($data);
      $this->lastID = $this->pdo->lastInsertId();
    } catch (Exception $ex) {
      $this->error = $ex;
      return false;
    }
    $this->stmt = null;
    return true;
  }

  function start () {
  // start() : auto-commit off

    $this->pdo->beginTransaction();
  }

  function end ($commit=1) {
  // end() : commit or roll back?

    if ($commit) { $this->pdo->commit(); }
    else { $this->pdo->rollBack(); }
  }

  function fetchAll ($sql, $cond=null, $key=null, $value=null) {
  // fetchAll() : perform select query (multiple rows expected)
  // PARAM $sql : SQL query
  //       $cond : array of conditions
  //       $key : sort in this $key=>data order, optional
  //       $value : $key must be provided. If string provided, sort in $key=>$value order. If function provided, will be a custom sort.

    $result = [];
    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($cond);
      // Sort in given order
      if (isset($key)) {
        if (isset($value)) {
          if (is_callable($value)) {
            while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
              $result[$row[$key]] = $value($row);
            }
          } else {
            while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
              $result[$row[$key]] = $row[$value];
            }
          }
        } else {
          while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
            $result[$row[$key]] = $row;
          }
        }
      }
      // No key-value sort order
      else {
        $result = $this->stmt->fetchAll();
      }
    } catch (Exception $ex) {
      $this->error = $ex;
      return false;
    }
    // Return result
    $this->stmt = null;
    return count($result)==0 ? false : $result ;
  }

  function fetch ($sql, $cond=null, $sort=null) {
  // fetch() : perform select query (single row expected)
  //           returns an array of column => value
  // PARAM $sql : SQL query
  //       $cond : array of conditions
  //       $sort : custom sort function

    $result = [];
    try {
      $this->stmt = $this->pdo->prepare($sql);
      $this->stmt->execute($cond);
      if (is_callable($sort)) {
        while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
          $result = $sort($row);
        }
      } else {
        while ($row = $this->stmt->fetch(PDO::FETCH_NAMED)) {
          $result = $row;
        }
      }
    } catch (Exception $ex) {
      $this->error = $ex;
      return false;
    }
    // Return result
    $this->stmt = null;
    return count($result)==0 ? false : $result ;
  }

  function fetchCol ($sql, $cond=null) {
  // fetchCol() : yet another version of fetch that returns a flat array
  // I.E. Good for one column SELECT `col` FROM `table`

    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($cond);
    $result = $this->stmt->fetchAll(PDO::FETCH_COLUMN, 0);
    return count($result)==0 ? false : $result;
  }
}

Next, we have a database library here, that will do all the SQL heavy lifting work.

FunctionDescription
__constructThe constructor, will automatically connect to the database when the object is created.
__destructThe destructor, will automatically close the database connection when the object is destroyed.
execRuns an insert, replace, update, or delete query.
startAuto-commit off, used for multiple queries and in conjunction with end.
endCommit or rollback? Used in conjunction with start.
fetchAllRun a select query, with multiple rows of results expected.
fetchRun a select query, with a single row of result expected. Will return an associative array of column > value.
fetchColRun a select query, but only on a single column of data. Will return a flat array of values on the selected column.

TAG LIBRARY

lib/lib-tag.php
fetchCol("SELECT `tag` FROM `tags` WHERE `post_id`=?", [$id]);
  }

  function reTag($id, $tags=null) {
  // reTag() : replace tags for the given post ID
  // PARAM $id : post ID
  //       $tags : array of tags
    // Auto-commit off
    $this->start();

    // Remove old tags first
    $pass = $this->exec(
      "DELETE FROM `tags` WHERE `post_id`=?", [$id]
    );

    // Add new tags - If any
    // Might be a good idea to limit the total number of tags...
    if ($pass && is_array($tags) && count($tags)>0) {
      $sql = "INSERT INTO `tags` (`post_id`, `tag`) VALUES ";
      $data = [];
      foreach ($tags as $t) {
        $sql .= "(?,?),";
        $data[] = $id;
        $data[] = $t;
      }
      $sql = substr($sql, 0, -1);
      $pass = $this->exec($sql, $data);
    }

    // End - commit or rollback
    $this->end($pass);
    return $pass;
  }
}

This is a simple library that will work with the tags.

FunctionDescription
getAllGet all the tags for the given post ID.
reTagDelete and replace tags for the given post.

AJAX HANDLER

ajax-tag.php
 0,
      "message" => "Invalid request"
    ]);
    break;

  // GET TAGS FOR POST
  case "get":
    $tags = $tagDB->getAll($_POST['post_id']);
    echo json_encode([
      "status" => is_array($tags) ? 1 : 0,
      "message" => $tags
    ]);
    break;
  // SAVE TAGS
  case "save":
    $pass = $tagDB->reTag($_POST['post_id'], json_decode($_POST['tags']));
    echo json_encode([
      "status" => $pass ? 1 : 0,
      "message" => $pass ? "OK" : $tagDB->error
    ]);
    break;
}

Of course, the library themselves will not do anything on their own, and so, we have to create this AJAX handler that will process the user requests. How it works is very simple, we give it a $_POST['req'] to specify which request we want, followed by the required parameters.

RequestDescription
get

Get all the tags for the given post ID. Parameters:

  • post_id
save

Save the tags for the given post ID. Parameters:

  • post_id
  • tags

STEP 3
CLIENT-SIDE SCRIPTS

For the final piece of the puzzle, we only have to create the user interface and allow users to tag the posts.

THE HTML

my-post.php
getAll($postID);

// HTML ?>


  
    Simple Tag Demo

Lord of the Minks

The undone complaint collapses past an east estate. The insulting nurse flames the era. A willed hierarchy surfaces. A tentative wife bites the consenting fence.

MANAGE TAGS

%s
", $t); } } ?>

This is just a simple dummy “edit post” page, and the only important part is the tag docket below, and take note of that hidden post_id field.

 

THE CSS

tag.css
/* [TAGS LIST] */
#tag_dock {
  max-width: 400px;
  padding: 20px;
  background: #f7f7f7;
}
#tag_dock h3 {
  margin: 0 0 3px 0;
  color: #bf4646;
}
#tag_list {
  width: 100%;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
}
#tag_list div.tag {
  box-sizing: border-box;
  font-size: 0.9em;
  width: 30%;
  margin: 3px;
  padding: 5px;
  border: 1px solid #aaa;
  background: #e8f2ff;
}
#tag_list div:hover {
  background: #ffe8e8;
  cursor: pointer;
}

/* [TAGS FORM] */
#tag_dock label, #tag_dock input {
  box-sizing: border-box;
  display: block;
  width: 100%;
  margin-top: 8px;
}
#tag_dock label {
  font-size: 0.9em;
  color: #888;
}
#tag_dock input {
  padding: 8px;
  font-size: 1em;
}
#tag_dock input[type=button] {
  background: #2f75b7;
  color: #fff;
  cursor: pointer;
  border: 0;
}

/* [DOES NOT MATTER] */
html, body {
  font-family: arial, sans-serif;
}

Just some cosmetics. Feel free to change this to fit your own website theme.

 

THE JAVASCRIPT

tag.js
var tag = {
  list : [], // Existing list of tags
  add : function (evt) {
  // tag.add() : press comma or enter to add tag

    if (evt.key=="," || evt.key=="Enter") {
      // Input check
      var tagged = evt.key=="," ? this.value.slice(0, -1) : this.value,
          error = "";

      // Freaking joker empty input
      if (tagged=="") {
        error = "Please enter a valid tag";
      }

      // Check if already in tags list
      if (error=="") {
        if (tag.list.indexOf(tagged) != -1) {
          error = tagged + " is already defined";
        }
      }

      // OK - Create new tag
      if (error=="") {
        var newTag = document.createElement("div");
        newTag.classList.add("tag");
        newTag.innerHTML = tagged;
        newTag.addEventListener("click", tag.remove);
        document.getElementById("tag_list").appendChild(newTag);
        tag.list.push(tagged);
        this.value = "";
      }

      // Not OK - Show error message
      else {
        this.value = tagged;
        alert(error);
      }
    }
  },

  remove : function () {
  // tag.remove() : remove tag

    // Remove tag from list array first
    // var pos = tag.list.indexOf(this.innerHTML);
    tag.list.splice(tag.list.indexOf(this.innerHTML), 1);

    // Remove HTML tag
    document.getElementById("tag_list").removeChild(this);
  },

  save : function () {
  // tag.save() : save the tags

    // DATA
    var data = new FormData();
    data.append('req', 'save');
    data.append('post_id', document.getElementById('post_id').value);
    data.append('tags', JSON.stringify(tag.list));

    // AJAX
    var xhr = new XMLHttpRequest();
    xhr.open('POST', "ajax-tag.php", true);
    xhr.onload = function(){
      var res = JSON.parse(this.response);
      // OK
      if (res.status==1) {
        alert("Save OK");
      } else {
        alert(res.message);
      }
    };
    xhr.send(data);
    return false;
  }
};

// INIT ON WINDOW LOAD
window.addEventListener("load", function() {
  // Get list of existing tags
  var all = document.querySelectorAll("#tag_list div.tag");
  if (all.length>0) {
    for (var t of all) {
      tag.list.push(t.innerHTML);
      // Attach remove listener to tags
      t.addEventListener("click", tag.remove);
    }
  }

  // Attach comma listener to input field
  document.getElementById("tag_in").addEventListener("keyup", tag.add);

  // Enable controls
  document.getElementById("tag_in").disabled = false;
  document.getElementById("tag_save").disabled = false;
});

Finally, this is the Javascript that will do all the rest of the magic – Take note, this is pure Javascript. No jQuery, no Bootstrap. Feel free to implement your own if you want, or make changes to how this one works.

FunctionDescription
tag.add

This is the function that is attached to the keyup event on the tag input field. It simply adds the tag to the list when the user hits the comma or enter key.

tag.remove

Removes the given tag from the list.

tag.save

Proceed with saving the tags list.

 

 

EXTRA
DOWNLOAD & MORE

That’s all for the code, and here is the download link as promised – Plus a small extra that may be useful to you.

 

SEARCH BY TAG

lib/lib-tag.php
fetchAll(
    "SELECT p.* FROM `tags` t LEFT JOIN posts `p` USING (`post_id`) WHERE t.`tag`=?",
    [$tag],
    "post_id"
  );
}

Now that we have a proper tag system, here is a small extra function that may be useful to you – Searching for all posts that have a given tag.

 

HOW TO IMPLEMENT

Some of you beginner code ninjas should be confused over how to implement this into your own project now. Remember the 3 steps in this guide, tackle them piece-by-piece and you will do just fine.

  • Database – Make sure that the existing table that you want to tag has a unique ID, then create the tags table with that ID as the foreign key.
  • Server-side scripts – The PHP libraries and AJAX handlers. Add more of your own functions that are relevant to your project.
  • Client-side scripts – Implement the tags docket into your own landing pages, the HTML, CSS, and Javascript.

 

SOURCE CODE DOWNLOAD

Click here to download the source code, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
  

CLOSING
WHAT’S NEXT?

Thank you for reading, and we have come to the end of this guide. I hope that it has helped you in your project, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!

The post 3 Steps Simple PHP Tag System appeared first on Code Boxx.



This post first appeared on Xxxxxxxxx, please read the originial post: here

Share the post

3 Steps Simple PHP Tag System

×

Subscribe to Xxxxxxxxx

Get updates delivered right to your inbox!

Thank you for your subscription

×