Yii2 unique validator join error - yii2

I have a user model. When I save the user I want to make sure the username is unique, so I have a rule [['username'], 'unique']. Users belong to offices, which belong to practices, which belong to organizations. Therefore I have the following queries:
class UserQuery extends \yii\db\ActiveQuery
{
public function init()
{
$this->joinWith('office');
parent::init();
}
}
class OfficeQuery extends \yii\db\ActiveQuery
{
public function init()
{
$this->joinWith('practice');
parent::init();
}
}
class PracticeQuery extends \yii\db\ActiveQuery
{
public function init()
{
$this->joinWith('org');
if (!Yii::$app->user->can('Corporate') and isset(Yii::$app->session['practice_id'])) $this->andWhere(['practice.id'=>Yii::$app->session['practice_id']]);
parent::init();
}
}
class OrgQuery extends \yii\db\ActiveQuery
{
public function init()
{
if (!Yii::$app->user->can('Xpress') and isset(Yii::$app->session['org_id'])) $this->andWhere(['org.id'=>Yii::$app->session['org_id']]);
parent::init();
}
}
However, when I try to save a User model, I get an undefined index exception on office_id in ActiveRelationTrait at line 494. I do have an office_id value in the User model.
Any ideas on what is going on?
rules from User model
public function rules()
{
return [
[['gender', 'phone1_type', 'phone2_type','birth_date'], 'string'],
[['gender','phone1_type','phone2_type'],'default'],
[['opt_out_date', 'last_login', 'created_at', 'updated_at'], 'safe'],
[['bounce_count', 'status_id', 'sort', 'image_id', 'type_id', 'office_id', 'practice_id', 'org_id'], 'integer'],
[['lastname', 'firstname', 'auth_key'], 'string', 'max' => 32],
[['nickname', 'middlename', 'phone1', 'phone2'], 'string', 'max' => 16],
[['username', 'password_hash', 'addr1', 'addr2', 'position', 'permission'], 'string', 'max' => 64],
[['prefix'], 'string', 'max' => 8],
[['suffix'], 'string', 'max' => 24],
[['email', 'work', 'work_url'], 'string', 'max' => 255],
[['opt_out_reason', 'work_title'], 'string', 'max' => 128],
[['zipcode'], 'string', 'max' => 10],
[['username'], 'unique'],
[['username','phone1_type','phone2_type'],'default'],
[['email'],'required', 'on'=>'newUser'],
[['org_id', 'practice_id', 'office_id', 'permission', 'email','username', 'position'],'required', 'on'=>'newUser'],
['userImages','safe'],
['tab','string'],
['relationship','integer'],
['responsible','boolean'],
[['zipcode'], 'exist', 'skipOnError' => true, 'targetClass' => \app\models\Zip::className(), 'targetAttribute' => ['zipcode' => 'zipcode']],
[['lastname','firstname'],'required'],
[['responsible','gender','birth_date','zipcode'],'required','on'=>['patient']],
['relationship','required','on'=>['patientParty']],
];
}
and before/after save actions
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
foreach(self::$dateFields as $date) if ($this->$date) $this->$date = date('Y-m-d',strtotime($this->$date)); else $this->$date = null;
if (!$this->zipcode) $this->zipcode = null;
if ($this->password) {
$this->password_hash = Yii::$app->security->generatePasswordHash($this->password);
$this->auth_key = Yii::$app->security->generateRandomString();
}
if (!$this->image_id and $this->images) $this->image_id = $this->images[0]->id;
return true;
} else {
return false;
}
}
public function afterSave($insert, $changedAttributes)
{
$auth = Yii::$app->authManager;
$auth->revokeAll($this->id);
$role = $auth->getRole($this->permission);
if ($role) $auth->assign($role,$this->id);
parent::afterSave($insert, $changedAttributes);
}

You can define a property (variable) in a model
public $office_id;

Related

Yii2 MultilingualBehavior throws getPrimaryKey error

