Sunday 24 November 2013

Full-Text Search using MySQL


Intended Audience

This tutorial is intended for developers using MySQL
(http://www.MySQL.com/) and PHP
(http://www.php.net) who want to create a searchable database of some sort of textual data. It will focus on the Full-text capabilities presented by MySQL, moving into the Boolean opportunities that are presented in the latest alpha version, 4.1, of MySQL.

Overview

Using directories to group articles by category is a great way to help people to navigate through many articles. At some point, however, someone will want to find all the articles that pertain to a certain topic that may not have a directory of it’s own, or may span many directories . This is what the search engine is for.

Learning Objectives

In this tutorial, you will learn:
  • How to modify your current database to accommodate Full-text searching
  • How to use a simple Full-text search to quickly gather relevant responses
  • How to execute a more complex Full-text search with Boolean capabilities
  • Tips on what to do and what not to do, as well as the security implications of some of the example scripts presented.

Definitions

MySQL – An Open Source database that is used by many PHP developers for it’s support and
speed, as well as because it’s free.

Full-text – Built in functionality in MySQL that allows users to search through certain
tables for matches to a string.

Boolean Search  A search which allows users to narrow their results through the use of
Boolean operators.

Boolean Operators – A deductive logical system by which a user can narrow
results through the use of AND, OR, XOR, and other
operators.

Background Information

Before the advent of the search engine, users had to search manually through dozens – or hundreds – of articles and tidbits to find the ones that were right for them. Nowadays, in our more user-centered world, we expect the results to come to the user, not the other way around. The search engine gets the computer to do the work for the user.

Prerequisites

  • MySQL version 3.23.23 or better for the simple Full-text searching
  • MySQL version 4.1 alpha or better for the complex Boolean searching
  • PHP & A knowledge thereof.

Synopsis


Let’s start with a quick review of our situation:

We have a database that contains articles. We might create a table of database contents using a statement like this:

CREATE TABLE articles (body
TEXT, title VARCHAR(250), id INT NOT NULL auto_increment, PRIMARY
KEY(id);


Let’s say we have about 100 of these articles, covering various topics: MySQL, PHP, and various other topics of that sort. How do the users find the tutorials they want? Remember, we need to bring the results to
the user. This is going to be a search engine operation.

Initial Ideas

When I started to work with my first database which was only a tenth of the size, my MySQL query went something like this:

SELECT * FROM articles WHERE body LIKE '%$keyword%';

This was slow and inefficient.Every time someone searched for an article, they got far too many results, and as the database grew the system became downright shameful.

So what is the solution? It’s right here: Full-text Searching.



The Solution: Setup

Full-text Search is a feature introduced to MySQL in version 3.23.23. This is how I used it to fix my problem:

I started out with an update to my table:


ALTER TABLE articles ADD FULLTEXT(body, title);

This set ups our Full-text index. The (body, title) part tells us that we can search the body and title for keywords later on. We’ll find out how to use this later, once we’ve overcome a potential problem.

In my original database BLOB was my datatype for the body of the article. What’s the problem, you ask? BLOBs are meant primarily for binary data. What use is searching binary data? MySQL has been programmed not to index BLOB datatypes for Full-text searching. If you try to index BLOB
datatypes, you get an Error 140.

The fix for this is simple:


ALTER TABLE articles MODIFY body TEXT;



That switches datatype from BLOB to TEXT, thus making a useful column for searching.





The Solution: Actually Doing Something


How do we get results? Let’s jump right in and try it
out:

<?php
    MySQL_connect("hostname", "username", "password");
    MySQL_select_db("our_db");
    $query = "
        SELECT * FROM articles
        WHERE MATCH(title, body) AGAINST ('PHP')
    ";
    $sql = MySQL_query($query);
    /* output results */
?>

What will this give us? Well, let’s go over Full-Text first.

According to the MySQL manual, Full-text is a “natural language search”; it indexes words that appear to represent the row, using the columns you specified. As an example, if all your rows contain “MySQL” then “MySQL” won’t match much. It’s not terribly unique, and it would return too many results. However, if

“MySQL” were present in only 5% of the rows, it would return those rows because it doesn’t appear too often to be known as a keyword that’s very common. (If you have “MySQL” in none of your rows, it’ll return nothing; duh.)

MySQL also does something pretty useful. It creates a score. This score is usually something like .9823475 or .124874, but always larger than zero. It can range up above 1, and I have seen it at 4 sometimes. (Don’t
try to multiply it by 100 and portray it as a % value; people will wonder why their keyword matches an article 431%!)

MySQL will also order a row by its score, descending.

Another useful tidbit: If you use
MATCH() AGAINST() Change the
document style for this to “Inline Code” twice in a query, as we
will, there is no additional speed penalty. You might expect that because you
are executing the same search twice the query would take twice as long, but in
fact MySQL remembers the results from the first search as it runs the second.

So, let’s talk about the actual query: We are taking every
column from articles, and searching “title” and “body” for
$keyword This is also Inline
Code. Pretty simple.

And if we want to display the score too:


<?php
    /* connect to MySQL (same as always) */
    $query = "
        SELECT *,
            MATCH(title, body) AGAINST ('PHP') AS score
        FROM articles
        WHERE MATCH(title, body) AGAINST('PHP')
    ";
    $sql = MySQL_query($query);
    /* display the results... */
?>

More about Basic Searching


What more is there to say? Well, there’s another feature I can introduce to you.
When most people meet up with a search box they don’t type in only one word. Not knowing the backend, they just type in as many words as they feel like!

MySQL realizes this and deals with it. If I were you, the only thing I would do is remove the commas that might be there, using str_replace. MySQL will take all the words, split them up, and then match using a natural language search.

As a secondary note, you should never send input directly from the user to the MySQL prompt because any number of characters could terminate your MySQL query and begin another dastardly statement. (This is presuming you replace PHP with a $keyword in the above script.)


Example: Basic Searching Application


Now that we know all about basic searching, you ask: What more is there to learn? Well, not much except that I’ve created a quick sample application for you. Let’s launch straight into the code. This bare bones
application will search for a phrase or a keyword that the user

inputs:

<?php
    /* call this script "this.php" */
    if ($c != 1) {
?>
<form action="this.php?c=1">
<input type="text" name="keyword">
<input type="submit" value="Search!">
</form>
<?php
    } else if ($c==1) {
        MySQL_connect("hostname", "username", "password");
        MySQL_select_db("database");
        $sql = "
            SELECT *,
                MATCH(title, body) AGAINST('$keyword') AS score
                FROM articles
            WHERE MATCH(title, body) AGAINST('$keyword')
            ORDER BY score DESC
        ";
        $res = MySQL_query($sql);
?>
<table>
<tr><td>SCORE</td><td>TITLE</td><td>ID#</td></tr>
<?php
        while($row = MySQL_fetch_array($rest)) {
            echo "<tr><td>{$sql2['score']}</td>";
            echo "<td>{$sql2['title']}</td>";
            echo "<td>{$sql2['id']}</td></tr>";
        }
        echo "</table>";
    }
?>

What does this script do? First, it checks $c to see if user input has been sent. If it has not, the form is displayed. If it has, the script moves onwards.

The same query that we’ve been using is used here: we match against what the user inputs. We then draw a table and display it in [semi-]pretty form. The ORDER BY score DESC Code Inline makes sure that the best scores (the most accurate matches) are shown first.

Important note: Never use this simple script in any production form because I have done absolutely no error checking. The $query variable provides an easy opening for an intruder to input something nasty into your query that might destroy your data.

Advanced Boolean Searching

If you need more options in your MySQL searching, or you haven’t finished your coffee yet, keep reading. The advanced search engine tutorial begins here.

Before we get started into the magic of bool, I recommend you do a quick SELECT version();

Code Inlineon your MySQL server. I spent several hours battling my computer until I read this line in the MySQL manual:

As of Version 4.0.1, MySQL can also perform Boolean full-text searches using the IN BOOLEAN MODE modifier.

Whoops; 4.0.1 is the newest, alpha release of MySQL Still check for this… it should by MySQL. If you’re looking to use this on a production server, I’d strongly recommend against that decision. I found I was
using a 3.23.23, and I had to set up an experimental MySQL server to use the bool functions that it offers now.

Overall, I was very pleased with the performance of the new bool searching; the scoring system is changed, but one can still manage. Within 15 minutes of upgrading, I had a simple bool search page up and running on my articles database.

Boolean: The Basic Technical Aspect

The only thing you change to use Boolean mode is the AGAINST() part of your query.
You add IN BOOLEAN MODE to the very end of it, and place the arguments right before it. E.g. to search for all the articles that contain the word PHP, you could write:

SELECT * FROM articles WHERE
MATCH(title, body) AGAINST('PHP' IN BOOLEAN MODE)DR[10]

That will find all the articles that contain the word “PHP” somewhere in them. It’s a fairly simple search. If you were to get more complex, and wanted everything that has to do with PHP, but not with MySQL, then you could execute this statement:

SELECT * FROM articles WHERE MATCH(title, body) AGAINST('+PHP -MySQL' IN BOOLEAN MODE);

There are more modifiers that one can use to search with, and I will quote from the MySQL manual since I see no point in typing out a synopsis of the manual:


Operator

Meaning

By default (when neither plus nor minus is specified) the word
is optional, but the rows that contain it will be rated higher. This mimics the
behavior of MATCH() ...
AGAINST()DR


+

A leading plus sign indicates that this word must be present
in every row returned.

-

A leading minus sign indicates that this word must not be
present in any row returned. 

< >

These two operators are used to change a word’s contribution
to the relevance value that is assigned to a row. The < operator decreases
the contribution and the > operator increases it. See the example below.

( )

. Parentheses are put round sub-expressions to give them
higher precedence in the search. 

~

A leading tilde acts as a negation operator, causing the
word’s contribution to the row relevance to be negative. It’s useful for marking
noise words. A row that contains such a word will be rated lower than others,
but will not be excluded altogether, as it would be with the minus
operator.

*

An asterisk is the truncation operator. Unlike the other
operators, it is appended to the word, or fragment, not prepended. 


Double quotes at the beginning and end of a phrase, matches
only rows that contain the complete phrase, as it was typed.
A Basic Boolean Searching Application

Again, we’ll start with the code straight off:

<?php
   /* call this script "advs.php" */
   if(!$c) {
?>
<form action="advs.php?c=1" method=POST>
<b>Find Results with: </b><br>
Any of these words: <input type="text" length=40 name="any"> <br>
All of these words: <input type="text" length=40 name="all"> <br>
None of these words: <input type="text" length=40 name="none"> <br>
<input type="submit" value="Search">
</form>
<?
   } else if($c) {
   MySQL_connect("hostname", "username", "password");
       MySQL_select_db("database");
   if((!$all) || ($all == "")) { $all = ""; } else { $all = "+(".$all.")"; }
   if((!$any) || ($any == "")) { $any = ""; }
   if((!$none) || ($none == "")) { $none = ""; } else { $none = "-(".$none.")"; }
   $query = "
       SELECT *,
          MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE) AS score
          FROM compsite
       WHERE MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE)";
      $artm1 = MySQL_query($query);
      if(!$artm1) {
         echo MySQL_error()."<br>$query<br>";
      }
      echo "<b>Article Matches</b><br>";
      if(MySQL_num_rows($artm1) > 0) {
         echo "<table>";
          echo "<tr><td>Score </td><td>Title </td><td>Body</td></tr>";
             while($artm2 = MySQL_fetch_array($artm1)) {
            $val = round($artm2['score'], 3);
            $val = $val*100;
            echo "<tr><td>$val</td>";
            echo "<td>{$artm2['title']}</td>";
            echo "<td>{$artm2['body']}</td></tr>";
         }
      echo "</table>";
   }
   else {
      echo "No Results were found in this category.<br>";
   }
   echo "<br>";
   }

After we get the input from the form, $c Code Inlineis set to 1 and we start the real work.

First we check our input. If it’s empty, we leave it empty, if it’s not, we append the proper + or – to it. The parentheses are to allow for the user typing more than 1 word in a given field.

$query = "
    SELECT *
        MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE) AS score
    FROM compsite
    WHERE
        MATCH(title, story) AGAINST ('$all $none $any' IN BOOLEAN MODE)";

