Why are Symfony2 validation propertyPath valus in square brackets? - symfony

I am validating some values:
$collectionConstraint = new Collection(array(
'email' => array(
new NotBlank(),
new Email(),
),
'password' => array(
new NotBlank(),
new MinLength(array('limit' => 6)),
new MaxLength(array('limit' => 25)),
),
));
$data = array('email' => $this->getRequest()->get('email'), 'password' => $this->getRequest()->get('password'));
$errors = $this->get('validator')->validateValue($data, $collectionConstraint);
But for some reason the fields (propertyPath) are stored with square brackets - I'd like to understand why Sf does that. I have to manually remove all the brackets which seems absurd so I think I am missing some functionality somewhere.
Dump of $errors:
Symfony\Component\Validator\ConstraintViolationList Object
(
[violations:protected] => Array
(
[0] => Symfony\Component\Validator\ConstraintViolation Object
(
[messageTemplate:protected] => This value should not be blank
[messageParameters:protected] => Array
(
)
[root:protected] => Array
(
[email] =>
[password] =>
)
[propertyPath:protected] => [email]
[invalidValue:protected] =>
)
[1] => Symfony\Component\Validator\ConstraintViolation Object
(
[messageTemplate:protected] => This value should not be blank
[messageParameters:protected] => Array
(
)
[root:protected] => Array
(
[email] =>
[password] =>
)
[propertyPath:protected] => [password]
[invalidValue:protected] =>
)
)
)
Even the toString function is useless.
"[email]: This value should not be blank","[password]: This value should not be blank"

Property paths can map either to properties or to indices. Consider a class OptionBag which implements \ArrayAccess and a method getSize().
The property path size refers to $optionBag->getSize()
The property path [size] refers to $optionBag['size']
In your case, you validate an array. Since array elements are also accessed by index, the resulting property path in the violation contains squared brackets.
Update:
You don't have to manually remove the squared brackets. You can use Symfony's PropertyAccess component to map errors to an array with the same structure as your data, for example:
$collectionConstraint = new Collection(array(
'email' => array(
new NotBlank(),
new Email(),
),
'password' => array(
new NotBlank(),
new MinLength(array('limit' => 6)),
new MaxLength(array('limit' => 25)),
),
));
$data = array(
'email' => $this->getRequest()->get('email'),
'password' => $this->getRequest()->get('password')
);
$violations = $this->get('validator')->validateValue($data, $collectionConstraint);
$errors = array();
$accessor = $this->get('property_accessor');
foreach ($violations as $violation) {
$accessor->setValue($errors, $violation->getPropertyPath(), $violation->getMessage());
}
=> array(
'email' => 'This value should not be blank.',
'password' => 'This value should have 6 characters or more.',
)
This also works with multi-dimensional data arrays. There the property paths will be something like [author][name]. The PropertyAccessor will insert the error messages in the same location in the $errors array, i.e. $errors['author']['name'] = 'Message'.

PropertyAccessor's setValue is no real help because it cannot handle multiple violations for a single field. For instance, a field might be shorter than a constraint length and also contain illegal characters. For this, we would have two error messages.
I had to create my own code:
$messages = [];
foreach ($violations as $violation) {
$field = substr($violation->getPropertyPath(), 1, -1);
$messages[] = [$field => $violation->getMessage()];
}
$output = [
'name' => array_unique(array_column($messages, 'name')),
'email' => array_unique(array_column($messages, 'email')),
];
return $output;
We manually strip the [] characters from the property path and create an
array of arrays of fields and corresponding messages. Later we transform the
array to group the messages by fields.
$session = $request->getSession();
$session->getFlashBag()->setAll($messages);
In the controller, we add the messages to the flash bag.

Related

Laravel cast json to array

The client attribute is a json filed in the database, which I caste as array in my model. However by retuning the Model to an array, the client attribute remains as json string:
$provider = TokenCacheProvider::all([
'name', 'auth_url', 'token_url', 'auth_endpoint', 'client'
])
->keyBy('name')
->toArray();
output:
Array
(
[name] => azure_ad
[auth_url] => /oauth2/v2.0/authorize
[token_url] => /oauth2/v2.0/token
[auth_endpoint] => https://login.microsoftonline.com/
[client] => {"tenant":"some value","client_id":"some value","client_secret":"some value"}
)
I would expect and output like this:
Array
(
[name] => azure_ad
[auth_url] => /oauth2/v2.0/authorize
[token_url] => /oauth2/v2.0/token
[auth_endpoint] => https://login.microsoftonline.com/
[client] => Array
(
[tenant] => 'some value'
[client_id] => 'some value'
[client_secret] => 'some value'
[scope] => 'some value'
)
)
My Model has the corresponding $casts:
protected $casts = [
'client' => 'array',
];
Edit
This comes a little bit closer to what I need, but it returns only the clients attribute 😵‍💫
$provider = TokenCacheProvider::all([
'name', 'auth_url', 'token_url', 'auth_endpoint', 'client'
])
->keyBy('name')
->map(function ($item) {
return json_decode($item->client);
})
->toArray();
Resolved 😎
$provider = TokenCacheProvider::all([
'name', 'auth_url', 'token_url', 'auth_endpoint', 'client'
])
->keyBy('name')
->map(function ($item) {
return collect([
'auth_url' =>$item->auth_url,
'token_url' =>$item->token_url,
'auth_endpoint' =>$item->auth_endpoint,
'client' =>json_decode($item->client,true)
]);
})
->toArray();
Not sure if this is the most beautiful solution but it does exactly what I want.
Output:
Array
(
[auth_url] => /oauth2/v2.0/authorize
[token_url] => /oauth2/v2.0/token
[auth_endpoint] => https://login.microsoftonline.com/
[client] => Array
(
[tenant] => some values
[client_id] => some values
[client_secret] => some values
[scope] => https://graph.microsoft.com/.default
)
)
Feedback and enhancements are highly welcome!
Cheers

