I have a model Printer, a model Category, and a model for the relation between the two models CategoryPrinterRel
In the CategoryPrinterRel model I need a unique validator between the $category_id, and the client of the printer $printer->client_id
Up till now I have tried
public function rules()
{
[['category_id', $this->printer->client_id], 'unique', 'targetAttribute' => ['category_id']]
}
Is there any other way to do this though?
The problem with the method I'm using is that when the printer object is empty, trying $this->printer->client_id gives an error
I was looking for something more elegant, or built in. For now I have opted for a custom validator however. In the model:
public function rules()
{
return [
[['category_id', 'printer_id'], 'integer'],
[['printer_id', 'category_id'], 'required'],
[['cat_id'],'validateUniquenessOnClient']
];
}
public function validateUniquenessOnClient($attribute, $params, $validator)
{
$isPrinterUniqueOnClient = DbPrinterRepository::IsPrinterCatRelUniqueOnClient($this->category_id, $this->printer_id);
if(!$isPrinterGroupUniqueOnClient)
{
$this->addError($attribute, "There is already a printer using that category ({$this->cat->name}).");
}
}
Related
I am developing a web application using Laravel 8. I have noticed that quite a lot of things have changed in Laravel 8 including factories.
I have a factory class MenuCategoryFactory for my MenuCategory model class with the following definition.
<?php
namespace Database\Factories;
use App\Models\Menu;
use App\Models\MenuCategory;
use Illuminate\Database\Eloquent\Factories\Factory;
class MenuCategoryFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* #var string
*/
protected $model = MenuCategory::class;
/**
* Define the model's default state.
*
* #return array
*/
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => Menu::factory()->create(),
];
}
}
In my code (database seeder class), I am trying to override the menu_id as follow while I am creating the factories.
$restaurant = Restaurant::first();
MenuCategory::factory()->create([
'menu_id' => $restaurant->menu->id
]);
But it is not using the value I passed, $restaurant->menu->id. Instead, it is creating a new menu. What is wrong missing in my code and how can I fix it?
In your factory definition, don't call ->create(), instead set it up like this:
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => Menu::factory(),
];
}
Then you you should be able to set up related models (assuming you the relationships setup in the model) like this:
$restaurant = Restaurant::first();
MenuCategory::factory()->for($restaurant)->create();
Change the definition to
public function definition()
{
return [
'name' => $this->faker->name,
'menu_id' => function() {
Menu::factory()->create()->id,
}
];
}
then you can replace the value
Here's a question about a topic that Ive been thinking about for a while.
In Yii2, it is recommended generally to create Form Models for your requests. Rules are added to those models to validate the input. An example is the EntryForm in the Yii2 guide
<?php
namespace app\models;
use Yii;
use yii\base\Model;
class EntryForm extends Model
{
public $name;
public $email;
public function rules()
{
return [
[['name', 'email'], 'required'],
['email', 'email'],
];
}
}
My problem is, when we have nested objects. An example is a form for creating Customer with multiple Branches. If Customer and Branch are two separate models, but both get submitted in one form, what is the best option for validating input from such a nested form. Bear in mind that here the input is nested. Example:
{
"name": "customer",
"vat_no": "12345678",
"time_zone": 277,
"category": 1,
"email": "customer#mycustomer.com",
"stores":[
{
"name": "store1",
"phone": 1234567
},
{
"name": "store2",
"phone": 2345678
}
]
}
For simple cases you may use one model and custom validator inside of your form model:
public function rules() {
return [
// ...
['stores', 'validateStores'],
];
}
public function validateStores() {
$phoneValidator = new StringValidator(); // use real validators
$nameValidator = new StringValidator(); // use real validators
foreach ($this->stores as $store) {
if (!$phoneValidator->validate($store['phone'], $error)) {
$this->addError('stores', $error);
return; // stop on first error
}
if (!$nameValidator->validate($store['name'], $error)) {
$this->addError('stores', $error);
return; // end on first error
}
}
}
validateStores() may be extracted to separate validator class, then you may also use EachValidator instead of foreach.
For more complicated nested models you should probably create separate StoreForm model for stores (so you will have nested form models), and call validate() on children.
/**
* #var StoreForm[]
*/
public $stores;
public function rules() {
return [
// ...
['stores', 'validateStores'],
];
}
public function validateStores() {
foreach ($this->stores as $store) {
if (!$store->validate()) {
$this->addError('stores', 'Stores config is incorrect.');
return;
}
}
}
I have a very simple scenario where I'm receiving a list of Variance Positions from the end user. To be able to validate the structure of the input, I created the following model for the single item that I should receive:
class VariancePositionsForm extends Model{
public $id;
public $position;
public function rules()
{
return [
[['id','position'], 'required'],
[['id', 'position'], 'integer'],
];
}
}
And in the controller, I have the following:
$variancePositions = [];
for($i=0;$i<sizeof(Yii::$app->request->post());$i++)
{
$variancePositions[] = new VariancePositionsForm();
}
VariancePositionsForm::loadMultiple($variancePositions, Yii::$app->request->post());
When I try to var_dump($variancePositions) however, I'm finding that its empty. In other words, loadMultiple() is not loading the models. What am I doing wrong?
Because you don't load the model from the form, only from json you have to add an empty string into the last parameter in this function:
VariancePositionsForm::loadMultiple($variancePositions, Yii::$app->request->post(), '');
look here:
https://github.com/yiisoft/yii2/blob/master/framework/base/Model.php#L884
I have advanced app. I create CRUD for User model. So i got update action. I tried to update password by adding
<?= $form->field($model, 'password')->passwordInput() ?>
But it call error, something like "password is write-only variable"
I tried to use field
<?= $form->field($model, 'new_password')->passwordInput() ?>
With adding in actionUpdate model->setPassword($this->new_password); and it throw Getting unknown property: common\modules\user\controllers\DefaultController::new_password.
But model->setPassword('123456'); successfully setting pussword 123456.
How can i get new_password field from view, to put it in model->setPassword('there');
Or maybe exist best way to do it?
UPD
I tried do it. Is not work.
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
if ($this->new_password) {
$this->setPassword($this->new_password);
}
return true;
} else {
return false;
}
}
UPD2
public function setPassword($password) {
$this->password_hash = Yii::$app->security->generatePasswordHash($password);
}
And password_hash writing in database. I can easy change hash, via generated CRUD, but don't know how to use setPassword() in updateAction.
You can try write updatePassword function like setPassword with another variable
public function updatePassword($new_password) {
$this->password_hash = Yii::$app->security->generatePasswordHash($new_password);
}
declare a variable
public $new_password;
And add it in rules()
public function rules() {
return [
//...
['new_password', 'required'],
['new_password', 'string', 'min' => 6],
];
}
And at actionUpdate in your controller add
$model->updatePassword($model->new_password);
This should help
Here "$this" is your Controller which of course, doesn't have 'new_password' property. You'd better not set new password in controller, but do it in model, for example in beforeSave method:
if ($this->new_password) {
$this->setPassword($this->new_password);
}
I am currently working in yii, I have designed a user module which consists user registraion, user login & change password rule.
for these three process I have designed only one model i.e. user model. in this model I have defined a rule:
array('email, password, bus_name, bus_type', 'required'),
This rule is valid for actionRegister. but now I want to define a new required rule for actionChangePassword,
array('password, conf_password', 'required'),
How can I define rule for this action ?
Rules can be associated with scenarios. A certain rule will only be used if the model's current scenario property says it should.
Sample model code:
class User extends CActiveRecord {
const SCENARIO_CHANGE_PASSWORD = 'change-password';
public function rules() {
return array(
array('password, conf_password', 'required', 'on' => self::SCENARIO_CHANGE_PASSWORD),
);
}
}
Sample controller code:
public function actionChangePassword() {
$model = $this->loadModel(); // load the current user model somehow
$model->scenario = User::SCENARIO_CHANGE_PASSWORD; // set matching scenario
if(Yii::app()->request->isPostRequest && isset($_POST['User'])) {
$model->attributes = $_POST['User'];
if($model->save()) {
// success message, redirect and terminate request
}
// otherwise, fallthrough to displaying the form with errors intact
}
$this->render('change-password', array(
'model' => $model,
));
}