0 follower

Criando um Modelo

Antes de escrever o código HTML necessário para um formulário, devemos decidir quais tipos de dados esperamos dos usuários e a quais regras eles devem estar de acordo. Uma classe de modelo pode ser utilizada para registrar essas informações. Um modelo, como descrito em Modelo, é o lugar central para manter as entradas fornecidas pelo usuário e validá-las.

Dependendo da forma como utilizamos as entradas dos usuários, podemos criar dois tipos de modelo. Se os dados são coletados, utilizados e, então, descartados, devemos criar um modelo de formulário (form model); porém, se a entrada do usuário deve ser coletada e armazenada em uma base de dados, devemos utilizar um active record. Ambos os tipos de modelo compartilham CModel como classe base, onde está definida uma interface comum necessária a formulários.

Nota: Nós utilizaremos modelos de formulários nos exemplos desta seção. Entretanto, o mesmo pode ser aplicado para modelos utilizando active record.

1. Definindo uma Classe de Modelo

No trecho de código abaixo, criamos uma classe de modelo chamada LoginForm que será utilizada para coletar os dados informados pelo usuário em uma página de login. Como essa informação é utilizada somente para autenticar o usuário e não necessita ser armazenada, criaremos a classe LoginForm como um modelo de formulário.

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
}

Foram declarados três atributos na classe LoginForm: $username, $password, e $rememberMe. Eles são utilizados para manter o nome de usuário e senha informados no formulário, bem como a opção se ele deseja que o sistema se lembre de seu login. Como a valor padrão de $rememberMe é false, a opção correspondente no formulário de login estará, inicialmente, desmarcada.

Informação: Em vez de chamarmos essas variáveis membro de propriedades, utilizamos o termo atributos para diferenciá-las de propriedades normais. Um atributo é uma propriedade utilizada, basicamente, para armazenar dados originados de entradas de usuários ou do banco de dados.

2. Declarando Regras de Validação

Uma vez que o usuário envia seus dados e o modelo é preenchido com eles, devemos garantir que essas informações sejam validadas antes de serem utilizadas. Para isso, utilizamos um conjunto de regras que são testadas contra os dados informados. Para especificar essas regras de validação, utilizamos o método rules(), que deve retornar um vetor contendo as configurações de regras.

class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;
 
    public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('password', 'authenticate'),
    );
    }
 
    public function authenticate($attribute,$params)
    {
        if(!$this->hasErrors())  // devemos autenticar o usuário somente se não existir erros de validação
        {
            $identity=new UserIdentity($this->username,$this->password);
            if($identity->authenticate())
            {
                $duration=$this->rememberMe ? 3600*24*30 : 0; // 30 dias
                Yii::app()->user->login($identity,$duration);
            }
            else
                $this->addError('password','Senha Incorreta.');
        }
    }
}

No código acima, especificamos que username e password são obrigatórios (required). Além disso, definimos que password deve ser autenticado (authenticate).

Cada regra retornada pelo método rules() deve estar no seguinte formato:

array('ListaDeAtributos', 'Validador', 'on'=>'ListaDeCenarios', ...opções adicionais)

Onde, ListaDeAtributos é uma string contendo todos os atributos, separados por vírgula, que devem ser validados de acordo com a regra; Validador determina que tipo de validação deverá ser efetuada; o parâmetro on é opcional e é utilizado para especificar uma lista de cenários onde a regra deve ser aplicada; opções adicionais são pares chave-valor, utilizados para iniciar as propriedades do validador.

Existem três maneiras de especificar o Validador em uma regra. Primeira, Validador pode ser o nome de um método na classe do modelo, como o authenticate no exemplo acima. Nesse caso, o método validador deve ter a seguinte assinatura:

/**
 * @param string o nome do atributo a ser validado
 * @param array opções especificadas na regra de validação
 */
public function nomeDoValidador($atributo,$parametros) { ... }

Segunda, Validador pode ser o nome de uma classe validadora. Dessa maneira, quando a regra é aplicada, uma instância dessa classe será criada para efetuar a validação. As opções adicionais na regra serão utilizadas para iniciar os valores dos atributos da instância. Uma classe validadora deve estender a classe CValidator.