I want to save multilingual record in my table but some error occurs - Call to a member function getPrimaryKey() on string. I am using multilingual behavior
and it is not for first time. Made two tables system_information and system_informationLang. This is my model:
<?php
namespace app\models;
use backend\models\CActiveRecord;
use Yii;
use omgdef\multilingual\MultilingualBehavior;
use omgdef\multilingual\MultilingualQuery;
/**
* This is the model class for table "system_information".
*
* #property int $id
* #property int $city_id
*
* #property SystemInformationlang[] $systemInformationlangs
*/
class SystemInformation extends CActiveRecord
{
public static function find()
{
return new MultilingualQuery(get_called_class());
}
public function behaviors()
{
$allLanguages = [];
foreach (Yii::$app->params['languages'] as $title => $language) {
$allLanguages[$title] = $language;
}
return [
'ml' => [
'class' => MultilingualBehavior::className(),
'languages' => $allLanguages,
//'languageField' => 'language',
//'localizedPrefix' => '',
//'requireTranslations' => false',
//'dynamicLangClass' => true',
//'langClassName' => PostLang::className(), // or namespace/for/a/class/PostLang
'defaultLanguage' => Yii::$app->params['languageDefault'],
'langForeignKey' => 'system_id',
'tableName' => "{{%system_informationlang}}",
'attributes' => [
'name',
'email',
'phone',
'address',
'facebook',
'instagram',
'google',
'linkin',
'fax',
'owner',
'latitude',
'longitude'
]
],
];
}
/**
* #inheritdoc
*/
public static function tableName()
{
return 'system_information';
}
/**
* #inheritdoc
*/
public function rules()
{
$string_255_lang = $this->multilingualFields([
'name',
'email',
'phone',
'address',
'facebook',
'instagram',
'google',
'linkin',
'fax',
'owner',
'latitude',
'longitude'
]);
$string_255 = [
'name',
'email',
'phone',
'address',
'facebook',
'instagram',
'google',
'linkin',
'fax',
'owner',
'latitude',
'longitude'
];
$require = ['name', 'phone', 'email', 'owner', 'address'];
$email_lang = $this->multilingualFields(['email']);
$email = ['email'];
return [
[$require, 'required'],
[$string_255, 'string', 'max' => 255],
[$string_255_lang, 'string', 'max' => 255],
[['city_id'], 'integer'],
[$email_lang, 'email'],
['email', 'email'],
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'city_id' => Yii::t('app', 'City ID'),
];
}
}
This is custom function for language fields creating :
protected function multilingualFields($fields){
$output = [];
foreach ($fields as $field){
foreach (Yii::$app->params['languages'] as $language) {
if(Yii::$app->params['languageDefault'] != $language){
$output[] = "{$field}_{$language}";
}
}
}
return $output;
}
And finally my controller:
public function actionCreate()
{
$model = new SystemInformation();
if ($model->load(Yii::$app->request->post())) {
$model->multilingualLoad($model, [
'name',
'email',
'phone',
'address',
'facebook',
'instagram',
'google',
'linkin',
'fax',
'owner',
'latitude',
'longitude'
]);
var_dump($model->save());
var_dump($model->getErrors());die;
if($model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}else{
Yii::$app->session->setFlash('error', Yii::t('app', 'Something went wrong. Please, try again later!'));
return $this->render('create', [
'model' => $model,
]);
}
}
return $this->render('create', [
'model' => $model,
]);
}
multilingualLoad is also a custom function for filling the lingual fields:
public function multilingualLoad($model, $props = []){
$model_name = explode('\\', get_class($model));
$model_name = end($model_name);
foreach (Yii::$app->params['languages'] as $language){
if(Yii::$app->params['languageDefault'] != $language){
foreach ($props as $property){
$prop_lang = "{$property}_{$language}";
$model->$prop_lang = Yii::$app->request->post($model_name)["{$property}_{$language}"];
}
}
}
}
I got a picture of the Yii2 error. I guess it is searching for the lang table but somehow $owner gets value of a string.
Thank you in advance!

yii2 custom validation addError message doesn't show

