Yii2 - Set safe attributes on scenarios - yii

How can I safe my attributes for a massive assignment when I'm using a scenario (in my example 'update' scenario)?
Here is my rules:
public function rules()
{
return [
[['user_id', 'type', 'name', 'status'], 'required'],
[['country_id', 'address', 'name', 'status'], 'safe', 'on' => 'update'],
];
}
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['update'] = ['user_id', 'type', 'name'];
return $scenarios;
}
When I'm checking the safe attributes in my controller using $model->safeAttributes(), I'm only getting the required attributes that are in the required of the 'update' scenario of the function scenarios().
And of course, the $model->load(Yii::$app->request->post()) function does not retreive other attributes.
How can I put them safe?
Even if I want to add some other rules, I can't find the way!

You need to add them all in scenario
$scenarios['update'] = ['user_id', 'type', 'name', 'country_id', 'address', 'name', 'status'];

Set the scenario before you load the model:
$model->setScenario('update');
$model->load(Yii::$app->request->post())

Related

D8 migrate to field_ip address attached to accounts

I am working on a migrate script in D8 that pulls from a MySQL DB and will populate a table in D8 created by this module...
https://github.com/bjaxelsen/field_ipaddress
This module is a D8 port of this one...
https://www.drupal.org/project/field_ipaddress
anyways I got the module installed and added the field to the accounts with the unlimited number of values.
A general user migrate was already made and works fine. It will migrate the username, email, and status into Drupal and all is good here.
The second migration is to move a list of user IP addresses into D8. My dataset will look like this...
user_id slstatus status_id ipFrom ipTo modifyDateUnix
50374 1 0 1.1.1.1 1.1.1.1 1505415351
25108 0 1 4.4.4.0 4.4.4.255 1479243329
The real code runs the INET_ATON function in mysql to return integers, but the above to show the intent.
In the prepare row function I also take the IP Addresses and convert them into hex values, which is needed for the db table storage.
slstatus relates to the user's active status and status_id relates to the ip record's status. These are used to determine a new variable called deleted. I also make an variable called idx which is a...well an index. All of these new fields are added back to the row.
My YAML looks like this and is where my headache begins...
id: UserIpsMigrate
migration_group: 'UMG'
label: 'User IP Migration'
source:
plugin: UserIpsMigrate
process:
'field_ip_address/bundle': 'user'
'field_ip_address/deleted': deleted
'field_ip_address/entity_id':
plugin: migration_lookup
migration: UserMigrate
source: user_id
'field_ip_address/revision_id':
plugin: migration_lookup
migration: UserMigrate
source: user_id
'field_ip_address/langcode': 'en'
'field_ip_address/delta': idx
'field_ip_address/field_ip_address_ip_ipv6': 0
'field_ip_address/field_ip_address_ip_from': ipFrom
'field_ip_address/field_ip_address_ip_to': ipTo
destination:
plugin: entity:user
default_bundle: migration
migration_dependencies:
required:
- user_migrate
The errors I am getting show that this migration is trying to create a user...which will fail with this data and so I am certain I need to use a different destination plugin, but I have no idea...
1) which destination plugin that I should be using
2) if I went "off the rails" with my yaml
I found a solution.
First I changed my yaml file to look like this.
id: UserIpsMigrate
migration_group: HEUMG
label: 'User IP Migration'
source:
plugin: tUserIpsMigrate
process:
bundle:
plugin: default_value
default_value: user
deleted: deleted
entity_id:
plugin: migration_lookup
migration: UserMigrate
source: user_id
revision_id:
plugin: migration_lookup
migration: UserMigrate
source: user_id
langcode:
plugin: default_value
default_value: en
delta: delta
field_ip_address_ipv6:
plugin: default_value
default_value: 0
field_ip_address_ip_from: ipFrom
field_ip_address_ip_to: ipTo
destination:
plugin: field_ip_address
migration_dependencies:
required:
- user_migrate
I also found that the functions GetIds() are very important in determining the mapping from source to destination targets and is used by the migration system when making the migration mapping table.
Extending the sqlBase class, I had a query to pull the data need for the migration and it would use 2 keys from different tables users and ipaddresses. So my source getIds() function looked like this...
/**
* {#inheritdoc}
*/
public function getIds() {
return [
'user_id' => [
'type' => 'integer',
'alias' => 'slc',
],
'siteLicense_id' => [
'type' => 'integer',
'unsigned' => FALSE,
],
];
}
...and my destination getIds ended up as this...
/**
* {#inheritdoc}
*/
public function getIds() {
return [
'entity_id' => [
'type' => 'integer',
'unsigned' => FALSE,
],
'deleted' => [
'type' => 'integer',
'size' => 'tiny',
],
'delta' => [
'type' => 'integer',
'unsigned' => FALSE,
],
'langcode' => [
'type' => 'string',
'max_length' => 32,
'is_ascii' => TRUE,
],
];
}
With the above getIds() defined you will have a new table create once you run a drush command, like "drush migrate:status".
The table "migrate_map_" + your migration id or in my case migrate_map_useripsmigrate is created with the fields of source_id_hash, sourceid1, sourceid2, destid1, destid2, destid3, destid4, source_row_status, rollback_status, last_imported and hash. The source and destination fields are mapped to the getIds defined above and this is how the migration system keeps track of all migrations ran, from this specific migration. Other migrations will have their own tables.
As for the destination plugin itself, I had to make my own destination plugin based on extending the DestinationBase class.
Extending DestinationBase requires you to define the internals of 3 functions, import, getIds and, fields. GetIds I covered above and fields are just like defining fields in an SQL source plugin. Import is how your destination plugin will save the source data to the destination. In my case, I used an insert query like this...
/**
* {#inheritdoc}
*/
public function import(Row $row, array $old_destination_id_values = []) {
$bundle = $row->getDestinationProperty('bundle');
$deleted = $row->getDestinationProperty('deleted');
$entity_id = $row->getDestinationProperty('entity_id');
$revision_id = $row->getDestinationProperty('revision_id');
$langcode = $row->getDestinationProperty('langcode');
$delta = $row->getDestinationProperty('delta');
$field_ip_address_ipv6 = $row->getDestinationProperty('field_ip_address_ipv6');
$field_ip_address_ip_from = $row->getDestinationProperty('field_ip_address_ip_from');
$field_ip_address_ip_to = $row->getDestinationProperty('field_ip_address_ip_to');
$result = $this->con->insert('user__field_ip_address')
->fields([
'bundle' => $bundle,
'deleted' => $deleted,
'entity_id' => $entity_id,
'revision_id' => $revision_id,
'langcode' => $langcode,
'delta' => $delta,
'field_ip_address_ipv6' => $field_ip_address_ipv6,
'field_ip_address_ip_from' => $field_ip_address_ip_from,
'field_ip_address_ip_to' => $field_ip_address_ip_to,
])
->execute();
return [
'entity_id' => $entity_id,
'deleted' => $deleted,
'delta' => $delta,
'langcode' => $langcode,
];
}
The return should be the same fields as defined in your destination getIds() function and in the same order.
This may not be the most ideal method of writing a destination plugin, but it does work.

