Relational attribute in Yii2 form - php

I am trying to get to figure out the proper way of handling a form receiving relational data in Yii2. I haven't been able to find any good examples of this. I have 2 models Sets and SetsIntensity, every Set may have one SetsIntensity associated with it. I'm am trying to make a form where you can input both at the same time. I'm not sure how to handle getting the input for a particular field 'intensity' in SetsIntensity.
Where
$model = new \app\models\Sets();
If I put it in the field like this client validation won't work and the attribute name is ambiguous and saving becomes difficult
<?= $form->field($model, 'lift_id_fk') ?>
<?= $form->field($model, 'reps') ?>
<?= $form->field($model, 'sets') ?>
<?= $form->field($model, 'type') ?>
<?= $form->field($model, 'setsintensity') ?>
I would like to do something like this but I get an error if I do
<?= $form->field($model, 'setsintensity.intensity') ?>
Exception (Unknown Property) 'yii\base\UnknownPropertyException' with message 'Getting unknown property: app\models\Sets::setsintensity.intensity'
I could do make another object in the controller $setsintensity = new Setsintensity(); but I feel this is a cumbersome solution and probably not good practice especially for handling multiple relations
<?= $form->field($setsintensity, 'intensity') ?>
relevant code from SetsModel
class Sets extends \yii\db\ActiveRecord
{
public function scenarios() {
$scenarios = parent::scenarios();
$scenarios['program'] = ['lift_id_fk', 'reps', 'sets', 'type', 'intensity'];
return $scenarios;
}
public function rules()
{
return [
[['lift_id_fk'], 'required'],
[['lift_id_fk', 'reps', 'sets','setsintensity'], 'integer'],
[['type'], 'string', 'max' => 1],
['intensity', 'safe', 'on'=>'program']
];
}
public function getSetsintensity()
{
return $this->hasOne(Setsintensity::className(), ['sets_id_fk' => 'sets_id_pk']);
}
SetsIntensity Model
class Setsintensity extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'setsintensity';
}
public function rules()
{
return [
[['sets_id_fk', 'intensity', 'ref_set'], 'required'],
[['sets_id_fk', 'intensity', 'ref_set'], 'integer']
];
}
public function getSetsIdFk()
{
return $this->hasOne(Sets::className(), ['sets_id_pk' => 'sets_id_fk']);
}
}
I was also thinking maybe I could put in a hasOne() relation for the specific attribute 'intensity' in 'Sets'

You should simply try this :
<?= $form->field($model->setsintensity, 'intensity') ?>
EDIT : And because "every Set may have one SetsIntensity", you should check this relation before displaying form, e.g. :
if ($model->setsintensity===null)
{
$setsintensity = new SetsIntensity;
$model->link('setsintensity', setsintensity);
}
PS: link method requires that the primary key value is not null.

Related

Value not updated in databases after validation and save success

