In an ActiveDataProvider you can use closures as values, like:
$dataprovider = new ArrayDataProvider([
'allModels' => $array
]);
$gridColumns = [
'attrib_1',
[
'attribute' => 'attrib_2',
'label' => 'Label_2',
'value' => function($model) {
return Html::encode($model->value_2);
}
],
'attrib_3'
];
echo GridView::widget([
'dataProvider'=> $dataprovider,
'columns' => $gridColumns
]);
Is it possible to do the same or something like this, in an ArrayDataProvider?
Yes. Only difference is that $model is not an object but array so:
'value' => function($model) {
return Html::encode($model['value_2']);
}
For this purpose, I have created an extended version of ActiveDataProvider, that for each model got from provider I call a callback.
This is the custom ActiveDataProvider, put in common\components namespace in this case.
<?php
namespace common\components;
class CustomActiveDataProvider extends \yii\data\ActiveDataProvider
{
public $formatModelOutput = null;
public function getModels()
{
$inputModels = parent::getModels();
$outputModels = [];
if($this->formatModelOutput != null)
{
for($k=0;$k<count($inputModels);$k++)
{
$outputModels[] = call_user_func( $this->formatModelOutput, $k , $inputModels[$k]);
}
}
else
{
$outputModels = $inputModels;
}
return $outputModels;
}
}
This is the action in controller that uses it. For reusability, I call a model method instead calling a clousure, but you can call also a clousure.
public function actionIndex()
{
$query = Model::find();
$dataProvider = new \common\components\CustomActiveDataProvider([
'query' => $query,
'pagination' => ['pageSize' => null],
'formatModelOutput' => function($id, $model) {
return $model->dataModelPerActiveProvider;
}
]);
return $dataProvider;
}
At last, this is the method getDataModelPerActiveProvider in model:
public function getDataModelPerActiveProvider()
{
$this->id = 1;
// here you can customize other fields
// OR you can also return a custom array, for example:
// return ['field1' => 'test', 'field2' => 'foo', 'field3' => $this->id];
return $this;
}
Related
I need help with this code, I'm new to Yii2.
I'm building a sample project to start, and I don't know why my code gets the right result but doesn't save the ids I need into the many-to-many table for the relationship.
I started from this sample in the wiki: https://www.yiiframework.com/wiki/708/book-has-author-many-to-many-relations-using-kartikselect2
My Model
public function rules()
{
return [
[['anno', 'durata', 'flagdelete', 'categoriaid'], 'integer'],
[['titolo', 'riassunto', 'regista', 'descrizione'], 'string', 'max' => 50],
[['attoriIds'], 'safe'],
];
}
public function attributeLabels()
{
return [
'titolo' => 'Titolo',
'anno' => 'Anno',
'durata' => 'Durata',
'riassunto' => 'Riassunto',
'regista' => 'Regista',
'descrizione' => 'Descrizione',
'categoriaid' => 'Categoria',
'nome' => 'Attori',
];
}
/**
* #var array
*/
public $attoriIds = [];
public function getdropAttori()
{
$data = Attori::find()->asArray()->all();
return ArrayHelper::map($data, 'id', 'nome');
}
/**
* #return mixed
*/
public function getAttoriIds()
{
return $this->attoriIds;
}
/**
* #param $attoriIds
*/
public function setAttoriIds($attoriIds)
{
$this->attoriIds = \yii\helpers\ArrayHelper::getColumn(
$this->getAttdvd()->asArray()->all(),
'attori_id'
);
}
/**
* #param $insert
* #param $changedAttributes
*/
public function afterSave($insert, $changedAttributes)
{
$actualAttoris = [];
$attoriExists = 0;
if (($actualAttoris = Attdvd::find()->andWhere(
"dvd_id = $this->id"
)->asArray()->all()) !== null
) {
$actualAttoris = ArrayHelper::getColumn($actualAttoris, 'attori_id');
$attoriExists = 1;
}
if (!empty($this->despIds)) {
foreach ($this->despIds as $id) {
$actualAttoris = array_diff($actualAttoris, [$id]);
$r = new Attdvd();
$r->dvd_id = $this->id;
$r->attori_id = $id;
$r->save();
}
}
if ($attoriExists == 1) {
foreach ($actualAttoris as $remove) {
$r = Attdvd::findOne(
['attori_id' => $remove, 'dvd_id' => $this->id]
);
$r->delete();
}
}
parent::afterSave($insert, $changedAttributes);
}
/**
* #return mixed
*/
public function getAttdvd()
{
return $this->hasMany(Attori::className(), ['id' => 'attori_id'])->viaTable(
'attdvd', ['dvd_id' => 'id']
);
}
My Controller
public function actionCreate()
{
$model = new Dvd();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('create', [
'model' => $model,
]);
}
public function actionUpdate($id)
{
$model = $this->findModel($id);
//i think the problem is the line below
$model->attoriIds = $model->AttoriIds;
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
}
return $this->render('update', [
'model' => $model,
]);
}
My form (that work properly):
<?= $form->field($model, 'AttoriIds')->widget(Select2::classname(), ['data'=>$model->dropAttori, 'options' => ['multiple' => true, 'placeholder' => 'Seleziona attori']])->label('Attori') ?>
It's bad way because the making of that code in multiple models will make models dirtier. The better way is usage of a universal component that can manage relations in your models. For example yii2-many-to-many-behavior
I am trying to use or operator in my rest request Yii2 but I cannot succeed.
Everytime I have this error :
[
{
"field": "filter",
"message": "Operator \"or\" requires multiple operands."
}
]
I tested several things but nothing works.
I want to filter
statut=0 or statut=1
Do you know or I can do it ?
I tried to
http://url/api/tickets/gestions?filter[or][statut][statut]=[0,1]
But it does not work
Here's the method within controller that manage this request :
public function actionIndex()
{
return ActionsHelper::actionIndex(
$this->modelClass,
$this->modelClass . 'Search'
);
}
$this->modelClass is defined above and is equal to 'api\modules\tickets\models\TicketGestion';
Here is ActionsHelper::actionIndex
public function actionIndex($model, $searchModel = null, $moreFilter = null,
$pagination = false)
{
$filterCondition = null;
if ($searchModel) {
$filter = new ActiveDataFilter([
'searchModel' => $searchModel
]);
if ($filter->load(\Yii::$app->request->get())) {
$filterCondition = $filter->build();
if ($filterCondition === false) {
return $filter;
}
}
}
$query = $model::find();
if ($filterCondition !== null) {
$query->andWhere($filterCondition);
}
if ($moreFilter !== null) {
$query->andWhere($moreFilter);
}
if ($pagination !== false) {
$pagination = [
'pageSize' => 100
];
}
return new ActiveDataProvider([
'query' => $query,
'pagination' => $pagination
]);
}
Here's the search model, generated by Gii
<?php
namespace api\modules\tickets\models;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
use api\modules\tickets\models\TicketGestion;
/**
* TicketGestionSearch represents the model behind the search form of `api\modules\tickets\models\TicketGestion`.
*/
class TicketGestionSearch extends TicketGestion
{
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['id', 'priorite', 'quicree', 'quirea', 'statut', 'recurrentid', 'typerea', 'client'], 'integer'],
[['dispatch', 'service', 'type', 'sujet', 'datecrea', 'dateecheance', 'daterea'], 'safe'],
[['duree'], 'number'],
];
}
/**
* {#inheritdoc}
*/
public function scenarios()
{
// bypass scenarios() implementation in the parent class
return Model::scenarios();
}
/**
* Creates data provider instance with search query applied
*
* #param array $params
*
* #return ActiveDataProvider
*/
public function search($params)
{
$query = TicketGestion::find();
// add conditions that should always apply here
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
$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;
}
if ($this->dispatch == 'null') {
$this->dispatch = 1;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'priorite' => $this->priorite,
'quicree' => $this->quicree,
'quirea' => $this->quirea,
'datecrea' => $this->datecrea,
'dateecheance' => $this->dateecheance,
'daterea' => $this->daterea,
'duree' => $this->duree,
'statut' => $this->statut,
'recurrentid' => $this->recurrentid,
'typerea' => $this->typerea,
'client' => $this->client,
]);
$query->andFilterWhere(['like', 'service', $this->service])
->andFilterWhere(['like', 'type', $this->type])
->andFilterWhere(['like', 'sujet', $this->sujet])
->andFilterWhere(['likes', 'dispatch', $this->dispatch]);
return $dataProvider;
}
}
You were on a right track, using ActiveDataFilter, but building arrays from get is done like this (example is from my controller):
http://localhost/ntb/web/index.php?r=assembly%2Findex&filter[or][0][status]=1004&filter[or][1][status]=1005&page=1&per-page=10
so for your example it should be like this:
http://url/api/tickets/gestions?filter[or][0][statut]=0&filter[or][1][statut]=1
This was the way to build a working 'or' filter for me.
I want to return different fields depends on scenario. How can I set it in dataProvider?
$query = User::find();
$activeData = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
]);
Fields in User model:
public function fields()
{
if ($this->scenario == 'statistics') {
return [
'id',
'email',
'count'
];
}
return [
'id',
'name'
];
}
What about using the $select property?
$query = User::find()->select(['id','email','count']);
$activeData = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
]);
Or even better, create an ActiveQuery class for them:
class UserQuery extends ActiveQuery
{
public function statistics()
{
return $this->select(['id','email','count']);
}
/* add as many filtering functions as you need here */
}
Then override the find() method in the User class to use it:
public static function find()
{
return new \app\models\UserQuery(get_called_class());
}
Then do:
$activeData = new ActiveDataProvider([
'query' => User::find()->statistics(),
'pagination' => [
'pageSize' => 10,
],
]);
Note: In default implementation of Yii2 RESTful API you can also select the required fields within url by doing: GET /users?fields=id,email,count
Simply get all the models using getModels() method, set scenario for all of them in the loop and then return data provider. Your example will change into following code:
$query = User::find();
$activeData = new ActiveDataProvider([
'query' => $query,
'pagination' => [
'pageSize' => 10,
],
]);
foreach($activeData->getModels() as $model) {
$model->scenario = 'statistics';
}
return $activeData;
renderPartial clientSide validation doesn't work. I want to render part of form with ajax. Ex.:
_form.php
$form = ActiveForm::begin([
'options' => [
'enableAjaxValidation' => true,
]
]);
$form->field($model, 'category_id')->dropDownList($category, [
'onchange'=>'
$.get( "'.Url::toRoute('/controller/params').'", { id: $(this).val() } )
.done(function( data ) {
$( "#offers-param-content" ).html( data );
}
);'
]);
Controller.php
public function actionParams($id)
{
$model = new Param();
$params = EavAttribute::find()->where(['category_id'=>$id])->all();
$this->renderPartial('_params', ['model' => $model, 'params' => $params];
}
_params.php
foreach($params as $item){
echo Html::activeTextInput('text', $model, $item->name);
}
If you want to enable client validation, then set this property to true.
$form = ActiveForm::begin([
'options' => [
'enableAjaxValidation' => true,
'enableClientValidation'=>true
]
]);
And use renderAjax() function in place of renderPartial() it will inject into the rendering result with JS/CSS scripts and files which are registered with the view
In your Controller.php you need to set layout to false and die the execution
public function actionParams($id)
{
$this->layout = false;
$model = new Param();
$params = EavAttribute::find()->where(['category_id'=>$id])->all();
$this->renderPartial('_params', ['model' => $model, 'params' => $params];
die;
}
Your not returning any validation errors from your controller to your view.
To archive that use
yii\widgets\ActiveForm::validate($yourModel);
If your not using activeform you can return errors by
$model->getErrors();//will return errors from $model->validate()
From your excerpt try this
public function actionParams($id) {
$model = new Param();
if ($model->load(Yii::$app->request->post())) {
if (Yii::$app->request->isAjax) {
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
return ActiveForm::validate($model); /* Validation error messages are returned by this static function */
}
$params = EavAttribute::find()->where(['category_id' => $id])->all();
$this->renderPartial('_params', ['model' => $model, 'params' => $params]);
}
}
Getting error when I am trying to create dynamic form in using yii2-dynamicform. at the time of create method it is working fine but at the time of update showing the error. I have two tables one is
1.vendors &
2.vendors_more_categories
Relation is 1-* between vendors & vendors_more_categories I just refereed https://github.com/wbraganca/yii2-dynamicform this link.
<?php
namespace app\controllers;
namespace backend\controllers;
use Yii;
use app\models\Vendors;
use app\models\VendorsSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;
use yii\filters\AccessControl;
use app\models\VendorsMoreCategories;
use backend\models\Model;
use yii\web\Response;
use yii\widgets\ActiveForm;
use yii\helpers\ArrayHelper;
/**
* VendorsController implements the CRUD actions for Vendors model.
*/
class VendorsController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'only' => ['index','create', 'update', 'delete'],
'rules' => [
[
'actions' => ['index','create', 'update', 'delete'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
],
],
];
}
/**
* Lists all Vendors models.
* #return mixed
*/
public function actionIndex()
{
$searchModel = new VendorsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single Vendors model.
* #param integer $id
* #return mixed
*/
public function actionView($id)
{
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new Vendors model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate()
{
$model = new Vendors();
$modelsVendorsMoreCategories = [new VendorsMoreCategories];
if($model->load(Yii::$app->request->post())){
$modelsVendorsMoreCategories = Model::createMultiple(VendorsMoreCategories::classname());
Model::loadMultiple($modelsVendorsMoreCategories, Yii::$app->request->post());
// validate all models
$valid = $model->validate();
$valid = Model::validateMultiple($modelsVendorsMoreCategories) && $valid;
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($flag = $model->save(false)) {
foreach ($modelsVendorsMoreCategories as $modelVendorsMoreCategories) {
$modelVendorsMoreCategories->vmc_ven_id = $model->ven_id;
if (! ($flag = $modelVendorsMoreCategories->save(false))) {
$transaction->rollBack();
break;
}
}
}
if ($flag) {
$transaction->commit();
$model->file = UploadedFile::getInstance($model, 'file');
$save_file = '';
if($model->file){
$imagename = Vendors::find()->orderBy('ven_id DESC')->one();
$imagename=$imagename->ven_id+1;
$imagepath = 'images/imgvendors/'; // Create folder under web/uploads/logo
$model->ven_business_logo = $imagepath.$imagename.'.'.$model->file->extension;
$save_file = 1;
}
if ($model->save(false)) {
if($save_file){
$model->file->saveAs($model->ven_business_logo);
}
return $this->redirect(['view', 'id' => $model->ven_id]);
}
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}else {
return $this->render('create', [
'model' => $model,
'modelsVendorsMoreCategories' => (empty($modelsVendorsMoreCategories)) ? [new VendorsMoreCategories] : $modelsVendorsMoreCategories
]);
}
}
/**
* Updates an existing Vendors model.
* If update is successful, the browser will be redirected to the 'view' page.
* #param integer $id
* #return mixed
*/
public function actionUpdate($id)
{
$model = $this->findModel($id);
//print_r($model->attributes);
$modelsVendorsMoreCategories = $model->ven_id;
if($model->load(Yii::$app->request->post())){
$oldIDs = ArrayHelper::map($modelsVendorsMoreCategories, 'id', 'id');
$modelsVendorsMoreCategories = Model::createMultiple(VendorsMoreCategories::classname(), $modelsVendorsMoreCategories);
Model::loadMultiple($modelsVendorsMoreCategories, Yii::$app->request->post());
$deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsVendorsMoreCategories, 'id', 'id')));
// validate all models
$valid = $model->validate();
$valid = Model::validateMultiple($modelsVendorsMoreCategories) && $valid;
if ($valid) {
$transaction = \Yii::$app->db->beginTransaction();
try {
if ($flag = $model->save(false)) {
if (! empty($deletedIDs)) {
Address::deleteAll(['id' => $deletedIDs]);
}
foreach ($modelsVendorsMoreCategories as $modelVendorsMoreCategories) {
$modelVendorsMoreCategories->vmc_ven_id = $model->ven_id;
if (! ($flag = $modelVendorsMoreCategories->save(false))) {
$transaction->rollBack();
break;
}
}
}
if ($flag) {
$transaction->commit();
$model->file = UploadedFile::getInstance($model, 'file');
$save_file = '';
if($model->file){
$imagepath = 'images/imgvendors/'; // Create folder under web/uploads/logo
$model->ven_business_logo = $imagepath.$model->ven_id.'.'.$model->file->extension;
$save_file = 1;
}
if ($model->save(false)) {
if($save_file){
$model->file->saveAs($model->ven_business_logo);
}
return $this->redirect(['view', 'id' => $model->ven_id]);
}
}
} catch (Exception $e) {
$transaction->rollBack();
}
}
}else {
return $this->render('update', [
'model' => $model,
'modelsVendorsMoreCategories' => (empty($modelsVendorsMoreCategories)) ? [new VendorsMoreCategories] : $modelsVendorsMoreCategories
]);
}
}
/**
* Deletes an existing Vendors model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param integer $id
* #return mixed
*/
public function actionDelete($id)
{
$this->findModel($id)->delete();
return $this->redirect(['index']);
}
/**
* Finds the Vendors model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return Vendors the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id)
{
if (($model = Vendors::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
//Function used for deleting the images
public function actionDeleteimg($id, $field)
{
$img = $this->findModel($id)->$field;
if($img){
if (!unlink($img)) {
return false;
}
}
$img = $this->findModel($id);
$img->$field = NULL;
$img->update();
return $this->redirect(['update', 'id' => $id]);
}
//Function used for getting more sub categories for vendor
public function actionGetSubCategories()
{
$mbcid=$_GET['ven_main_category_id'];
$sbcid=$_GET['ven_sub_category_id'];
echo $mbcid;
}
public function actionLists($id)
{
$countVendors = Vendors::find()->where(['ven_contact_person_id' => $id])->count();
$vendors = Vendors::find()->where(['ven_contact_person_id' => $id])->all();
if ($countVendors > 0) {
foreach ($vendors as $vendor) {
echo "<option value='" . $vendor->ven_id . "'>" . $vendor->ven_company_name . "</option>";
}
} else {
echo "<option></option>";
}
}
}
You accessing modelsVendorsMoreCategories[0] (as an element of an array )
'model'=> $modelsVendorsMoreCategories[0],
in your DinamicForm widget but when you update the model you pass as $modelsVendorsMoreCategories this value
$modelsVendorsMoreCategories = $model->ven_id;
(don't seems an array, you must be sure this object contain an array with a proper element in 0 index))
'modelsVendorsMoreCategories' => (empty($modelsVendorsMoreCategories)) ?
[new VendorsMoreCategories] : $modelsVendorsMoreCategories
]);
$modelsVendorsMoreCategories should contain the VendorsMoreCategories model in actionUpdate method. In this code ($modelsVendorsMoreCategories = $model->ven_id) $modelsVendorsMoreCategories contains $model->ven_id. It is an integer value, not the VendorsMoreCategories object.
The Vendors model should contain a relation on the VendorsMoreCategories model:
class Vendors extends \yii\db\ActiveRecord
{
....
public function getVendorsMoreCategories()
{
return $this->hasMany(VendorsMoreCategories::className(), 'vendor_id'=>'id']);
}
}
And then, you should use that relation in your actionUpdate method:
$model = $this->findModel($id);
$modelsVendorsMoreCategories = $model->vendorsMoreCategories;
if(!$modelsVendorsMoreCategories) {
$modelsVendorsMoreCategories = [new VendorsMoreCategories];
}
In your actionUpdate you have:
$modelsVendorsMoreCategories = $model->ven_id;
But you should have:
$modelsVendorsMoreCategories = $model->nameOfMyRelation;
To get what's the real name, go in your $model, and look for something like:
/**
* #return \yii\db\ActiveQuery
*/
public function getNameOfMyRelation()
{
return $this->hasMany(VendorsMoreCategories::className(), ['ven_id' => 'id']);
}
If you don't have any function making the relation of this two tables, write one. If you having trouble doing that, you can always use the gii's model generator and check the Vendors model (you dont need to replace it, just preview the code).
Check your create.php file in view folder, pass required variable on
_form.php file from here as:-
<?= $this->render('_form', [
'model' => $model,
'modelsAddress' => $modelsAddress,
]) ?>
Check Your create file in view folder:
Controller:
Controller pass the parameter into create.php
return $this->render('create', [
'model' => $model,
'modelsVendorsMoreCategories' => (empty($modelsVendorsMoreCategories)) ? [new VendorsMoreCategories] : $modelsVendorsMoreCategories
]);
View:create.php
If You miss the parameter: 'modelsVendorsMoreCategories' =>$modelsVendorsMoreCategories.
It shows the Undefined variable error in _form.php page.
<?= $this->render('_form', [
'model' => $model,
'modelsVendorsMoreCategories' =>$modelsVendorsMoreCategories
])?>
View:_form.php
$modelsVendorsMoreCategories[0];
the paramater not passing before now it passing.