That’s the final query that we use. $all, $none Code Inline, and $any have already been prepared for the query, and they are inserted. Score is returned as a column to order them by (if we wanted to do that), and from there on, we just have to output the results.


   if(MySQL_num_rows($artm1) > 0) {
      echo "<table>";
    echo "<tr><td>Score </td><td>Title </td><td>Body</td></tr>";
          while($artm2 = MySQL_fetch_array($artm1)) {
         $val = round($artm2['score'], 3);
         $val = $val*100;
         echo "<tr><td>$val</td>";
         echo "<td>{$artm2['title']}</td>";
         echo "<td>{$artm2['body']}</td></tr>";
      }
   echo "</table>";

That’s the output code. If there’s less than 1 row to output, we send a “no records found” message out.

That’s about it for Full-Text searching.

Resources


MySQL Man Page for Full-Text:
http://www.MySQL.com/doc/F/u/Fulltext_Search.html

Codeigniter MVC class and functions template

Hi this is Anjaneya Vadivel, Every time I make a website I usually try to plan my classes out ahead of time in a regular text file, this small step will reduce the amount of time spent figuring out how to design my application considerably. I find this especially useful when working with CodeIgniter. I can even create all the files ahead of time, (usually I use the touch command in terminal to do it quickly).

This is how my template might look before being filled out :

Example Class Template

MODEL: (classes with functions to GET/INSERT/UPDATE/DELETE from the database, called from controller classes)

model_class.php
[some functions listed here]

CONTROLLER: (functions to handle user requests : call model functions and load up views)

class_a.php
[some functions listed here]

class_b.php
[some functions listed here]

VIEW: (PHP files that contain the necessary html to see the page, loaded from controller classes)

template/html_head.php
template/html_tail.php

Example Blog Class Template

Once again this is not a completed template below as the functions would usually be filled in with as much information as I can plan before starting the actual application. This means thinking of all parameter names, and the insides of the functions before starting.

MODEL:

posts_model.php

get_posts()
get_post_by_id()
get_tags_by_post_id()
insert_post()
update_post_by_id()
delete_post_by_id()
etc…

CONTROLLER:

posts.php

index()
etc

pages.php

index($page)
etc…

VIEW:

template/html_head.php
template/html_tail.php
post/single_view.php
post/multi_view.php
page/contact_us
page/home
etc…

Why plan?

If you don’t already create a template to help organize your thoughts when programming, you should try it! It’s the easiest way to see the amount of work that needs to be done, divide work among multiple people easily, and help make MVC programming a little more intuitive.

How much planning do you do before starting your application?

Simple PHP Code in Codeigniter

This section will show you how to create the admin page for our blog, with pages to create and edit blog posts. By the end of this part you should have a functioning admin page (that requires login!) displaying a list of posts from the database, with links to view, edit, or delete the post.

We’re gonna want to add the methods to insert, update, and delete posts. With CodeIgniter manipulating database data is simplified.

Go back to the Posts model located at :

/application/models/posts.php

Copy the extra methods to our Post model from the code below :

  1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
  2. class Posts extends CI_Model
  3. {
  4.        
  5.         function __construct()
  6.         {
  7.                 parent::__construct();
  8.         }
  9.         // If $postId is NULL, gets all posts, otherwise get single post from db
  10.         // returns $post[]
  11.         public function get_posts($postId)
  12.         {
  13.                 $post=array();
  14.                 if ($postId !== null){         
  15.                         $query = $this->db->get_where('posts', array('id' =>$postId));
  16.                         if ($query->num_rows() == 1) { 
  17.                                 foreach($query->result() as $row){
  18.                                 $post['id'] = $row->id;
  19.                                 $post['title'] = $row->title;
  20.                                 $post['summary'] = $row->summary;
  21.                                 $post['content'] = $row->content;
  22.                                 }
  23.                                 return $post;
  24.                         }      
  25.                 } else {
  26.                         $query = $this->db->get('posts');
  27.                         if ($query->num_rows() !== 0 ){
  28.                                 foreach($query->result() as $row){
  29.                                 $post['id'][] = $row->id;
  30.                                 $post['title'][] = $row->title;
  31.                                 $post['summary'][] = $row->summary;
  32.                                 $post['content'][] = $row->content;
  33.                                 }
  34.                                 return $post;
  35.                         }
  36.                        
  37.                 }              
  38.         }
  39.         function insert_post($data){
  40.                 $this->db->insert('posts', $data);
  41.                 return;
  42.         }
  43.         function update_post($postId, $data){
  44.                 $id = $postId;         
  45.                 $this->db->where('id',$id);
  46.                 $this->db->update('posts', $data);
  47.                 return;
  48.         }
  49.         function delete_post($postId){
  50.                 $id = $postId;         
  51.                 $this->db->delete('posts',array('id' => $id));
  52.                 return;
  53.         }
  54. }

Views

Once again we’re going to need to create a few simple view files for our admin index page, a new html head wrapper file that will have a ‘Logout’ link instead of an ‘Admin’ link, and forms for creating/editing posts.

Create admin_html_head.php at this location :

/application/views/template/admin_html_head.php

Copy the code below :
  1. <!DOCTYPE html>
  2.         <head>
  3.                 <link rel="stylesheet" type="text/css" href="<?php echobase_url('style.css');?>">
  4.         </head>
  5.         <body>
  6.                 <h1><?php echo anchor('','My Blog'); ?></h1>
  7.                 <div id="loginDiv"><?php echo anchor('admin/logout','Logout');?></div>
  8.                 <hr/>

Create index.php at this location :

/application/views/admin/index.php

Copy the code below :
  1. <?php
  2.         echo '<p>Welcome To The Admin Page '.$username.'! All posts available for edit or deletion is listed below.</p><br/>';
  3.         echo anchor('admin/create','Create New Post');
  4.         $count = count($post['id']);
  5.         for ($i=0;$i<$count;$i++)
  6.         {
  7.                 echo '<div class="postDiv">';
  8.                 echo '<h4>'.$post['title'][$i];
  9.                 echo anchor('blog/view/'.$post['id'][$i],' [view]');
  10.                 echo anchor('admin/edit/'.$post['id'][$i],' [edit]');
  11.                 echo anchor('admin/delete/'.$post['id'][$i],' [delete]</h4>');
  12.                 echo '<p>'.$post['summary'][$i].'</p>';
  13.                 echo '</div>';
  14.         }
  15. ?>

reate create.php at this location :

/application/views/admin/create.php

Copy the code below :
  1. <?php
  2.         echo validation_errors();
  3. ?>
  4.         <h4>Create A New Post Below</h4>
  5.         <form action="" method="post" >
  6.         <p>Title:</p>
  7.         <input type="text" name="title" size="50"/><br/>       
  8.         <p>Summary:</p>
  9.         <textarea name="summary" rows="2" cols="50"></textarea><br/>
  10.         <p>Post Content:</p>
  11.         <textarea name="content" rows="6" cols="50"></textarea><br/>
  12.         <input type="submit" value="Save" />
  13. <?php
  14.         echo anchor('admin','Cancel');
  15. ?>
  16.         </form>

Create edit.php at this location :

/application/views/admin/edit.php

Copy the code below :
  1. <?php
  2.         echo validation_errors();
  3. ?>
  4.         <h4>Edit "<?php echo $post['title']; ?>" Below</h4>
  5.         <form action="" method="post" >
  6.                 <p>Title:</p>
  7.                 <input type="text" name="title" size="50" value="<?php echo$post['title']; ?>"/><br/> 
  8.                 <p>Summary:</p>
  9.                 <textarea name="summary" rows="2" cols="50"><?php echo$post['summary']; ?>
  10.                 </textarea><br/>
  11.                 <p>Post Content:</p>
  12.                 <textarea name="content" rows="6" cols="50"><?php echo$post['content']; ?>
  13.                 </textarea><br/>
  14.                 <input type="submit" value="Save" />
  15. <?php
  16.         echo anchor('admin','Cancel');
  17. ?>
  18.         </form>

Controller

Our final controller will tie all of our admin page logic together, calling the method functions and loading the view files we just created as requested.

Create admin.php at this location :

/application/controllers/admin.php

Copy the code below :
  1. <?php if (!defined('BASEPATH')) exit('No direct script access allowed');
  2. class Admin extends CI_Controller
  3. {
  4.         function __construct()
  5.         {
  6.                 parent::__construct();
  7.                 $this->load->helper(array('form','url'));
  8.                 $this->load->library(array('tank_auth','form_validation'));
  9.                 $this->load->model('posts');
  10.         }
  11.         function index() {
  12.                 if (!$this->tank_auth->is_logged_in()) {
  13.                         redirect('/auth/login');
  14.                 } else {
  15.                         $data['post'] = $this->posts->get_posts(null);
  16.                         $data['userId'] = $this->tank_auth->get_user_id();
  17.                         $data['username'] = $this->tank_auth->get_username();
  18.        
  19.                         $this->load->view('template/admin_html_head', $data);
  20.                         $this->load->view('admin/index', $data);
  21.                         $this->load->view('template/html_tail', $data);
  22.                 }
  23.         }
  24.         function create(){
  25.                 $data['userId'] = $this->tank_auth->get_user_id();
  26.                 $data['username'] = $this->tank_auth->get_username();
  27.                 $this->form_validation->set_rules('title','title','required');
  28.                 $this->form_validation->set_rules('summary','summary','required');
  29.                 $this->form_validation->set_rules('content','content','required');
  30.                 if($this->form_validation->run()==FALSE)
  31.                 {
  32.                         $this->load->view('template/admin_html_head',$data);
  33.                         $this->load->view('admin/create',$data);
  34.                         $this->load->view('template/html_tail',$data);
  35.                 } else {
  36.                         $data = $_POST;
  37.                         $this->posts->insert_post($data);
  38.                         redirect('admin');
  39.                 }
  40.         }
  41.         function edit($postId){
  42.                 $data['userId'] = $this->tank_auth->get_user_id();
  43.                 $data['username'] = $this->tank_auth->get_username();
  44.                 $data['post'] = $this->posts->get_posts($postId);
  45.                 $this->form_validation->set_rules('title','title','required');
  46.                 $this->form_validation->set_rules('summary','summary','required');
  47.                 $this->form_validation->set_rules('content','content','required');
  48.                 if($this->form_validation->run()==FALSE)
  49.                 {
  50.                         $this->load->view('template/admin_html_head',$data);
  51.                         $this->load->view('admin/edit',$data);
  52.                         $this->load->view('template/html_tail',$data);
  53.                 } else {
  54.                         $data = $_POST;
  55.                         $this->posts->update_post($postId, $data);
  56.                         redirect('admin');
  57.                 }
  58.         }
  59.         function delete($postId){
  60.                 $this->posts->delete_post($postId);
  61.                 redirect('admin');
  62.         }
  63.         function logout(){
  64.                 redirect ('/auth/logout');
  65.         }
  66. }

Change Tank Auth’s Behavior

Since we’re only using the Tank Auth authentication for our admin page, instead of our entire application, we need to tweak it’s functionality a bit.

Currently logging in to the admin section will take us directly back to the main blog index (although the user is successfully logged in), and then clicking the ‘Admin’ link one more time is required to gain access to the Admin index page. We’re going to fix this by redirecting back to our ‘Admin’ controller index immediately after a successful login.

Open auth.php located here:

/application/controllers/auth.php

Find the line that says (around line 71) :
  1. // success
  2. redirect('');

Replace with the code below and save :
  1. // success
  2. redirect('admin');
Also, logging out would currently bring us immediately back to the login form, instead of back to the blog index as we would want. We’re going to fix this by inserting a new line telling the controller to redirect back to admin index right after logging a user out.

Go back to auth.php, find method ‘logout’ (around line 106) :
  1. function logout()
  2.         {
  3.                 $this->tank_auth->logout();
  4.                 $this->_show_message($this->lang->line('auth_message_logged_out'));
  5.                
  6.         }

Replace with the code below and save :

  1. function logout()
  2.         {
  3.                 $this->tank_auth->logout();
  4.                 redirect('');
  5.                 //$this->_show_message($this->lang->line('auth_message_logged_out'));
  6.                
  7.         }

Before our blog is complete we’re going to want to change to tell the Auth controller to wrap it’s loaded views around our HTML wrappers just like our other controllers.

Find these lines one at a time:

$this->load->view(‘auth/login_form’, $data);
$this->load->view(‘auth/register_form’, $data);
$this->load->view(‘auth/forgot_password_form’, $data);
$this->load->view(‘auth/reset_password_form’, $data);
$this->load->view(‘auth/change_password_form’, $data);
$this->load->view(‘auth/unregister_form’, $data);

For EACH line you must insert the following lines :

Insert this BEFORE the line:
$this->load->view(‘template/html_head’, $data);

Insert this AFTER the line:
$this->load->view(‘template/html_tail’, $data);

Using the same HTML wrappers over and over, we can quickly create many lovely views for the Tank Auth forms easily.
Registration form with the HTML wrappers around it.
Login form with the HTML wrappers around it.

Conclusion

You should now have a simple blog that displays posts as well as having a separate admin section to serve as a very simple “content management system” CMS.

Index for our Admin controller.


blog-admin-create
blog-admin-edit
 hope this helped you!

I hope all goes well! Look forward to seeing more post from Anjaneya Vadivel  in the future.

Please comment below!

Simple CRUD in Laravel Framework

Creating, reading, updating, and deleting resources is used in pretty much every application. Laravel helps make the process easy using reso...