Połączenie sfGuardUser oraz sfGuardUserProfile

Author: przemyslaw.piechota (przemyslaw.piechota) | sierpień 4th, 2009
avatar

Przy ostatnich pracach związanych z przeniesienie naszej aplikacji z symfony 1.0 na symfony 1.2 natknęliśmy się na kilka problemów z pluginem sfGuardPlugin, a dokładniej z połączeniem administracji sfGuardUser oraz sfGuardUserProfile.
Chcieliśmy aby podczas listowania użytkowników, poprzez moduł sfGuardUser, można było wyświetlać nie tylko pola znajdujące się w tabeli sf_guard_user ale także wybrane pola z profilu usera, a także mieć możliwość filtrowania po nich.

Na początek stworzymy sobie możliwość wyświetlania pól z profilu użytkownika podczas listowania. Otwieramy generator.yml z modułu sfGuardUser, który w oryginale wygląda tak:

generator:
  class: sfPropelGenerator
  param:
    model_class:           sfGuardUser
    theme:                 admin
    non_verbose_templates: true
    with_show:             false
    singular:              ~
    plural:                ~
    route_prefix:          sf_guard_user
    with_propel_route:     1

    config:
      fields:
        password_again: { label: "Password (again)" }

      list:
        title:   User list
        display: [=username, created_at, last_login]

      form:
        class: sfGuardUserAdminForm
        display:
          "NONE":                   [username, password, password_again]
          "Permissions and groups": [is_active, is_super_admin, sf_guard_user_group_list, sf_guard_user_permission_list]

      edit:
        title: Editing User "%%username%%"

      new:
        title: New User

Interesuje nas dokładnie sekcja

list:
  title:   User list
  display: [=username, created_at, last_login]

Do wyświetlania informacji z pól z profilu użytkownika, będziemy korzystać z możliwości użycia własnych partiali.
Nas interesuje np. imię, nazwisko, oraz adres e-mail użytkownika, dlatego wprowadzamy takie modyfikacje:

list:
  title:   User list
  display: [=username, created_at, last_login, _first_name, _last_name, _email]

Następnie tworzymy 3 pratiale w katalogu sfGuardUser/templates/ :
_first_name.php

<?php echo $sf_guard_user->getProfile()->getFirstName(); ?>

_last_name.php

<?php echo $sf_guard_user->getProfile()->getLastName(); ?>

_email.php

<?php echo $sf_guard_user->getProfile()->getEmail(); ?>

Po tym

symfony cc

I po odświeżeniu strony z listowaniem użytkowników będziemy mieli także informacje o imieniu, nazwisku oraz adresie email.
Wszytko pięknie tylko po takich modyfikacjach nie można sortować po dodanych polach. A to dlatego, że generator podczas tworzenia kodu naszych template’ów sprawdza czy pole które ma być wyświetlone istnieje w modelu.
Dlatego musimy obejść to sprawdzanie poprzez zmodyfikowanie informacji o polach w listowaniu (generator.yml):

list:
  title:   User list
  display: [=username, created_at, last_login, _first_name, _last_name, _email]
  fields:
    last_name:      { is_real: true }
    first_name:     { is_real: true }
    email:          { is_real: true }

Teraz po wyczyszczeniu cache i odświeżeniu strony mamy już możliwość sortowania po polach z profilu.

Jeżeli przejdziecie teraz w środowisko dev zauważycie, że nagle zwiększyła się bardzo ilość zapytań do bazy.
A to ze względu, że dla każdego użytkownika pobierany jest osobno jego profil.
Należałoby to zoptymalizować, aby nie wywoływać niepotrzebnie tylu zapytań. Tutaj natknęliśmy się na kilka problemów, ponieważ klasa sfGuardUserPeer nie posiada metody doSelectJoinsfGuardUserProfile.
Otwieramy plik sfGuardUserPeer.php i wprowadzamy pewne modfikacje:

<?php
class sfGuardUserPeer extends PluginsfGuardUserPeer
{
  public static function doSelectJoinsfGuardUserProfile(Criteria $c, $con = null, $join_behavior = Criteria::LEFT_JOIN)
  {

    foreach (sfMixer::getCallables('BasesfGuardUserPeer:doSelectJoin:doSelectJoin') as $callable)
    {
      call_user_func($callable, 'BasesfGuardUserPeer', $c, $con);
    }

    $c = clone $c;

    if ($c->getDbName() == Propel::getDefaultDB()) {
      $c->setDbName(self::DATABASE_NAME);
    }

    sfGuardUserPeer::addSelectColumns($c);
    $startcol = (sfGuardUserPeer::NUM_COLUMNS - sfGuardUserPeer::NUM_LAZY_LOAD_COLUMNS);
    sfGuardUserProfilePeer::addSelectColumns($c);

    $c->addJoin(array(sfGuardUserPeer::ID,), array(sfGuardUserProfilePeer::USER_ID,), $join_behavior);
    $stmt = BasePeer::doSelect($c, $con);
    $results = array();

    while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
      $key1 = sfGuardUserPeer::getPrimaryKeyHashFromRow($row, 0);
      if (null !== ($obj1 = sfGuardUserPeer::getInstanceFromPool($key1))) {
      } else {

        $omClass = sfGuardUserPeer::getOMClass();

        $cls = substr('.'.$omClass, strrpos('.'.$omClass, '.') + 1);
        $obj1 = new $cls();
        $obj1->hydrate($row);
        sfGuardUserPeer::addInstanceToPool($obj1, $key1);
      }
      $key2 = sfGuardUserProfilePeer::getPrimaryKeyHashFromRow($row, $startcol);
      if ($key2 !== null) {
        $obj2 = sfGuardUserProfilePeer::getInstanceFromPool($key2);
        if (!$obj2) {

          $omClass = sfGuardUserProfilePeer::getOMClass();

          $cls = substr('.'.$omClass, strrpos('.'.$omClass, '.') + 1);
          $obj2 = new $cls();
          $obj2->hydrate($row, $startcol);
          sfGuardUserProfilePeer::addInstanceToPool($obj2, $key2);
        }
        $obj1->setProfile($obj2);

      }
      $results[] = $obj1;
    }
    $stmt->closeCursor();
    return $results;
  }
}

A w pliku sfGuardUser.php, dodajemy funkcję setProfile :

<?php
class sfGuardUser extends PluginsfGuardUser
{
  public function setProfile( $profile )
  {
    $this->profile = $profile;
  }
}

Na koniec musimy tylko zmienić peer_method wywoływaną przez generator do pobierania wyników.
Otwieramy plik sfGuardUser/lib/sfGuardUserGeneratorConfiguration.class.php i dodajemy funkcję getPeerMethod:

<?php

/**
 * sfGuardUser module configuration.
 *
 * @package    sfGuardPlugin
 * @subpackage sfGuardUser
 */
class sfGuardUserGeneratorConfiguration extends BaseSfGuardUserGeneratorConfiguration
{
  public function getPeerMethod()
  {
    return 'doSelectJoinsfGuardUserProfile';
  }
}

Teraz po wyczyszczeniu cache, odświeżeniu strony ilość zapytań do bazy powinna spaść.

Na dzisiaj to tyle. Zostało jeszcze opisanie jak filtrować po polach z profilu użytkownika ale to już innym razem.

Tags: , , ,

Podyskutuj na ten temat:

2 Responses to “Połączenie sfGuardUser oraz sfGuardUserProfile”

  1. avatar smentek pisze:

    Po wykonaniu czynności opisanych w tym tutku, pojawił sie następujący błąd:

    500 | Internal Server Error | PropelException
    ‘email’ could not be found in the field names of type ‘fieldName’. These are: Array
    (
    [id] => 0
    [username] => 1
    [algorithm] => 2
    [salt] => 3
    [password] => 4
    [created_at] => 5
    [last_login] => 6
    [is_active] => 7
    [is_super_admin] => 8
    )

  2. avatar smentek pisze:

    Rozwiązanie:
    symfony propel:generate-crud backend sfGuardUserProfile sfGuardUserProfile
    symfony propel:generate-crud backend sfGuardUser sfGuardUser

Leave a Reply