Save value of checkboxlist to db in yii2 - php

I have a yii2 form which contain a checkbox list items which i made like this:
<?php $CheckList = ["users" => 'Users', "attendance" => 'Attendance', "leave" => 'Leave', "payroll" => 'Payroll'];?>
<?= $form->field($model, 'MenuID')->checkboxList($CheckList,['separator'=>'<br/>']) ?>
Now what i need is to save the values in the database column as a comma separated value.
I tried to modify the create function in my controller in this way:
public function actionCreate()
{
$model = new Role();
if ($model->load(Yii::$app->request->post())) {
if ($model->MenuID != " ") {
$model->MenuID = implode(",", $model->MenuID);
}
$model->save();
return $this->redirect(['view', 'id' => $model->RoleID]);
} else {
return $this->render('create', [
'model' => $model,
]);
}
}
But the values are not being saved in the database

You need to set your model rules().
When you call $model->load(Yii::$app->request->post()); the framework call method setAttributes() with param $safeOnly = true. This method with param $safe = true check if attributes are safe or not according to the rules of model. If you haven't any rules on the model all attributes are considered unsafe so your model is not populated.
Add rules() on your model and your code works
class Role extends yii\db\ActiveRecord
{
...
public function rules()
{
return [
['MenuID', 'your-validation-rule'],
];
}
...
Some additional info
N.B. If you do not specify scenario in the rules the default scenario is 'default' and if during instantiate of model object you set scenario to another didn't work. My example:
You have the same rules as I wrote before and you run this code
...
$model = new Role(['scenario' => 'insert']);
if ($model->load(Yii::$app->request->post())) {
...
model is empty after load becouse any rules is founded in 'insert' scenario and your problem is back. So if you want a rule that work only in particular scenario you must add 'on' rules definition. Like this:
...
public function rules()
{
return [
['MenuID', 'your-validation-rule', 'on' => 'insert'],
];
}
...
For more example and explanations visit:
Declaring Rules
load()
setAttributes()
safeAttributes()

Related

Rules Scenario on model based action and parameter

Is it possible to create customize rule for a model in Yii2 with
name of action and its parameter.
I know that rules in model can create by on action.
Because I have a model and controller like this :
Model
public function rules()
{
$rules = [
[['pre_approved_by', 'pre_approved_date'], 'required', 'on' => 'pre'],
[['first_approved_by', 'first_approved_date'], 'required', 'on' => 'first'],
];
return $rules;
}
Controller
public function acctionApprove($hierarchy, $id){
$model = $this->findModel($id);
if($hierarchy != 1){
$model->scenario = "pre";
}else{
$model->scenario = "first";
}
}
Please advise
You can create a conditional validation. With this kind of rule you can write your own function.
For example:
['state', 'required', 'when' => function($model) {
return $model->country == 'USA';
}]
For a detailed explaination, check out the official doc

Laravel : how to validate a form field according to possible values of a Model's attribute?

I'm performing validation of a form, where a user may select a range of values (based on a set of entries in a model)
E.g. I have the Model CfgLocale(id, name)
I would like to have something like:
CfgLocale->listofAvailableIds() : return a array
What I did is:
Inside Model this method:
class CfgLocale extends Model
{
protected $table = 'cfg_locales';
public static function availableid()
{
$id_list = [];
$res = self::select('id')->get();
foreach($res as $i){
$id_list[] = $i->id;
}
return $id_list;
}
}
On Controller for validation I would do then:
$this->validate($request, [
'id' => 'required|integer|min:1',
...
'locale' => 'required|in:'.implode(',', CfgLocale::availableid()),
]);
Any better Idea, or Laravel standard to have this done?
Thanks
You can use exists rule of laravel.You can define a validation rule as below. Might be this can help.
'locale' => 'exists:cfg_locales,id'
Use this code instead,
class CfgLocale extends Model
{
protected $table = 'cfg_locales';
public static function availableid()
{
return $this->pluck('id')->toArray();
}
}
pluck method selects the id column from your table and toArray method converts your model object collection into array.
Know more about Laravel Collections here.
This will return an array of IDs:
public static function availableid()
{
return $this->pluck('id')->toArray();
}
https://laravel.com/docs/5.3/collections#method-pluck
https://laravel.com/docs/5.3/collections#method-toarray

Yii2 dataprovider with manytomany relation

I try to build a grid view with many-to-many relations. So I need a query for the ActiveDataProvider .
I have a table 'ressource', a table 'type' and between them a table 'historique'.
I have the good relation in my models but I don't know how to create the dataProvider.
In my model Ressource :
public function getHistorique()
{
return $this->hasMany(Historique::className(), ['idType' => 'idType']);
}
public function getType()
{
return $this->hasMany(Type::className(), ['idType' => 'idType'])
->viaTable(Historique::className(), ['idRessource' => 'idRessource']);
}
In my model Historique :
public function getType()
{
return $this->hasOne(Type::className(), ['idType' => 'idType']);
}
public function getRessource()
{
return $this->hasOne(Ressource::className(), ['idRessource' => 'idRessource']);
}
and finally in my model Type :
public function getHistorique()
{
return $this->hasMany(Historique::className(), ['idType' => 'idType']);
}
public function getRessource()
{
return $this->hasMany(Ressource::className(), ['idRessource' => 'idRessource'])
->viaTable(Historique::className(), ['idType' => 'idType']);
}
So in the Controller (in fact my ModelSearch), I want to have ressources with type from the table historique. I don't know what I have to add after
Ressource::find();
I think you use RessourceSearch()->search() method. So inside it you have something like this:
$query = Ressource::find();
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
// Here is list of searchable fields of your model.
$query->andFilterWhere(['like', 'username', $this->username])
->andFilterWhere(['like', 'auth_key', $this->auth_key])
return $dataProvider;
So, basically, you need to add additional Where you your query and force to join relation table. You can do that using joinWith method to join additional relation and andFilterWhere using table.field notation for adding filter parameters. For example:
$query = Ressource::find();
$query->joinWith(['historique', 'type']);
$query->andFilterWhere(['like', 'type.type', $this->type]);
$query->andFilterWhere(['like', 'historique.historique_field', $this->historique_field]);
Also do not forget to add rules for additional filters in your search model. For example above, you should add to your rules() array something like that:
public function rules()
{
return [
// here add attributes rules from Ressource model
[['historique_field', 'type'], 'safe'],
];
}
You can use any additional validation rules for that fields

yii2 validation rules on update

I have a model and validation rules for it:
class User extends ActiveRecord implements IdentityInterface
{
...
public function rules()
{
return [
[['username', 'password', 'email'], 'required', 'on' => 'insert'],
[['password', 'email'], 'required', 'on' => 'update'],
]
}
Actually the code produces no validators. When I remove 'on' section, everything goes well.
Digging in official documentation and search thru The Web didn't help me to understand what is the issue, and why can't I have custom required fields sets for different actions.
The Scenario is not automaticaly setted by Yii2 ActiveReccoed. If you need a specific scenario you must create it and assign
E.g. for update ...
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['update'] = ['password', 'email'];//Scenario Values Only Accepted
return $scenarios;
}
Also you can set scenario in your actionUpdate
public function actionUpdate($id)
{
$model = $this->findModel($id);
$model->scenario = 'update';
........
}

Validating a Unique Slug on Update in Laravel 5

I currently have a model that has a text field and a slug field.
I validate that the slug is unique in my form request class:
public function rules()
{
return [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:questions'
];
}
This works fine on create and properly denies the creation of duplicate slugs. However on my update method, it won't let me save a record because the slug already exists. Of course the slug does exist, but it exists on the record being edited, so I would like to continue to allow it to be saved. However, it should not be able to be changed to a slug on ANOTHER record.
Here's what my update ArticlesController method looks like:
public function update(Article $article, ArticleRequest $request)
{
$article->update($request->all());
return redirect('articles');
}
Is there a way to make this work in L5?
Try to modify your rule like following(in form request class):
public function rules()
{
return [
'name' => 'required,min:3',
'slug' => 'required|alpha_dash|unique:categories,slug,'.$this->id')
];
}
It works for me.
In unique rule you may specify id you want to ignore.
You can create 2 separate request (one for create and one for update), but you can do it also this way checking if if is set(I assume your update url looks like /questions/2 ):
public function rules()
{
$rules = [
'name' => 'required|min:3',
'slug' => ['required', 'alpha_dash']
];
$rule = 'unique:questions';
$segments = $this->segments();
$id = intval(end($segments));
if ($id != 0) {
$rule .= ',slug,' . $id;
}
$rules['slug'][] = $rule;
return $rules;
}
}
If you must have the ability to update a slug, projects I've worked on usually require it is not editable after creation, then you can use laravel's built in rule to ignore a certain record on the table by primary key.
$rules['slug'] = "required|unique:questions,slug,{$id}";
http://laravel.com/docs/5.0/validation
see "Forcing a unique rule to ignore a given ID"
In EditArticleRequest:
public function $rules ()
{
$id = $this->id;
return [
'name' => 'required|min:3',
'slug' => "required|alpha_dash|unique:articles,slug,$id",
];
}
Here is how I do it in Laravel 5.3 in details:
1- Create a new Form Request class by executing the next command in your terminal:
php artisan make:request ArticleFormRequest
Where ArticleFormRequest is the name of the form request class. This command will create a file called ArticleFormRequest.php in app/Http/Requests directory.
2- Open that created file and remove its content then place the next content in it:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Article;
class ArticleFormRequest extends FormRequest
{
protected $rules = [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:articles,slug',
];
// protected $user; // in case you want the current authenticated user
protected $request_method;
protected $id;
public function __construct(Request $request)
{
// $request->user() returns an instance of the authenticated user
// $this->user = $request->user(); // in case you want the current authenticated user
// $request->method() returns method of the request (GET, POST, PUT, DELETE, ...)
$this->request_method = strtoupper($request->method());
// segments(): Returns an array containing all of the segments for the request path
// it is important to assign the returned "segments" array to a variable first before using it, otherwise an error will occur
$segments = $request->segments();
// note this way will be valid only if "id" of the element is the last segment
$this->id = end($segments);
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules = $this->rules;
if ($this->request_method == "POST") {
// do nothing..
} elseif (in_array($this->request_method, ["PUT", "PATCH"])) {
$article = Article::find($this->id);
if ($article) {
// forcing a unique rule to ignore a given id | https://laravel.com/docs/5.3/validation
$rules["slug"] = [
"required",
"alpha_dash",
Rule::unique("articles", "slug")->ignore($article->id, "id"),
];
// this is also can be used
// $rules['slug'] = "required|alpha_dash|unique:articles,slug,$article->id,id";
}
}
return $rules;
}
}
3- In your controller, you can use that ArticleFormRequest in store() and update() methods like this:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ArticleFormRequest;
class ArticlesController extends Controller
{
public function store(ArticleFormRequest $request)
{
// your code here..
}
public function update(ArticleFormRequest $request, $id)
{
// Your code here..
}
}
As already mentioned you can use the ignore feature in the validator functionality.
Just reference the id of the item you wish to ignore and make sure that when you update you use a patch request!
See more info here! http://laravel.com/docs/5.0/validation#rule-unique
protected $rules = [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:questions'
];
public function rules()
{
$rules = $this->rules;
if ($this->isMethod('patch'))
{
$id = $this->articles;
$rules['slug'] = $rules['slug'].',slug,'.$id;
}
return $rules;
}

Categories