Add new value to dropdown list - yii2

In a projects/create active form I have a field "related company account" as a dropdown (select2 by kartik). Behind this field I'd like to place a plus sign or something else to add new accounts to the dropdown with the following behavior:
gather all input done so far (like $input = compact(array_keys(get_defined_vars())); but probably needed on client side)
jump to accounts/create and pass $input
after submiting the new account jump back to projects/create (e.g. return $this->redirect(Yii::$app->request->referrer);) and fill the previously entered data (extract($input, EXTR_PREFIX_SAME, "arr");)
I'm struggling now with several issues:
Is this process according to best practice or should I change something fundamentally?
How is the button like? Submit button, link or some form of javascript?
Problem with Submit button is that not all required fields may be filled. So saving and resuming/updating the project model might not be possible.
Problem with link is that it is constructed before data was entered
Problem with javascript is that I have no glue
Any hints are welcome. Thank you in advance.

One alternative i would suggest is using Session.
As for the "Add Accounts" button, i would use Submit button, and give different name to actual Submit button (two submit button in form, as answered in here). So, the projects/create view will look like this :
<?php $form = ActiveForm::begin(); ?>
...
...
...
<?= $form->field($model, 'account_id')->widget(Select2::classname(), [
'data' => ArrayHelper::map(Account::find()->all(), "id", "name"),
'options' => ['placeholder' => 'Select a related company account ...'],
'pluginOptions' => [
'allowClear' => true
],
]) ?>
<?= Html::submitButton('Add Account ;)', ['class' => 'btn btn-success', 'name' => 'add_account_submit']) ?>
...
...
...
<div class="form-group">
<?= Html::submitButton($model->isNewRecord ? 'Create' : 'Update', ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
</div>
<?php ActiveForm::end(); ?>
And then check in ProjectsController, which submit button pressed by user. If add account was pressed, then save the inputed field (i would put this function in model for clearance), else, save the model or anything. And, before all that, check if session about project is set, if yes then pre-load it to model (again, in model). Okay, like they say, one code is worth a thousand words, so, this is ProjectsController will look like :
class ProjectsController extends Controller
{
...
...
...
public function actionCreate($category)
{
$model = new Projects();
if (Projects::isSavedInSession()) {
$model->loadFromSession();
}
if (Yii::$app->request->post('add_account_submit')) { // if add_account_submit is clicked
$model->saveTosession(Yii::$app->request->post('Projects')); // I assume your model named Projects, if not, change this value to your model name
return $this->redirect(['accounts/create']);
}
if ($model->load(Yii::$app->request->post()) && $model->save()) {
$model->clearSession(); // we dont need the session anymore
return $this->redirect(['index');
}
return $this->render('create', [
'model' => $model,
]);
}
...
...
...
}
And Projects model will look like :
class Projects extends \yii\db\ActiveRecord
{
...
...
...
public static function isSavedInSession() { // why this is static is beyond this question context
if (Yii::$app->session->get('projects')) return true;
return false;
}
public function loadFromSession() {
if (Yii::$app->session->get('projects_name')) $this->name = if (Yii::$app->session->get('projects_name'));
if (Yii::$app->session->get('projects_account_id')) $this->account_id = if (Yii::$app->session->get('projects_account_id'));
...
... // insert all model's field here
...
}
public function saveToSession($fields) {
Yii::$app->session->set('projects', 1);
foreach ($fields as $field=>$value) {
Yii::$app->session->set('projects_' . $field, $value);
}
}
public function clearSession() {
Yii::$app->session->remove('projects'));
Yii::$app->session->remove('projects_name'));
Yii::$app->session->remove('projects_account_id'));
...
... // insert all model's field here
...
}
...
...
...
}
And in the AccountsController, just tell the program to jump back to projects/create if projects session is set, like so :
class AccountsController extends Controller
{
...
...
...
public function actionCreate($category)
{
$model = new Accounts();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
if (Projects::isSavedInSession()) {
return $this->redirect(['projects/create');
}
return $this->redirect(['index');
}
return $this->render('create', [
'model' => $model,
]);
}
...
...
...
}
Well, it's looks bit lengthy, but yeah, it's worth trying. Anyway, you could use this approach for another purpose, save current form state for example.
Oh, one more thing, i haven't tested this in real code, so if any error exposed on my code, hit me up in comment.
Happy coding. :)

Related

How to make active item of SideNav in another style?

