Литий и проверка входных данных сложной формы - как?

Я сделал довольно много руководств по литию (ссылки ниже, если они помогают кому-то другому, а также чтобы показать, что я выполнил свою домашнюю работу :), и я понимаю самые основные части создания моделей, представлений, контроллеров и использования MVC для создания запись БД на основе ввода формы.

Однако я новичок в MVC для веб-приложений и Lithium, и я не уверен, как мне писать код в более сложных ситуациях. Это общий вопрос, но у меня есть два конкретных вопроса о проверке:

  • Как мне проверить данные о дате, отправленные из формы?
  • Как мне проверить, что два поля электронной почты пользователя имеют одинаковое значение?

Я был бы очень благодарен за любую помощь с этими вопросами, и конкретные примеры, подобные этому, также действительно помогут мне понять, как делать хорошее кодирование MVC в других ситуациях!

Ввод даты - проверка разделения данных на несколько входов формы

По причинам пользовательского интерфейса форма регистрации просит пользователей ввести свой DOB в трех полях:

<?=$this->form->field('birthday', array('type' => 'select', 'list' => array(/*...*/))); ?>
<?=$this->form->field('birthmonth', array('type' => 'select', 'list' => array(/*...*/))); ?>
<?=$this->form->field('birthyear', array('type' => 'select', 'list' => array(/*...*/))); ?>

Как лучше всего проверить эту серверную часть? Я думаю, мне следует воспользоваться преимуществами автоматической проверки, но я не уверен, как лучше всего это сделать для набора переменных, которые на самом деле не являются частью модели. Например.:

  • Должен ли я постобработать $this->request->data в UsersController? Например. измените $this->request->data внутри UsersController перед передачей Users::create.
  • Должен ли я вытащить поля формы из $this->request->data и использовать статический вызов Validator::isDate внутри UsersController?
  • Есть ли способ написать правило проверки в модели для комбинаций переменных формы, которые не являются частью модели?
  • должен ли я переопределить Users::create и выполнить там всю дополнительную проверку и постобработку?

Все это похоже на то, что они могут сработать, хотя некоторые кажутся немного некрасивыми, и я не знаю, какие из них могут вызвать у меня серьезные проблемы в будущем.

[РЕДАКТИРОВАТЬ: с этим тесно связана проблема объединения трех полей формы в одно поле, которое будет сохранено в модели]

Ввод электронной почты - проверка того, что два поля формы идентичны, но сохраняется только одно

Для здравого смысла / общепринятой практики форма регистрации просит пользователей дважды указать свой адрес электронной почты:

<?=$this->form->field('email_address'); ?>
<?=$this->form->field('verify_email_address'); ?>

Как я могу написать правило автоматической проверки, которое проверяет, что эти два поля формы имеют одинаковое значение, но сохраняет только email_address в базе данных?

Похоже, что это почти тот же вопрос, что и предыдущий, потому что список возможных ответов, которые я могу придумать, тот же - поэтому я отправляю это как один вопрос, но я был бы очень признателен за вашу помощь с обеими частями, поскольку я думаю, что решение этого будет тонким, отличным и в равной степени поучительным!

[РЕДАКТИРОВАТЬ: с этим тесно связана проблема не сохранения verify_email_address в моей модели и БД]

Некоторые справочные материалы по литию

Я читал другие, но эти три руководства привели меня туда, где я нахожусь с пользователями и формами регистрации сейчас ...

Некоторые другие вопросы StackOverflow по тесно связанным темам (но без ответа, а также не по литию)

  • Один ответ на этот вопрос предлагает создать отдельный контроллер (и модель и ...?) - мне он не очень "литиевый", и я беспокоюсь, что он может быть хрупким / легко глючным.
  • Эта замечательная история убедила меня, что я был прав беспокоился о том, чтобы вставить его в контроллер, но я не уверен, что было бы хорошим решением
  • Этот в представлениях заставляет меня думать, что я должен каким-то образом включить его в модель, но я не знаю, как лучше всего это сделать в литии (см. мой маркированный список в разделе «Ввод даты» выше)
  • И эта презентация Scribd задала вопрос: надеясь ответить на последней странице ... после чего он остановился, не ответив на него!

NB: ответы в стиле CakePHP тоже подходят. Я не знаю, но это похоже, и я уверен, что могу перевести с него, если мне нужно!


person cfogelberg    schedule 29.03.2012    source источник
comment
Резюме обсуждения с софойCitizen (2012-04-01): Представленные данные должны быть подвергнуты последующей обработке в контроллере. Это связано с тем, что модель хранит дату, а не три отдельных поля, составляющих дату. Передача проверочной информации обратно в представление немного сложно, но одно из решений - добавить поле DOB в форму после трех полей ввода даты. Если для этого поля DOB установлено значение «Нет», это может по-прежнему разрешать отображение сообщений об ошибках для него (по доверенности для других полей даты).   -  person cfogelberg    schedule 02.04.2012
comment
Журналы чата здесь   -  person cfogelberg    schedule 04.04.2012


