Yii2: Filter query by pivot table when using `joinWith()` - yii2

Take the following sample Cart model. It has a CartItem "pivot record/table" which links it to Item.
class Cart extends ActiveRecord {
public function getCartItems() {
return $this
->hasMany(CartItem::class, ['cart_id' => 'id'])
->inverseOf('cart');
}
public function getItems($callback = null) {
return $this
->hasMany(Item::class, ['item_id' => 'id'])
->via('cartItems', $callback);
}
}
(Example #1) At this point I would be able to filter either by Item's activequery or CartItem's query like so:
$booksAddedToCartSinceYesterday = $cart
->getItems(function($cartItemQuery) {
$cartItemQuery->andWhere('cartItem.created_at > NOW()');
})
->andWhere(['item.category' => 'books']);
(Example #2) But how do I accomplish the same when I use the static find() method in combination with joinWith()? In the following example I am only able to filet by Item's ActiveQuery, but I no longer have any reference to the CartItem's ActiveQuery object:
$booksAddedToCartSinceYesterday = Cart::find()
->andWhere(['cart.user_id' => $some_user_id])
->joinWith([
'items' => function($itemQuery) {
$itemQuery->andWhere(['item.category' => 'books']);
},
]);
How do I modify the code above so that I am able to filter CartItem junction table records like I did in my example #1? How do I access the junction ActiveQuery object so that I can call $cartItemQuery->andWhere('cartItem.created_at > NOW()');?

you can pass any variable value in anonymous function with use keyword link below
make your magic code here for filter or any
->joinWith([
'items' => function($itemQuery) use ($var1,$var2){
$itemQuery->andWhere(['item.category' => 'books']);
$itemQuery->andWhere(['some_condition' => $var1]); <<<---------
},
]);

Related

HasONE YII2 link with 2 attributes

``` public function getContaCorrente()
{
return $this->hasOne(\app\models\ContaCorrente::className(), ['id' => 'conta_corrente_id']);
}```
Is it possible to add two attributes in the same get? For example, I have this get above and I want to join it with the get below, as they both look for the same table but different variables.
public function getContaCorrenteAdesao()
{
return $this->hasOne(\app\models\ContaCorrente::className(), ['id' => 'conta_corrente_adesao']);
}
Since this is a query builder you can do something like that:
public function getContaCorrenteAdesao()
{
return $this->hasOne(\app\models\ContaCorrente::className(), ['id' => 'conta_corrente_adesao'])->orWhere(['id' => 'conta_corrente_id']);
}

Yii2 SearchModel query - map integer values to strings

What I am trying to achieve:
I have a table with a type field which holds integer values. These integer values represent different strings.
I want to be able to search the table using the string values that the integers represent.
E.g type = abc rather than type = 0.
What have I tried:
I have created a query class for the model and tried to make use of the $boolean_map property:
class ReportQuery extends FilterableQuery
{
protected $filterable = [
'type' => 'LIKE',
'removed_the_rest'
];
protected $boolean_map = ["type" => [ 'addacs' => 0, "arudd" => 1,]];
}
Then I have overridden the find method of the model to use the query class:
public static function find()
{
$query = new ReportQuery(get_called_class());
return $query;
}
And in the search model I have:
public function search($params)
{
$query = Report::find();
$dataProvider = new ActiveDataProvider([
'query' => $query
]);
$this->load($params, '');
if (!$this->validate()) {
return $dataProvider;
}
// grid filtering conditions
$query->andFilterWhere([
'type' => $this->type,
]);
$query->andFilterWhere(['like', 'type', $this->type]);
return $dataProvider;
}
When searching by the string values I get an empty result. Searching by the integer values produces the data.
Any help is appreciated. Thanks.
Maybe it's better for you to make filter on that column instead of searching by string. You can do it for string as follows.
$filter = [
'example1' => 1,
'example2' => 2,
'example3' => 3,
];
$query->andFilterWhere(['like', 'type', $this->filter[$this->type]);
or in this place
// grid filtering conditions
$query->andFilterWhere([
'type' => $this->filter[$this->type],
])
also you can make filter dropdown on column, and for dropdown of that filter you can pass this array and just do
$query->andFilterWhere([
'type' => $this->type,
])
Why do you create mapping mechanism in query object? Okay, you show integer type as a string in frontend of your application, but the query shouldn't have details of representation. You should map string type to integer type in your search model. For example:
class ReportSearchModel extends ReportModel
{
public function mapType($value)
{
$items = [
'addacs' => 0,
'arudd' => 1
];
return array_key_exists($value, $items) ? $items[$value] : null;
}
public function search($params)
{
//another code
$query->andFilterWhere([
'type' => $this->mapType($this->type),
])
//another code
}
}
The alternative way is using an enum instead of mapping.

Yii2: how to remove required attribute in a view?

I have a text field that was defined as required in its model. But a view needs not be required. I try this way to remove the required attribute but it doesn't work:
<?= $form->field($model, 'city')->textInput(['required' => false]) ?>
I need to change it in a view or in its controller. But not in its model (because others view needs the required attribute.).
I know how to do it using jQuery but I prefer with PHP/Yii2.
Update (requiered by the nice help of #Muhammad Omer Aslam):
My model is called Persons.
My view is called _form.
My controller is called PersonsControllers. It has the update function:
actionUpdate($id):
public function actionUpdate($id)
{
$model = $this->findModel($id); // How to add my new scenario here?
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id_person]);
}
return $this->render('update', [
'model' => $model,
]);
}
You can use scenarios to make the field required or not for the specific view. You can assign the active fields that are required for the scenario, and those fields will be the subject to validation.
I assume the model is Profile. In below example firstname, lastname and city is required in the default scenario.
A model may be used in different scenarios, by default the scenario default is used. Let's say in your case we can declare a scenario special that will only require firstname and lastname. In your model, you will declare a constant for the scenario name, and then override the scenarios() method, key=>value pairs with the active field names being passed in form of an array to the value will be assigned.
namespace app\models;
use yii\db\ActiveRecord;
class Profile extends ActiveRecord
{
const SCENARIO_SPECIAL = 'special';
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios[self::SCENARIO_SPECIAL] = ['firstname', 'lastname'];
return $scenarios;
}
}
and then inside your controller/action for that view where you do not want the city field to be required, initialize the Profile model object as below
public function actionProfile(){
$model = new \common\models\Profile(['scenario'=> \common\models\Profile::SCENARIO_SPECIAL]);
return $this->render('profile',['model'=>$model]);
}
Now if you submit the form inside this view it will ask only for the firstname and lastname whereas in your previous forms/views if you try to submit the form it will ask you to provide the city when trying to submit, you don't have to change or add anything for the rest of the forms or the rules.
As you are trying to update the record and do not want the city to be required when updating the record, the only difference that could be is to assign the scenario like below as you are not creating a new object for the model.
$model->scenario=\common\models\Profile::SCENARIO_SPECIAL;
In the model:
const SCENARIO_MYSPECIAL = 'myspecial';
public function rules()
{
return [
[['id_person', 'city'], 'required', 'on' => self::SCENARIO_DEFAULT],
[['id_person'], 'required', 'on' => self::SCENARIO_MYSPECIAL],
];
}
In the controller:
public function actionUpdate($id)
{
$model = $this->findModel($id);
$model->scenario = 'myspecial';
if ($model->load(Yii::$app->request->post()) && $model->save()) {
return $this->redirect(['view', 'id' => $model->id_person]);
}
return $this->render('update', [
'model' => $model,
]);
}
go to the model and remove the attribute
public function rules()
{
return [
[['id_person', 'city'], 'required'],
[['id_person'], 'required'],
];
}
EX:
public function rules()
{
return [
[['id_person'], 'required'],
[['id_person'], 'required'],
];
}