Hi everyone i'm having trouble with my software developed with yii2.
I Have a model called Anagrafica and with its primary key id. With this model everything works.
I also have a model called AnagraficaOpzioniCarriera which extend the first one.
I have a view anagrafica/index that show a Kartik grid with the data of people enrolled that you can find in anagrafica. Admin user can update the data of an Anagrafica model by clicking on an the attribute "cognome" that render to anagrafica/update.
this is the command that call the controller AnagraficaController to reach anagrafica/update
'cognome'=>Grid::Labels('cognome',['anagrafica/update'],\app\helpers\Permits::allow('anagrafica','update'),'id','btn','info','10%'),
This is AnagraficaController
public function actionUpdate($id,$error=0,$message='')
{
$id = (int)$id;
$model = Anagrafica::findOne(['id' => $id]);
$model->scenario = 'update';
if ($model->load(Yii::$app->request->post())) {
if($model->validate()){
}
if($model->save(false)){
return $this->redirect(['anagrafica/update','id'=>$model->id]);
}
}
}
return $this->render('update', ['model' => $model, 'extended'=>true]);
}
i removed some portions of code to semplify it, but this is the core.
One time the view anagrafica/update is reached in this page i have an ActiveForm to modify data of the model and i have a render to a grid that show the attributes contained in AnagraficaOpzioniCarriera about the $model that i'm updating.
<?= $this->render('_opzioni_carriera',['parent'=>$model]); ?>
anagrafica/_opzioni_carriera view contain a Kartik grid that shows the column in the model AnagraficaOpzioniCarriera
<?php
use kartik\grid\GridView;
use kartik\select2\Select2;
use kartik\widgets\ActiveForm;
use kartik\editable\Editable;
use kartik\widgets\SwitchInput;
use yii\helpers\ArrayHelper;
use app\helpers\Autoconfigurazione;
use app\models\AnagraficaOpzioniCarriera;
use app\helpers\Grid;
use yii\helpers\Html;
use app\helpers\UserInfo;
/* #var $this yii\web\View */
/* #var $model app\models\AnagraficaOpzioniCarriera*/
$model = new AnagraficaOpzioniCarriera(['scenario'=>'search']);
?>
<div class="">
<?php
echo GridView::widget([
'options'=>[
'id'=>'opzioni_carriera',
],
'dataProvider'=> $model->search($parent->id,Yii::$app->request->queryParams),
'showPageSummary'=>false,
'headerRowOptions'=>['class'=>'kartik-sheet-style'],
'pjax'=>true, // pjax is set to always true for this demo
'pjaxSettings'=>[
'neverTimeout'=>true,
],
'toolbar'=> [
[
'content'=>''
],
],
'panel'=>[
'heading'=>false,
'footer'=>false,
'after'=>false,
],
'columns' => Grid::gridColumns([
'model'=>$model,
'checkbox'=>false,
'remove'=>Grid::gridRemove($model),
'extraOptions' =>[
'cashback' =>Grid::YNColumn('cashback',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'compensa'=>Grid::YNColumn('compensa',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'associazione'=>Grid::YNColumn('associazione',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
'formazione'=>Grid::YNColumn('formazione',['anagrafica-opzioni-carriera/update', 'id' => $parent->id],'left',true,'5%'),
],
]);
?>
</div>
cashback, compensa etc.. are the attributes in the model AnagraficaOpzioniCarriera.
Here when i try to update this attributes everything looks fine, the function model->validate() and model->load returns true value, but at the end of the process doesn't works.
Honestly i don't know what i have to return from the function of the controller.
public function actionUpdate($id)
{
$model = AnagraficaOpzioniCarriera::findOne(['id_anagrafica' => $id]);
if (!$model) {
// Se l'anagrafica opzioni carriera non esiste, genera un'eccezione 404
throw new \yii\web\NotFoundHttpException(Yii::t('app', 'The requested page does not exist.'));
}
$model->scenario = 'update';
if ($model->load(Yii::$app->request->post()) && $model->validate()) {
if(Yii::$app->request->post('cashback') != null) $model->cashback = Yii::$app->request->post('cashback');
if(Yii::$app->request->post('compensa') != null) $model->cashback = Yii::$app->request->post('compensa');
if(Yii::$app->request->post('associazione') != null) $model->cashback = Yii::$app->request->post('associazione');
if(Yii::$app->request->post('formazione') != null) $model->cashback = Yii::$app->request->post('formazione');
if ($model->save()) {
return Json::encode(["success" => true, 'message' => 'Dati aggiornati']);
}
}
// Mostra il form di modifica
return $this->render('_opzioni_carriera', [
'parent' => $model,
]);
}
anyone can help me? i hope i explained my problem in a good form, but my english is not the best, i know. Anyway thanks in aadvance to everyone who want to try to help me, if you need anything other you can easily ask.
I tried every everything, also a logger but nothing worked
Like someone suggest these are the rules of the model AnagraficaOpzioni, but like i said prevously model->validate() works, for this reason i think the problem is not over there
public function rules()
{
return [
[['id_anagrafica'], 'required'],
[['id_anagrafica'], 'integer'],
[['cashback', 'compensa', 'associazione', 'formazione'], 'required', 'on'=>['update']],
[['cashback', 'compensa', 'associazione', 'formazione'], 'integer'],
[['id_anagrafica', 'cashback', 'compensa', 'associazione', 'formazione',], 'safe', 'on'=>['search']],
];
}

Yii2 - Validate attribute with keys

I have a problem with attribute key validation.
In model I have property like:
public $_fileds = [];
and My view
<?= $form->field($model, '_fileds[name]') ?>
<?= $form->field($model, '_fileds[type]') ?>
I want to set "_fields[name]" as requierd field. For example I tried add to rules in model something like this:
public function rules()
{
return [
[['_fields[name]'], 'required']
];
}
but this not working :(
Does anyone have any solution?
Sorry for my not good English and thanks in advance for all solutions.
You can write your own validation.
Remove: public $_fileds = [];
public function rules()
{
return [
[['_fields[name]'], 'validateName']
];
}
In same model class:
public function validateName():bool
{
... your checks(if fails return false) ...
return true;
}
But there is one issue: if some of checks fails you, in normal cases, need to set error message using $this->addError() which will correctly work with attributes which are not array elements because as first element of $this->addError() you need to pass attribute name as string where '_fileds' is ok but '_fileds[name]' won't work. So you also need to find a way to return errors if they appear. Same issue with attribute labels and hints.

Yii2 saving model with relationship property

iam trying to save model from form, that have relationship defined via junction table, but since the property is relationship object it is read-only and it fails on validation.
Model relationship:
public $payer
/**
* #return \yii\db\ActiveQuery
*/
public function getPayerRelationship()
{
return $this->hasMany(PartyRelationship::className(), ['contract_id' => 'id'])->where(['relationship' => 'P']);
}
public function getPayers(){
return $this->hasMany(ContractingParty::className(), ['id' => 'contracting_party_id'])
->via('payerRelationship');
}
public function getContractors() { // could be a static func as well
$model = ContractingParty::find()->asArray()->all();
return ArrayHelper::map($model, 'id', 'subject_name');
}
Form view:
<?= $form->field($model, 'payers')->widget(Select2::classname(), [
'data' => $model->getContractors(),
'language' => 'en',
'options' => ['placeholder' => '-- Select company --'],
'pluginOptions' => [
'allowClear' => true,
'multiple' => true,
],
'showToggleAll' => false
]) ?>
It wont validate or save, because of read-only property payers. I tried to use different property in $form->field($model, 'payer'... (instead of payers), then validation works and even saving works, but trouble is, that editing have no preselected values of that model, because they are in model->payers. And i have no idea, what iam supposed to pass here instead of this relationship object (or property of model in general).
Maybe iam plainly blind, but in manual there is a lot of information about getting data from db, but almost no info about saving.
(btw. i saw this post: Yii2 Invalid Call: Setting read-only property - but that didnt give me any new piece of information at all).
Is my form design wrong, or model design (Meaning i should just create form field using two models)? Thanks
Adding setters to model:
public function setPayer(){
$payer_id_array = array();
$payer_array = ArrayHelper::toArray($this->payers);
foreach ($payer_array as $value){
$payer_id_array [] = $value['id'];
}
$this->payer = $payer_id_array;
}
public function setRecipient(){
$recipient_id_array = array();
$recipient_array = ArrayHelper::toArray($this->recipients);
foreach ($recipient_array as $value){
$recipient_id_array [] = $value['id'];
}
$this->recipient = $recipient_id_array;
}
and manually into controller (action create and update):
$model->setPayer();
$model->setRecipient();
seems to fix the conflict between the names of relation and property passed into the field.

Yii2 - automatically generate placeholder attribute

I'm trying to create an 'auto-placeholder' element using Yii2 and since I couldn't find an actual answer to my question, I thought I'd try it here.
For example, I have this field:
<?= $form->field($model, 'username',
[
'template'=>'{input}{label}{error}'
])
->textInput(['placeHolder'=>'{name}')
->label(false);
?>
However this case would obviously render "name" in the placeholder attribute.
But I would like to generate the placeholder attribute automatically depending on the model's variable I'm using, causing it to render the following:
<input type="text" id="loginform-username" class="form-control" name="LoginForm[username]" placeholder="Username">
Is there a known way of accessing and inserting the form->field's attribute and displaying it inside its own element?
Yes we can do by defining the attributes labels in model file like below.
public function attributeLabels() {
return [
'username' => 'Username',
];
}
then you can fetch the label automatically based on fields like following.
<?= $form->field($model, 'username',
[
'template'=>'{input}{label}{error}'
])
->textInput(['placeholder' => $model->getAttributeLabel('username'))
->label(false);
?>
I hope this will sort it out your problem.
If you are in for some extra hassle you can extend ActiveField class for that.
class MyActiveField extends \yii\widgets\ActiveField
{
public function textInput($options = [])
{
if (empty($options['placeholder'])) {
$options['placeholder'] = $this->model->getAttributeLabel($this->attribute);
}
return parent::textInput($options);
}
}
Now just need to use your class instead of default one.
You can do every time in view:
<?php $form = ActiveForm::begin([
'fieldClass' => 'fully\qualified\name\of\MyActiveField'
]); ?>
Or extend ActiveForm:
class MyActiveForm extends \yii\widgets\ActiveForm
{
$fieldClass = 'fully\qualified\name\of\MyActiveField';
}
and use it instead of default ActiveForm widget:
<?php $form = MyActiveForm::begin(); ?>
Now you can use <?= $form->field($model, 'attribute')->textInput() ?> (or just <?= $form->field($model, 'attribute') ?> since textInput is default) and placeholder should be there.

Yii2: custom validation for multiple attributes using OR not working

I am trying to write a rule that validates if attribute_a or attribute_b is set;
one of the following attributes must be set : licitatii_publice or licitatiile_atribuite
The following code does not work;
<?php
namespace common\models;
use yii\base\Model;
class AbonamentValidare extends Model {
public $licitatii_publice;
public $licitatiile_atribuite;
public $zone;
public $judete;
public $tari;
public static $targetAttribute = [];
public function rules() {
return [
[['zone'], 'required'],
[['licitatii_publice', 'licitatiile_atribuite', 'tari', 'judete'], 'safe'],
['licitatii_publice', 'validate_tip_licitatie', 'targetAttribute' => ['licitatii_publice', 'licitatiile_atribuite']],
];
}
function validate_tip_licitatie($attribute, $param) {
print_r($attribute);
$this->addError($attribute, 'eroarea');
}
public function attributeLabels() {
return array(
'licitatii_publice' => 'lp',
'licitatiile_atribite' => 'la',
'tari' => 'tari',
'judete' => 'judete',
'zone' => 'zone',
);
}
public function save() {
return false;
}
}
?>
Just wanted to update this answer for Yii2 case.
In Yii2 the validators have a skipOnEmpty attribute which is by default set to true. This implies custom validators will not be called if the field is empty, which might not be the required behavior, especially in this case where either of one attribute is mandatory. In order to fix this issue we need to set the skipOnEmpty to false as shown below.
[['licitatii_publice, licitatiile_atribuite'], 'validate_tip_licitatie', 'skipOnEmpty'=> false],
Well what I have done in a case like this is to create the validator like this:
................
return [
[['zone'], 'required'],
[['licitatii_publice', 'licitatiile_atribuite', 'tari', 'judete'], 'safe'],
[['licitatii_publice, licitatiile_atribuite'], 'validate_tip_licitatie'],
];
............
function validate_tip_licitatie($attribute, $param) {
if(!$this->licitatii_publice && $this->licitatiile_atribuite)
$this->addError($attribute, 'eroarea');
}
In this way you show both fields with an error.
However I have done this in Yii1, but from what I read Yii2 should be the same. The logic is the same.
If you want to show error only for 1 attribute you can always just use
return [
[['zone'], 'required'],
[['licitatii_publice', 'licitatiile_atribuite', 'tari', 'judete'], 'safe'],
[['licitatii_publice'], 'validate_tip_licitatie'],
];
What you are trying to do is more fancy :), I get that. If you really want to use targetAttribute you might have to do it like this
https://github.com/yiisoft/yii2/blob/master/framework/validators/ExistValidator.php
Just build your own validator class.
Well. After reading about the exist validator i believe that is exactly what you need. It has examples on how to use it.

Categories