Ответы (1)


Я бы порекомендовал сделать это в Model, а не в Controller - так будет происходить независимо от того, откуда вы делаете сохранение.

Для проблемы с полем даты в вашей модели переопределите метод save() и обработайте преобразование нескольких полей в данных в одно поле даты перед вызовом parent::save для фактического сохранения. Там могут произойти любые сложные манипуляции.

Описанная в вашем комментарии методика использования скрытого поля формы для отображения сообщений об ошибках звучит неплохо.

Для сравнения того, что два поля электронной почты равны, я бы рекомендовал определить собственный валидатор. Вы можете сделать это в начальной загрузке с помощью Validator :: add.

use lithium\util\Validator;
use InvalidArgumentException;

Validator::add('match', function($value, $format = null, array $options = array()) {
    $options += array(
        'against' => '',
        'values' => array()
    );
    extract($options);
    if (array_key_exists($against, $values)) {
        return $values[$against] == $value;
    }
    return false;
});

Затем в вашей модели:

public $validates = array(
    "email" => array(
        "match",
        "message" => "Please re-type your email address.",
        "against" => "email2"
    )
);

Изменить. Согласно комментариям, вот способ проверки настраиваемого правила в контроллере:

public function save() {
    $entity = MyModel::create($this->request->data);
    $rules = array(
        "email" => array(
            "match",
            "message" => "Please re-type your email address.",
            "against" => "email2"
        )
    );

    if (!$entity->validates($rules)) {
        return compact('entity');
    }

    // if your model defines a `$_schema` and sets `$_meta = array('locked' => true)`
    // then any fields not in the schema will not be saved to the db

    // here's another way using the `'whitelist'` param
    $blacklist = array('email2', 'some', 'other', 'fields');
    $whitelist = array_keys($entity->data());
    $whitelist = array_diff($whitelist, $blacklist);

    if ($entity->save(null, compact('whitelist'))) {
        $this->redirect(
            array("Controller::view", "args" => array($entity->_id)),
            array('exit' => true)
        );
    }

    return compact('entity');
}

Преимущество установки данных для объекта заключается в том, что они будут автоматически предварительно заполнены в вашей форме, если произойдет ошибка проверки.

person rmarscher    schedule 03.04.2012
comment
спасибо за ответ и ваше предложение поместить его в модель, чтобы сделать его доступным везде, звучит умно. Поскольку эти три поля сейчас используются только при отправке формы, а не в других ситуациях, я добавлю в модель метод данных формы процесса и передам возвращаемое значение из него в функцию сохранения. Ваша идея с валидатором также кажется действительно умной, я не думал об этом так. Я не понимаю, как / где $options['values'] заполняется полями отправленной формы. Это происходит автоматически? - person cfogelberg; 04.04.2012
comment
$options['values'] автоматичен. Посмотрите Model :: validates. Вызывается $validator::check($entity->data(), $rules, $options), и $entity->data() передается в конечном итоге $options['values']. Это немного запутано, но в Validator::check() есть $rule += $options + compact('values');, где $values - это $entity->data(), а затем $rule + $options передается Validator::rule(). - person rmarscher; 10.04.2012
comment
Спасибо за разъяснение и объяснение потока кода. Наконец-то у меня появилась возможность следить за этим, и он работает отлично, пока оба поля являются частью модели. Если он предназначен только для проверки ввода (например, подтверждение адреса электронной почты, где я не хочу сохранять адрес дважды), тогда он не работает. Вместо этого, пройдя через код и поговорив с людьми на # li3 о вводе и проверке данных, кажется, что лучший способ справиться с такой проверкой - это поймать ее в контроллере и вручную обновить $foo->_errors, где $foo - это объект Entity, возвращаемый create(). - person cfogelberg; 11.04.2012
comment
Хорошо ... Я только что отредактировал свой ответ, чтобы попытаться сделать что-то лучшее из обоих миров. Он использует метод Entity validates, но делает это в контроллере. Я включил несколько примечаний по блокировке вашей схемы или предоставлению белого списка, чтобы дополнительные поля не сохранялись в БД. Я только что заметил, что '$_meta['locked']' недокументирован. Для этого мне, возможно, придется отправить запрос на перенос. На встрече, которую мы провели в феврале, Нейт упомянул, что он переписывает способ работы схемы и создает класс схемы вместо простого массива в Model. Ваше здоровье. - person rmarscher; 13.04.2012