Yii -CExistValidator for external key - yii

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?

Related

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.

Phalcon: save reference to new record in one transaction

Im trying to save a reference to a new object in a single transaction as shown here in the documentation under 'implicit transactions':
I am creating two new objects of the same class, one is then referencing the other. From the documentation, the save should be performed on TreeNodeA when TreeNodeB is saved and the ID will be passed to TreeNodeB->parent_tree_node_id
This doesnt seem to be working, and it still being passed as an object as Im getting the error on the save function:
Object of class TreeNodes could not be converted to string
I've tried writing a saveTreeParentNodeId function in the model and also setting it using the alias, but neither seem to work.
$treeNode = new TreeNodes();
$treeNode->setConnectionService(Registry::setConnection(MyModel::MAIN_DB));
$parentNode = $treeNode->findFirst();
$treeNodeA = new TreeNodes();
$treeNodeA->tree_id = $parentNode->tree_id;
$treeNodeA->tree_parent_node_id = $parentNode;
$treeNodeA->tree_level_id = 2;
$treeNodeA->node_desc = "Test Node A";
$treeNodeB = new TreeNodes();
$treeNodeB->tree_id = $parentNode->tree_id;
$treeNodeB->tree_parent_node_id = $treeNodeA;
$treeNodeB->tree_level_id = 3;
$treeNodeB->tree_desc = "Test Node B";
$treeNodeB->save();
The model:
class TreeNodes extends MyModel
{
public $node_id;
public $tree_id;
public $tree_parent_node_id;
public $tree_level_id;
public $node_desc;
public function getSource()
{
return "TreeNodes";
}
public function setTreeParentNodeId(TreeNodes $parentNode){
$this->tree_parent_node_id = $parentNode->node_id;
}
public function initialize()
{
parent::initialize();
$this->belongsTo(
'tree_id',
'Organisations',
'TreeID',
array(
'alias' => 'organisation',
'reusable' => true
)
);
$this->hasOne(
'tree_id',
'TreeType',
'tree_id',
array(
'alias' => 'type',
'reusable' => true
)
);
$this->hasOne(
'tree_parent_node_id',
'TreeNodes',
'node_id',
array(
'alias' => 'parentNode'
)
);
}
}
Update
By updating the model to use belongsTo, Phalcon recognises the parentNode.
$this->belongsTo(
'tree_parent_node_id',
'TreeNodes',
'node_id',
array(
'alias' => 'parentNode'
)
);
This enables $treeNodeA to save implicitly when $treeNodeB is saved.
$treeNodeA->parentNode = $parentNode;
Unfortunately, $treeNodeB with a reference to $treeNodeA as the parentNode is NOT saved. No error message is returned either, just 'true'.
In the documentation example you linked, they assign the $robotPart object to $robot->robotPart. robotPart refers to the linked RobotPart object and not to the ID ( to which you are trying to assign your object )
$treeNodeB = new TreeNodes();
$treeNodeB->tree_id = $parentNode->tree_id;
// $treeNodeB->tree_parent_node_id = $treeNodeA;
$treeNodeB->parentNode = $treeNodeA;
$treeNodeB->save();
You should use parentNode here because this is the name you gave to your relationship via hasOne.
I haven't tested this myself, but by following the documentation's logic, this should push you in the right direction.
Alter you model relationships so you have both sides of the relation
// let Phalcon know that "tree_parent_node_id" is a reference to "node_id"
$this->hasOne(
'tree_parent_node_id', // your column
'TreeNodes', // referenced table
'node_id', // referenced table column
array(
'alias' => 'parentNode',
'foreignKey' => true
)
);
// let Phalcon know that "node_id" is being referenced as a FK in "TreeNodes"
$this->belongsTo(
'node_id', // PK
'TreeNodes', // referenced table
'tree_parent_node_id', // referenced table column
array('foreignKey' => ['message' => 'FK constraint error between node_id and tree_parent_node_id'])
);

SilverStripe duplicate entries even with unique index

I'm trying to prevent duplicate records when adding customer records in my CRM with the following index:
private static $indexes = array(
'IndexFirstSurName' => array(
'type' => 'unique',
'value' => '"FirstName","Surname"'
)
);
Note that I extended Customer from Member where FirstName and Surname came from:
class Customer extends Member
But SilverStripe is still allowing duplicate entries of FirstName and Surname combination? Has anyone experienced the same problem?
The Man, in my experience a validate() is still needed even when using indexing:
public function validate() {
$result = parent::validate();
if(Member::get()->filter(array('FirstName' => $this->FirstName, 'Surname' => $this->Surname))->first()) {
$result->error('First and Surname must be unique for each member.');
}
return $result;
}
Alternately for a more robust breakout:
public function validate() {
$result = parent::validate();
if($member = Member::get()->filter(array('FirstName' => $this->FirstName, 'Surname' => $this->Surname))->first()) {
if($member->FirstName == $this->FirstName){
$result->error('Your Surname is fine, please change your First Name.');
}
if($member->Surname == $this->Surname){
$result->error('Your First Name is fine, please change your Surname.');
}
}
return $result;
}
note that I extended Customer from Member were FirstName and Surname came from
I wonder if SilverStripe is attempting to set indexes on the non-existent fields Customer.FirstName and Customer.Surname. Maybe try qualifying the columns by prepending the table that is actually having the indexes added to it like this:
private static $indexes = array(
'IndexFirstSurName' => array(
'type' => 'unique',
'value' => '"Member"."FirstName","Member"."Surname"'
)
);
You might also consider decorating Member instead of subclassing it. That way you wouldn't need to qualify the query fragments in this way.
The way to extend Member on SilverStripe is by extending DataExtension. As theruss is saying, you are trying to create a unique index on the table Customer, where you prabably do not have the fields FirstName and Surname.
Try this instead
class Customer extends DataExtension
{
private static $indexes = array(
'IndexFirstSurName' => array(
'type' => 'unique',
'value' => '"FirstName","Surname"'
)
);
}
And then let SilverStripe know about your extension in config.yml
Member:
extensions:
- Customer
Now run /dev/build?flush and you should see your index being created.
Check here for more information about extensions.