Filter by a field that's a foreign key

Using Yii2.
I have a table called 'calificacion' that has a foreign key 'alumno-id' that points to field id in table alumno.
The column defined in the GridView widget is:
[
'header' => 'Alumno',
'attribute' => 'alumno.id',
'value' => 'alumno.name'
],
And it's showing perfectly, but the filter, in the header of the column, is not appearing. I want to have a textbox for writing the name of the alumno and get filtered. How can I achieve that?
EDIT: Here're the files https://dl.dropboxusercontent.com/u/7059378/Desktop.zip
First declare an attribute in your SearchModel.
public $alumno_name;
In your search model's rule add:
[['alumno_name'], 'safe'],
Join with alumno relation in search method (supponsinbly there is a alumno relation in your Calificacion model):
$query = Calificacion::find()
->joinWith(['alumno alumno']);
To sort with $alumno_name add:
$dataProvider->sort = [
'attributes' => [
//Other attributes here
'alumno_name' => [
'asc' => ['alumno.name' => SORT_ASC],
'desc' => ['alumno.name' => SORT_DESC],
],
]
];
To filter this you have to add:
$query->andFilterWhere(['like', 'alumno.name', $this->alumno_name]);
Finally in your grid view add:
[
'attribute' => 'alumno_name',
'value' => 'alumno.name'
],
You can find more info here and here.
Also instead of header use 'label' => 'Alumno'.
You must first define a relation in Model of that table 'calificacion', like
public function getAlumno()
{
return $this->hasOne(Alumno::className(), ['id' => 'alumno_id']);
}
Than in the search model of Calificacion set that you are joining tables after validation, like
$query->joinWith('alumno');
Than set search like
$query->andFilterWhere([
'alumno.id' => $this.alumno_id
]);

How to make field enum migration yii2

I make field ENUM and the result is error when I use yii migrate/up on CMD windows.
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
$this->createTable('{{%user_social_media}}', [
'social_media' => $this->ENUM('facebook', 'google', 'twitter', 'github'),
'id' => $this->primaryKey(),
'username' => $this->string(),
'user_id' => $this->integer(11),
'created_at' => $this->integer(11),
'updated_at' => $this->integer(11),
], $tableOptions);
}
There is no enum() method at the moment since not every DB is supporting ENUM fields. You can do it manually though:
'social_media' => "ENUM('facebook', 'google', 'twitter', 'github')",
Note:
This solution is for Mysql only
For related Postgresql content visit here
Actually the best way of working this and keeping your migrations clean would be by using some tool/helper like the one provided by the yii2mod team https://github.com/yii2mod/yii2-enum
this way you can build the enum functionality on code, works like a charm.
i.e. an enum for genderType
<?php
namespace common\models\enums;
use yii2mod\enum\helpers\BaseEnum;
/**
* Class GenderType
*
* #package yii2mod\settings\models\enumerables
*/
class GenderType extends BaseEnum
{
// add as many genders as you need
const MALE_TYPE = 'MALE';
const FEMALE_TYPE = 'FEMALE';
public static $list = [
self::MALE_TYPE => 'Male',
self::FEMALE_TYPE => 'Female',
];
}
Use the following methods to access your Enum:
createByName() - Creates a new type instance using the name of a
value.
getValueByName() - Returns the constant key by value(label)
createByValue() - Creates a new type instance using the value.
listData() - Returns the associative array with constants values and
labels
getLabel()- Returns the constant label by key
getConstantsByName() - Returns the list of constants (by name) for
this type.
getConstantsByValue() - Returns the list of constants (by
value) for this type.
isValidName() - Checks if a name is valid for
this type. isValidValue() - Checks if a value is valid for this type.