when i use custom validations on yii2 dynamic forms it doesn't show any error messages below the input field.Below I have posted my model.
It doesn't show any error messges when qty field gets validated
namespace frontend\models;
use Yii;
class OrderD extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'order_d';
}
public function rules()
{
return [
[['item_id', 'qty', 'price', 'value'], 'required'],
[['item_id'], 'integer'],
[['price', 'value'], 'number'],
[['order_code'], 'string', 'max' => 10],
[['item_id'], 'exist', 'skipOnError' => true, 'targetClass' => Item::className(), 'targetAttribute' => ['item_id' => 'id']],
[['order_code'], 'exist', 'skipOnError' => true, 'targetClass' => OrderH::className(), 'targetAttribute' => ['order_code' => 'code']],
['qty', 'validateQty']
];
}
public function validateQty($attribute)
{
$qty = $this->$attribute;
if ($qty >= 5)
{
$this->addError('qty', "qty validation successful");
}
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'item_id' => 'Item ID',
'order_code' => 'Order Code',
'qty' => 'Qty',
'price' => 'Price',
'value' => 'Value',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getItem()
{
return $this->hasOne(Item::className(), ['id' => 'item_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getOrderCode()
{
return $this->hasOne(OrderH::className(), ['code' => 'order_code']);
}
}
Be sure to know custom validations are php functions and not convert as javascript to validate in runtime .. those will work after the page has submit and send to controller ..
here is simple sample you want :
['qty','custom_function_validation'],
];
}
public function custom_function_validation($attribute, $params){
if($this->$attribute>5){
$this->addError($attribute,'it\'s more than 5');
}
}
To create a validator that supports client-side validation, you should implement the yii\validators\Validator::clientValidateAttribute() method which returns a piece of JavaScript code that performs the validation on the client-side.
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
// your validation
JS;
}
See the documentation here: http://www.yiiframework.com/doc-2.0/guide-input-validation.html#implementing-client-side-validation
Use method addError() in a controller and then just render your view file
Example
if ($promo_code) {
if ($promo_code->status == '1') {
$message = 'This combination is incorrect.';
$model->addError('promo_code', $message);
return $this->render('index', compact('model'));
}
$promo_code->status = '1';
$promo_code->save();
$model->save();
}

Yii2 How to auto register new user without using site/signup

I want to signup new user every time I add a new client. I do not want to manually register a new user using the web/site/signup form via the browser.
I am trying to auto register new users from ClientController and I am having no luck at all. No error returned, it is as if the code doesn't run at all.
I can easily register the same user through site controller at site/signup but can not at all auto register through another controller by calling SignupForm model. Please help
I have this in my client controller
namespace frontend\controllers;
use Yii;
use yii\web\Controller;
use yii\helpers\Html;
use frontend\models\Client;
use frontend\models\SignupForm;
class ClientController extends Controller
{
public function behaviors()
{
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
],
],
];
}
public function actionCreate()
{
$model = new Client;
if ($model->load(Yii::$app->request->post()))
{
$model->save();
$useremail = $model->Email;
$signupForm = new SignupForm();
$signupForm->username = $useremail;
$signupForm->password = $useremail;
$signupForm->email = $useremail;
$signupForm->created_at = time();
$signupForm->picture = null;
$signupForm->created_by = Yii::$app->session->get('USER_ID');
$NewUser = $signupForm->signup();
print_r($NewUser);
exit;
}
else{
return $this->render('create', [
'model' => $model
]);
}
}
}
and this is my SignupForm code:
<?php
namespace frontend\models;
use common\models\User;
use yii\base\Model;
use Yii;
/**
* Signup form
*/
class SignupForm extends Model
{
public $username;
public $email;
public $password;
public $picture;
public $created_by;
/**
* #inheritdoc
*/
public function rules()
{
return [
['username', 'filter', 'filter' => 'trim'],
['username', 'required'],
['username', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This username has already been taken.'],
['username', 'string', 'min' => 2, 'max' => 255],
['email', 'filter', 'filter' => 'trim'],
['email', 'required'],
['email', 'email'],
['email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'],
['password', 'required'],
['password', 'string', 'min' => 6],
[['picture'], 'string'],
[['picture'],'default', 'value'=>null],
[['created_by'], 'integer'],
[['created_by'], 'default', 'value'=>Yii::$app->session->get('USER_ID')],
];
}
/**
* Signs user up.
*
* #return User|null the saved model or null if saving fails
*/
public function signup()
{
$this->created_by = Yii::$app->session->get('USER_ID');
if ($this->validate()) {
$user = new User();
$user->username = $this->username;
$user->email = $this->email;
$user->setPassword($this->password);
$user->generateAuthKey();
$user->created_by = $this->created_by;
$user->picture = $this->picture;
//Save user
$user->save(false);
return $user;
}
return null;
}
}

yii2 add search widget grid view

**how can i add girdView search to my relation table column ?
i have all girlview search box except my relation table
(developersActivity.developer_point) and ('developersActivity.project_done')
i have thier value but widget search box not apear above them**
my controller
$searchModel = new DeveloperSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('dashboard', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel
]);
my search model
class DeveloperSearch extends Developers
{
public function rules()
{
return parent::rules();
}
public function scenarios()
{
return Model::scenarios();
}
public function search($param)
{
$query = Developers::find();
$dataProvider = new ActiveDataProvider([
'query' => $query
]);
$this->load($param);
$query->joinWith('developersActivity');
$dataProvider->setSort([
'attributes'=> [
'name',
'developersActivity.developer_point'=>[
'asc'=>['developer_point'=>SORT_ASC],
'desc'=>['developer_point'=>SORT_DESC],
]
]
]);
$query->andFilterWhere([
'developer_id' => $this->developer_id,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'phone', $this->phone])
->andFilterWhere(['like', 'email', $this->email]);
return $dataProvider;
}
}
the model
class Developers extends ActiveRecord
{
public static function tableName()
{
return 'developers';
}
public function rules()
{
return [
[['name',
'family',
'phone',
'email',
'address',
'brithday',
'age',
'ability',
'role',
'join_date',
], 'required'],
[['developer_id'], 'integer'],
['email','email'],
[['phone'],'integer', 'min' => 10],
[['address'], 'string', 'max' => 100],
[['name'], 'string', 'min' => 3],
];
}
public function getDevelopersActivity(){
return $this->hasOne(DevelopersActivity::className(),['developer_activity_id' => 'developer_id']);
}
}
and developersActivity model class
class DevelopersActivity extends ActiveRecord
{
public function rules()
{
return [
[['developer_activity_id',
'developer_point',
'project_done',
'free_rate',
'address',
'estimate_for_next_project',
'date_project_done',
'number_of_project',
'activity_rate',
], 'safe'],
];
}
}
here is the view
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'developer_id',
'name',
'email',
'phone',
'email',
'developersActivity.developer_point',
'developersActivity.project_done'
// 'value'=>'developersActivity.point',
//'contentOptions'=>['style'=>'width: 120px;']
],
]);
?>
in your model add a getter for the field (assuming the field is named actvityname)
/* Getter for deleveloer activity name */
public function getDevelopersActivityName() {
return $this->developersActivity->activityname;
}
in your searchModel
add a public var for your related field and declare as safe in rules
/* your calculated attribute */
public $activityName;
/* setup rules */
public function rules() {
return [
/* your other rules */
[['activityName'], 'safe']
];
}
and in the filter you can add the filter for your field
// filter by developer activity
$query->joinWith(['developersActivity' => function ($q) {
$q->where('yor_develeoper_activity_table.your_developer_activity_column LIKE "%' . $this->activityName. '%"');
}]);
return $dataProvider;
in gridview you can refer directly using
'columns' => [
['class' => 'yii\grid\SerialColumn'],
'id',
'fullName',
'activityName',
['class' => 'yii\grid\ActionColumn'],
]
you can take a look ad this tutorial for some suggestions http://www.yiiframework.com/wiki/621/filter-sort-by-calculated-related-fields-in-gridview-yii-2-0/

