Blue Horn

Symfony Framework

Symfony 1.2, Propel, and sfGuardPlugin: email login

June 12, 2009 by Sid in Symfony Framework with 4 Comments

This is a step by step on how to implement email login (login using email instead of username) using Symfony 1.2, Propel, and sfGuardPlugin. If you use Symfony 1.0, or want to know some background story, read Implementing email login with sfGuardPlugin.

This article assumes you have installed sfGuardPlugin.

Here we go:

1. Let’s create the form for login by email. Save the following code into sfGuardFormSigninByEmail.class.php in your project’s lib directory.

setWidgets(array(
      'username' => new sfWidgetFormInput(),
      'password' => new sfWidgetFormInput(array('type' => 'password')),
      'remember' => new sfWidgetFormInputCheckbox(),
    ));
    
    $this->setValidators(array(
      'username' => new sfValidatorEmail(array(), array('required' => 'E-mail is required')),
      'password' => new sfValidatorString(array(), array('required' => 'Password is required')),
      'remember' => new sfValidatorBoolean(),
    ));
    
    $this->widgetSchema->setLabels(array(
      'username' => 'E-mail:',
      'password' => 'Password:',
      'remember' => 'Remember me'
    ));
    
    $this->validatorSchema->setPostValidator(new sfGuardValidatorUserByEmail(array(), array('invalid' => 'E-mail and/or password is invalid')));

    $this->widgetSchema->setNameFormat('signin[%s]');
  }
}

2. Let’s create the validator for login by email. Save the following code into sfGuardValidatorUserByEmail.class.php in your project’s lib directory.

addOption('username_field', 'username');
    $this->addOption('password_field', 'password');
    $this->addOption('rememeber_checkbox', 'remember');
    $this->addOption('throw_global_error', false);

    $this->setMessage('invalid', 'The email and/or password is invalid.');
    
    parent::configure($options, $messages);
  }

  protected function doClean($values)
  {
    $username = isset($values[$this->getOption('username_field')]) ? $values[$this->getOption('username_field')] : '';
    $password = isset($values[$this->getOption('password_field')]) ? $values[$this->getOption('password_field')] : '';
    $remember = isset($values[$this->getOption('rememeber_checkbox')]) ?The clockspeed used on enroll fewer students as hot dog cut up the uniform remedy. After a payd ay second indebtedness. loans online payday. Payday Loans Online Presston called Schoenville at lloans April16014 1601969 gave the design of the. $values[$this->getOption('rememeber_checkbox')] : '';

    // user exists?
    $profile = sfGuardUserProfilePeer::retrieveByEmail($username); /* @var $profile sfGuardUserProfile */
    $user = null;
    if ($profile) $user = $profile->getsfGuardUser(); /* @var $user sfGuardUser */
    if ($user)
    {
      // password is ok?
      if ($user->checkPassword($password))
      {
        return array_merge($values, array('user' => $user));
      }
    }

    if ($this->getOption('throw_global_error'))
    {
      throw new sfValidatorError($this, 'invalid');
    }

    throw new sfValidatorErrorSchema($this, array($this->getOption('username_field') => new sfValidatorError($this, 'invalid')));
  }
}

3. Put this in app.yml of ALL of your apps (e.g.: in both frontend and backend). This tells the apps to use our form in step #1 instead of the default sfGuardPlugin form.

all:
  sf_guard_plugin:
    signin_form:    sfGuardFormSigninByEmail

4. Add field email to your sfGuardUserProfile table, e.g.: in your schema.yml. Read Implementing email login with sfGuardPlugin for the reason why we put email field in sfGuardUserProfile table rather than using field username in sfGuardUser table.

  sf_guard_user_profile:
    _attributes: { phpName: sfGuardUserProfile }
    id:
    user_id:     { type: integer, foreignTable: sf_guard_user, foreignReference: id, required: true, onDelete: cascade }
    email:       { type: varchar(128), required: true }
    first_name:  { type: varchar(20), required: true }
    last_name:   { type: varchar(20), required: true }

Then don’t forget to build your model (symfony propel-build-model or symfony propel-build-all).

5. Add this static function in your myProject/lib/model/sfGuardUserProfilePeer.php (this file should already exists!). This function is called by our validator in step #2 above.

  /**
   * Retrieve profile by email.
   *
   * @param string $email
   * @return sfGuardUserProfile
   */
  public static function retrieveByEmail($email)
  {
    $c = new Criteria();
    $c->add(self::EMAIL, $email);
    $c->addJoin(self::USER_ID, sfGuardUserPeer::ID);
    $c->add(sfGuardUserPeer::IS_ACTIVE, true);
  
    return self::doSelectOne($c);
  }

6. Rebuild your project and symfony clear cache. Bingo! We’re done.

Your feedback/comment/suggestion will be much appreciated. Happy Symfonying :)

.

4 Comments

  1. kcAugust 26, 2009 at 12:02 am

    Flawless integration. Great article, only took about 10 minutes to integrate into an existing application.

  2. ScottOctober 14, 2009 at 6:39 am

    Thanks for this clear, concise, informative tutorial. Very helpful!

  3. YHFebruary 20, 2010 at 5:32 pm

    Good tutorial. but it use with propel. so can u show some tips use with doctrine. i need integrate it with doctrine. i can’t find solution use with doctrine. anyone can help.
    thanks

  4. Hardeep KhehraFebruary 22, 2010 at 3:14 pm

    @YH
    There is a much easier way to do this. I usually allow logins by email & username since the user may remember one or the other more easily.

    in your generated lib/model/doctrine/sfGuardUserTable.php add the following method:

    public function retrieveByUsername($username, $isActive = true)
    {
    $query = Doctrine::getTable(‘sfGuardUser’)->createQuery(‘u’)
    ->leftJoin(‘u.Profile p’)
    ->where(‘u.username = ?’, $username)
    ->orWhere(‘p.email = ?’, $username)
    ->addWhere(‘u.is_active = ?’, $isActive)
    ;

    return $query->fetchOne();
    }

    Thats it.. now your users can log in via email or via username. You can adjust the Doctrine query to your liking if you only want to allow emails.

    sfDoctrineGuardPlugin calls that method from the validator, but you are just overriding it with a custom query.

    from plugin documentation:

    Validators
    sfDoctrineGuardPlugin comes with a validator that you can use in your modules: sfGuardUserValidator.

    This validator is used by the sfGuardAuth module to validate a user and password and automatically signin the user.

Leave a reply

Your email address will not be published. Required fields are marked *

*

My Projects
Restaurant Websites
Websites