Symfony: вопрос о маршрутизации и пробках

Чтобы не отображать идентификаторы участников моей социальной сети в URL-адресе, я создал этот маршрут:

perfil_miembro:
  url: /miembros/:nombre_apellidos
  class: sfDoctrineRoute
  options: { model: Usuario, type: object}
  param: { module: miembros, action: show}

И добавил эту строку в действие show:

$this->usuario = $this->getRoute()->getObject();

Это работает нормально: когда я нажимаю на их имена, отображается соответствующий профиль, а URL-адрес имеет следующий тип:

frontend_dev.php/miembros/Мария+де+Мигель+Альварадо

Теперь я хотел бы указать имена в URL-адресе, поэтому я изменил маршрут следующим образом:

perfil_miembro:
  url: /miembros/:nombre_apellidos_slug
  class: sfDoctrineRoute
  options: { model: Usuario, type: object}
  param: { module: miembros, action: show}

И я создал эти методы:

public function getNombreApellidosSlug()
{

     return Tirengarfio::slugify($this->getNombreApellidos());
}

class Tirengarfio
{
  static public function slugify($text)
  {

    // replace all non letters or digits by -
    $text = preg_replace('/\W+/', '-', $text);

    // trim and lowercase
    $text = strtolower(trim($text, '-'));

    return $text;
  }
}

Теперь, когда я нажимаю на имя участника, отображается этот URL:

frontend_dev.php/miembros/мария-де-мигель-альварадо

Но он всегда показывает профиль первого члена, который у меня есть в файле приспособлений.

Как заставить его работать?

Ubuntu 8.04 — Symfony 1.3.


person ziiweb    schedule 13.07.2010    source источник


Ответы (2)


Поле slug должно быть реальным столбцом, а не виртуальным, который вы создали.
При доступе к URL-адресу доктрина ищет один объект, соответствующий полям, найденным в URL-адресе. В вашем случае это означает отсутствие полей. , поэтому вы видите самую первую запись в своей таблице. Регистратор запросов должен показать что-то вроде select * from tablename limit 1;.

Примечание относительно вашего URL: вы уверены, что не будет нескольких людей с одинаковым именем? Если произойдет такая коллизия, никто не сможет увидеть страницу 2-го, 3-го и т.д. человека. Я бы включил идентификатор в URL-адрес в виде /miembros/:id/:slug, чтобы он оставался удобочитаемым для человека и, конечно же, не сталкивался.

ОБНОВЛЕНИЕ
В первом комментарии @Raise предлагает использовать соленый хэш идентификатора вместо самого идентификатора. Это лучше, чем моя первоначальная идея включения идентификатора.
Плагин sfDoctrineGuardPlugin создает новую соль для каждого пользователя, сохраняет ее и использует для установки/проверки пароля. Вам понадобятся новые поля в таблице пользователей для хэша (соль не нужно хранить, идентификатор не изменится), и ваш URL-адрес будет выглядеть как /miembros/:hash/:slug.

person Maerlyn    schedule 13.07.2010
comment
+1 за предложение «уникализации» URL-адреса, чтобы избежать коллизий, хотя я думаю, что некоторый хэш идентификатора и соли был бы лучше, чтобы избежать раскрытия идентификатора на веб-сайте. Действительно ли Doctrine строит такой запрос и возвращает первую строку в БД? Это беспокоит, если это правда. - person Raise; 14.07.2010
comment
@Поднять хэш - хорошая идея. Что касается построения запроса, то я пропел-юзер, но по симптомам только так и могу себе представить. Я согласен, что подсчет может быть для того, чтобы увидеть, как записи могут соответствовать запросу, но это не обязательно. - person Maerlyn; 14.07.2010
comment
Спасибо, Maerlyn, вы написали, что я бы включил идентификатор в URL-адрес, но это не очень хорошая практика, как вы можете видеть здесь (поиск постоянных ссылок) symfony-project.org/gentle-introduction/1_4/en/ - person ziiweb; 14.07.2010
comment
@Raise (надеюсь, вы это читали) как маршруты propel, так и доктрины вызывают $variables = $this->getRealVariables(), поэтому они могут просто игнорировать переменные URL, которые не сопоставляются со столбцами БД. См. строку 46 sfPropelRoute и строку 68 sfDoctrineRoute (конечно, версия 1.4). - person Maerlyn; 14.07.2010
comment
@Maerlyn, я не совсем понимаю эту часть: вам понадобятся новые поля в вашей таблице пользователей для хэша (соль не нужно хранить, идентификатор не изменится). Не могли бы вы помочь мне еще немного? - person ziiweb; 15.07.2010
comment
Чтобы иметь возможность фильтровать хеш, вам нужно, чтобы он был полем в вашей таблице (точно так же, как слаг). На всякий случай его также следует посолить, но соль хранить не нужно, так как хеш не нужно будет регенерировать, а просто использовать. Когда регистрируется новый пользователь, вы генерируете соль (подойдет случайное число или time()), используете ее для хеширования идентификатора, а затем сохраняете хэш в таблице пользователей. Теперь ясно? - person Maerlyn; 15.07.2010

Вы можете использовать эти параметры маршрутизации: options: { model: Usuario, type: object, method: getObjectBySlug }, но вам нужен метод getObjectBySlug(), который извлекает ваш объект с учетом слага. Теперь у вас есть getNombreApellidosSlug(), который делает прямо противоположное. Проблема в том, что обычно невозможно узнать, соответствует ли слаг «maria-martinez» пользователю «Maria Martinez», «Maria Martínez», «MaRiA MaRtInEz» или «Maria Martíñez», так что это проблема. Вы можете решить эту проблему, имея столбец «slug».

Мой совет — использовать Sluggable поведение Doctrine, который отвечает за краткий столбец.

Я использую его в этом любимом проекте именно для этого. Использовать его довольно просто:

Сначала вы активируете его в схеме:

actAs:
  Timestampable: ~
  Sluggable:
    fields: [name]
    indexName: name_slug
    canUpdate: true
    unique: true

И он создаст и будет поддерживать столбец с именем «slug».

Затем используйте его в маршрутизации. В моем случае это:

list_permalink:
  url: /:slug
  class: sfDoctrineRoute
  options: { model: SkinnyList, type: object, method: getObjectBySlug }
  param: { module: list, action: show }
  requirements: { sf_method: get }

Вам понадобится метод getObjectBySlug в lib/ модель/доктрина/yourmodelTable.class.php:

  public function getObjectBySlug($options = array())
  {
    if (!isset($options['slug']))
    {
      throw new InvalidArgumentException('The slug is required in the options');
    }
    $q = $this->createQuery('td')->where('td.slug = ?', $options['slug']) ;

    return $q->fetchOne();
  }

В действии вы можете получить объект, выполнив:

$this->list = $this->getRoute()->getObject();
person nacmartin    schedule 17.07.2010