Sunday, April 21, 2013

Sorting with Set::sort() in CakePHP 1.2

Sorting data returned from a database query

You're probably thinking "Err... idiot... just use the ORDER BY functionality in the model!". Yeah, we could do that, but that's not much fun!
Here is my freshly baked People controller, which gracefully passes some data to my view. Nothing special here.

<?php
class PeopleController extends AppController {

    var $name = 'People';
    
    function index() {
        $this->set('people', $this->Person->find('all'));
    }
    
}
?>
 
The $people array is in the standard format we receive from our model, and has a belongsTo association (Occupation).
Array
(
    [0] => Array
        (
            [Person] => Array
                (
                    [id] => 1
                    [occupation_id] => 2
                    [name] => Harry Potter
                    [birth_date] => 1980-07-31
                )

            [Occupation] => Array
                (
                    [id] => 2
                    [name] => Student
                )

        )

    [1] => Array
        (
            [Person] => Array
                (
                    [id] => 2
                    [occupation_id] => 1
                    [name] => Albus Dumbledore
                    [birth_date] => 1881-08-01
                )

            [Occupation] => Array
                (
                    [id] => 1
                    [name] => Headmaster
                )

        )

    [2] => Array
        (
            [Person] => Array
                (
                    [id] => 3
                    [occupation_id] => 3
                    [name] => Severus Snape
                    [birth_date] => 1959-01-09
                )

            [Occupation] => Array
                (
                    [id] => 3
                    [name] => Professor
                )

        )

)

Default order

Instead of dumping the entire contents of the $people array after each Set::sort() call, I will present the data in a pretty table.
Person.id Person.name Person.birth_date Occupation.id Occupation.name
1 Harry Potter 1980-07-31 2 Student
2 Albus Dumbledore 1881-08-01 1 Headmaster
3 Severus Snape 1959-01-09 3 Professor

Person.birth_date DESC

Set::sort() currently takes 3 arguments - the array to sort, the array value to sort on, and the sort order. As with the other Set methods, we specify the value to sort on using a key path. This makes it super easy to deal with complex arrays.

$people = Set::sort($people, '{n}.Person.birth_date', 'desc');
 

Person.id Person.name Person.birth_date Occupation.id Occupation.name
1 Harry Potter 1980-07-31 2 Student
3 Severus Snape 1959-01-09 3 Professor
2 Albus Dumbledore 1881-08-01 1 Headmaster

Occupation.name ASC

It's just as simple sorting on a value of an associated model, in this case the person's occupation. Unfortunately the sort method doesn't current have a default sort order, so we need to specify this. We can also use the PHP constants, SORT_ASC and SORT_DESC.

$people = Set::sort($people, '{n}.Occupation.name', SORT_ASC);
 

Person.id Person.name Person.birth_date Occupation.id Occupation.name
2 Albus Dumbledore 1881-08-01 1 Headmaster
3 Severus Snape 1959-01-09 3 Professor
1 Harry Potter 1980-07-31 2 Student

Saturday, April 20, 2013

YII model with some action perform

Add custom error to a model:
$model->addError('email', 'No valid account associated with this email address.');

Validate a model:
$model->validate();

Yii model without table

<?php
class RegistrationForm extends CFormModel
{
    public $email;
    public $password;
    public $repassword;
    public $displayname;
    public $verifycode;


    public function rules()
    {
        return array(
            array('email, password, repassword', 'required'),
            array('password, repassword', 'length', 'min'=>4),
            array('email, password, repassword, displayname', 'length', 'max'=>200),
            array('email', 'unique'),
            array('repassword', 'compare', 'compareAttribute'=>'password'),
            array('email', 'email'),
            array('verifycode', 'captcha', 'allowEmpty'=>!CCaptcha::checkRequirements()),
        );
    }
    
    
    public function attributeLabels()
    {
        return array(
            'email'=>'Email',
            'password'=>'Password',
            'repassword'=>'Re-Password',
            'displayname'=>'Display Name',
            'verifycode'=>'Confirm you are not alien',
        );
    }
           
}

Yii create a new model with common attributes

