Filter Eloquent Results by Overriding in Laravel (v 3 & 4)

Now Laravel has this feature built-in

What is Eloquent?

Eloquent has been a really popular ORM in the PHP community lately, it was initially built for Laravel framework only. But since Laravel 4 all the components including Eloquent are built as Packages which may depend on other packages. These packages can be installed through Composer and can be browsed on Packagist. So what’s cool? Think of people out there who either don’t use Laravel or don’t use any framework at all. They might roll their own code or just whack a bunch of packages (like Eloquent) from Packagist together. If you use Laravel you can install and use various Symfony components/packages or any other package like Hybrid Auth with a simple command.

Lets Code

When we run queries from Eloquent sometimes we need to filter results from the very beginning. I will do it with an example to make understanding easy, but you are free to apply it however you want.

So sometimes we have users in database and admin can ban few of them for whatever reason, then lets assume you set users table’s status=0 for these users, now you have to filter (status=1) everywhere to restrict banned users from appearing (like login, profile view, edit send message etc.), and this can be real pain and not-a-cool-way as well. What if we can filter banned users right from our Eloquent model each time, so however we call users, User::all() or User::find(2) or User::where('username', 'like', '%usman%')->get() or whatever, banned users won’t appear in the results as long as you use this Eloquent model? Pretty cool yeah! Lets do it:

You have a User model which extends Eloquent, put this code into that class:

    /**
     * Overrides (Laravel or)Illuminate\Database\Eloquent\Model 's query()
     *
     * @return mixed
     */
    public function newQuery($excludeDeleted = true)
    {
        $query = parent::newQuery($excludeDeleted);
        // A much better way to restrict access to all banned (status = 0) users
        // from beginning (including search, profile views etc.), disabling them
        // to access anything even if they were logged in previously.

        $query->where('status', '!=', 0);
        return $query;
    }

That’s pretty much it, now you will never see banned users 🙂

What if We Need to Get Banned Users?

Well, above code will basically serve the purpose, but what if we need to get banned users (too) sometimes, like in the admin panel or if we want to check why the login was denied, is it because his status=0? Well you can do it. After this feature, your overall class will look as follows:

class User extends Eloquent
{
    protected static $_allowBannedUsers = false;

    /**
     * Overrides (Laravel or)Illuminate\Database\Eloquent\Model 's query()
     *
     * @return mixed
     */
    public function newQuery()
    {
        $query = parent::newQuery();
        // A much better way to restrict access to all banned (status = 0) users
        // from beginning (including search, profile views etc.), disabling them
        // to access anything even if they were logged in previously.

        if(! static::$_allowBannedUsers)
        {
            $query->where('status', '!=', 0);
        }else{
            // If its true then make it false so that banned users don't appear in next calls.
            static::$_allowBannedUsers = false;
        }
        return $query;
    }

    /**
     * @return User
     */
    public static function allowBannedUsers()
    {
        static::$_allowBannedUsers = true;
        return new static;
    }
}

The above code seems self explanatory. Now you can get banned users if needed just by chaining allowBannedUsers() method, for example:
User::allowBannedUsers()->all() or User::allowBannedUsers()->find(2) or User::allowBannedUsers()->where('username', 'like', '%usman%')->get()

Laravel 3?

If you are using Laravel 3 then please change the name of the method newQuery() to query() (without any parameters) like:

public function query()

and

$query = parent::query();

If you like this tirck then you may like my new project Pixie Database Query Builder. It has Query Events built-in for doing such tricks.

Conclusion

Feel free to do whatever you need instead of just restricting banned users. Also I would like to hear any better solution or if you think this solution has any pitfall. How do you solve this problem? Please comment below.

I will be glad if this helps you, please share this tutorial if you think it would help others.

Stay tuned for more Laravel tutorials.

XSS Filter in Laravel Framework

If you are connected with the PHP development world then you must have heard the name Laravel and you know what it is. If you don’t know, Laravel is a modern PHP framework which utilizes modern PHP features and uses some of the existing frameworks’ components to truly awesomify PHP development.

Back in 2012 I gave Laravel a try out of curiosity and I was amazed by the flexibility, features and easiness it offers. I took another decison to learn it and then to use it on my new projects. Now I have learned Laravel and developed projects using it. I am not leaving development with CodeIgniter, just using both, in fact my current long term work Vegan Cuts is based on CodeIgniter.

One thing you might miss on Laravel is XSS (Cross-site Scripting) Cleaning/Filtering method for inputs, specially if you have come from CodeIgniter background. I truly understand that its better to do it on output and Laravel has a method HTML::entities with a handy shorthand e() (removed in Laravel 4) which converts HTML characters into entities, its almost PHP’s native function htmlentities() (also Blades’s syntax can escape data). But I do prefer filtering/sanitizing both inputs and outputs, I don’t see any reason to allow saving HTML tags in database, also you may accidentally forget to use htmlentities() on your output and it puts you on risk. So I prefer to strip all the tags from input globally and then for a better security use htmlentities() on output.

Here we go …