Nota: Quando especificamos regras para um modelo active record, podemos utilizar a opção especial on. Os valores dessa opção podem ser insert ou update, de forma que a regra seja aplicada somente ao inserir ou atualizar um registro. Se não for utilizada, a regra será aplicada em ambos os casos, quando o método save() for utilizado.

Terceira, Validador pode ser um alias (apelido) predefinido para uma classe validadora. No exemplo acima, o nome required é um alias para a classe CRequiredValidator, a qual valida se o valor do atributo não está vazio. Abaixo, temos uma lista completa dos aliases (apelidos) predefinidos:

  • boolean: alias para CBooleanValidator, garante que o valor de um atributo seja somente
    CBooleanValidator::trueValue ou CBooleanValidator::falseValue.

  • captcha: alias para CCapthcaValidator, garante que o atributo é igual ao código de verificação exibido em um CAPTCHA.

  • compare: alias para CCompareValidator, garante que o atributo é igual a outro atributo ou a uma constante.

  • email: alias para CEmailValidator, garante que o atributo é um endereço de email válido.

  • default: alias para CDefaultValueValidator, utilizado para atribuir um valor padrão (default) aos atributos especificados.

  • exist: alias para CExistValidator, garante que o valor do atributo existe na coluna da tabela informada.

  • file: alias para CFileValidator, garante que o atributo contém o nome de um arquivo enviado via upload.

  • filter: alias para CFilterValidator, modifica o atributo com um filtro.

  • in: alias para CRangeValidator, garante que o dado informado está entre uma lista específica de valores.

  • length: alias para CStringValidator, garante que o tamanho do dado está dentro de um tamanho específico.

  • match: alias para CRegularExpressionValidator, garante que o dado informado casa com um expressão regular.

  • numerical: alias para CNumberValidator, garante que o dado informado é um número válido.

  • required: alias para CRequiredValidator, garante que o valor do atributo não está vazio.

  • type: alias para CTypeValidator, garante que o atributo é de um tipo específico.

  • unique: alias para CUniqueValidator, garante que o dado informado é único na coluna da tabela do banco de dados informada.

  • url: alias para CUrlValidator, garante que o dado informado é uma URL válida.

Abaixo listamos alguns exemplos da utilização de validadores predefinidos:

// username é obrigatório
array('username', 'required'),
// username deve ter entre 3 e 12 caracteres
array('username', 'length', 'min'=>3, 'max'=>12),
// quando estiver no cenário register, password deve ser igual password2
array('password', 'compare', 'compareAttribute'=>'password2', 'on'=>'register'),
// quando estiver no cenário login, password deve ser autenticado
array('password', 'authenticate', 'on'=>'login'),

3. Atribuição Segura de Atributos

Nota: a atribuição de atributos baseada em cenários está disponível desde a versão 1.0.2 do framework.

Normalmente, depois que uma instância de um modelo é criada, precisamos popular seus atributos com as informações enviadas pelo usuário. Isso pode ser feito de uma maneira conveniente, utilizando a atribuição em massa, como pode ser visto no código abaixo:

$model=new LoginForm;
$model->scenario='login';
if(isset($_POST['LoginForm']))
    $model->attributes=$_POST['LoginForm'];

Note: Nota: A propriedade scenario está disponível desde a versão 1.0.4. A atribuição em massa irá utilizar o valor dessa propriedade para determinar quais atributos podem ser atribuídos dessa maneira. Nas versões 1.0.2 e 1.0.3, para fazer a atribuição em massa em um cenário específico, deveríamos proceder da seguinte maneira:

$model->setAttributes($_POST['LoginForm'], 'login');

Nesse trecho de código, temos uma atribuição em massa que atribui cada entrada em $_POST['LoginForm'] ao atributo correspondente no modelo, no cenário login. Isso é equivalente a:

foreach($_POST['LoginForm'] as $name=>$value)
{
    if($name é um atributo seguro)
        $model->$name=$value;
}

