Yii2 - prevent behavior on insert - php

I have this behavior in my model:
public function behaviors()
{
return [
'styles' => [
'class' => ImageStyleBehavior::className(),
'path' => \Yii::getAlias('#webroot') . '/files/userphotos/styles',
'url' => \Yii::getAlias('#web') . '/files/userphotos/styles',
'attribute' => 'photo',
'styles' => [
'300x300' => [$this, 'style300'], //can be any valid callable
'100x100' => [$this, 'style100'], //can be any valid callable
]
]
];
}
The photo have default value of noavatar.png, and when I try to insert, I get this error:
Exception 'Imagine\Exception\RuntimeException' with message 'Unable to open image /var/www/c2c/Care2Shine/www/files/userphotos/'
Is there a way for me to prevent behavior on insert actions?

You can remove specific named behaviors by detaching them:
$model->detachBehavior('styles');
Or, if it's the only behavior, you can just detach all:
$model->detachBehaviors();
To ensure you only detach on insert, check isNewRecord property.

Does the ImageStyleBehavior extend the AttributeBehavior ? in that case you should be able to use:
public function behaviors()
{
return [
[
'class' => AttributeBehavior::className(),
'attributes' => [
ActiveRecord::EVENT_BEFORE_INSERT => 'attribute1',
ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
],
'value' => function ($event) {
return 'some value';
},
],
];
}

Related

How to make action() accessible on my Controller, any idea?

Some help needed?
I already going numb on this, on the controller, the action link is not being found (404), but the action "Other" works fine.
- basic theme
- inside a module
- url I am calling is /admin/banner-image/link
- namespace app\modules\admin\controllers
I am using krajee EditableColumn and grid here.
Please, any idea, what am I missing?
https://gist.github.com/moplin/0c43c38bf0b42d74a3a13a51e4356edf
class BannerImageController extends Controller
{
public function actions()
{
return ArrayHelper::merge(parent::actions(), [
'link' => [
'class' => EditableColumnAction::className(),
'modelClass' => CexBannerImg::className(),
'outputValue' => function ($model, $attribute, $key, $index) {
return $model->$attribute;
},
'outputMessage' => function($model, $attribute, $key, $index) {
return '';
},
]
]);
}
public function actionOther()
{
return 'other';
}
}
and the EditableColumn configuration en the grid.
[
'class' => 'kartik\grid\EditableColumn',
'attribute' => 'route',
'filter' => false,
'editableOptions' => [
'header' => '¿Link?',
'size'=>'md',
'formOptions'=>['action' => ['banner-image/link']],
'inputType' => Editable::INPUT_TEXT ,
],
'format' => ['text'],
],

Yii2 Attach behavior to Application::EVENT_BEFORE_REQUEST