So how can we do XSS Clean on all the inputs in Laravel? I have a solution for you, if you don’t have a library to write common methods you may need frequently then I ask you to create a new library Common in application/library (in case of Laravel 4, create a Common model in app/models). Put this two methods in your Common library/model:

    /*
     * Method to strip tags globally.
     */
    public static function globalXssClean()
    {
        // Recursive cleaning for array [] inputs, not just strings.
        $sanitized = static::arrayStripTags(Input::get());
        Input::merge($sanitized);
    }

    public static function arrayStripTags($array)
    {
        $result = array();

        foreach ($array as $key => $value) {
            // Don't allow tags on key either, maybe useful for dynamic forms.
            $key = strip_tags($key);

            // If the value is an array, we will just recurse back into the
            // function to keep stripping the tags out of the array,
            // otherwise we will set the stripped value.
            if (is_array($value)) {
                $result[$key] = static::arrayStripTags($value);
            } else {
                // I am using strip_tags(), you may use htmlentities(),
                // also I am doing trim() here, you may remove it, if you wish.
                $result[$key] = trim(strip_tags($value));
            }
        }

        return $result;
    }

Then put this code in the beginning of your before filter (in application/routes.php, in Laravel 4 it should be in app/filters.php):

    // Our own method to defend XSS attacks globally.
    Common::globalXssClean();

Note: I am using strip_tags(), you may use htmlentities(), also I am doing global trimming here, you may remove it, if you wish. Stripping tags will disable your users to store any HTML tags, if you need your user to write WYSIWYG content then I suggest you to use Markdown and convert it to HTML while doing output, it is the secured way, there are many libraries to help you.

More Laravel tutorials are coming, stay tuned 🙂

WordPress like Option feature for your CodeIgniter application

If you have developed a large application you may have faced this problem, in some cases we have some settings or options to save in the database but the data is not so large to create a db table for it, also it may do not require many rows. In that case we should have a table for settings, where we can store all our settings or options with a key then retrieve it. Our option can be a text value or an array or an object. WordPress has a really good feature for such cases its the Option Mechanism. By using the appropriate function, options can be addedchangedremoved, and retrieved.

I loved this feature too much that I added such feature in my CodeIgniter applications, because I don’t always use WordPress. Now I want to share my code with you which may help you in various tasks. Its not the exact same as WordPress. I have used 4 functions for it add_option, update_option, get_option and delete_option. The good part is you can store a PHP array, object or a string value (including number) with add_option(‘name_of_option’,’value’) or with update_option(‘name_of_option’,’value’) and whenever you retrieve your data with get_option(‘name_of_option’) it returns the data with the same format.
(Sorry, if you have used it in WordPress you may already know it and some info below, but its for those who haven’t used).

You may download the code from GitHub, also any contribution is highly appreciated.

Functions:

add_option(‘name_of_option’,’value’) will store data in the db with ‘name_of_option’ if the ‘name_of_option’ is already used it will return false, use add_option if you want to store a data for the first time such as installing an app.

update_option(‘name_of_option’,’value’) will update the ‘name_of_option’ if it is already used. If ‘name_of_option’ is not found it will add ‘name_of_option’. So use update_option in general case.

get_option(‘name_of_option’) will return the value of ‘name_of_option’ in original data type. Will return false if option is not found.

delete_option(‘name_of_option’) will delete the option and return true/false on success/failure.

Usage Example:

Below is the usage for CodeIgniter controller, I have made it as a CodeIgniter Helper. You will find instructions below on how to implement this Helper. I assume you have database set up in application/config/database.php

public function index()
{
	//load our helper,
	//better to autoload it by editing application/config/autoload.php
	$this->load->helper('option_helper');

	//text value example
	update_option('username','Usman');
	echo get_option('username');
	//array example
	$user_info=array(
		'username'		=>	'Usman',
		'profession'	=>	'Developer',
		'location'		=>	'Sylhet, Bangladesh',
	);
	update_option('user_info',$user_info);
	$return_value=get_option('user_info');
	print_r($return_value);
	echo $return_value['location'];

	//delete example

	delete_option('username');
	//delete_option('user_info');
}

Implementation:

1. Create a table in your database like below (of course its MySQL) :

