Phalcon form validation messages - forms

There are some misunderstandings about how the messaging works for the Forms in Phalcon. Say we have a form and trying to add some extended error message for one of fields named 'code' in controller:
$form = new SampleForm();
Implementation of SampleForm is done in a corresponding class through initialize and the code element is $code = new \Phalcon\Forms\Element\Text('code');
The next code adds the message :
$form->get('code')->appendMessage(new \Phalcon\Validation\Message("The Code desn\'t exist or not valid"));
but trying to receive this message like
$form->getMessagesFor('code')
gives me nothing (the dump):
Phalcon\Validation\Message\Group Object
(
[_position:protected] =>
[_messages:protected] =>
)
Another attempt via
$form->get('code')->getMessages()
gives (the dump):
Phalcon\Validation\Message\Group Object
(
[_position:protected] =>
[_messages:protected] => Array
(
[0] => Phalcon\Validation\Message Object
(
[_type:protected] =>
[_message:protected] => The Code desn\'t exist or not valid
[_field:protected] =>
[_code:protected] => 0
)
)
)
My question: what am I doing wrong and why $form->[get/has]MessagesFor($name) doesn't work as expected?

I'm sorry to tell you, form handling is really poorly implemented and messed up in Phalcon, as I can see now, after a few months work.
In this case, you have multiple getMessage functions and they DO NOT return the same value. Even some of them returns a reference and some of them returns a copy of the error messages, and if you are appending messages to the copy, they won't be available elsewhere through the getMessage functions.
This even changed form version 1.2 to 1.3, we had a hard time figuring out which functions should we use. If you like, you could check out the C code behind this, in the Phalcon repository, it has been a great help for me to figure out why things don't work the way I expected.
My advices is, print the values of getMessage functions: Form::getMessages(), Form::getMessagesFor(), Form::get('element_name')->getMessages(). Then try appending new messages to them and print them again. See which one contains your messages.
I ended up getting messages for Phalcon's built in validation classes by $form->get('email')->getMessages() and getting my own added in the controller by $form->getMessagesFor('email'). I am still searching for a solution to get all the messages in one place.

Serin is right, messages are saved into different objects, so i came up with getting all of those like this
<?php foreach ($form as $element) {
//Get any generated messages for the current element
foreach ([$form->getMessagesFor($element->getName()), $element->getMessages()] as $messages) {
if (count($messages)) {
//Print each element
foreach ($messages as $message) {
echo $message;
}
}
}
}?>
Let's hope easier method will be added in nearest future:

Related

Convert lead into a contact using a logic hook

I looked in the dev guide and online but can't seem to find any code examples. I'm just trying to convert a lead into a contact.
For example I have a lead with ID: 7, what I'm trying to do is possibly modify a bean property to convert that lead into a contact (not sure if this approach is correct). Something like
$bean->convert("Contact");
$bean->save();
Thank you for your advice
In sugar/suite, conversion is a process which is copying a bean from one module to another module e.g. lead into contact.
I have been using this function to make it happen.
function outright_copy_bean($old_bean){
$new_bean = new $old_bean->object_name;
foreach($new_bean->field_defs as $key => $value){
if(in_array($key, array("id", "date_entered"))){
continue;
}
if($value["type"] == "link"){
continue;
}
$new_bean->$key = $old_bean->$key;
}
$new_id = $new_bean->save();
return $new_id
}

trouble with boolean value used as switch for showing or not in my view

It must be an understanding typo issue. I have a recordset which I use for initializing during import data.
I don't want to show the special record in my views.
My database with the record looks like follows:
In my method I tried to add a where statement:
The type is tinyint.
public function fetchAllP($projectid)
{
$show = 0;
return $this->tableGateway->select(['ProjectID' => $projectid],['show_not' => false]);
}
As you can see I tried with false/0 whatever. Anyway the method works without the show_not equation.
Where is my error? It should be quite simle, but I won't see the mistake and the record is still shown. Any help appreciated.
Try
return $this->tableGateway->select(['ProjectID' => $projectid, 'show_not' => false]);
The selectmethod accepts only one parameter.

