I'm new to yii2, I'm trying to send a request to the database, but it comes out that such columns cannot be null, although when checking through #var_dump I can see the sending data, what is the matter and how to fix it
---> Controller
public function actionSignup()
{
$model = new Signup();
if (isset($_POST['Signup'])) {
$model->attributes = Yii::$app->request->post('Signup');
if ($model->validate()) {
$model->signup();
# code...
}
}
return $this->render('signup', ['model'=>$model]);
---> View page
<?php
use \yii\widgets\ActiveForm;
?>
<?php
$form = ActiveForm::begin(['class'=>'form-horizontal']);
?>
<?= $form->field($model,'email')->textInput(['autofocus'=>true]) ?>
<?= $form->field($model,'password')->passwordInput() ?>
<div>
<button type="submit" class="btn primary-btn">Submit</button>
</div>
<?php
ActiveForm::end();
?>
---> Model
class Signup extends Model
{
public $email;
public $password;
public function reles()
{
return [
[['email', 'password'], 'required'],
['email', 'email'],
['email', 'unique', 'targetClass'=>'app\models\User'],
['password', 'string', 'min'=>2,'max'=>10]
];
}
public function signup()
{
$user = new User();
$user->email = $this->email;
$user->password = $this->password;
return $user->save();
}
---> phpmyadmin database
namespace app\models;
use yii\db\ActiveRecord;
class User extends ActiveRecord
{
}
--> var_dump
--> sql
Try this:
**
* Signs user up.
*
* #return mixed
*/
public function actionSignup()
{
$model = new SignupForm()
//Sign up if it is post request
if ($model->load(Yii::$app->request->post()) && $model->signup()) { //Here YOU LOAD your POST data in to the Lodal using load() method.
Yii::$app->session->setFlash('success', 'Thank you for registration. Please check your email for further instructions.', false);
return $this->goHome();
} else {
Yii::$app->session->setFlash('success', 'Sorry, you can not register right now.', false);
return $this->redirect('/login');
}
}
In Model
First of all, the method for attribute rules is called "rules" I don't know if it's a typo from you in the question or this is the problem; If there are no rules for user input data the returned values will be NULL, I have faced this issue before, I didn't include a rules() method (or in your case it's name is not correct) and the model returned null for attribute values. Your code have to be:
public function rules()
{
return [
[['email', 'password'], 'required'],
['email', 'email'],
['email', 'unique', 'targetClass'=>'app\models\User'],
['password', 'string', 'min'=>2,'max'=>10]
];
}
Technical note: normally, or based upon my understanding a password should have a minimum of 8 not 2 characters, if the password entered is only 2 characters it can be brute-forced in a second.
In Controller
As in #Serghei Leonenco's Answer, Yii2 already provides some functions to use to handle post requests and so, so please stick to Yii2 functions it can enhance your app security
So in the actionSignup() method the code ought to be
public function actionSignup()
{
$model = new Signup();
// Loads the post request directly onto the model and validates it against the model rules() method
if($model->load(Yii::$app->request->post()) && $model->validate){
$model->signup();
// Model validated and saved successfully, IT OUGHT TO BE SAVED IN THE DATABASE AT THIS POINT
// Here you can set a flash or redirect the user to another page
}
return $this->render('signup', ['model'=>$model]);
}
In View
At last, in your view code it's better to use Yii2 internal functions for Html buttons
<?= Html::submitButton('Signup', ['class' => 'btn btn-primary']) ?>
Of course don't forget to use yii\helpers\Html
use yii\helpers\Html;
Related
I'm creating a website using Yii2. There is a model called Contact Form like this:
class ContactForm extends Model
{
public $name;
public $email;
public $subject;
public $body;
public $response;
public $response2;
public function rules()
{
return [
[['name', 'email', 'subject', 'body'], 'required'],
['email', 'email'],
['response', function ($attribute, $params)
{
$this->addError($attribute, 'Wrong response 1');
}, 'skipOnError' => false],
['response2', 'validresponse2'],
];
}
public function validresponse2($attribute, $params) {
$this->addError($attribute, 'Wrong response 2');
}
}
There are 2 attributes (response and response2) which have custom validation.
Both custom validation are not working. They didn't pop up an error message at all. When I put echo "asd; die(); inside those functions, They didn't die too.
Is my rule setting wrong? I've searched through other questions but no luck.
UPDATE
Here is how I validate the form in my controller
$model = new ContactForm();
$model->subject = "New Message";
if (isset($_POST['ContactForm'])) {
$model->attributes = $_POST['ContactForm'];
$model->response = "";
if ($model->validate()) {
echo "validated successfully";
die();
}
}
And it is always validated successfully
Instead of skipOnError you need to set skipOnEmpty to false because otherwise empty attribute does not trigger validation.
skipOnError is true by default and means that in case of validation error in one rule the rest of rules for this attribute are skipped.
I have a model and validation rules for it:
class User extends ActiveRecord implements IdentityInterface
{
...
public function rules()
{
return [
[['username', 'password', 'email'], 'required', 'on' => 'insert'],
[['password', 'email'], 'required', 'on' => 'update'],
]
}
Actually the code produces no validators. When I remove 'on' section, everything goes well.
Digging in official documentation and search thru The Web didn't help me to understand what is the issue, and why can't I have custom required fields sets for different actions.
The Scenario is not automaticaly setted by Yii2 ActiveReccoed. If you need a specific scenario you must create it and assign
E.g. for update ...
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['update'] = ['password', 'email'];//Scenario Values Only Accepted
return $scenarios;
}
Also you can set scenario in your actionUpdate
public function actionUpdate($id)
{
$model = $this->findModel($id);
$model->scenario = 'update';
........
}
I'm trying to allow a user to create 2 models in one form. Post has a one to one relationship with Story.
My code looks something like this :
public function actionCreate()
{
post = new post();
story = new story();
if (isset($_POST['post']))
{
$post->attributes = $_POST['post'];
$story->attributes = $_POST['story'];
// force the views to show errors for both $post and $story
$post->validate();
// this one will always fail because the required foreign key field is not set until the post is saved.
$story->validate();
if ($post->save())
{
$story->post_id = $post->id;
$story->save();
}
}
}
I need to call validate so that the view shows errors on all fields, however since the model is just being created, post doesn't have an id yet so I can't assign it to story. This means validate for story will always fail.
Is there a way I can validate the model when it's still new without throwing away the required rule for the foreign key.
you can do it with a "scenario" in which indicate when to use it and what fields does .. for example ..
<?php
class User extends Model
{
public $name;
public $email;
public $password;
public function rules(){
return [
[['name','email','password'],'required'],
['email','email'],
[['name', 'email', 'password'], 'required', 'on' => 'register'],
];
}
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['login'] = ['name','password'];//Scenario Values Only Accepted
return $scenarios;
}
}
?>
<?php
...
class UserController extends Controller
{
..
// APPLY SCENARIOS
// scenario is set as a property
............
public function actionLogin(){
$model = new User;
$model->scenario = 'login';
.............
}
// scenario is set through configuration
public function actionRegister(){
$model = new User(['scenario' => 'register']);
..............
}
}
?>
In this sample you ca use two scenario 'login' validate two field 'register' validate three..
see this doc for moore sample and thisi from Yii
I'm having radio button with two value i.e, Individual and Firm.
I am looking for one scenario where if radio button having value Firm is selected, then CompanyName textinput should act as mandatory (required) field. And, when radio button having value Individual is selected, Then CompanyName textinput should act as Optional field.
I was not getting how to do it. I tried to add addAttribute in CompanyName textinput as mandatory. but it didn't worked as RegisterForm.php (model) is having few rules specified.
So, any idea how to do it. I'm not getting. Any help ?
register.php (view)
<?php $form = ActiveForm::begin(['id' => 'register-form']); ?>
.
.
.
<?= $form->field($model, 'AdminType')
->radioList(array('Individual'=>'An Individual', 'Firm'=>'Firm/Company/Group of Employees'))
->label('Are You')?>
<?= $form->field($model, 'CompanyName')->textInput()->label('Company Name') ?>
<div class="form-group">
<?= Html::submitButton('Register', ['class' => 'btn btn-success', 'name' => 'register-button' ]) ?>
</div>
<?php ActiveForm::end(); ?>
<script>
$('input[type="radio"]').click(function()
{
if($(this).attr("value")=="Firm")
{
$('#registerform-companyname').addAttribute("required");
}
});
</script>
RegisterForm.php (model)
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use kartik\password\StrengthValidator;
class RegisterForm extends Model
{
public $fname;
public $lname;
public $email;
public $password;
public $confirmPassword;
public $AdminType;
public $CompanyName;
public $verifyCode;
public function rules()
{
return [
[['fname','lname', 'email', 'password','confirmPassword','verifyCode','AdminType'], 'required'],
['email', 'email'],
['confirmPassword', 'compare', 'compareAttribute' => 'password'],
['verifyCode', 'captcha'],
];
}
First of all, having any kind of front end validation is BAD, as i can circumvent it by generating a post programmatically, and it will save the record without any problems, making a possibility of inconsistent records a reality.
What you want to do instead is create a custom validation rule function in your model, as well as add the rule to the validation array:
<?php
namespace app\models;
use Yii;
use yii\base\Model;
use kartik\password\StrengthValidator;
class RegisterForm extends Model
{
public $fname;
public $lname;
public $email;
public $password;
public $confirmPassword;
public $AdminType;
public $CompanyName;
public $verifyCode;
public function rules()
{
return [
[['fname','lname', 'email', 'password','confirmPassword','verifyCode','AdminType'], 'required'],
['email', 'email'],
['confirmPassword', 'compare', 'compareAttribute' => 'password'],
['verifyCode', 'captcha'],
//add rule that uses the validator function
['AdminType','radioValidator'],
];
}
//implement the validator
public function radioValidator($attribute, $params)
{
if($this->$attribute === 'Firm' && empty($this->$attribute))
$this->addError('CompanyName', 'CompanyName cannot be blank');
}
}
?>
Now your field generating code should look like this
<?= $form->field($model, 'CompanyName')->textInput()->label('Company Name')->error() ?>
Hope this helps you
Edit: As I am used to working with AR classes(which generally, when generated with gii, have validation automatically ), it did not cross my mind that you are using just a form model ( the one that was given as an example in the basic app)
forget the ->error() in the field, also make sure you have the row
if ($model->load(Yii::$app->request->post()) && $model->validate()) {...}
in your action
At last I Got,
['company_name', 'required', 'when' => function($model){
return ($model->user_type == 'Firm' ? true : false);
}, 'whenClient' => "function (attribute, value) {
return $('input[type=\"radio\"][name=\"Users[user_type]\"]:checked').val() == 'Firm';
}"],
I'm using Yii MongoDbSuite.
I need to create form field for password repeat validation, which shouldn't appear in MongoDB.
Because of I'm using yii forms, all fields which i want to add in view i should declare as public in my model. But my model extend MongoDocument, so all decleared public fields after save() appear in MongoDb. How can i declare field, wich would appear in model, but doesn't appear in Mongo.
In a CActiveRecord model, you can add a property that is not a database column. And it will be treated almost the same as other database backed properties.
public $new_password;
public $new_password_confirm;
...
return array(
array('E_NAME, E_EMAIL, new_password, new_password_confirm', 'required'),
array('new_password', 'compare', 'compareAttribute'=>'new_password_confirm'),
...
<div class="row">
<?php echo $form->labelEx($model, 'new_password'); ?>
<?php echo $form->passwordField($model, 'new_password'); ?>
<?php echo $form->error($model,'new_password'); ?>
</div>
<div class="row">
<?php echo $form->labelEx($model, 'new_password_confirm'); ?>
<?php echo $form->passwordField($model, 'new_password_confirm'); ?>
<?php echo $form->error($model,'new_password_confirm'); ?>
</div>
Also a CFormModel model is a model that has no database backed properties.
Unfortunately, due to the way YiiMongoDBSuite works, Nikos' answer won't work completely.
YiiMongoDBSuite saves in a weird way to accomodate the schemaless design of MongoDB by reflecting all public attributes into database attributes.
It starts (in the update function) calling toArray: https://github.com/canni/YiiMongoDbSuite/blob/master/EMongoDocument.php#L593 and then finishs in _toArrray with setting all public attributes as databse properties: https://github.com/canni/YiiMongoDbSuite/blob/master/EMongoEmbeddedDocument.php#L304.
What you can do is set some protected attributes in your model. This should make them not go to the database and be saved, i.e.
class MyAwesomeModel{
public $somedbvar;
protected $somenotdbvar;
}
You can then use that protected property the same way that Nikos' shows.
To improve on #Sammaye 's answer, it is correct, YMDS does assign attributes from class variables.
Although this is not the problem here. The problem here imo, is lazy coding.
Example on how I would tackle the problem
User Model (based on the suit's example)
class User extends EMongoDocument
{
public $username;
public $email;
public $password;
public function getCollectionName()
{
return 'users';
}
public function rules() {
return array(
array('username, email, password', 'required'),
);
}
public function attributeLabels()
{
return array(
'username' => 'UserName',
'email' => 'Email',
'password' => 'Password',
);
}
public static function model($className = __CLASS__)
{
return parent::model($className);
}
public function getForm()
{
$form = new UserForm();
$form->userid = $this->primaryKey();
return $form;
}
}
Form Model :
<?php
class UserForm extends CFormModel
{
public $userid; //something to refer back to your User model on save, you might need this as a hidden field
public $username;
public $email;
public $password;
public $password_again;
public function rules()
{
return array(
array('username, email, password, password_again', 'required'),
array('password', 'compare', 'compareAttribute'=>'password_again', 'strict'=>true),
);
}
public function attributeLabels()
{
return array(
'password_again'=>'Re-type Password',
);
}
public function save()
{
if($this->validate())
{
$user = User::findByPk($this->userid);
$user->username = $this->username;
$user->email = $this->email;
$user->password = hash('sha512',$this->password);
if($user->save())
{
//do stuff
}
}
}
}
And in the view:
<?php
$model = $model->form;
$form = $this->beginWidget('CActiveForm', array(
'id'=>'user-form',
'enableAjaxValidation'=>true,
'enableClientValidation'=>true,
'focus'=>array($model,'firstName'),
));
Ofcourse this is just an example on how to solve your problem without running into problem after problem
(a lazy way that would work btw, is simply declaring an attribute unsafe and never it would never be saved)