How to check current password is correct from cakephp model. I've searched a lot but not found any details information.
The solution in this post worked for me. Just don't forget to add the use Cake\Auth\DefaultPasswordHasher; at the top of your UsersTabel model.
I found something very useful for checking the current password on http://base-syst.com/password-validation-when-changing-password-in-cakephp-3/ .
Insert this in your template(change_password.ctp):
<div class="users form large-9 medium-9 columns">
<?= $this->Form->create() ?>
<fieldset>
<?= $this->Form->input('current_password', ['type' => 'password', 'label'=>'Current Password', 'value' => ''])?>
<?= $this->Form->input('password', ['type'=>'password' ,'label'=>'Password', 'value' => '']) ?>
<?= $this->Form->input('confirm_password', ['type' => 'password' , 'label'=>'Repeat password', 'value' => ''])?>
</fieldset>
<?= $this->Form->button(__('Save')) ?>
<?= $this->Form->end() ?>
Insert this in your (Users)Table, for validation:
use Cake\Auth\DefaultPasswordHasher;
use Cake\Validation\Validator;
public function validationDefault(Validator $validator )
{
$validator
->add('current_password','custom',[
'rule'=> function($value, $context){
$user = $this->get($context['data']['id']);
if ($user) {
if ((new DefaultPasswordHasher)->check($value, $user->password)) {
return true;
}
}
return false;
},
'message'=>'The old password does not match the current password!',
])
->notEmpty('current_password');
$validator
->add('password', [
'length' => [
'rule' => ['minLength', 6],
'message' => 'The password have to be at least 6 characters!',
]
])
->add('password',[
'match'=>[
'rule'=> ['compareWith','confirm_password'],
'message'=>'The passwords does not match!',
]
])
->notEmpty('password');
$validator
->add('confirm_password', [
'length' => [
'rule' => ['minLength', 6],
'message' => 'The password have to be at least 6 characters!',
]
])
->add('confirm_password',[
'match'=>[
'rule'=> ['compareWith','password'],
'message'=>'The passwords does not match!',
]
])
->notEmpty('confirm_password');
return $validator;
}
Insert this in your (Users)Controller:
public function change_password() {
$user = $this->Users->get($this->Auth->user('id'));
if ($this->request->is(['patch', 'post', 'put'])) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
$this->set(compact('user'));
$this->set('_serialize', ['user']);
}
Related
I have these Model Classes
Model:Subject
public function attributeLabels()
{
return [
'subject_id' =>Yii::t('app', 'ID'),
'subject_title' => Yii::t('app', 'Subject Title'),
];
}
Model:Grouping
public function attributeLabels()
{
return [
'grouping_id' => Yii::t('app', 'Grouping ID'),
'grouping_name' => Yii::t('app', 'Grouping Name'),
];
}
Model:SubjectGrouping
public function attributeLabels()
{
return [
'subject_grouping_id' => 'Subject Grouping ID',
'subject_grouping_grouping_id' => 'Subject Grouping Grouping ID',
'subject_grouping_subject_id' => 'Subject Grouping Subject ID',
];
}
Model:Exam
public function attributeLabels()
{
return [
'exam_id' =>Yii::t('app', 'ID'),
'exam_name' => Yii::t('app', 'Exam Name'),
'exam_group_id' => Yii::t('app', 'Exam Group'),
];
}
Model:SubjectGrouping has Foreign Keys from Model: Grouping (subject_grouping_grouping_id) and Model:Subject (subject_grouping_subject_id)
In Model:Exam, exam_group_id relates to subject_grouping_grouping_id in Model:SubjectGrouping.
Controller
public function actionCreate()
{
$model = new Exam();
if (Yii::$app->request->isAjax && $model->load(Yii::$app->request->post())) {
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return ActiveForm::validate($model);
}
if ($model->load(Yii::$app->request->post())) {
$model->attributes = $_POST['Exam'];
$model->save();
return $this->redirect(['index']);
} else {
return $this->render(
'create', [
'model' => $model
]
);
}
}
View
<div class="col-xs-12 col-sm-4 col-lg-4">
<?=$form->field($model, 'exam_name')->textInput(['maxlength' => 50, 'placeholder' => 'Enter Exam Name'])?>
</div>
<div class="col-xs-12 col-sm-4 col-lg-4">
<?=$form->field($model, 'exam_group_id')->widget(
Select2::classname(), [
'data' => ArrayHelper::map(app\models\Grouping::find()->all(), 'grouping_id', 'grouping_name'),
'language' => 'en',
'options' => ['placeholder' => '--- Select Grouping ---'],
'pluginOptions' => [
'allowClear' => true
]
]
);
?>
</div>
From the diagram, how do I achieve these?
when Dropdownlist for exam_group_id is onChange the list of subjects with respect to grouping is displayed using the relationship between Model: SubjectGrouping and Model: Subject.
Others fields such as exam_name (textInput) and exam_group_id (Dropdownlist) should be save into the Model: Exam. But the subject list should not be saved. It is only for display purpose.
How do I achieve these?
I am using kartik\widgets\Select2 for the dropdownlist
I have done similar code by Kartik extension for state and country data.
here is the code for that.
<?php
//in view use this
use kartik\widgets\DateTimePicker; // or kartik\select2\Select2
use kartik\widgets\DepDrop;
use kartik\widgets\Select2;
?>
<?php
echo
$form->field($model, 'country_id')->widget(
Select2::className(),
[
'data' => \yii\helpers\ArrayHelper::map(common\models\Country::find()->all(), 'id', 'name'),
'options' => [
'id' => 'country_id',
'prompt' => Yii::t('aspns', 'Select')
]
]
);
?>
<?php
echo
$form->field($model, 'state_id')->widget(
DepDrop::classname(),
[
'data' => !empty($model->state_id) ? \yii\helpers\ArrayHelper::map(common\models\State::find()->where(['country_id' => $model->country_id])->all(), 'id', 'name') : [],
'options' => ['id' => 'state_id', 'placeholder' => Yii::t('aspns', 'Select')],
'pluginOptions' => [
'depends' => ['country_id'],
'url' => Url::to(['//country/get-states'])
]
]
)->label(Yii::t('aspns', 'State'));
?>
// In Controller section you need to do this
public function actionGetStates() {
$out = [];
if (isset($_POST['depdrop_parents'])) {
$parents = $_POST['depdrop_parents'];
if ($parents != null) {
$country_id = (int)$parents[0];
$out = State::find()->where(['country_id'=>$country_id])->select(['id', 'name'=>'name'])->asArray()->all();
return Json::encode(['output'=>$out, 'selected'=>'']);
}
}
return Json::encode(['output'=>'', 'selected'=>'']);
}
Change the code according to your requirement.
Thanks
I am using wbraganca dynamic form widget. It works fine for the Create action.
Let me thanks for those guys making great tutorial video on youtube!!!
I am working on the Update action now. I work it on a purchase order function.
the controller of update action :
public function actionUpdate($id)
{
$model = $this->findModel($id);
$modelsItem = $model->purchaseitems;
if ($model->load(Yii::$app->request->post()) ) {
$oldIDs = ArrayHelper::map($modelsItem, 'purchaseitem_id', 'purchaseitem_id');
$modelsItem = Model::createMultiple(Purchaseitem::classname(), $modelsItem);
Model::loadMultiple($modelsItem, Yii::$app->request->post());
$deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsItem, 'purchaseitem_id', 'purchaseitem_id')));
$valid = $model->validate();
$valid = Model::validateMultiple($modelsItem) && $valid;
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($flag = $model->save(false)) {
if (! empty($deletedIDs)) {
Purchaseitem::deleteAll(['purchaseitem_id' => $deletedIDs]);
}
foreach ($modelsItem as $modelItem) {
$modelItem->purchaseitem_purchaseorder_id = $model->purchaseorder_id;
$modelItem->purchaseitem_description = Inventory::findOne($modelItem->purchaseitem_inventory_id)->inventory_partno;
if (! ($flag = $modelItem->save(false))) {
$transaction->rollBack();
break;
}
}
}
if ($flag) {
$transaction->commit();
return $this->redirect(['view', 'id' => $model->purchaseorder_id]);
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
//return $this->redirect(['view', 'id' => $model->purchaseorder_id]);
} else {
return $this->render('update', [
'model' => $model,
'modelsItem' => (empty($modelsItem)) ? [new Purchaseitem] : $modelsItem
]);
}
}
But I think the problem may happen on the view file, as the Select2 field can show the value, which is the 'id' of the product rather than the product code.
view:
<div class="panel panel-default">
<div class="panel-body">
<?php DynamicFormWidget::begin([
'widgetContainer' => 'dynamicform_wrapper',
'widgetBody' => '.container-items',
'widgetItem' => '.item',
'limit' => 50,
'min' => 1,
'insertButton' => '.add-item',
'deleteButton' => '.remove-item',
'model' => $modelsItem[0],
'formId' => 'dynamic-form',
'formFields' => [
'purchaseitem_inventory_id',
'purchaseitem_qty',
'purchaseitem_cost_usd',
'purchaseitem_deliverydate',
],
]); ?>
<?php foreach ($modelsItem as $i => $modelItem): ?>
<div class="item">
<?php
// necessary for update action.
if (! $modelItem->isNewRecord) {
echo Html::activeHiddenInput($modelItem, "[{$i}]purchaseitem_id");
}
?>
<div class="row">
<?= $form->field($modelItem, "[{$i}]purchaseitem_inventory_id")->widget(
Select2::classname(), [
'pluginOptions' => [
'allowClear' => true,
'minimumInputLength' => 2,
'language' => [
'errorLoading' => new JsExpression("function () { return 'Error on finding results...'; }"),
],
'ajax' => [
'url' => Url::toRoute('inventory/inventorylist'),
'dataType' => 'json',
'data' => new JsExpression('function(params) { return {q:params.term}; }')
],
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
'templateSelection' => new JsExpression('function (purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
],
])->label(false) ?>
<?= $form->field($modelItem, "[{$i}]purchaseitem_qty")->textInput(['maxlength' => true])->label(false) ?>
<?= $form->field($modelItem, "[{$i}]purchaseitem_cost_usd")->textInput(['maxlength' => true])->label(false) ?>
<?= $form->field($modelItem, "[{$i}]purchaseitem_deliverydate")->widget(
DatePicker::className(), [
'options' => [
['placeholder' => 'Please enter delivery date'],
],
'removeButton' => false,
'pluginOptions' => [
'autoclose'=>true,
'format' => 'yyyy-mm-dd',
'todayHighlight' => true,
]
]
)->label(false) ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
I have a thought that the problem maybe related to that few lines of JsExpression function.
'escapeMarkup' => new JsExpression('function (markup) { return markup; }'),
'templateResult' => new JsExpression('function(purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
'templateSelection' => new JsExpression('function (purchaseitem_inventory_id) { return purchaseitem_inventory_id.text; }'),
For the Select2 query URL method is here:
public function actionInventorylist($q = null, $id = null) {
Yii::$app->response->format = yii\web\Response::FORMAT_JSON;
$out = ['results' => ['id' => '', 'text' => '']];
if (!is_null($q)) {
$query = new Query;
$query->select('inventory_id AS id, inventory_partno AS text')
->from('inventory')
->where(['like', 'inventory_partno', $q])
->limit(10);
$command = $query->createCommand();
$data = $command->queryAll();
$out['results'] = array_values($data);
}
elseif ($id > 0) {
$out['results'] = ['id' => $id, 'text' => Inventory::find($id)->inventory_partno];
}
return $out;
}
I can load the record, when I click in the update view. Most of the data are feed in right place of the form, except the 'partno' field. I use Select2 to let user select partno by text and store the 'id' in table. It works on the Create view.
but in the update view, it only show the 'id' instead of the 'partno'.
if I make input to the field, I can select 'other' partno only, let me explain here:
if there are 2 code, "ABC" with 'id' is 1, "XYZ" with 'id' 2.
the record original is "ABC", the field show "1".
If I input "XYZ", it will show "XYZ" as normal effect of widget. But, if I change back to "ABC", it will show "1" instead of "ABC".
And the form also cannot submit for update. the button click with no effect.
I am new to Yii2 framework, and quite stuck on this issue, does anyone knows how can I solve this?
THANKS!!!!
I just solve the problem, a few issue happened actually.
To solve the Select2 widget cannot display the partno instead of the ID, I find the partno by the ID and feed it with initValueText in Select2. For my case:
$partno = empty($modelItem->purchaseitem_inventory_id) ? '':Inventory::findOne($modelItem->purchaseitem_inventory_id)->inventory_partno;
$form->field($modelItem, "[{$i}]purchaseitem_inventory_id")->widget(Select2::classname(), ['initValueText' => $partno, ...
About the update POST fail issue, I got error of Getting unknown property: backend\models\Purchaseitem::id, and I found it happened on the Dynamic-form widgets Model.php line 25. That lines is $keys = array_keys(ArrayHelper::map($multipleModels, 'id', 'id'));
If I change the 'id' to my ID field name, i.e 'purchaseitem_id', it will work, but this model will only work for this Id field name afterward. So I get the model's primaryKey and make it work for my other model.
add this line $primaryKey = $model->tableSchema->primaryKey; and modify above line $keys = array_keys(ArrayHelper::map($multipleModels, $primaryKey, $primaryKey));
In the rules() of my RegisterForm model:
[ 'user_username', 'unique', 'targetClass' => 'app\models\User', 'message' => 'This username is already been taken.' ],
In my controller:
$model = new RegisterForm();
if ( $model->load( Yii::$app->request->post() ) ) {
if ( $user = $model->register() ) {
return $this->redirect( [ '/login' ] );
}
}
In RegisterForm:
public function register() {
$user = new User();
$user->user_firstname = $this->user_firstname;
$user->user_lastname = $this->user_lastname;
$user->user_username = $this->user_username;
$user->user_email = $this->user_email;
$user->setPassword( $this->user_password );
if ( !$user->validate() ) {
return null;
}
if ( $user->save() ) {
return $user;
}
return null;
}
Form:
<?php $form = ActiveForm::begin(); ?>
<?= $form->field( $model, 'user_firstname' )->textInput( [ 'maxlength' => true ] ) ?>
<?= $form->field( $model, 'user_lastname' )->textInput( [ 'maxlength' => true ] ) ?>
<?= $form->field( $model, 'user_username' )->textInput( [ 'maxlength' => true ] ) ?>
<?= $form->field( $model, 'user_email' )->textInput( [ 'maxlength' => true ] ) ?>
<?= $form->field( $model, 'user_password' )->passwordInput() ?>
<?= $form->field( $model, 'user_password_repeat' )->passwordInput() ?>
<?= Html::submitButton( 'Register', [ 'class' => 'btn btn-primary', 'name' => 'register-button' ] ) ?>
<?php ActiveForm::end(); ?>
Yet when I enter a username that I know already exists, the error never comes up and the record tries to save, though I get: Integrity constraint violation: 1062 Duplicate entry ...
EDIT: if I add the unique rule to the User model itself the form will not submit if I input a username that exists, the errors just don't show up
Like I have suspected, you are not checking for unique user_username attribute in client side. The reason why it's not working it's because you are not sending Ajax requests to check the results from database. Unlike other rules, unique rule requires additional Ajax requests to server since it would be pretty bad thing if Javascript would retrieve all currently registered username and store somewhere in Client side.
To solve you problem, in form write something like this:
$form = ActiveForm::begin([
'enableAjaxValidation' => true,
'validationUrl' => [<URL HERE>],
]);
Now you have to create a method (action) in controller that returns validation (not just unique, all of them) back to ActiveForm. So it could be something like this:
public function actionAjaxValidation()
{
$post = Yii::$app->request->post();
$model = new YourClass();
if (!$model->load($post)) {
throw new HttpException(403, 'Cannot load model');
}
$array = ActiveForm::validate($model);
return json_encode($array);
}
I am getting frusted because the edit.ctp is not recognizing the stored data within a multible value checkbox. This is my code:
Controller:
public function edit($id = null) {
$this->set('userfunc', $this->Auth->user('userfunction_id'));
$user = $this->Users->get($id, [
'contain' => []
]);
if ($this->request->is(['patch', 'post', 'put'])) {
$user = $this->Users->patchEntity($user, $this->request->data);
if ($this->Users->save($user)) {
$this->Flash->success(__('The user has been saved.'));
return $this->redirect(['action' => 'index']);
} else {
$this->Flash->error(__('The user could not be saved. Please, try again.'));
}
}
**$selection = explode('#',$user->member);**
$userfunctions = $this->Users->Userfunctions->find('list', ['limit' => 200]);
$userappartments = $this->Users->Userappartments->find('list', ['limit' => 200]);
$userparkings = $this->Users->Userparkings->find('list', ['limit' => 200]);
$this->set(compact('user', 'userfunctions', 'userappartments', 'userparkings', 'selection'));
$this->set('_serialize', ['user']);
}
edit.ctp:
<?php
echo $this->Form->label('User.status', 'Status');
$options = $statusList;
echo $this->Form->radio('status', $statusList);
echo $this->Form->label('User.member', 'Mitglied');
echo $this->Form->input('member', ['options' => $memberList, 'multiple' => 'checkbox', 'label' => false, 'selected' => $selection]);
echo $this->Form->input('userappartment_id', ['options' => $userappartments, 'label' => 'Wohnung']);
echo $this->Form->input('userparking_id', ['options' => $userparkings, 'label' => 'Parkplatz']);
echo $this->Form->input('contact', ['label' => 'Kontakt']);
?>
================
print_r is displaying an array that looks fine:
enter image description here
but the field in the form is emty:
enter image description here
It there any way to make 'data-confirm' => Please enter the number' not just confirm but some like JS prompt and get imputed values to sent it to controller?
<?php \yii\widgets\Pjax::begin(['id' => 'pjax-orders_table','clientOptions' => ['method' => 'POST'], 'enablePushState'=>false]) ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'id'=>'orders_table',
'columns' => [
['class' => 'yii\grid\SerialColumn'],
///some columns
[
'class' => 'yii\grid\ActionColumn',
'template' => '{update} {delete}{some}',
'buttons' => [
'some' => function ($url,$model,$key) {
if($model->status=='not confirm')
{
return Html::a('<span class="glyphicon glyphicon-trash"</span>',['my/some', 'id' => $model->id],[
'title' => Yii::t('yii', 'Delete'),
'data-confirm' => Please enter the number',
'data-method' => 'post',
]);
}
},
],
],
],
]); ?>
<?php \yii\widgets\Pjax::end() ?>
In controller
public actionSome()
{ $dataProvider = new ActiveDataProvider();
$dataProvider->query = Orders::find()->all();
return $this->render('some',['dataProvider'=>$dataProvider]);
}
instead of Html::a() use Html::button()where button id = some_item_id and then write this JS code
$('.buttons_class').click(function(){
var selection;
do{
selection = parseInt(window.prompt("Please enter a number from 1 to 100", ""), 10);
}
while(isNaN(selection));
if(!isNaN(selection))
{
$.post("some",{id:45,prompt_value:selection}, function(response)
{
console.log(response);
$.pjax.reload({container:'#pjax-orders_table'});
});
}
})