I am trying to check for a cookie on Application::EVENT_BEFORE_REQUEST. What I did is overriding the events function from Behavior model and return a custom function checkLanguage on the event that I mentioned above. I am triggering as beforeRequest in my controller ( in first I tried in the backend/config/main.php but it seems that the CheckIfLoggedIn class can't be reached from there ) and the request goes e to the public function events() in the CheckIfLoggedIn class but doesn't go on to the checkLanguage function.
This is my SiteController behaviors:
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['login', 'error'],
'allow' => true,
],
[
'actions' => ['logout', 'index', 'language'],
'allow' => true,
'roles' => ['#'],
],
],
],
'verbs' => [
'class' => VerbFilter::className(),
'actions' => [
'logout' => ['post'],
],
],
'as beforeRequest' => [
'class' => 'backend\components\CheckIfLoggedIn'
]
];
}
and CheckIfLoggedIn.php class:
<?php
namespace backend\components;
use yii\base\Behavior;
use yii\web\Application;
class CheckIfLoggedIn extends Behavior
{
public function events()
{
return [
Application::EVENT_BEFORE_REQUEST => "changeLanguage"
];
}
public function changeLanguage()
{
if(\Yii::$app->getRequest()->getCookies()->has('lang')){
\Yii::$app->language = \Yii::$app->getRequest()->getCookies()->getValue('lang');
}
}
}
The thing is you are trying to attach an Application event at controller level inside the behavior whereas the documentation says you should use the Application config .
Make the following updates, remove the events() function from your class.
backend/components/CheckIfLoggedIn.php
namespace backend\components;
use yii\base\Behavior;
class CheckIfLoggedIn extends Behavior
{
public function changeLanguage()
{
if(\Yii::$app->getRequest()->getCookies()->has('lang')){
\Yii::$app->language = \Yii::$app->getRequest()->getCookies()->getValue('lang');
}
}
}
and add the following to the common/config or backend/config if you want it for backend only
'on '.yii\web\Application::EVENT_BEFORE_REQUEST => [
'backend\components\CheckIfLoggedIn','changeLanguage'
] ,
remember to add it at the same level where id or components index is defined like this
return [
'id' => 'app-backend' ,
'on '.yii\web\Application::EVENT_BEFORE_REQUEST => [
'backend\components\CheckIfLoggedIn','changeLanguage'
] ,
For the first try add a die("hello world"); in the start of changeLanguage so you can confirm it is entering the function changeLanguage.
Hope it helps

zf2/zf3 how to validate dependent inputs in collection's fieldset?

I have a form. The form has a Collection whose target element is a fieldset with a checkbox and a couple of text fields. The fieldset attached as the target element to Collection looks like this (simplified to avoid too much code):
class AFieldset extends Fieldset implements InputFilterProviderInterface
{
public function __construct(HydratorInterface $hydrator)
{
parent::__construct();
$this->setHydrator($hydrator)
->setObject(new SomeObject());
$this->add([
'type' => Hidden::class,
'name' => 'id',
]);
$this->add([
'type' => Checkbox::class,
'name' => 'selectedInForm',
]);
$this->add([
'type' => Text::class,
'name' => 'textField1',
]);
$this->add([
'type' => Text::class,
'name' => 'textField2',
]);
}
public function getInputFilterSpecification()
{
return [
'selectedInForm' => [
'required' => false,
'continue_if_empty' => true,
'validators' => [
['name' => Callback::class // + options for the validator],
],
],
'id' => [
'requred' => false,
'continue_if_empty' => true,
],
'textField1' => [
'required' => false,
'continue_if_empty' => true,
'validators' => [
['name' => SomeValidator::class],
],
],
'textField2' => [
'required' => true,
'validators' => [
['name' => SomeValidator::class],
],
],
],
}
}
I'd like to validate textField1 and textField2 based on if selectedInForm checkbox is checked in the form.
How could I do this?
I though of using a Callback validator for selectedInForm checkbox like this:
'callback' => function($value) {
if ($value) {
$this->get('textField1')->isValid();
// or $this->get('textField1')->getValue() and do some validation with it
}
}
but the problem with it is that, for some reason, the posted value of textField1 value isn't attached to the input yet. Same is true for textField2.
Two option is available. One is where you started, with callback validators.
The other one is to write a custom validator, and to make it reusable I recommend this solution.
<?php
use Zend\Validator\NotEmpty;
class IfSelectedInFormThanNotEmpty extends NotEmpty
{
public function isValid($value, array $context = null): bool
{
if (! empty($context['selectedInForm']) && $context['selectedInForm']) {
return parent::isValid($value);
}
return true;
}
}
And then you can use it as every other validator:
'textField2' => [
'required' => true,
'validators' => [
['name' => IfSelectedInFormThanNotEmpty::class],
],
],
This may not be your exact case, but I hope it helped to get the idea.
You may define options to make it more reusable with a configurable conditional field in public function __construct($options = null).

Require at least one element in ZF2 FieldSet

The problem
I have a Form and a FieldSet. I would like to validate that the FieldSet is not empty. Also, I want to validate each field in the FieldSet.
So far, whatever I have tried is validating one or the other, but not both. If elements is present in the Form's input filter specification, then it validates that elements is not empty, but does not validate the bar and baz fields of FieldSet. And, of course, the other way around. Any clue as to how to approach this issue would be much appreciated.
The Form
class FooForm extends Form implements InputFilterProviderInterface
{
public function init()
{
$this->add([
'name' => 'elements',
'type' => Collection::class,
'required' => true,
'options' => [
'target_element' => [
'type' => SomeElementFieldSet::class
]
]
]);
}
public function getInputFilterSpecification()
{
return [
[
'name' => 'elements',
'required' => true,
'validators' => [
['name' => 'NotEmpty']
]
]
];
}
}
The FieldSet
class SomeElementFieldSet extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add(['name' => 'bar']);
$this->add(['name' => 'baz']);
}
public function getInputFilterSpecification()
{
return [
[
'name' => 'bar',
'required' => true,
'validators' => [
['name' => 'NotEmpty']
]
],
[
'name' => 'baz',
'required' => true,
'validators' => [
['name' => 'NotEmpty']
]
]
];
}
}
Edit: Added full validation spec.
After getting some hints on Google and digging through the source code, I found a solution. Unfortunately the zend-inputfilter implementation is a little buggy and won't work nicely with getInputFilterSpecification(), but we can just construct our own InputFilter and return that directly:
The Form
class FooForm extends BaseForm
{
public function init()
{
$this->add([
'name' => 'elements',
'type' => Collection::class,
'options' => [
'target_element' => [
'type' => SomeElementFieldSet::class
]
]
]);
}
public function getInputFilter()
{
if (!$this->filter) {
$this->filter = new InputFilter();
/** #var Collection $elementsCollection */
$elementsCollection = $this->fieldsets['elements'];
/** #var SomeElementFieldSet $elementsFieldSet */
$elementsFieldSet = $elementsCollection->getTargetElement();
$collectionFilter = new CollectionInputFilter();
$collectionFilter->setIsRequired(true);
$collectionFilter->setInputFilter(
$elementsFieldSet->getInputFilterSpecification()
);
$this->filter->add($collectionFilter, 'elements');
}
return $this->filter;
}
}
This will validate that there is at least one element in the collection. And will validate all the elements one by one by the FieldSet's specification.
One problem persists, though. Whenever the collection is empty, the validation will return false, but will not return any messages. This is due to a bug in the zend-inputfilter component. Issues reported here and here. But that is another problem altogether.
Use setValidationGroup() method in the Form object by specifying an array of input fields you want to validate. Please refer to the Doc!
You may give a try this way. Though I have added some extra fields to the form for testing purpose only.
class FooForm extends Form implements InputFilterProviderInterface
{
public function __construct($name = null, $options = array())
{
parent::__construct($name, $options);
$this->add(['name' => 'title']);
$this->add([
'name' => 'elements',
'type' => Collection::class,
'required' => true,
'options' => [
'target_element' => [
'type' => SomeElementFieldSet::class,
],
],
]);
$this->add([
'type' => 'submit',
'name' => 'submit',
'attributes' => [
'value' => 'Post'
],
]);
// I pointed this. Here you can specify fields to be validated
$this->setValidationGroup([
'title',
'elements' => [
'bar',
],
]);
}
public function getInputFilterSpecification()
{
return [
[
'name' => 'title',
'required' => true,
'validators' => [
['name' => 'NotEmpty']
]
],
];
}
}
And your fieldset class should be
class SomeElementFieldSet extends Fieldset implements InputFilterProviderInterface
{
public function init()
{
$this->add(['name' => 'bar']);
$this->add(['name' => 'baz']);
}
public function getInputFilterSpecification()
{
return [
[
'name' => 'bar',
'required' => true,
'validators' => [
['name' => 'NotEmpty']
]
],
[
'name' => 'baz',
'required' => true,
'validators' => [
['name' => 'NotEmpty']
]
]
];
}
}
Hope this would help!

filter for custom column in grid view which is not in table yii2

I have added this 'sla_status' field in gridview and every thing is working fine only issue is I am unable to set filter values for this.
'sla_status' is not in my table.
[
'label' => Yii::t('app','Sla Status'),
'format' => 'raw',
'filterType' => GridView::FILTER_SELECT2,
'filter' => $status,
'filterWidgetOptions' => [
'options' => [
'placeholder' => Yii::t('app','All...' )
],
'pluginOptions' => [
'allowClear' => true
]
],
'headerOptions' => ['style' => 'text-align:center;color:#337ab7'],
'value' => function ($model, $key, $index, $widget)
{ }
]
and $status is $status = array('0'=>Yii::t('app', 'Inactive'),'1'=>Yii::t('app', 'Active'));
Add a public variable $sla_status and add sla_status as safe in the rules array in search model
public $sla_status;
public function rules()
{
return [
[['sla_status'], 'safe'],
];
}
in Search model you should add attribute sla_status
and in search() add required where.
for example:
class TaskSearch extends Task{
public $sla_status;
public function search($params){
$query = Task::find();
if($this->sla_status){ $query->andWhere(...); }
}}
Add your field "sla_status" to "safe" in rules method of your model or search model like
[['sla_status'], 'safe'],

Categories