Add new attribute dynamically to the existing model object in Yii2 framework

In Yii2 framework is it possible to add a new attribute dynamically to an existing object, which is retrieved from Database?
Example
//Retrieve from $result
$result = Result::findone(1);
//Add dynamic attribute to the object say 'result'
$result->attributes = array('attempt' => 1);
If it is not possible, please suggest an alternate best method to implement it.
Finally I would be converting the result to a json object. In my application, at the behaviour code block, I have used like this:
'formats' => [
'application/json' => Response::FORMAT_JSON,
],
You can add define a public variable inside your model, that will store dynamic attributes as associative array. It'll look something like this:
class Result extends \yii\db\ActiveRecord implements Arrayable
{
public $dynamic;
// Implementation of Arrayable fields() method, for JSON
public function fields()
{
return [
'id' => 'id',
'created_at' => 'created_at',
// other attributes...
'dynamic' => 'dynamic',
];
}
...
..in your action pass some dynamic values to your model, and return everything as JSON:
public function actionJson()
{
\Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$model = Result::findOne(1);
$model->dynamic = [
'field1' => 'value1',
'field2' => 2,
'field3' => 3.33,
];
return $model;
}
In result you will get JSON like this:
{"id":1,"created_at":1499497557,"dynamic":{"field1":"value1","field2":2,"field3":3.33}}

Yii Gridview show image from related data

i have table feedback and user, i am trying to show user's image on feedback page.
i am using grid view, this is my gridview.
[ 'attribute' => 'iduser.photo',
'headerOptions' => ['width' => '20px'],
'format' => 'image',
'value'=> function($data) { return $data->imageurl; },
],
and the model
public function getImageurl()
{
return \Yii::$app->request->BaseUrl.'/../../'.$this->hasOne(User::className(), ['photo' => 'photo']);
}
i get right url but the photoname is wrong the result is "photo", i want getting the data form entity photo?
Inside your model you should have a way to get the User related to your feedback like this:
public function getUser() {
return $this->hasOne(User::className(), ['id' => 'user_id']);
}
Then your getImageurl method should look something like this:
public function getImageurl()
{
return \Yii::$app->request->BaseUrl.'/../../'.$this->user->photo;
}
I would recommend checking out Aliases, you can use them instead of \Yii::$app->request->BaseUrl. For example, this is the implementation i use to get a file url to show to the user:
public function getFileUrl() {
return Yii::getAlias('#web/uploads/'.$this->fileName);
}