SugarCRM - Add leads with auto-incremented ID

I use the SOAP API to add new leads to SugarCRM. Additionally, I use a plugin to assign an auto-incremented lead ID whenever a new lead is created (http://www.sugarforge.org/projects/autoincrement/).
Now, the plugin works fine, if I create a new lead via frontend. But, if I use the SOAP API, the function from the module, which assigns the auto-increment ID to the lead, does not trigger.
I create the lead via
$module = 'Leads';
$params = array(
'session' => $session,
'module_name' => $module,
'name_value_list' => array(
array('name' => 'id', 'value' => ''),
//array('name' => 'int_lead_id_c', 'value' => ''),
array('name' => 'first_name', 'value' => $_POST["first_name"]),
array('name' => 'last_name', 'value' => $_POST["last_name"]),
array('name' => 'phone_home', 'value' => $_POST["phone"]),
array('name' => 'email1', 'value' => $_POST["email"]),
array('name' => 'assigned_user_id', 'value' => '1'),
)
);
//Create the Lead record
$lead_result = $soapclient->call('set_entry', $params);
The function in the module is this one:
class SugarFieldAutoincrement extends SugarFieldBase {
/**
* Override the SugarFieldBase::save() function to implement the logic to get the next autoincrement value
* and format the saved value based on the attributes defined for the field.
*
* #param SugarBean bean - the bean performing the save
* #param array params - an array of paramester relevant to the save, most likely will be $_REQUEST
* #param string field - the name of the field
*/
public function save(&$bean, $params, $field, $properties, $prefix = '') {
}
}
How can I make sure, that this function is also triggered, when adding leads via SOAP API?
Thanks a lot for your help! :-)
David
You would need to set the field type to 'autoincrement' and the dbType to 'int' in the vardef record for the field.
If I'm not mistaken, the Database has a UUID() trigger on insert for most tables, so you should be able to completely remove the id field.
If you want to trigger the function before saving, you can use beforeSave logic hook.

Yii -CExistValidator for external key

In a simple project with Yii I have a model:
Checkins.php
* The followings are the available columns in table 'checkins':
* #property integer $id
* #property integer $user_id
* #property integer $item_id
* #property double $lat
* #property double $long
The two values $user_id and $item_id belong to other two tables:
public function relations()
{
// NOTE: you may need to adjust the relation name and the related
// class name for the relations automatically generated below.
return array(
'user' => array(self::BELONGS_TO, 'Users', 'user_id'),
'item' => array(self::BELONGS_TO, 'Items', 'item_id'),
);
}
I defined some validator:
public function rules()
{
// NOTE: you should only define rules for those attributes that
// will receive user inputs.
return array(
array('user_id, item_id, lat, long', 'required'),
array('item_id', 'exist', 'on'=>'create', 'attributeName'=>'id', 'className'=>'Items'),
array('user_id, item_id', 'numerical', 'integerOnly'=>true),
array('lat, long', 'numerical'),
// The following rule is used by search().
// Please remove those attributes that should not be searched.
array('id, user_id, item_id, lat, long', 'safe', 'on'=>'search'),
);
}
When in the actionCreate the method save() execute all the validators are working but not the one designed to check the presence of the external key in the model Items
array('item_id', 'exist', 'on'=>'create', 'attributeName'=>'id', 'className'=>'Items'),
And in case I try to save a Checkins that has a value in item_id without having the same id in the Items I don's any validation error.
Is this the right approach?
Thanks
I think it is most likely because you don't set the model's scenario to 'create' before saving (I'm just guessing that, because you don't attach the $checkin->save() code in the controller's actionCreate.). The other validation worked most likely because they aren't set to a specific scenario (i.e. they will work in all validation.).
For example if there is no Item with id 5, the code below
$checkin = new Checkin();
$checkin->text = 'test';
$checkin->item_id = 5;
if (!$checkin->validate()){
print_r($checkin->errors);
}
will work normally since the Checkin's scenario is not set to 'create' (the default is 'insert'). But I tried the code below
$checkin = new Checkin();
$checkin->scenario = 'create';
//or you can set it in the instantiation
//$checkin = new Checkin('create');
$checkin->text = 'test';
$checkin->item_id = 5;
if (!$checkin->validate()){
print_r($checkin->errors);
}
This will result a validation error.
Can you paste your model saving code?