Yii2 remove unique validator - yii2

I have an AR model, it has the following rules:
/**
* #inheritdoc
*/
public function rules() {
return [
[['category_id', 'source', 'url', 'title', 'thumbs', 'duration', 'status', 'created_at'], 'required'],
[['category_id', 'status', 'views', 'ratings', 'created_at'], 'integer'],
[['rating'], 'double'],
[['source', 'url', 'title', 'slug'], 'string', 'max' => 255],
[['url'], 'unique', 'on' => 'create'],
[['category_id'], 'exist', 'skipOnError' => true, 'targetClass' => Category::className(), 'targetAttribute' => ['category_id' => 'id']],
];
}
I want to do a soft delete so I have the following.
/**
* Deletes an existing Video model.
* If deletion is successful, the browser will be redirected to the 'index' page.
* #param integer $id
* #return mixed
*/
public function actionDelete($id)
{
$model = $this->findModel($id);
$model->status = 0;
//var_dump($model->validate());
//var_dump($model->getErrors());die;
$model->save();
return $this->redirect(['index']);
}
But unfortunately I cannot change the status of model, because the validation says that (The url xxxxxxx has been taken) so I went to the PostgreSql, and I checked the records, but unfortunately only the updating record has this value! So in my mind the Yii2 unique validatios is bad. I would like to remove the unique validator, but it seems it is impossible. Because I commented out the uniqure row in the rule array, but it did not help me. I restarted the machine, but I do not know, it seems Yii2 want always check the url is unique or not.

You can use scenario
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['soft_delete'] = ['status',]; //Scenario Values Only Accepted
return $scenarios;
}
public function actionDelete($id)
{
$model = $this->findModel($id);
$model->status = 0;
$model->scenario = 'soft_delete';
//var_dump($model->validate());
//var_dump($model->getErrors());die;
$model->save();
return $this->redirect(['index']);
}
or another way is suppress validation for this action
public function actionDelete($id)
{
$model = $this->findModel($id);
$model->status = 0;
//var_dump($model->validate());
//var_dump($model->getErrors());die;
$model->save(false);
return $this->redirect(['index']);
}

Related

How to use relations using joinWith in yii2 search model