Yii RBAC make Users update profile by himself

I'm trying to do this with mongodbauthmanager. I'm follow step by step in Usage section but finally i'm getting PHP warning: Illegal offset type. I had posted this question at Yii Extension before clone to SO:
Please tell me what is wrong?
1// Config
'authManager'=>array(
'class' =>'CMongoDbAuthManager',
'showErrors' => true,
),
2// Create auth items in db
$auth = new CMongoDbAuthManager();
$bizRule = 'return Yii::app()->user->id==$params["User"]->_id;';
$auth->createTask('updateSelf', 'update own information', $bizRule);
//I had tried with $auth->createOperation() but they has the same error
$role = $auth->createRole('user');
$role->addChild('updateSelf');
$auth->save();
and here is result in db
result in db http://i.minus.com/iIpXoBlDxaEfo.png
**3// Checking access in controller ** - UPDATE CODE AND ERROR
public function actionUpdate($id)
{
$model=$this->loadModel($id);
$params = array('User'=>$model);
if (!Yii::app()->user->checkAccess('updateSelf', Yii::app()->user->id,$params) )
{
throw new CHttpException(403, 'You are not authorized to perform this action');
}
//another statement ...
}
4// Getting error:
Fatal error : Cannot use object of type MongoId as array in F:\Data\03. Lab\www\yii\framework\web\auth\CAuthManager.php(150) : eval()'d code on line 1
RESOLVED PROBLEM
Base-on the answer of #Willem Renzema, I resolve my problem. Now, I update here and hope it useful for someone have this error.
0// First, config authManager with defaultRoles
'authManager'=>array(
'class'=>'CMongoDbAuthManager',
'showErrors' => true,
'defaultRoles'=> array('user'),//important, this line help we don't need assign role for every user manually
),
1// Fix save id in UserIdentity class
class UserIdentity extends CUserIdentity
{
private $_id;
//...
public function authenticate()
{
//...
$this->_id = (string)$user->_id;//force $this save _id by string, not MongoId object
//...
}
//...
}
2// Fix $bizrule in authe items
($bizrule will run by eval() in checkAccess)
//use _id as string, not MongoId object
$bizRule = 'return Yii::app()->user->id==(string)$params["User"]->_id;';
3// And user checkAccess to authorization
public function actionUpdate($id){
/**
* #var User $model
*/
$model=$this->loadModel($id);
$params = array('User'=>$model);
if (!Yii::app()->user->checkAccess('updateSelf', $params) )
{
throw new CHttpException(403, 'You are not authorized to perform this action');
}
//...
}
4// Done, now we can use checkAccess :D
First off, your original use of checkAccess was correct. Using Yii::app()->user->checkAccess() you are using the following definition:
http://www.yiiframework.com/doc/api/1.1/CWebUser#checkAccess-detail
Now, CWebUser's implementation of checkAccess calls CPHPAuthManager's implementation, which is where you encountered your problem with an illegal offset type.
http://www.yiiframework.com/doc/api/1.1/CPhpAuthManager#checkAccess-detail
An Illegal offset type means you are attempting to access an array element by specifying its key (also known as: offset) with a value that doesn't work as a key. This could be another array, an object, null, or possibly something else.
Your stack trace posted on the extensions page reveals that the following line gives the problem:
if(isset($this->_assignments[$userId][$itemName]))
So we have two possibilities for the illegal offset: $userId and $itemName.
Since $itemName is clearly a string, the problem must be with $userId.
(As a side note, the fact that your stack trace revealed surrounding code of this error also revealed that, at least for CPHPAuthManager, you are using a version of Yii that is prior to 1.1.11. Observe that lines 73 and 74 of https://github.com/yiisoft/yii/blob/1.1.11/framework/web/auth/CPhpAuthManager.php do not exist in your file's code.)
At this point I would have guessed that the problem is that the specified user is not logged in, and so Yii::app()->user->id is returning null. However, the new error you encountered when placing Yii::app()->user->id as the 2nd parameter of checkAccess reveals something else.
Since the 2nd parameter is in fact what should be the $params array that appears in your bizRule. Based on the error message, this means that Yii::app()->user->id is returning a mondoId type object.
I was unfamiliar with this type of object, so looked it up:
http://php.net/manual/en/class.mongoid.php
Long story short, you need to force Yii::app()->user->id to return the string value equivalent of this mondoId object. This likely set in your UserIdentity class in the components folder. To force it to be a string, simply place (string) to force a type conversion.
Example:
$this->_id = (string)$User->_id;
Your exact code will vary, based on what is in your UserIdentity class.
Then, restore your checkAccess to the signature you had before, and it should eliminate the Illegal offset error you encountered originally.
Note however that I have not used this extension, and while performing the following actions should fix this issue, it may cause new issues if the extension relies on the fact that Yii::app()->user->id is a mondoId object, and not a string.

DBIx::Class Wrapping/overloading a column accessor

Using DBIx::Class I am trying to manipulate the data of a column whenever it is being updated or retrieved. For instance, before it goes into the database I would like to encrypt it, and whenever it is being accessed I would like to decrypt it. I am following this example in the DBIx::Class::Manual::Cookbook, however I can't seem to get it to work. I have placed the following in my User schema. For testing I am just using the name column, I know it doesn't make sense:
__PACKAGE__->add_columns("name" => { accessor => '_name' });
sub name {
my $self = shift;
# If there is an update to the column, we'll let the original accessor
# deal with it.
if(#_) {
return $self->_name('test 1');
}
# Fetch the column value.
my $name = $self->_name;
$name = 'test 2';
return $name;
}
I can't see what I'm doing any different than what the cookbook says. Can't anyone help me understand what I'm doing wrong? Thanks!
DBIx::Class has a component for that called FilterColumn.
There are various modules on CPAN using that component like DBIx::Class::EncodedColumn and PassphraseColumn.
If you tell us what you use case is we might give you more/better suggestions.

Display Zend_Form form errors in ViewScript

I'm trying to display all form errors before the form using a ViewScript. Here is the code that I'm currently trying to use within my ViewScript:
<div class="errors">
<?php echo $this->formErrors($this->element->getMessages()); ?>
</div>
This call gives me an error message:
Warning: htmlspecialchars() expects parameter 1 to be string, array given
I've seen this same code suggested other places but its not working for me. If I print out $this->element->getMessages() I do see the error messages as the following:
Array ( [myField] => Array ( [isEmpty] => Value is required and can't be empty ) )
Any ideas?
The getMessages() returns an array of form element names as keys which each contain an array of errors for that element. So basically instead of handing the formErrors view helper:
Array ( [isEmpty] => Value is required and can't be empty )
You are handing it:
Array ( [myField] => Array ( [isEmpty] => Value is required and can't be empty ) )
You would want to do something like this instead:
$arrMessages = $this->myForm->getMessages();
foreach($arrMessages as $field => $arrErrors) {
echo sprintf(
'<ul><li>%s</li>%s</ul>',
$this->myForm->getElement($field)->getLabel(),
$this->formErrors($arrErrors)
);
}
As Mark points out in his answer, the getMessages() returns an array of form element names as keys which each contain an array of errors for that element; and his solution is:
$arrMessages = $this->myForm->getMessages();
foreach($arrMessages as $field => $arrErrors) {
echo sprintf(
'<ul><li>%s</li>%s</ul>',
$this->myForm->getElement($field)->getLabel(),
$this->formErrors($arrErrors)
);
}
This works, as long as getMessages() results in a two-dimensional array. However, if the form is based on relational data sets generated by Doctrine (or some other plugin), the error message associated with a field might also be an array and the above code will crash because it treats $arrErrors as a string when it turns out to be an array.
To capture the error messages if there's a second data set we could introduce a foreach statement nested inside the first foreach statement, but that won't work when getMessages() results in a two-dimensional array; nor does it work if the data sets are more than two-deep.
In a relational data scenario where we don't know how deep the error message comes from, a scalable solution is
$arrMessages = $this->myForm->getMessages();
print_r ($arrMessages);