The rules() method, like most of the Yii methods, returns an array of data:
public function rules()
{
    return array(/* actual rules */);
}
The first, most obvious, restriction is to indicate that a field is required. Just use a comma-separated string of field names as the first returned value and the word required as the second:
array('name, email, subject, body', 'required'),
You can also specify that a value must be a number or, more specifically, an integer. This syntax is slightly different. Here, I indicate that the ext field must be an integer:
array('ext', 'numerical', 'integerOnly'=>true),
For strings, you can restrict the length to a maximum value:
array('name','length','max'=>40),
Or a minimum value:
array('name','length','min'=>6),
Or both:
array('name','length','min'=>6, 'max'=>40),
Another useful validation routine is to check that a string is an email address. I do that here, to a userEmail field:
array('userEmail', 'email'),
To indicate that a string needs to be a URL, use:
array('link', 'url'),
Another useful rule is for comparisons, like when a user registers and you have to make sure that the confirmed password matches the password:
array('password1', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
There is also the “safe” rule. This rule is used to provide access to data that isn’t otherwise validated. For example, an email address is already considered to be “safe” because it must abide by the email rule, but the Employee Model has the leaveDate field which won’t have any rules applied to it (in part, because there are no date-specific rules and also because the field can be null). To be able to refer to that value, it must be declared as safe:
array('leaveDate', 'safe'),
If there are multiple fields to make safe, just separate them with commas.
There’s also a rule to indicate which fields should be safe to search on. By default, every rule is made safe in a search, but you may want to change that behavior by removing fields from the list:
array('id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate', 'safe', 'on'=>'search'),
So, using this information, the complete rules() method for my Employee Model is:
public function rules()
{
    return array(
        array('departmentId, firstName, lastName, email, hireDate', 'required'),
        array('departmentId, ext', 'numerical', 'integerOnly'=>true),
        array('firstName', 'length', 'max'=>20),
        array('lastName', 'length', 'max'=>40),
        array('email', 'length', 'max'=>60),
        array('email', 'email'),
        array('leaveDate', 'safe'),
        array('id, departmentId, firstName, lastName, email, ext, hireDate, leaveDate', 'safe', 'on'=>'search'),
 );
}
Moving on, another key Model method is relations(), which indicates the relationship between Models. If your database is designed properly, this method will already be properly filled out, again thanks to Gii. Here’s how the relations() method in the Employee Model looks:
public function relations()
{
    return array( 
        'department' => array(self::BELONGS_TO, 'Department', 'departmentId'
    );
}
The relation is given a name, here department. The relation indicates that the departmentId column in the Employee Model (i.e., this one) belongs to the Department Model. Here’s how this will come into play: When loading the information for an employee, you can also load any of its relations. By doing so, references to department will equate to the Department Model record that is the employee’s department. So if the $model object represents the Employee being viewed, then $model->department->name would be the name of the department that the employee is associated with.
In the Department Model, this relation is defined:
public function relations()
{
    return array('employees' => array(self::HAS_MANY, 'Employee', 'departmentId') );
}
So if $model represents a specific Department being viewed, then $model->employees is an array of Employee objects, each of which representing one Employee in that department.
The relations between Models is a key player in complex MVC sites. Through properly defined relations, associated content can be retrieved. You’ll learn more about this in the next couple of posts.
Moving on, a more trivial, but still nice, method is attributeLabels(). This method returns an associative array of fields and the labels to use for those fields in forms, error messages, and so forth. The Yii framework does a great job of making these conversations automatically, like firstName becoming First Name and departmentId becoming just Department. But you may want to still customize these. For the Employee Model, here’s what I have:
public function attributeLabels()
{
    return array(
        'id' => 'Employee ID',
        'departmentId' => 'Department',
        'firstName' => 'First Name',
        'lastName' => 'Last Name',
        'email' => 'Email',
        'ext' => 'Ext',
        'hireDate' => 'Hire Date',
        'leaveDate' => 'Leave Date',
    );
}

Complete Example


<?php
class PasswordReset extends CActiveRecord {
    public static function model($className=__CLASS__)
    {
        return parent::model($className);
    }
    public function tableName()
    {
        return 'password_reset';
    }
    public function rules()
    {
        return array(
            array('user_user_id, request_time, request_hash, request_done', 'required'),
            array('user_user_id, request_time, request_done', 'numerical'),
            array('request_done','length','min'=>6, 'max'=>1),
            array('request_time','length','min'=>6, 'max'=>10),
            array('request_hash','length','max'=>100),
            array('user_user_id, request_time, request_hash, request_done', 'safe', 'on'=>'search')
        );
    }
    public function relations()
    {
        return array(
            'User' => array(self::BELONGS_TO, 'User', 'user_user_id')
        );
    }
    public function attributeLabels()
    {
        return array(
            'request_time' => 'Request Time',
            'request_hash' => 'Request Hash',
            'request_done' => 'Request Done'	
        );
    }
    public function search()
    {
        $criteria=new CDbCriteria;

        $criteria->compare('request_time', $this->request_time, true);
        $criteria->compare('request_hash', $this->request_hash, true);
        $criteria->compare('request_done', $this->request_done, true);
        $criteria->compare('User.id', $this->User->id);
        $criteria->compare('request_done', '0', true);

        return new CActiveDataProvider($this, array(
            'criteria'=>$criteria
        ));
    }
    public function findByParameter($fieldname, $fieldvalue)
    {
        $criteria=new CDbCriteria;
        $criteria->compare($fieldname, $fieldvalue, true);		
        return new CActiveDataProvider($this, array(
            'criteria' => $criteria
        ));
    }
    public function findByParameters($parameters)
    {
        $criteria=new CDbCriteria;
        foreach ($parameters as $key => $value) {
            $criteria->compare($key, $value, true);
        }                
        return new CActiveDataProvider($this, array(
            'criteria' => $criteria,
        ));
    }
    public function getRequestList() {
        $parameters = array('request_done' => '0');
        $requests = PasswordReset::model()->findByParameters($parameters);
        return CHtml::listData($requests->getData(), 'password_reset_id', 'request_hash');
    }
}
?>

Login to your site using google account php

Step 1:
Create a Google APIs Console Account

Step 2:
Download php library

Step 3:
Write the following code to a php file:

<?php
session_start();
require_once './src/Google_Client.php';
require_once './src/contrib/Google_Oauth2Service.php';
$baseURL = "http://localhost/lightopenid/";
$client = new Google_Client();
$client->setApplicationName("Google User Info PHP Starter Application");
// Visit https://code.google.com/apis/console?api=plus to generate your
// oauth2_client_id, oauth2_client_secret, and to register your oauth2_redirect_uri.
$client->setClientId('rtrt.apps.googleusercontent.com');
$client->setClientSecret('RInoMtVa_rtrt-EQ2');
$client->setRedirectUri('http://localhost/lightopenid/');

$oauth2 = new Google_Oauth2Service($client);

if (isset($_GET['code'])) {
    $client->authenticate($_GET['code']);
    $_SESSION['token'] = $client->getAccessToken();
    header('Location: ' . filter_var($baseURL, FILTER_SANITIZE_URL));
    return;
}
if (isset($_SESSION['token'])) {
    $client->setAccessToken($_SESSION['token']);
}

if (isset($_REQUEST['logout'])) {
    unset($_SESSION['token']);
    $client->revokeToken();
}

if ($client->getAccessToken()) {
    $user = $oauth2->userinfo->get();

    // These fields are currently filtered through the PHP sanitize filters.
    // See http://www.php.net/manual/en/filter.filters.sanitize.php
    $email = filter_var($user['email'], FILTER_SANITIZE_EMAIL);
    $img = filter_var($user['picture'], FILTER_VALIDATE_URL);
    $personMarkup = "$email<div><img src='$img?sz=50'></div>";

    // The access token may have been updated lazily.
    $_SESSION['token'] = $client->getAccessToken();
} else {
    $authUrl = $client->createAuthUrl();
}
?>
<!doctype html>
<html>
<head><meta charset="utf-8"></head>
<body>
<header><h1>Google UserInfo Sample App</h1></header>
<?php if(isset($personMarkup)): ?>
<?php print $personMarkup ?>
<?php endif ?>
<?php
  if(isset($authUrl)) {
    print "<a class='login' href='$authUrl'>Connect Me!</a>";
  } else {
   print "<a class='logout' href='?logout'>Logout</a>";
  }
?>
</body></html>

Login into your site using facebook account

Step 1:

The primary step is to visit http://developers.facebook.com/apps.
If you are visiting this URL for the first time, you will have a window similar to the one shown below in which Facebook developer app will request your permission to access your basic information.

Click the ‘Allow’ button.

Step 2:

Now you be on a page that shows you your recently viewed app. Don’t worry if it doesn’t show any app. It just means that you haven’t created any app yet.
Now, click ‘Create New App’ button.


In the window that pops up, enter the name of your application. Mark the check box ‘I agree to Facebook Terms’ and click ‘Continue’ button.
Now you may be asked to solve a captcha which verifies that you are human.

Step 3:

You will be taken to the application basic settings page.
On the top portion of the page you will have your ‘App ID’ and ‘App Secret’.
Enter your domain name as ‘App Domain’. Please note that a ‘your-domain.com’ will include ‘*.your-domain.com’ also. That is, you can use the app on any of your sub-domains. You can even enter ‘localhost’ if you are testing your application on your local machine. If localhost not working you need to setup your virtual host in case you use apache as server.
In the ‘Website with Facebook Login’ section, you need to enter ‘Site URL’. ‘Site URL’ will be entry page for your application. As we are making a webpage that has f-connect enabled, we need to give the address of the webpage that we are about to make.Because of security reasons, Facebook will redirect users to this URL only. We will be creating webpage in the later sections. If you are not sure about what your URL will be, just leave the field blank as you can fill it any time later.











Step 4:
Now write your php application. Suppose file index.php.
Define your scopes to want from user:
https://developers.facebook.com/docs/reference/login/#permissions
email, read_stream, user_birthday, user_location, user_work_history, 
user_hometown, user_photos



<?php
session_start();
$app_id = "250395365104464";
$app_secret = "3b616d9db3dc586b5ed784ef89e5f1a5";
$my_url = "http://thecontactspace.com/site/fb";
$scope = "email";
if(isset($_GET["logout"])) {
    unset($_SESSION["__access_token"]);
}

if(isset($_SESSION["__access_token"])) {
    echo "<a href='?logout'>Logout</a>";
    $graph_url = "https://graph.facebook.com/me?" .  
        $_SESSION["__access_token"];
    $user = json_decode(file_get_contents($graph_url));
    print_r($user); 
    die();
} else {
    if(!isset( $_REQUEST["code"] ) ) {
        $dialog_url = "http://www.facebook.com/dialog/oauth?".
            "scope=$scope&client_id=" . 
            $app_id . "&redirect_uri=" .  
            "".rawurlencode($my_url);
        echo("<script> top.location.href='" . $dialog_url . "'</script>");
        return;
    } else {
        $code = $_REQUEST["code"];
        $token_url = "https://graph.facebook.com/oauth/access_token?client_id="
        . $app_id . "&redirect_uri=" . urlencode($my_url) . "&client_secret="
        . $app_secret . "&code=" . $code;

        $access_token = file_get_contents($token_url);
        $_SESSION["__access_token"] = $access_token;
        header('Location: ' . $my_url);
    }
}
?>

Friday, April 19, 2013

Check password strength / safety with PHP and Regex

<?php
function checkPasswordLength($password, &$message) {
    if(strlen($password) < 8) {
        $message = "Password too short!";
        return false;
    }
    if(strlen($password) > 20) {
        $message = "Password too long!";
        return false;
    }
    if( !preg_match("#[0-9]+#", $password) ) {
        $message = "Password must include at least one number!";
        return false;
    }
    if( !preg_match("#[a-z]+#", $password) ) {
        $message = "Password must include at least one letter!";
        return false;
    }
    if( !preg_match("#[A-Z]+#", $password) ) {
        $message = "Password must include at least one CAPS!";
        return false;
    }
    if( !preg_match("#\W+#", $password) ) {
        $message = "Password must include at least one symbol!";
        return false;
    }
    return true;
}
?>