I have two models with related data fbl_leagues and fbl_country tables. fbl_leagues have a column country_id which is related to fbl_country, now in yii2 gridView i was able to do something like [
'attribute' => 'country_id',
'value' => 'country.name',
], which gives the name of the country rather than the countries id, then i also want to enable searching by country name rather than country_id but i get the below error
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'country.name' in 'where clause'
The SQL being executed was: SELECT COUNT(*) FROM `fbl_leagues` LEFT JOIN `fbl_country` ON `fbl_leagues`.`country_id` = `fbl_country`.`id` WHERE `country`.`name` LIKE '%s%'
Below is my LeagueSearch model
class LeagueSearch extends League
{
/**
* {#inheritdoc}
*/
public function rules()
{
return [
[['id', 'created_at', 'updated_at'], 'integer'],
[['name','country_id'], 'safe'],
];
}
/**
* {#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 = League::find();
$query->joinWith('country');
// 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;
}
// grid filtering conditions
$query->andFilterWhere([
'id' => $this->id,
'created_at' => $this->created_at,
'updated_at' => $this->updated_at,
]);
$query->andFilterWhere(['like', 'name', $this->name])
->andFilterWhere(['like', 'country.name',$this->country_id]);
return $dataProvider;
}
}
and my league model
{
return [
[['country_id', 'created_at', 'updated_at'], 'integer'],
[['name','country_id'], 'required'],
[['name'], 'string', 'max' => 100],
[['country_id'], 'exist', 'skipOnError' => true, 'targetClass' => Country::className(), 'targetAttribute' => ['country_id' => 'id']],
];
}
/**
* {#inheritdoc}
*/
public function attributeLabels()
{
return [
'id' => Yii::t('app', 'ID'),
'country_id' => Yii::t('app', 'Country Name'),
'name' => Yii::t('app', 'Name'),
'created_at' => Yii::t('app', 'Created At'),
'updated_at' => Yii::t('app', 'Updated At'),
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getCountry()
{
return $this->hasOne(Country::className(), ['id' => 'country_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getPerfectSelections()
{
return $this->hasMany(PerfectSelection::className(), ['league_id' => 'id']);
}
Now i noticed that when i comment out $query->joinWith('country'); then there will be no error but searching not working as expected
Hmm silly me i guess i later figure out the problem the table name is fbl_country so i suppose write fbl_country.name but wrote country.name in my search model, it's being a long day though, thanks for all

Yii2, how to use or operator in rest request

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.

yii2 custom validation addError message doesn't show

when i use custom validations on yii2 dynamic forms it doesn't show any error messages below the input field.Below I have posted my model.
It doesn't show any error messges when qty field gets validated
namespace frontend\models;
use Yii;
class OrderD extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'order_d';
}
public function rules()
{
return [
[['item_id', 'qty', 'price', 'value'], 'required'],
[['item_id'], 'integer'],
[['price', 'value'], 'number'],
[['order_code'], 'string', 'max' => 10],
[['item_id'], 'exist', 'skipOnError' => true, 'targetClass' => Item::className(), 'targetAttribute' => ['item_id' => 'id']],
[['order_code'], 'exist', 'skipOnError' => true, 'targetClass' => OrderH::className(), 'targetAttribute' => ['order_code' => 'code']],
['qty', 'validateQty']
];
}
public function validateQty($attribute)
{
$qty = $this->$attribute;
if ($qty >= 5)
{
$this->addError('qty', "qty validation successful");
}
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'item_id' => 'Item ID',
'order_code' => 'Order Code',
'qty' => 'Qty',
'price' => 'Price',
'value' => 'Value',
];
}
/**
* #return \yii\db\ActiveQuery
*/
public function getItem()
{
return $this->hasOne(Item::className(), ['id' => 'item_id']);
}
/**
* #return \yii\db\ActiveQuery
*/
public function getOrderCode()
{
return $this->hasOne(OrderH::className(), ['code' => 'order_code']);
}
}
Be sure to know custom validations are php functions and not convert as javascript to validate in runtime .. those will work after the page has submit and send to controller ..
here is simple sample you want :
['qty','custom_function_validation'],
];
}
public function custom_function_validation($attribute, $params){
if($this->$attribute>5){
$this->addError($attribute,'it\'s more than 5');
}
}
To create a validator that supports client-side validation, you should implement the yii\validators\Validator::clientValidateAttribute() method which returns a piece of JavaScript code that performs the validation on the client-side.
public function clientValidateAttribute($model, $attribute, $view)
{
return <<<JS
// your validation
JS;
}
See the documentation here: http://www.yiiframework.com/doc-2.0/guide-input-validation.html#implementing-client-side-validation
Use method addError() in a controller and then just render your view file
Example
if ($promo_code) {
if ($promo_code->status == '1') {
$message = 'This combination is incorrect.';
$model->addError('promo_code', $message);
return $this->render('index', compact('model'));
}
$promo_code->status = '1';
$promo_code->save();
$model->save();
}

YII2 Call to a member function isAttributeRequired() on a non-object

I am new with Yii2, im trying to make sales transaction, but i have problem for dropdownlist in detail section.
for this module, i have 3 tables :
1. for header, table salesheader
2. for detail, table salesdetail
3. for item, table item
this is my code :
on controller :
public function actionUpdate($id)
{
$model = $this->findModel($id); //find appropriate record in salesheader
//$modeldetail = salesdetail::findModel($id);
$modeldetail = new salesdetail();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['index']);
} else {
$item = item::find()->select(["concat('[',code,'] - ', name) as item", "id"])->indexBy('id')->column();
$detail = salesdetail::find()->select(["*"])->all(); //find appropriate record in salesdetail
return $this->render('update', [
'model' => $model,
'modeldetail' => $modeldetail,
'item' => $item,
'detail' => $detail
]);
}
}
on salesheader model
namespace app\models;
use Yii;
class salesheader extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'salesheader';
}
public function rules()
{
return [
[['partnerId', 'date', 'term', 'name'], 'required'],
[['name', 'invNumber'], 'string'],
[['partnerId', 'term'], 'integer']
];
}
public function attributeLabels()
{
return [
'partnerId' => 'Customer',
'date' => 'Date',
'invNumber' => 'Inv. Number',
'term' => 'Term',
'name' => 'Name'
];
}
on salesdetail model
namespace app\models;
use Yii;
class salesdetail extends \yii\db\ActiveRecord
{
public static function tableName()
{
return 'salesdetail';
}
public function rules()
{
return [
[['itemId', 'qty', 'price'], 'required'],
[['unit'], 'string']
];
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'itemId' => 'Item',
];
}
}
on my view
<?= $form->field($detail, 'itemId')->dropDownList($item)->label(false) ?>
and when open the page in browser, i got this error "Call to a member function isAttributeRequired() on a non-object"
and if i change this line on controller
//$modeldetail = salesdetail::findModel($id);
$modeldetail = new salesdetail();
to this one
$modeldetail = salesdetail::findModel($id);
//$modeldetail = new salesdetail();
then the error would be "Call to undefined method app\models\salesdetail::findModel()"
can somebody tell me what am i doing wrong ?
and how to get appropriate data from salesdetail based on salesheader and put it on dropdownlist ?
Thank you for ur help