Yii with join using CDbCriteria and CActiveDataProvider with custom Search

Hi I am using Yii to create an Application
I have modified the search() in model and I have come up with a problem.
Users can log in as admins, managers, clients
When a user is logged as a manager, he can view clients only from the store they both belong. So far so good, I've managed to accomplish that.
Now the problem is when I try to prevent managers from viewing other managers from the same store (and therefore edit each other's accounts) in CGridView.
The relations
/**
* #return array relational rules.
*/
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(
'authitems' => array(self::MANY_MANY, 'Authassignment', 'authassignment(userid, itemname)'),
'additionalContacts' => array(self::HAS_MANY, 'AdditionalContact', 'Client_Id'),
'store' => array(self::BELONGS_TO, 'Store', 'Store_Id'),
'user' => array(self::BELONGS_TO, 'User', 'User_Id'),
'genericPoints' => array(self::HAS_MANY, 'GenericPoint', 'Client_Id'),
);
}
The Custom model search
public function searchCustom()
{
// Warning: Please modify the following code to remove attributes that
// should not be searched.
$criteria=new CDbCriteria;
$criteria->addCondition('t.id !='.$this->id);
$criteria->compare('t.Created',$this->Created,true);
$criteria->compare('t.Updated',$this->Updated,true);
$criteria->compare('t.Discount',$this->Discount,true);
$criteria->compare('t.Discount_Type',$this->Discount_Type,true);
$criteria->addCondition('t.Store_Id ='.$this->Store_Id);
//$criteria->addCondition('t.id !='.$this->id);
//GET FIELDS FROM USER IN SEARCH
$criteria->with=array('user');
$criteria->compare('user.id',$this->User_Id,true);
$criteria->compare('user.First_Name',$this->First_Name,true);
$criteria->compare('user.Last_Name',$this->Last_Name,true);
$criteria->compare('user.Username',$this->Username,true);
$criteria->compare('user.Email',$this->Email,true);
$criteria->addCondition('user.Status = 1');
$criteria->with=array('authitems');
$criteria->compare('authitems.userid',$this->id,false);
$criteria->compare('authitems.itemname','client',false);
$criteria->together = true;
$criteria->order = 't.Created DESC';
return new CActiveDataProvider($this, array(
'criteria'=>$criteria,
));
}
And this the error I am getting
CDbCommand failed to execute the SQL statement: SQLSTATE[42S22]: Column not found: 1054 Unknown column 'user.Status' in 'where clause'. The SQL statement executed was: SELECT COUNT(DISTINCT `t`.`id`) FROM `client` `t` LEFT OUTER JOIN `authassignment` `authitems_authitems` ON (`t`.`id`=`authitems_authitems`.`userid`) LEFT OUTER JOIN `authassignment` `authitems` ON (`authitems`.`itemname`=`authitems_authitems`.`itemname`) WHERE (((((t.id !=2) AND (t.Store_Id =1)) AND (user.Status = 1)) AND (authitems.userid=:ycp0)) AND (authitems.itemname=:ycp1))
I know it has something to do with the relations the gii set up for me but I can't pinpoint it.
Perhaps authitems' MANY_MANY relation stops the user relation from loading?
Instead of making another assignement for $criteria->with (which will override the previous one), you should simply try :
$criteria->with=array('user', 'authitems');

Kohana ORM: Get results based on value of a foreign table

taxonomies
-id
-name
taxonomy_type
-taxonomy_id
-type_id
I've configured two models:
class Model_Taxonomy{
protected $_has_many = array('types'=>array());
}
class Model_Taxonomy_Type{
protected $_belongs_to = array('taxonomy' => array());
}
*Please note that taxonomy_type is not a pivot table.*
A taxonomy can have multiple types associated.
Then, what I'm trying to do is get all taxonomies that belong to a given type id.
This is would be the SQL query I would execute:
SELECT * FROM taxonomies, taxonomy_type WHERE taxonomy_type.type_id='X' AND taxonomies.id=taxonomy_type.taxonomy_id
I've tried this:
$taxonomies = ORM::factory('taxonomy')
->where('type_id','=',$type_id)
->find_all();
Obviously this doesn't work, but I can't find info about how execute this kind of queries so I have no clue.
class Model_Taxonomy{
protected $_belongs_to = array(
'types' => array(
'model' => 'Taxonomy_Type',
'foreign_key' => 'taxonomy_id'
)
);
}
class Model_Taxonomy_Type{
protected $_has_many = array(
'taxonomies' => array(
'model' => 'Taxonomy',
'foreign_key' => 'taxonomy_id'
)
);
}
And use some like that:
$type = ORM::factory('taxonomy_type')
->where('type_id', '=', $type_id)
->find();
if( ! $type->taxonomies->loaded())
{
types->taxonomies->find_all();
}
type_id column is a PK of taxonomy_type table, am I right?
So, you have one (unique) taxonomy_type record, and only one related taxonomy object (because of belongs_to relationship). Instead of your:
get all taxonomies that belong to a
given type id
it will be a
get taxonomy for a given type id