A tarefa de decidir se um dado é seguro ou não é baseada no valor de retorno do método safeAttributes e o cenário especificado. Por padrão, esse método retorna todas as variáveis membro públicas como atributos seguros para a classe CFormModel, ou todas as colunas de uma tabela, menos a chave primária, como atributos para a classe CActiveRecord. Nós podemos sobrescrever este método para limitar esses atributos seguros de acordo com os cenários. Por exemplo, um modelo usuário deve conter vários atributos, mas no cenário login, precisamos apenas do username e do password. Podemos especificar esses limites da seguinte maneira:

public function safeAttributes()
{
    return array(
        parent::safeAttributes(),
        'login' => 'username, password',
    );
}

Mais precisamente, o valor de retorno do método safeAttributes deve ter a seguinte estrutura:

array(
   // esses atributos podem ser atribuídos em massa em qualquer cenário
   // isso não ser explicitamente especificado, como vemos abaixo
   'attr1, attr2, ...',
     *
   // esses atributos só podem ser atribuídos em massa no cenário 1
   'cenario1' => 'attr2, attr3, ...',
     *
   // esses atributos só podem ser atribuídos em massa no cenário 2
   'cenario2' => 'attr1, attr3, ...',
)

Se os cenários não são importantes para o modelo, ou se todos os cenários tem o mesmo conjunto de atributos, o valor de retorno pode ser simplificado para um simples string:

'attr1, attr2, ...'

Para dados não seguros, devemos atribui-los individualmente aos atributos, como no exemplo a seguir:

$model->permission='admin';
$model->id=1;

4. Disparando a Validação

Uma vez que o modelo tenha sido populado com os dados enviados pelo usuário, podemos executar o método CModel::validate() para disparar o processo de validação. Esse método retorna uma valor indicando se a validação ocorreu com sucesso ou não. Para modelos utilizando CActiveRecord, a validação pode ser disparada automaticamente quando o método CActiveRecord::save() é executado.

Quando chamamos CModel::validate(), podemos especificar um parâmetro com o nome de um cenário. Assim, somente as regras desse cenário serão aplicadas na validação. Uma regra é aplicada a um cenário, se não existir a opção on nela, ou, caso exista, seu valor corresponda ao cenário especificado.

Por exemplo, executamos o código a seguir para executar a validação ao registrar um usuário:

$model->scenario='register';
$model->validate();

Nota: A propriedade scenario está disponível a partir da versão 1.0.4. O método de validação irá utilizar essa propriedade para determinar quais regras irá utilizar. Nas versões 1.0.2 e 1.0.3, devemos informar o cenário da seguinte maneira:

$model->validate('register');

Devemos declarar as regras de validação na classe do modelo de formulário da seguinte maneira:

public function rules()
{
    return array(
        array('username, password', 'required'),
        array('password_repeat', 'required', 'on'=>'register'),
        array('password', 'compare', 'on'=>'register'),
    );
}

Como resultado, a primeira regra será aplicada para todos os cenários, enquanto as outras duas serão aplicadas apenas no cenário register

Nota: validação baseada em cenários está disponível desde a versão 1.0.1.

5. Recuperando Erros de Validação

Podemos usar o método CModel::hasErrors() para verificar se existe algum erro de validação e, caso existir, podemos utilizar o método CModel::getErrors() para obter as mensagens de erro. Ambos os métodos podem ser utilizados para verificar erros em todos os atributos de uma única vez, ou para cada atributo individualmente.

6. Rótulos de Atributos

Quando desenvolvemos um formulário, normalmente precisamos exibir um rótulo para cada campo. Esse rótulo indica ao usuário que tipo de informação espera-se que ele informe naquele campo. Embora podemos escrever esses rótulos diretamente na visão, seria mais flexível e conveniente poder especifica-los diretamente no modelo correspondente.

Por padrão, a classe CModel irá retornar o nome do atributo como seu rótulo. Essa característica pode ser alterada sobrescrevendo o método attributeLabels(). Como veremos nas subseções a seguir, especificando rótulos nos modelos nos permite criar formulários poderosos de uma maneira mais rápida.