Unable to save model attribute as null?

Here is my model rules
public function rules() {
return [
[['header_image', 'profil_picture'], 'default', 'value' => null],
[['yahoo_id', 'whats_app_id', 'bbm_id'], 'string', 'max' => 20],
[['bbm_id'], 'match', 'pattern' => '/^[a-zA-Z0-9]+$/', 'message' => 'Alpha numeric only'],
[['header_image_file', 'profil_picture_file'], 'file', 'skipOnEmpty' => true, 'extensions' => 'png, jpeg, jpg, bmp', 'maxSize' => 10240 * 20240 * 2],
[['deskripsi_toko'], 'string', 'max' => 300],
[['agent_id', 'nama_toko', 'tag_line', 'header_image', 'profil_picture', 'yahoo_id', 'whats_app_id', 'bbm_id', 'deskripsi_toko'], 'filter', 'filter' => function($value) {
return BeoHelper::replace_mc($value);
}],
];
}
Here is my controller
$storeSetting->header_image = null;
if($storeSetting->save()){
return $this->redirect(Yii::$app->request->referrer);
}
replace_mc function
public static function replace_mc($str)
{
$new_data = preg_replace('/[^A-Za-z0-9\-\ \.\:\#\+]/', '', $str);
return $new_data;
}
Record save successfully, but header_image is empty string instead of null?
It should set header_image as null, Where I'm doing wrong?
Thanks in advance.
Try with additional condition in replace_mc():
public static function replace_mc($str)
{
return $str === null
? $str
: preg_replace('/[^A-Za-z0-9\-\ \.\:\#\+]/', '', $str);
}