I want the items in my SideNav by Kartik in the yii2 app to change the color when it is active(was clicked and open).
Sorry, I am pretty new for PHP and Yii and the question might be seen obvious but I really stack here.
I have already tried to use the "active" option that is explained in the documentation but it doesn't work. It doesn't show any error but is not working. I have a file adminMenu.php where the SideNav is written. and the panel.php view file where I showing it.
Also, I tried to add echo
$this->render('adminMenu'['currentPage'=>'admin/personal']);
but it shows error and thus I comment it for now.
adminMenu.php:
class adminMenu extends Widget{
public function init(){
$curentpage = Yii::$app->controller->id ;
parent::init();
ob_start();
echo SideNav::widget([
'type' => SideNav::TYPE_PRIMARY,
'headingOptions' => ['class'=>'head-style'],
'items' => [
['label' => 'Personal',
'url' => ['/admin/personal'],
'active' => ($curentpage == 'admin/personal')],
['label' => 'Clients',
'url' => ['/admin/clients'],
'active' => ($curentpage == 'admin/clients')],
...
]);
panel.php:
if(\Yii::$app->user->can('Admin')){
echo adminMenu::widget();
//echo $this->render('adminMenu'['currentPage'=>'admin/personal']);
}
i think that you mistake is in this line
$curentpage = Yii::$app->controller->id;
Yii::$app->controller->id only return the name of your controller , in this casi will be "admin" then you compare "admin" equals "controller/action" ('admin/personal') this never will be equals .
To do this , you can do a concat the actual controller and the actual action like this :
$curentpage = Yii::$app->controller->id.'/'.Yii::$app->controller->action->id;
and the comparation will be success and the "active" class add to you sidebar

How to make that GridView buttons update and delete just only visible for admins?

I am new to Yii2 and I have 3 kind of user rights:
Admin, moderator and user. I have my GridView and I don't want to show for the user the Update and Delete buttons, just only the GridView. How should I do that?
Here is my actionCreate, there is an input form:
public function actionCreate()
{
$model = new Project();
$model->scenario = Project::SCENARIO_CREATE;
if ($model->load(Yii::$app->request->post())) {
if ($model->save()) {
Yii::$app->getSession()->setFlash('success', Yii::t('app', 'Skelbimas sėkmingai pridėtas!'));
return $this->redirect(['index']);
}
}
return $this->render('create', [
'model' => $model,
]);
}
I've tried to search the information according to this, but couldn't find useful ones. Thanks for any help or information.
To accomplish this, you have to use the property $visibleButtons of you ActionColum class.
So:
'visibleButtons' = [
'update' => Yii::$app->user->can('update'), // or whatever condition
'delete' => Yii::$app->user->can('update')
]
and so on. Each Key on the visibleButtons array is the name of the button.
Yii Framework's guide
    .........
        [
            'class'=>'yii\grid\ActionColumn',
            'template'=> '{view} {update} {delete} ',
            'buttons'=> [
                'update'=> function($url,$model) {
if (Yii::$app->user->can('admin')) {
                    returnHtml::a( '<span class="glyphicon glyphicon-pencil"></span>', $url);
}
                },
                'delete'=>function($url,$model,$key) {
if (Yii::$app->user->can('admin')) {
                        returnHtml::a('delete', $url);
}
                },
            ],
        ],
One possibility would be using the 'template' attribute of youe 'ActionColumn' like:
[
...
'template'=> (user has only user rights ? '{view}' ? '{view} {update} {delete}')
...
]
Please, bare in mind that even though this solution will hide the buttons for users with only user right, it won't prevent them of accessing update and delete action urls, so you have to check permissions also in the controller level.

Getting field value in controller

I want to get value of field in controller.
could you please help me?
here is my form code:
<?php
$form = ActiveForm::begin([
'id' => 'request-form',
'action' => 'site/request_page',
'method' => 'post',
'fieldConfig' => ['autoPlaceholder' => false]
]);
?>
<?= $form->field($model, 'workroom_id')->label(FALSE) ?>
and this is my controller code:
public function actionRequest_page() {
echo Yii::$app->request->post('workroom_id');
die();
}
But I got nothing in result.
write workroom_id in safe rule like this-
public function rules()
{
return [
[['workroom_id'],'safe']
];
}
Use bellow code -
echo Yii::$app->request->post('MODEL_NAME')['workroom_id'];
You should expand your action form-attribute. By using Url::to() for instance. As in echo \yii\helpers\Url::to(['site/request_page']);
And access your post data differently. Try var_dump(Yii::$app->request->post()); to see what your form data looks like. The other answer shows how to access it correctly.
The docs have an excellent starting place for working with forms.

Pre-population of checkboxList with DynamicModel issue

I have created a DynamicModel to build a search form which contains a checkboxList with the items being populated by the records of a model. The form works fine, however the form appears on the results page and all fields are populated with the previously selected values apart from the checkboxList.
Controller:
$model = DynamicModel::validateData(
['date_from',
'date_to',
'client_site',
'report_types',
]);
$model->addRule(['client_site'], 'integer');
$model->addRule(['client_site', 'report_types'], 'required');
$model->addRule(['date_from','date_to'], 'string');
$model->load(Yii::$app->request->post()) && $model->validate();
$reportTypes = ArrayHelper::map(ReportType::find()->asArray()->all, 'id', 'name');
return $this->render('print-report-form', [
'report_types' => $reportTypes,
'model' => $model,
]);
View:
<?= $form->field($model, 'report_types[]')
->inline(false)
->checkboxList($reportTypes);
?>
Do I need to tie the $reportTypes in within the model another way? Any ideas on why the selected checkboxes are not being pre-populated on the form submission?
First of all, there is a mistake in view form field, variable name is wrong, it should be
<?= $form->field($model, 'report_types[]')
->inline(false)
->checkboxList($report_types);
?>
then in controller
$model = DynamicModel::validateData(
['date_from',
'date_to',
'client_site',
'report_types',
]);
$model->addRule(['client_site'], 'integer');
$model->addRule(['client_site', 'report_types'], 'required');
$model->addRule(['date_from','date_to'], 'string');
$posted_model = clone $model;
$reportTypes = ArrayHelper::map(ReportType::find()->asArray()->all, 'id', 'name');
if($posted_model->load(Yii::$app->request->post()) && $posted_model->validate())
{
// save data or do as per your requirement with $posted_model
// if nothing to be done, and just rendering back to form then
return $this->render('print-report-form', [
'report_types' => $reportTypes,
'model' => $model, // line X
]);
}
else
{
return $this->render('print-report-form', [
'report_types' => $reportTypes,
'model' => $model, // line X
]);
}
This was happening because when the view was rendering the very first time, all checkbox are empty, but when submit the form the model gets filled up with POSTed data ie all its attribute are set and then always you were rendering POSTed model ie the model filled with data.
Now with the above situation you are not rendering POSTed model, you are always rendering empty new model.
This was the situation where you need empty checkbox.
Second Situation:
If you need checkbox to be prepopulated then
remove [] in form field
<?= $form->field($model, 'report_types')
->inline(false)
->checkboxList($report_types);
?>
and replace line X by
'model' => $posted_model,
Here you will get filled checkboxes

Yii2 Behaviors / Scenarios Modify Attribute

I have a model "Product" that I would like to modify or "mutate" one of its attributes for, but only in specific instances.
I store attribute, price as an integer. So $1.99 gets stored as 199.
I would like to incorporate this with the activeForm in such a way that when getting the price it converts to "1.99" in the field (visually). But when I submit the form, before validation, it modifies the price from "1.99" to "199".
I'm assuming this will require Behaviors, and specifically attaching a behavior to the model before creating the active form. However, I'm still confused on how to set this up. I see there is an AttributeBehavior class or I can make my own Behavior class, but I've been having trouble figuring out implementation in this case.
The situation:
foreach ($store_item->storeProducts as $i=>$product) {
?>
<tr>
<td>
<?= $form->field($product, '['.$i.']price')->label(false); ?>
</td>
</tr>
<?php
$i++;
}
?>
Here is a scenario where I check for empty attribute and assign value before saving. Note owner returns the Model so that you can access model attributes and functions that are public. Let me know if I can explain anything further
public function behaviors()
{
return [
[
'class' => AttributeBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => 'yourAttrib',
],
'value' => function ($event) {
$code = "N/A";
if(!empty($this->owner->yourAttrib))
{
$code = $this->owner->yourAttrib; //here change your attribute accordingly
}
return $code;
},
],
//other behaviors
];
}
You could simply use a getter/setter, e.g. :
public function getRealPrice()
{
return $this->price/100;
}
public function setRealPrice($value)
{
$this->price = $value*100;
}
And don't forget to :
add realPrice in your model's rules,
use realPrice in your form (instead of price).