Parsing cybersourse Response in PHP

I Need to parse the below array and retrieve values;status,id.
Below is the PHP Array
Array ( [0] => CyberSource\Model\PtsV2PaymentsPost201Response Object ( [container:protected] =>
Array ( [links] => CyberSource\Model\PtsV2PaymentsPost201ResponseLinks Object ( [container:protected] =>
Array ( [self] => CyberSource\Model\PtsV2PaymentsPost201ResponseLinksSelf
Object ( [container:protected] => Array ( [href] => /pts/v2/payments/621258854395 [method] => GET ) )
[reversal] => [capture] => [customer] => [paymentInstrument] => [shippingAddress] => [instrumentIdentifier] => ) )
[id] => 621258854395
[submitTimeUtc] => 2021-05-17T13:40:55Z
[status] => AUTHORIZED
[reconciliationId] => 621258854395
[errorInformation] =>...............
Below is my php code
$parseApiResponse = new CyberSource\Model\PtsV2PaymentsPost201Response($apiResponse);
print_r("Status"." ".$parseApiResponse->getStatus());
Please assist in resolving this.
Want to do a similar thing as I am able to do in java as below
String status = result.getStatus();
I hope this helps someone. It took me a while to figure this out. The original poster was so close. It's just...
print_r("Status"." ".$apiResponse[0]->getStatus());
No need to instantiate a new "PtsV2PaymentsPost201Response()". The response element ([0]) has already done that when you call, "createPayment()", etc.
lib/Model/PtsV2PaymentsPost201Response.php lists all the available getters. getId(), getStatus(), getLinks(),etc.
$parseApiResponse = new CyberSource\Model\PtsV2PaymentsPost201Response($apiResponse);
$result = \GuzzleHttp\json_decode( $parseApiResponse[0] , true );
return = $result['links'];

Typo3 8.7 return a GLOBAL be_user value from a TCA Override page

I have created a new back-end variable called "userval".
I can reference this value in any controller by simply calling:
$GLOBALS['BE_USER']->user['userval']
However I need to get this value from another extensions, via the TCA/Overrides/tt_content.php page. The above code doesn't work here (I receive an empty value). Is it possible to access the BE_USER values from tt_content.php?
Update - I didn't manage to figure it out but I found a better solution that might work for others.
I needed this information because I wanted to return a list of items from a table that was linked to the field "userval". But the example below has been simplified for more general use..
I discovered that tt_content.php can have data passed directly to it via a controller. In the tt_content.php file, add the following:
'tx_some_example' =>
array(
'config' =>
array(
'type' => 'select',
'renderType' => 'selectSingle',
'items' => array (),
'itemsProcFunc' => 'SomeDomain\SomeExtensionName\Controller\ExampleController->getItems',
'selicon_cols' => 8,
'size' => 1,
'minitems' => 0,
'maxitems' => 1,
),
'exclude' => '1',
'label' => 'Some example',
),
...note the "itemsProcFunc" line. This references returned data from the ExampleController.
In the ExampleController, here's how you can send the data into tt_content.php:
public function getItems($config){
//Sample data - this could be a DB call, a list in a folder, or a simple array like this
$sampleArray[
"id1" => "ID 1 Label",
"id2" => "ID 2 Label",
"id3" => "ID 3 Label",
];
$optionList = [];
$count=0;
//Cycle through each item, and add it to the desired dropdown list
foreach ($sampleArray as $id => $label) {
$optionList[$count] = [0 => $label, 1 => $id];
$count++;
}
//Pass the data back to the tt_content.php page
$config['items'] = array_merge($config['items'], $optionList);
return $config;
}

CakePHP deep (multiple related models) validation?

I have a model structure that is:
Organization belongsTo Address belongsTo CountryCode
So, Organization has foreign keys: mailing_address_id and physical_address_id
Address has foreign key: country_code_id
In the Organization model, relationship is defined as:
public $belongsTo = array(
'MailingAddress' => array('className'=>'Address', 'foreignKey'=>'mailing_address_id')
, 'PhysicalAddress' => array('className'=>'Address', 'foreignKey'=>'physical_address_id')
);
This appears to be working great - validation is functioning properly, etc.
In the Address model, relationship is defined as:
public $belongsTo = array(
'CountryCode' => array('className'=>'CountryCode', 'foreignKey'=>'country_code_id')
);
In my OrganizationsController, in a function to create a new organization, I'm testing validation using this code:
if($this->Organization->saveAll(
$data, array('validate'=>'only')
)) {
// Validates
$this->DBG('Org validated.');
} else {
// does not validate
$this->DBG('Org NOT NOT NOT validated.'.print_r($this->Organization->invalidFields(),true));
}
The $data array looks like this going into validation.
2015-06-08 21:03:38 Debug: Array
(
[Organization] => Array
(
[name] => Test Organization
)
[MailingAddress] => Array
(
[line1] => 100 Main Street
[line2] =>
[city] => Houston
[state] => TX
[postal_code] => 77002
[CountryCode] => Array
(
[name] => United St
)
)
[PhysicalAddress] => Array
(
[line1] => 100 Main Street
[line2] =>
[city] => Houston
[state] => TX
[postal_code] => 77002
[CountryCode] => Array
(
[name] => United St
)
)
)
The country code SHOULD NOT validate with the rules I have set in the CountryCode model:
public $validate = array(
'name' => array(
'nonemptyRule' => array(
'rule' => 'notEmpty'
,'required' => 'create'
,'message' => 'Must be provided.'
)
,'dupeRule' => array(
'rule' => array('isUnique', array('name','code'), false)
,'message' => 'Duplicate'
)
)
,'code' => array(
'rule' => 'notEmpty'
,'required' => 'create'
,'message' => 'Must be provided.'
)
);
However, validation PASSES on Organization->saveAll.
Also, if I attempt to access the CountryCode model from the OrganizationController, it's not loaded.
As in:
$this->Organization->MailingAddress->CountryCode->invalidate('name','Invalid!');
In that case, I get an error that CountryCode is null.
Any ideas why CountryCode wouldn't be validating or loaded?
Is validation supposed to work two steps away?
It turns out, there IS a deep option when validating (and saving). It's documented here with the saveAll options:
http://book.cakephp.org/2.0/en/models/saving-your-data.html
So, the validation block in the question works perfectly well if you include the deep option like so:
if($this->Organization->saveAll(
$data, array('validate'=>'only', 'deep'=>true)
)) {
// Validates
$this->DBG('Org validated.');
} else {
// does not validate
$this->DBG('Org NOT NOT NOT validated.'.print_r($this->Organization->invalidFields(),true));
}

cannot get checkboxes value using drupal form api

i have form in drupal which uploads images and has got few checkboxes in it.
Here is the form:
$form['checklist_fieldset'] = array(
'#type' => 'fieldset',
'#title' => t('Check List'),
'#collapsible' => FALSE,
'#collapsed' => FALSE,
);
$form['checklist_fieldset']['heating'] = array(
'#type' => 'checkboxes',
'#title' => t('Heating options'),
'#options' => array(
'0' => t('Yes'),
'1' => t('No')
),
'#description' => t('Heating details.')
);
and here is my submit function where i am processing image upload and grabbing the checkboxes value as well. I am getting the success message and image is getting uploaded but not getting the value of check boxes.
function property_add_view_submit($form,&$form_state){
$validators = array();
if($file = file_save_upload('p_file1',$validators,file_direcotry_path)){
$heating = array_keys($form_state['values']['heating']);
drupal_set_message(t('Property Saved! '.$heating));
dpm( $form_state['values']['heating']);
}
When you use #options on a FAPI element the value passed to the $form_state is the array key, so you don't need to use array_keys().
I'm not sure why you're using checkboxes for a yes/no, usually one would use a simple checkbox element. However if that's really what you want to do:
Your #options can't contain on option with 0 as the array key, it will be automatically filtered out and you'll never know if that option has been checked.
You should use $heating_options_chosen = array_filter($form_state['values']['heating'] to get the selected checkbox options.
I honestly think your code should look like this though:
$form['checklist_fieldset']['heating'] = array(
'#type' => 'checkbox',
'#title' => t('Heating options'),
'#options' => array(
'1' => t('Yes'),
'0' => t('No')
),
'#description' => t('Heating details.')
);
$heating_checked = $form_state['values']['heating'] == 1;
If I have checkbox Friends and options are like
[ ] abc
[ ] def
[ ] ghi
[ ] jkl
And I want to know which options user have marked, then use below function.
if ($form_state->getValue('friends') != NULL) {
foreach ($form_state->getValue('friends') as $key => $value) {
if ($value != 0) {
$friends = $friends . ", " . $key;
$friends = substr_replace($friends, "", 0, 1);
}
}
}
If user has chosen abc and ghi then you will get 1,3 as result in $friends
If you wanted to know the value then use $friends = $friends.", ".$value;
it worked for me..hope it will help you as well :)

Resources