I have a table "attendance" and when I perform create action on it ,it works properly like the screen-shot below,
In this form I have used jquery to disable form field 'time in' and 'time out' if the status value is "Absent" or "Leave".
my jquery code is below
<script>
$(document).ready(function() {
$("#status_id").on('change', function()
{
if($("#status_id").val()=='Absent' || $("#status_id").val()=='Leave')
{
$("#attendance-time_in").val('');
$('#attendance-time_in').attr('disabled', true);
$("#attendance-time_out").val('');
$('#attendance-time_out').attr('disabled', true);
}
else {
$("#attendance-time_in").val();
$('#attendance-time_in').attr('disabled', false);
$("#attendance-time_out").val();
$('#attendance-time_out').attr('disabled', false);
}
});
});
</script>
and _form.php code is
<?= $form->field($model, 'status', ['inputOptions' => ['class' => 'form-control','id' => 'status_id']])
->dropDownList([ 'Present' => 'Present', 'Absent' => 'Absent', 'Leave' => 'Leave',], ['prompt' => 'Select status']); ?>
<?=
$form->field($model, 'time_in')->widget(TimePicker::classname(), [
'name' => 'time_in',
'options'=>[
'id'=>'attendance-time_in',
],
]);
?>
<?=
$form->field($model, 'time_out')->widget(TimePicker::classname(), [
'name' => 'time_out',
'options'=>[
'id'=>'attendance-time_out',
],
]);
?>
its works fine for create action but when i update any data
like if i update data of arsalan khalid ,and i put absent instead of present the jquery works properly but when i click on update button it does not update the 'time in' and 'time out' attributes while all other attributes gets updated.for 'time in' and 'time out' it takes the previous value that it have i.e 9:40am and 5:52pm but i want it should have null values as i have selected "Absent" in status.
AttendanceController Update Action
public function actionUpdate($id)
{
$model = $this->findModel($id);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
Attendance models Rules
public function rules()
{
return [
[[ 'staff_id'], 'integer'],
[['daytime'], 'safe'],
[['staff_id'], 'required'],
[['status','time_in', 'time_out'], 'string', 'max' => 45]
];
}
i am new in yii2.
Many thanks in advance.
What you need to do is create another rule inside your Attendance model.
[['time_in', 'time_out'], 'filter', 'filter' => function ($value) {
if($this->status_id == AbsentStatus->status_id)
{
return null;
}
return $value;
}],
This way, if the status_id is equal to the AbsentStatus, you set the value of time_in and time_out as null.
Edit: you should substitute 'AbsentStatus->status_id' in the condition to the value of the id of your absent status.
I think one problem is the html id name fo the fields
In Yii2 the html id of an input field inside an active form is, by default, formed automatically in the format 'classname-fieldname' (all lowercase).
eg: namimg the model class = "Customer" the filed name is like
<input type="text" id="customer-name" class="form-control" name="Customer[name]" maxlength="16">
Then your field id should named 'attendance-status_id' , 'attendance-timein_id', and so on...
Try using this format for the html id
And second, just for test, modify your controller this way
if ($model->load(Yii::$app->request->post()) && $model->save(false)) {
adding false to save() in saving process are not involved the validation rules.
Related
Yii2 Gridiview filter not working properly. Selecting one filter have impact on other filters. Changing one filter (dropdown) auto-select the values of other filters (dropdowns). This problem also exists in URL as well, changing one filter appends the other filters in URL as well and result shown as combined. but in reality only one filter should be applied which is being changed.
// Search Model, adding dummy table names
public function search($params)
{
$query = Model::find()->with('model_b');
if (empty($params['sort'])) {
$query->orderBy("group, " . Model::getSortByType() . ', "title"');
}
$dataProvider = new ActiveDataProvider([
'query' => $query,
'sort' => [
'attributes' => [
'code',
'title',
'updated_at'
]
]
]);
$this->load($params);
if (!$this->validate()) {
// uncomment the following line if you do not want to return any records when validation fails
// $query->where('0=1');
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'type' => $this->type,
'price_type' => $this->price_type,
'status' => $this->status,
'terms_related' => $this->terms_related,
'required' => $this->required,
'group' => $this->group,
]);
$query->andFilterWhere(['ilike', 'title', $this->title]);
$query->andFilterWhere(['is_qr' => $this->is_qr]);
return $dataProvider;
}
//Controller
public function actionIndex()
{
$searchModel = new ModelSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render("index", [
"searchModel" => $searchModel,
"dataProvider" => $dataProvider,
]);
}
// In View, the filter I change
[
'attribute' => 'is_qr',
'format' => 'boolean',
'filter' => [1 => 'TRUE', 0 => 'FALSE'],
'content' => function ($service) { return ((int) $service->is_qr === 1) ? 'TRUE' : 'FALSE'; }
],
// the filter being changed with above filter
[
'attribute' => 'terms_related',
'filter' => array(0 => 'FALSE', 1 => 'TRUE'),
'content' => function ($service) { return ((int) $service->terms_related === 1) ? 'TRUE' : 'FALSE'; }
]
Observations:
Consider I have 5 filters in a GridView.
Action 1: I changed a filter, only that filter is applied first time but after page reload, other filters are being populated with values with "0". Because on selecting one filter, all filters are being pushed in URL with empty values other than selected one. And filters with empty values are being applied to rest of the filters with "0" value
Problem
The problem is, once I select a filter, gridview sends all possible filters in URL. The filters I did not select, have empty values.
Yii::$app->request->queryParams
This has all filters and the filters other than I selected have empty values, and
$this->load($params);
in search() deals empty values as 0. So, filters that I have not touched are being populated with "0" value.
I have found the solution, it is a custom solution but works for me.
I created a Trait
trait ParamsTrimable
{
public function trimParams($params, $modelClass)
{
$modelClass = basename(str_replace('\\', '/', $modelClass));
if ($params[$modelClass]) {
$params[$modelClass] = array_filter($params[$modelClass], function ($value) {
return ($value !== '');
});
}
return $params;
}
}
And before
$this->load($params);
I called trait's function i.e.
$params = $this->trimParams($params, static::class);
$this->load($params);
Reason behind the trait solution is, this problem may occur in other listings as well. To fix, we only need to use trait and call the function to remove empty values from params.
I had two drop down in form. I load value for second dropdown based on first dropdown's value.
In model I specified second dropdown as normal variable but it get
input in multiple select.
I write required validation for both first and second drop down.
model
*/
public function rules()
{
return [
[['emp_id', 'role_team'], 'required'],
[['emp_id', 'role_team'], 'integer'],
[['status', 'team_status'], 'boolean'],
[['type'], 'string']
];
}
In form
<?php $form = ActiveForm::begin([
'enableClientValidation'=>true,
'enableAjaxValidation'=>true,
'formConfig' => ['labelSpan' => 4,'showErrors'=>true,'showHints'=>true],]); ?>
<?php
echo Form::widget([
'model' => $model,
'form' => $form,
'columns'=>4,
'attributes' => [
'role_team' => ['type'=>Form::INPUT_WIDGET,
'label'=>'Team',
'widgetClass'=>'\kartik\select2\Select2',
'options'=>['data'=>Teams::find()->select(['team_name'])->indexBy('team_id')->column(),'options'=>['placeholder'=>'Team Name','id'=>'team-category']]],
'emp_id' => ['type'=>Form::INPUT_WIDGET,'label'=>'Employees',
'widgetClass'=>'\kartik\select2\Select2',
'options'=>['data'=> EmpDetails::find()->select(['emp_name'])->indexBy('emp_id')->column(),'options'=>['placeholder'=>'Employee Name','multiple'=>true,'id'=>'team-sub-category']]],
]
]);
?>
It has not throw error before submitting the form.
I have ajax validation on unique on create it work fine but when I want to update i cant becouse this show message that this name is already used but i not owerwrite this name it is name from database on update click. Its does not matter whcich record i want to update it always show me message that name is already used. Whan can i do to disable message when i not change my input to name which is in base. Now it is so update action automatically filed my inputs but when i not change anythink i have this error on save
<?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data'], 'id'=>$model->formName(), 'enableAjaxValidation'=>true, 'validationUrl'=>Url::toRoute('category/validation')]) ?>
My controller:
public function actionValidation(){
$model= new SmCategory;
if(Yii::$app->request->isAjax && $model->load(Yii::$app->request->post()))
{
Yii::$app->response->format='json';
return ActiveForm::validate($model);
}
}
my rules:
public function rules()
{
return [
[['Name'], 'required'],
['Name', 'unique', 'targetClass' => 'common\models\Smcategory', 'message' => 'This name has already been taken.'],
[['Rel_Category', 'IsDeleted'], 'integer'],
[['File'],'file'],
[['Name', 'Label'], 'string', 'max' => 45],
[['Picture'], 'string', 'max' => 255]
];
}
The problem is here :
$model= new SmCategory;
This code is ok for create, not for update since it will not use the existing model for validation, it could be (just an example and assuming id is the primary key) :
public function actionValidation($id = null)
{
$model = $id===null ? new SmCategory : SmCategory::findOne($id);
if(Yii::$app->request->isAjax && $model->load(Yii::$app->request->post()))
{
Yii::$app->response->format='json';
return ActiveForm::validate($model);
}
}
And you could update validationUrl in your view :
$validationUrl = ['category/validation'];
if (!$model->isNewRecord)
$validationUrl['id'] = $model->id;
$form = ActiveForm::begin([
'options' => ['enctype' => 'multipart/form-data'],
'id' => $model->formName(),
'enableAjaxValidation' => true,
'validationUrl' => $validationUrl,
]);
I add another table field in my view , but the search button disappeared , How can I retrieve this form button ?
SaleItems = And a table mysql database.
<?php
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'formatter' => ['class' => 'yii\i18n\Formatter','nullDisplay' => ''],
'columns' => [
['class' => 'yii\grid\SerialColumn'],
**[
'attribute' => 'Data',
'content' => function(SaleItems $model, $key, $index, $column) {
return date('d/m/Y', strtotime($model->sale->date));
},
'header' => 'DATA'
],**
]); ?>
</div>
The search field disappears because in not setted properly in searchModel ..
for this you must extend the related modelSeacrh adding the relation with the related model and the filter for the field you need ..
for this you must set in the base mode, the relation
public function getSale()
{
return $this->hasOne(Sale::className(), ['id' => 'sale_id']);
}
/* Getter for sale data */
public function getSaleData() {
return $this->sale->data;
}
then setting up the search model declaring
/* your calculated attribute */
public $date;
/* setup rules */
public function rules() {
return [
/* your other rules */
[['data'], 'safe']
];
}
and extending
public function search($params) {
adding proper sort, for data
$dataProvider->setSort([
'attributes' => [
.....
'data' => [
'asc' => ['tbl_sale.data' => SORT_ASC],
'desc' => ['tbl_sale.data' => SORT_DESC],
'label' => 'Data'
]
]
]);
proper relation
if (!($this->load($params) && $this->validate())) {
/**
* The following line will allow eager loading with country data
* to enable sorting by country on initial loading of the grid.
*/
$query->joinWith(['sale']);
return $dataProvider;
}
and proper filter condition
// filter by sale data
$query->joinWith(['sale' => function ($q) {
$q->where('sale.data = ' . $this->data . ' ');
}]);
Once you do al this the searchModel contain the information for filter and the search field is showed in gridview
What in this answer is just a list of suggestion
you can find detailed tutorial in this doc (see the scenario 2 and adapt to your need)
http://www.yiiframework.com/wiki/621/filter-sort-by-calculated-related-fields-in-gridview-yii-2-0/
I'm a beginner of Yii, that's why can't figure out, what's wrong.
I use the same model - Users - for login, registration and edition profile.
Users model:
class Users extends \yii\db\ActiveRecord {
public $password_repeat; //have no this value in DB, that's why write it as property directly. Need only for profile editing
public function rules() {
return [
[['username', 'password', 'authKey', 'accessToken'], 'safe'],
[['username', 'password'], 'required'],
[['username', 'password', 'authKey', 'accessToken'], 'string', 'max' => 255],
[['is_admin'], 'boolean'],
//don't use scenarios() here. Use 'on' instead
['password_repeat', 'required', 'on' => 'update'],
['password_repeat', 'compare', 'compareAttribute'=>'password', 'message'=>"Passwords don't match", 'on' => 'update' ],
];
}
}
view snippet:
<?php $form = ActiveForm::begin([
'id' => 'data-form',
'options' => ['class' => 'form-horizontal', 'enctype' => 'multipart/form-data'],
'fieldConfig' => [
'template' => "{label}\n<div class=\"col-lg-3\">{input}</div>\n<div class=\"col-lg-6\">{error}</div>",
'labelOptions' => ['class' => 'col-lg-1 control-label'],
],
]); ?>
<?= $form->field($user_data, 'username') ?>
<?= $form->field($logo_data, 'imageFile')->fileInput() ?>
<?= $form->field($user_data, 'password')->passwordInput(['value'=>''])?>
<?= $form->field($user_data, 'password_repeat')->passwordInput(['value'=>''])?>
<?= Html::submitButton('Zapisz', ['class' => 'btn btn-primary btn-block', 'name' => 'data-button']) ?>
<?php ActiveForm::end(); ?>
Site controller action:
public function actionProfile(){
//3 model instances to retrieve data from users && company_profiles and logo
$user_data = Users::find()->where(['id'=>Yii::$app->user->id])->one();
$company_profile_data = CompanyProfiles::find()->where(['user_id'=>Yii::$app->user->id])->one();
$logo_data = new UploadLogo();
//here I upload file through $logo_data
if (isset($_POST['Users'])){
$user_data->username = $_POST['Users']['username'];
$company_profile_data->firm_data = $_POST['CompanyProfiles']['firm_data'];
//other assignments
$user_data->scenario = 'update'; //specially for 'profile' page
if (($_POST[Users][password] !== '') && ($_POST[Users][password_repeat]) !== '' && ($_POST[Users][password] == $_POST[Users][password_repeat]))
$user_data->password_repeat = $user_data->password = md5($_POST[Users][password]); //MAYBE, problem is here?
$user_data->update();
$company_profile_data->update();
}
return $this->render('profile', ['user_data' => $user_data, 'company_profile_data' => $company_profile_data, 'logo_data' => $logo_data]);
}
I use update scenario for profile editing page. During this scenario password_repeat is required and need to be the same as password. But it doesn't work as expected.
A: When i try to submit form without entered password AND repass, I see message "can't be blank" only for password. Then I put value in ONLY pass input, and page reloads (DB table doesn't changes), and after reloading I see message "can't be blank" this time for repass field! But the most strange is that before next page reloading everything work normal. After reloading goto A; :)
If I delete scenario, everything is ok with validation. But I can't delete it, cause in this case registration fails (repass field becomes required to fill).
So, what need I to do my with pass-repass functionality to work it fine (with scenario 'update')? Where is mistake?
Got it! I just need to move this codeline:
$user_data->scenario = 'update';
from IF stmt. And now I want to make a habit to assign scenario property directly under creating model instance.