I have question about constraints,
https://symfony.com/doc/current/reference/constraints.html
I have entity with multiple checkboxes (boolean type) .
Now i need some non-standard logic here like:
if checkbox A is checked then checkbox B must be checked too.
if checkboxes A,B,C are checked then checkbox D must be checked too
And now the question is :
Am i able to make logic like this with constraints (if yes - it would be grat if someone can provide example) ? or only way is to move this logic to controller ?
As your logic is specific, you have to do some specific code to validate it.
Symfony gives you 3 tools to do it:
The Callback Constraints which is a custom php function (see #Yoshi answer's)
The Expression Constraint which is a custom expression parsed. Example:
/**
* #Assert\Expression(
* "'1' in this.getCheckboxValues() and !('3' in this.getCheckboxValues())",
* )
*/
Or your own custom constraint
I think the simplest solution would be to use a callback contraint:
Something like:
<?php
namespace App\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
class Entity
{
private $checkbox_a = false;
private $checkbox_b = false;
private $checkbox_c = false;
private $checkbox_d = false;
/**
* #Assert\Callback
*
* #param ExecutionContextInterface $context
* #param mixed $payload
*/
public function validate(ExecutionContextInterface $context, $payload)
{
if ($this->checkbox_a && !$this->checkbox_b) {
$context->buildViolation('Must be checked because of checkbox_a!')
->atPath('checkbox_b')
->addViolation();
}
if ($this->checkbox_a && $this->checkbox_b && $this->checkbox_c && !$this->checkbox_d) {
$context->buildViolation('Must be checked because of all others are checked!')
->atPath('checkbox_d')
->addViolation();
}
}
}
Related
I'm building the admin for a Magento2 store (currently on 2.1.7, they want to use the newest version until we go live and then want to stabilize a particular version). The module in question is supposed to display all existing orders, with an actionsColumn that contains links to cancel, edit, and open a detailed overview of the purchased items associated with that order. The order detail page contains a grid view that should display all order items associated with an order number passed in the URL.
In order to filter out Order Items that don't relate to the specific Order Number, I've extended the \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult class. This works except for one weird caveat. If, in the addFieldToFilter call, I replace $ordNum with, say, '10000', it grabs the correct data. When using $ordNum to call this dynamically, however, it returns no rows at all. This despite trying all sorts of casting and === checks to ensure that there's no difference between the hardcoded and dynamic values. Is this a Magento bug? I can't at all figure out why this would be the case.
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$ordNum = $request->getParam('order_num');
$this->addFieldToFilter('order_num', ['eq' => $ordNum]); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
I just fixed it by using mentioned below steps
Store param value in session in controller
public function execute() {
$this->_catalogSession->setTokenId($this->request->getParam('entity_id'));
$this->_view->loadLayout();
$this->_view->loadLayoutUpdates();
$this->_view->getPage()->getConfig()->getTitle()->set(__('Redeem Token History'));
$this->_view->renderLayout();
}
Use session value in dataprovider
$tokensCollection->addFieldToFilter('token_id', ['eq' => $this->_catalogSession->getTokenId()]);
Enjoy :)
Try this in place of the getParam statement:
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
Just to make sure we are on the same page, this is the full code:
<?php
class OrderItems extends \Magento\Framework\View\Element\UiComponent\DataProvider\SearchResult
{
protected function _initSelect()
{
$this->filterByOrderNum();
parent::_initSelect();
return $this;
}
private function filterByOrderNum()
{
$request = \Magento\Framework\App\ObjectManager::getInstance()
->get('Magento\Framework\App\Request\Http');
$url = parse_url($request);
$path = explode('/',$url['path']);
$ordNum = $path[3];
$this->addFieldToFilter('order_num', $ordNum); //if I switch this to hardcoded 10000, this works. With the variable, no dice.
return $this;
}
}
We have solved this issue by doing the following :
/**
* CcCustompriceProductListingDataProvider constructor.
* #param string $name
* #param string $primaryFieldName
* #param string $requestFieldName
* #param \Magento\Framework\Api\Search\ReportingInterface $reporting
* #param \Magento\Framework\Api\Search\SearchCriteriaBuilder $searchCriteriaBuilder
* #param \Magento\Framework\App\RequestInterface $request
* #param \Magento\Framework\Api\FilterBuilder $filterBuilder
* #param array $meta
* #param array $data
* #throws \Exception
*/
public function __construct(
$name,
$primaryFieldName,
$requestFieldName,
ReportingInterface $reporting,
SearchCriteriaBuilder $searchCriteriaBuilder,
RequestInterface $request,
FilterBuilder $filterBuilder,
array $meta = [],
array $data = []
) {
$data['config']['filter_url_params']['product_id'] = $request->getParam('cppc_product_id', 0);
parent::__construct($name, $primaryFieldName, $requestFieldName, $reporting, $searchCriteriaBuilder, $request, $filterBuilder, $meta, $data);
}
You do not need to use any other function. The reason why this is is because it is also updated with an update URL and that does not have that parameter. By using adding that to the data it also parses that into the update url.
You can see that here (Parent function)
/**
* #return void
*/
protected function prepareUpdateUrl()
{
if (!isset($this->data['config']['filter_url_params'])) {
return;
}
foreach ($this->data['config']['filter_url_params'] as $paramName => $paramValue) {
if ('*' == $paramValue) {
$paramValue = $this->request->getParam($paramName);
}
if ($paramValue) {
$this->data['config']['update_url'] = sprintf(
'%s%s/%s/',
$this->data['config']['update_url'],
$paramName,
$paramValue
);
$this->addFilter(
$this->filterBuilder->setField($paramName)->setValue($paramValue)->setConditionType('eq')->create()
);
}
}
}
I am using TYPO3 7.6.10 and I build my first extension.
I want to add a property to my object in the createAction function of my controller.
But the modifications are nt saved.
Here is my code:
/**
* action create
*
* #param \Typo3\LpSurvey\Domain\Model\Sigil $newSigil
* #param array $answers
* #internal param Survey $newSurvey
*/
public function createAction(Sigil $newSigil, Array $answers)
{
$newSurvey = $this->objectManager->get('Typo3\LpSurvey\Domain\Model\Survey');
$this->userID = $GLOBALS['TSFE']->fe_user->user['uid'];
//this modifications are saved
foreach ($answers as $key => $answer) {
$newSurveyItem = $this->objectManager->get('Typo3\LpSurvey\Domain\Model\SurveyItem');
$newSurveyItem->setQuestionId($key);
$newSurveyItem->setValue($answer);
$newSurvey->addAnswer($newSurveyItem);
}
//BUT this modification is not saved
$newSigil->setUserID($this->userID);
$newSigil->setSurvey($newSurvey);
$this->sigilRepository->add($newSigil);
$this->redirect('list');
}
If I debug my object $newSigil the userID is set, but after adding to the repository the default value will be saved.
I dont understand why.
I also try to persist manually with following code, but no solution:
/**
* #var \typo3\CMS\Extbase\Persistence\Generic\PersistenceManager
* #inject
*/
protected $persistenceManager;
public function createAction(Sigil $newSigil, Array $answers)
{
$newSurvey = $this->objectManager->get('Typo3\LpSurvey\Domain\Model\Survey');
$this->userID = $GLOBALS['TSFE']->fe_user->user['uid'];
foreach ($answers as $key => $answer) {
$newSurveyItem = $this->objectManager->get('Typo3\LpSurvey\Domain\Model\SurveyItem');
$newSurveyItem->setQuestionId($key);
$newSurveyItem->setValue($answer);
$newSurvey->addAnswer($newSurveyItem);
}
$newSigil->setUserID($this->userID);
$newSigil->setSurvey($newSurvey);
$this->persistenceManager->persistAll();
$this->sigilRepository->add($newSigil);
$this->redirect('list');
}
I hope the question is understandable
Best regards Felix
Maybe UserID is not correct named? If your database field is called user_id your property for the domain should userId. Only if your database field is called user_i_d it should userID.
Is it possible to create custom HTML attributes on CHtml::checkboxList?
For example, I want to generate an input like this, adding the custom attribute "data-input-x":
<input class="customClass" id="Model_inputX_0" value="1" name="Model[relationX][]" type="checkbox" data-input-x="3">
I already tried using the code bellow, but it not worked:
echo $form->checkboxList($model, 'relationX', $dataList, array('class'=>'checkboxFase refeicaoFaseComum', 'data-input-x'=>3));
If you run your code and inspect element it you will see values the values created by Yii, the difference. Echos under a foreach loop will work nicely..
You can extend CHtml like that:
In folder "components" you create a new file named MyCHtml. In there create the class MyCHtml and copy the core code of framework for checkBoxList (https://github.com/yiisoft/yii/blob/1.1.16/framework/web/helpers/CHtml.php#L1123).
class MyCHtml extends CHtml {
//Final method is provided below
}
Then you add the parameter $extraAttributes=array() after $htmlOptions=array().
The trick is to add those attributes and their values at $htmlOptions array of each input.
If all your configurations are correct and you have access to your componenets as normal, you can call the new checkBoxList function like this:
<?php
//Values can be created dynamically or statically depending on situation
//Each value corresponds to each checkbox value that you want to contain the extra attribute
$extraAttributes = array(
'data-input-x'=>array(
6=>'k',
11=>'a',
7=>'b'),
'data-input-y'=>array(
6=>'c',
2=>'d'),
);
echo MyCHtml::checkboxList(($name, $select, $data, $htmlOptions, $extraAttributes);
?>
The whole class is the following:
<?php
class MyCHtml extends CHtml
{
/**
* Generates a list box.
* ...
* #param array $extraAttributes extra HTML attributes corresponding on each checkbox
* ...
*/
public static function checkBoxList($name,$select,$data,$htmlOptions=array(), $extraAttributes=array())
{
$template=isset($htmlOptions['template'])?$htmlOptions['template']:'{input} {label}';
$separator=isset($htmlOptions['separator'])?$htmlOptions['separator']:self::tag('br');
$container=isset($htmlOptions['container'])?$htmlOptions['container']:'span';
unset($htmlOptions['template'],$htmlOptions['separator'],$htmlOptions['container']);
if(substr($name,-2)!=='[]')
$name.='[]';
if(isset($htmlOptions['checkAll']))
{
$checkAllLabel=$htmlOptions['checkAll'];
$checkAllLast=isset($htmlOptions['checkAllLast']) && $htmlOptions['checkAllLast'];
}
unset($htmlOptions['checkAll'],$htmlOptions['checkAllLast']);
$labelOptions=isset($htmlOptions['labelOptions'])?$htmlOptions['labelOptions']:array();
unset($htmlOptions['labelOptions']);
$items=array();
$baseID=isset($htmlOptions['baseID']) ? $htmlOptions['baseID'] : self::getIdByName($name);
unset($htmlOptions['baseID']);
$id=0;
$checkAll=true;
foreach($data as $value=>$labelTitle)
{
$checked=!is_array($select) && !strcmp($value,$select) || is_array($select) && in_array($value,$select);
$checkAll=$checkAll && $checked;
$htmlOptions['value']=$value;
$htmlOptions['id']=$baseID.'_'.$id++;
//********This does the trick
foreach($extraAttributes as $attributesKey => $attributesValue) {
$found = false;
foreach($attributesValue as $subAttributesKey => $subAttributesValue) {
if ($value === $subAttributesKey) {
$htmlOptions[$attributesKey] = $subAttributesValue;
$found = true;
break;
}
}
if (!$found) {
$htmlOptions[$attributesKey] = '';
}
}
//********All the rest is the same with core method
$option=self::checkBox($name,$checked,$htmlOptions);
$beginLabel=self::openTag('label',$labelOptions);
$label=self::label($labelTitle,$htmlOptions['id'],$labelOptions);
$endLabel=self::closeTag('label');
$items[]=strtr($template,array(
'{input}'=>$option,
'{beginLabel}'=>$beginLabel,
'{label}'=>$label,
'{labelTitle}'=>$labelTitle,
'{endLabel}'=>$endLabel,
));
}
if(isset($checkAllLabel))
{
$htmlOptions['value']=1;
$htmlOptions['id']=$id=$baseID.'_all';
$option=self::checkBox($id,$checkAll,$htmlOptions);
$beginLabel=self::openTag('label',$labelOptions);
$label=self::label($checkAllLabel,$id,$labelOptions);
$endLabel=self::closeTag('label');
$item=strtr($template,array(
'{input}'=>$option,
'{beginLabel}'=>$beginLabel,
'{label}'=>$label,
'{labelTitle}'=>$checkAllLabel,
'{endLabel}'=>$endLabel,
));
if($checkAllLast)
$items[]=$item;
else
array_unshift($items,$item);
$name=strtr($name,array('['=>'\\[',']'=>'\\]'));
$js=<<<EOD
jQuery('#$id').click(function() {
jQuery("input[name='$name']").prop('checked', this.checked);
});
jQuery("input[name='$name']").click(function() {
jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
});
jQuery('#$id').prop('checked', !jQuery("input[name='$name']:not(:checked)").length);
EOD;
$cs=Yii::app()->getClientScript();
$cs->registerCoreScript('jquery');
$cs->registerScript($id,$js);
}
if(empty($container))
return implode($separator,$items);
else
return self::tag($container,array('id'=>$baseID),implode($separator,$items));
}
public static function activeCheckBoxList($model,$attribute,$data,$htmlOptions=array())
{
self::resolveNameID($model,$attribute,$htmlOptions);
$selection=self::resolveValue($model,$attribute);
if($model->hasErrors($attribute))
self::addErrorCss($htmlOptions);
$name=$htmlOptions['name'];
unset($htmlOptions['name']);
if(array_key_exists('uncheckValue',$htmlOptions))
{
$uncheck=$htmlOptions['uncheckValue'];
unset($htmlOptions['uncheckValue']);
}
else
$uncheck='';
$hiddenOptions=isset($htmlOptions['id']) ? array('id'=>self::ID_PREFIX.$htmlOptions['id']) : array('id'=>false);
$hidden=$uncheck!==null ? self::hiddenField($name,$uncheck,$hiddenOptions) : '';
return $hidden . self::checkBoxList($name,$selection,$data,$htmlOptions);
}
/**
* Generates a push Html button that can submit the current form in POST method.
* #param string $label the button label
* #param mixed $url the URL for the AJAX request. If empty, it is assumed to be the current URL. See {#link normalizeUrl} for more details.
* #param array $ajaxOptions AJAX options (see {#link ajax})
* #param array $htmlOptions additional HTML attributes. Besides normal HTML attributes, a few special
* attributes are also recognized (see {#link clientChange} and {#link tag} for more details.)
* #return string the generated button
*/
public static function ajaxSubmitHtmlButton($label,$url,$ajaxOptions=array(),$htmlOptions=array())
{
$ajaxOptions['type']='POST';
$htmlOptions['type']='submit';
return self::ajaxHtmlButton($label,$url,$ajaxOptions,$htmlOptions);
}
}
I want to validate a form that i submit via Ajax but this doesn't work for me. Please help to solve this.
In My annotation builder I used :
<?php
namespace Application\Model;
use Zend\Form\Annotation;
/**
* #Annotation\Name("sejour")
* #Annotation\Hydrator("Zend\Stdlib\Hydrator\ObjectProperty")
*/
class Sejour {
public $id;
/**
* #Annotation\Validator({"name":"StringLength", "options":{"min":1, "max":3}})
*/
public $titre;
public $agenceId;
public $agenceLibelle;
public function exchangeArray($data)
{
$this->id = (isset($data['id'])) ? $data['id'] : null;
$this->titre = (isset($data['titre'])) ? $data['titre'] : null;
$this->agenceId = (isset($data['agenceId'])) ? $data['agenceId'] : null;
$this->agenceLibelle = (isset($data['agenceLibelle'])) ? $data['agenceLibelle'] : null;
}
}
In my controller file:
$values = json_decode(file_get_contents('php://input'), true);
$builder = new AnnotationBuilder();
$entity = new Sejour();
$form = $builder->createForm($entity);
$form->bind($entity);
$form->setData($values);
if ( $form->isValid()) {
/* save datas in base */
}else{
$errors = $form->getMessages();
print_r($errors);
}
Each time i got no errors
Please help me
Thanks
This is very good example.
http://samsonasik.wordpress.com/2012/10/11/zend-framework-2-using-zend-form-and-ajax/
Try this
Depending on your Zend Framework 2 version you may have hit the fact that preferFormInputFilter flag has changed it's default value in zf2 2.2.4.
The flag regulates the behavior of the form deciding if, during validation, the form should use its own input filters (that you have beforehand provided) or those provided by each element.
The default for forms created with AnnotationBuilder is to set the flag to true and so, since you have pasted no code where you actually set a filter, the default filter is none.
You may want to enable default element filters for the elements you added by adding:
$form->setPreferFormInputFilter(FALSE);
just after you create the form.
Another option would naturally be to provide actual input filters to the form.
I have simple clasS:
User:
id | login | password | desc
and form with this.
How can i make - if input password is null(strlen == 0) then in model doesn`t matter.
Now i have in User.class.php in function save:
$this->setPassword(sha512($this->getPassword));
You have to ensure not to have a "NOT NULL" on DB level, so empty values are ignored normally by ORM.
There are 2 possibilties to change a given password by a custom function, so i'll give you some simple examples.
1) In your model file (i guess you have doctrine or propel ?!) :
/**
* via object's event handler
*/
preSave(){
if(strlen($this->getPassword()) > 0){
$this->setPassword(sha512($this->getPassword()));
}
}
2) Or even as a form validator :
/**
* custom validator
*/
class myValidatorOldPassword extends sfValidatorBase{
/**
* Clean and validate user input
*
* #param mixed $value Value of form input
* #return mixed Value
*/
protected function doClean($value)
{
// trim is not needed
$clean = (string) $value;
// password is ok?
if (strlen($clean) > 0)
{
return sha512($clean);
}
// Throw error - if you want
// throw new sfValidatorError($this, 'invalid', array('value' => $value));
// or return empty value
return $clean;
}
}
Of course this code may be improved, as it's only hint for you.