CREATE TABLE IF NOT EXISTS `tbl_option` (

`option_id` bigint(20) NOT NULL AUTO_INCREMENT,

`option_name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,

`option_value` longtext COLLATE utf8_unicode_ci NOT NULL,

`option_type` varchar(20) COLLATE utf8_unicode_ci NOT NULL,

PRIMARY KEY (`option_id`),

UNIQUE KEY `option_name` (`option_name`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=59 ;

2. As I have made a CodeIgniter Helper for this, keep the code in application/helpers folder (in most cases) and name it option_helper.php. I haven’t used any model for simplicity.

3. Copy the code below and paste it to your option_helper.php, that’s it.

<?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
function add_option($name,$value) 
{ 	         
      $CI =& get_instance(); 
      $CI->load->database();
	$query=$CI->db->select('*')->from('tbl_option')->where('option_name',$name)->get();

	//option already exists
	if($query->num_rows() > 0)
		return false;

	$data_type='text';
	if(is_array($value))
	{
		$data_type='array';
		$value=serialize($value);
	}
	elseif(is_object($value))
	{
		$data_type='object';
		$value=serialize($value);
	}

	$data=array(
		'option_name'=>$name,
		'option_value'=>$value,
		'option_type'=>$data_type,
	);
	$CI->db->insert('tbl_option',$data);
}

function update_option($name,$value)
{
	$CI =& get_instance();
	$CI->load->database();

	$data_type='text';
	if(is_array($value))
	{
		$data_type='array';
		$value=serialize($value);
	}
	elseif(is_object($value))
	{
		$data_type='object';
		$value=serialize($value);
	}

	$data=array(
		'option_name'=>$name,
		'option_value'=>$value,
		'option_type'=>$data_type,
	);
	$query=$CI->db->select('*')->from('tbl_option')->where('option_name',$name)->get();

	//if option already exists then update else insert new
	if($query->num_rows() < 1) return $CI->db->insert('tbl_option',$data);
	else		  return $CI->db->update('tbl_option',$data,array('option_name'=>$name));
}

function get_option($name)
{
	$CI =& get_instance();
	$CI->load->database();
	$query=$CI->db->select('*')->from('tbl_option')->where('option_name',$name)->get();
	//option not found
	if($query->num_rows() < 1) return false; 	 	$option=$query->row();

	if('text'==$option->option_type)
		$value=$option->option_value;
	elseif('array'==$option->option_type || 'object'==$option->option_type)
		$value=unserialize($option->option_value);

	return $value;
}

function delete_option($name)
{
	$CI =& get_instance();
	$CI->load->database();
	return $CI->db->delete('tbl_option',array('option_name'=>$name));
}

If you have found it useful, please comment below. Any bug report or suggestions are most welcome.

Creating a simple Facebook Application

Facebook Applications are getting much popular these days, its good, but the bad is Facebook frequently changes its API, which causes the resource and tutorials on this subject to be obsolete. That’s why I decided to write an up to date post on how to create an IFrame based Facebook Application using PHP. I am using PHP SDK (3.1) and Graph API, you have to download PHP SDK from here. Well, I am covering the coding part here and not going through how to create and setup a Facebook Application, if you don’t know how then this article might be a good read.

I will create an app which will show all friends’ name. So we will learn, how to …
-Include PHP SDK
-Authenticate the user
-Set scope or permission
-Get friend list
-Print friends’ names

I am using an index.php file and a folder named fb_sdk which will contain facebook.php and base_facebook.php obtained from Facebook PHP SDK (path_to_php_sdk/src/).  Now we will start writing code in index.php

First we will include the PHP SDK file like below:

require 'fb_sdk/facebook.php';

Now we will instantiate the PHP SDK, here we will provide the Application Id (appId) and Application Secret (secret) obtained from our App settings in Facebook.

$facebook = new Facebook(array(
  'appId'  => 'your_app_id',
  'secret' => 'your_app_secret',
));

Now we will get user info from Facebook to check if the user is authenticated or not

$user = $facebook->getUser();

// We may or may not have this data based on whether the user is logged in.
//
// If we have a $user id here, it means we know the user is logged into
// Facebook, but we don't know if the access token is valid. An access
// token is invalid if the user logged out of Facebook.

if ($user) {
  try {
    // Proceed knowing you have a logged in user who's authenticated.
    $user_profile = $facebook->api('/me');

  } catch (FacebookApiException $e) {
    error_log($e);
    $user = null;
  }
}

Now if the user is not authenticated or user has not allowed our app to access certain info then we will redirect user to a URL called loginUrl to allow our app to access his info. As we need user’s friend list only we are asking for read_friendlists permission only. You can ask for additional permissions if you need, a list permissions can be found here.

// Login or logout url will be needed depending on current user state.
if ($user) {
  $logoutUrl = $facebook->getLogoutUrl();

} else {
  $loginUrl = $facebook->getLoginUrl(array(
            'scope' => 'read_friendlists', //you can add more permissions here separated by commas
			'redirect_uri'=>'https://apps.facebook.com/usman_test_app/' //give your app's canvas URL here, its just example
            )
       );
    //redirect user to loginUrl
    echo "<script type='text/javascript'>top.location.href = '$loginUrl';</script>";
    exit;
}

Now we have permission to access user’s friend list. So we have to get the friend list using the api() function. The returned value will be an array, then we will loop through the array and print the friend’s name.

?>
<!doctype html>
<html xmlns:fb="http://www.facebook.com/2008/fbml">
  <head>
    <title>Facebook App Example</title>
  </head>
  <body>
    <?php if ($user){
			//lets get the friend list
			$friends   = $facebook->api('/me/friends');
			//check if we have a valid list
			if(!is_array($friends))
				exit("Unable to get friend list.");

			//now we will print friend name from array
			foreach($friends['data'] as $friend)
			{
				echo $friend['name'].'<br/>';
			}
	?>

    <?php } ?>

  </body>
</html>

Well we are done now. You have to put index.php and the fb_sdk folder in the root of your app folder. You can access the app by visiting your Canvas URL If you like the post please share it and comment below. Also you can comment if you have any problem or relevant question.