How to make automatic assignment of id values in the form of creating an item

I have form of creating shop item which was generated by Gii.
So i myself must to enter id value. But I need to get rid of this field, and make an assignment automatically.
For auto assignment creating and updating time, i just adding this function in my module
public function behaviors() {
return [
TimestampBehavior::className(),
];
}
How can i do this for id value?
UPD
Rules:
public function rules() {
return [
[['category_id', 'title', 'desc', 'price', ], 'required'],
[['id', 'category_id', 'price', 'in_stock', 'discount', 'created_at', 'updated_at'], 'integer'],
[['desc', 'options', 'photos'], 'string'],
[['title'], 'string', 'max' => 100]
];
}
UPD 2
<?php
namespace backend\modules\shop\controllers;
use backend\modules\shop\models\ShopCategories;
use Yii;
use backend\modules\shop\models\ShopItems;
use backend\modules\shop\models\ShopItemsSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
/**
* ItemsController implements the CRUD actions for ShopItems model.
*/
class ItemsController extends Controller {
public function behaviors() {
return [
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'delete' => ['post'],
],
],
];
}
/**
* Lists all ShopItems models.
* #return mixed
*/
public function actionIndex() {
$searchModel = new ShopItemsSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Displays a single ShopItems model.
* #param integer $id
* #return mixed
*/
public function actionView($id) {
return $this->render('view', [
'model' => $this->findModel($id),
]);
}
/**
* Creates a new ShopItems model.
* If creation is successful, the browser will be redirected to the 'view' page.
* #return mixed
*/
public function actionCreate() {
$model = new ShopItems();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('create', [
'model' => $model,
'category' => ShopCategories::find()->all()
]);
}
}
/**
* Updates an existing ShopItems 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);
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
'category' => ShopCategories::find()->all()
]);
}
}
/**
* Deletes an existing ShopItems 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 ShopItems model based on its primary key value.
* If the model is not found, a 404 HTTP exception will be thrown.
* #param integer $id
* #return ShopItems the loaded model
* #throws NotFoundHttpException if the model cannot be found
*/
protected function findModel($id) {
if (($model = ShopItems::findOne($id)) !== null) {
return $model;
} else {
throw new NotFoundHttpException('The requested page does not exist.');
}
}
}
You should not include attributes that are managed automatically or by programmer in model validation rules. Validation make sense only for user entered data.
Remove id from that line and everything should be OK:
[['id', 'category_id', 'price', 'in_stock', 'discount', 'created_at', 'updated_at'], 'integer'],
created_at and updated_at are also redundant because they don't depend on user, so this is enough:
[['category_id', 'price', 'in_stock', 'discount'], 'integer'],
Update:
After deeper investigation we found that primary key didn't have auto increment. After executing this query problem disappeared:
ALTER TABLE `shop_items`
CHANGE COLUMN `id` `id` INT(11) NOT NULL AUTO_INCREMENT,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`id`);
Credits for Miroslav's answer to this question.