Yii2 : Setting Access Control matchcallback - yii2

I just want to know if I can do this so I dont have to repeat the same code over and over
[
'allow' => true,
'actions' => ['index', 'update', 'view', 'logout'],
'roles' => ['#'],
'matchCallback' => function(){
return (Yii::$app->user->identity->team_id == '47' && Yii::$app->user->identity->team_id == '62'
&& Yii::$app->user->identity->team_id == '63' && Yii::$app->user->identity->team_id == '64'
&& Yii::$app->user->identity->team_id == '65' && Yii::$app->user->identity->team_id == '66' && Yii::$app->user->identity->role_id == '1');
}
],
Or something similar to that.
Thank you

First, I don't understand why you are using && instead of ||. Is it a typo?
Now for answering the question, I think it will be better for example to create a function for that purpose in the Model representing your identity like below: (I am sure you will need it later somewhere else)
public function belongsToGroup()
{
if( $this->team_id == '47'
|| $this->team_id == '62'
...
&& $this->role_id == '1' ){
return true;
} else {
return false;
}
}
And then just call your function in the authorisation control:
[
'allow' => true,
'actions' => ['index', 'update', 'view', 'logout'],
'roles' => ['#'],
'matchCallback' => function($rule, $action){
return Yii::$app->user->identity->belongsToGroup();
}
],

Related

"When" not getting validated in fluent Validation, why my second "When" condition is always true ..?

When(opt => opt.submitOrderRequest.requestHeader.operation.ToString() == "NewActivation" || opt.submitOrderRequest.requestHeader.operation.ToString() == "HardwareUpgrade", () =>
{
RuleForEach(x => x.submitOrderRequest.requestBody.device).Cascade(CascadeMode.StopOnFirstFailure).ChildRules(device =>
{ ...stuff RuleFor
When(opt => opt.submitOrderRequest.requestHeader.operation.ToString() ==
"HardwareUpgrade" &&
opt.submitOrderRequest.requestHeader.operation.ToString() != "NewActivation", () =>
{
}
}
}

Filter a gridview column that is processed by a function in yii2

I have a column named wp_status. If person A, person B, person C, person D approves a work, the value of column wp_status will be changed to Approved, else the value will be as it is in the database - Assigned.
The code to change the value dynamically in the gridview is -
[
'label' => 'Status',
'attribute'=>'wp_status',
'value' => function ($model) {
return $model->Status();
}
],
And the function Status in model Workpermit is -
public function Status()
{
//$data = Workpermit::findOne($this->id);
$total = $this->wp_status;
if($this->wp_type == 'Safe Work Permit' && $this->wp_apoapproval == 'Approved' && $this->wp_spsapproval == 'Approved' && $this->wp_officerapproval == 'Approved'){
$total = 'Approved';
}
return $total;
}
This works fine so far. But I'm not sure how to filter it with Kartik Select2 widget. I tried like following -
[
'label' => 'Status',
'attribute'=>'wp_status',
'filterType'=>GridView::FILTER_SELECT2,
'filter'=>ArrayHelper::map(Workpermit::Status()->asArray()->all(), 'total', 'total'),
'filterWidgetOptions'=>[
'pluginOptions'=>['allowClear'=>true],
],
'filterInputOptions'=>['placeholder'=>'Permit Status'],
'value' => function ($model) {
return $model->Status();
}
],
And here I'm getting error - Using $this when not in object context
could be uisng a getter
public function getStatus()
{
//$data = Workpermit::findOne($this->id);
$total = $this->wp_status;
if($this->wp_type == 'Safe Work Permit' && $this->wp_apoapproval == 'Approved' && $this->wp_spsapproval == 'Approved' && $this->wp_officerapproval == 'Approved'){
$total = 'Approved';
}
return $total;
}
the call $model->status (status lowercase)
[
'label' => 'Status',
'attribute'=>'wp_status',
'value' => function ($model) {
return $model->status;
}
],

Yii2 Dynamic form update fail on kartik-Select2

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));

Color of gridview cells, several condition

How to change color of cell like this
if($data['type'] == 'London' or $data['type'] == 'Manchester' and $data['from_sale']){
return ['style' => 'background-color:#BCC6F0;'];
}
If in row 'type' value is London or Manchester then change color of cell in row 'from_sell'. Color must changed only in two cells in row 'from_sell'
Use contentOptions:
[
'attribute' => 'attribute_name',
'contentOptions' => function ($model, $key, $index, $column) {
return ['style' => 'background-color:'
. ($model->type == 'London' ? 'red' : 'blue')];
},
],
Since Yii uses Bootstrap you are better of using a Bootstrap class rather than style as below:
'contentOptions' => function($model) {
if ($model->taskduedt < date("Y-m-d")) {
return ['class' => 'table-danger',];
} elseif ($model->taskduedt == date("Y-m-d")) {
return ['class' => 'table-warning',];
}
else return ['class' => '',];
},

Yii2 When Validation

Out of two pairs of input fields I only need one or the other. I can't get the validation right.
listing_image_url and poster_image_url should only be required if $model->listingImage is null.
Also tried using strlen($model->listingImage) == 0.
[['listing_image_url', 'poster_image_url'], 'required',
'when' => function($model){
var_dump($model->listingImage); //result is empty string '0'
return $model->listingImage == NULL && $model->posterImage == NULL;
},'whenClient' => "function(attribute, value) {
return $('#vod-listingimage').val() == '' && $('#vod-posterimage').val() == '';
}", 'message' => 'look'
],
Just as above but the other way around.
[['listingImage', 'posterImage'], 'required',
'when' => function($model) {
return $model->listing_image_url == NULL && $model->poster_image_url == NULL;
},
'whenClient' => "function(attribute, value) {
return $('#vod-listing_image_url').val() == '' && $('#vod-poster_image_url').val() == '';
}", 'message' => 'hi'
],
You could create your own inline validator for the model validation on backend side, like this:
[['listingImage', 'posterImage'], function($attribute, $params) {
if ($this->listingImage === null && empty($this->$attribute)) {
$this->addError($attribute, 'Can not be blank if listingImage is null');
}
}]
In order to also provide the client side validation you can build a custom standalone validator.
I tried myself something similar and the behavior is odd indeed.
But you can create a validator that checks if only one of the two fields is selected.
public function validateListingAgainstPoster($attribute, $params)
{
if (!empty($this->listing_image_url) && !empty($this->poster_image_url)) {
$this->addError($attribute, 'Only one of "Listing" or "Poster" fields can be selected.');
}
if (empty($this->listing_image_url) && empty($this->poster_image_url)) {
$this->addError($attribute, 'Please select one of "Listing" or "Poster Group".');
}
}
And in your rules:
[['listing_image_url', 'poster_image_url'], 'validateListingAgainstPoster', 'skipOnEmpty' => false, 'skipOnError' => false],