Yii Validation; combining validators with OR logic - validation

Yii built-in validation returns invalid if one of the set of validators in the array fails,
e.g.
array(
array('A', 'required'),
array('B', 'required')
)
will return invalid if either A or B is empty. (AND logic)
I would like to combine these validators so that it will return invalid only if all individual validators return invalid (OR logic).
The example above would return invalid only in the case that both A and B are empty.
Is there a generic built-in way to do this?
If not I am expecting to have to extend the library to handle something like the following:
array(
array('', 'COrValidator',
array('A', 'required'),
array('B', 'required')
)
)

You may want to create an AnyValidValidator so that you can do something like this:
array(
array('attribute1, attibute2', 'anyValid', array('required')),
);
The problem with
array(
array('', 'COrValidator',
array('A', 'required'),
array('B', 'required')
)
)
is that, without significant modification of the validator functionality, the COrValidator validator will not be applied to any attributes (no attributes specified).

Related

Laravel updateOrInsert with 'OR' or 'AND' operator in first arguments?

While it is possible to have multiple arguments for the updateOrInsert in Laravel query builder and what is the operator used by default.
For example in the documentation it is mentioned:
DB::table('users')
->updateOrInsert(
['email' => 'john#example.com', 'name' => 'John'],
['votes' => '2']
);
Does that mean that email && name are checked or does it mean email || name is checked? How can we control it for one or the other if required?
Please forgive me if this is a silly question or if it is not worded as per the correct vocabulary, as I am new to Laravel. I couldn't find this information in the documentation or API.
updateOrInsert() method is used to update an existing record in the database if matching the condition or create if no matching record exists. Its return type is Boolean.
Syntax :
DB::table('blogs')->updateOrInsert(
[Conditions],
[fields with value]
);
In your query :
DB::table('users')->updateOrInsert(
['email' => 'john#example.com', 'name' => 'John'],
['votes' => '2']
);
It will check if email == 'john#example.com' & name == 'john', then it will update votes=2.

how to use 'like' instead of '=' in magento collection filter for custom module

i have managed to do this with addFieldToFilter default functionality like this:
$collection->addFieldToFilter(
array('first_name','last_name'),
array($post['firstName'],$post['lastName'])
);
this is working when i print sql (this is just part from the whole statement being printed):
((first_name = 'r') OR (last_name = 't'))
now i am trying to understand how to use 'like' instead of =.
need some help in understanding that.
your time and answers are highly appericiated thank you.
It's weird, but after some researching into your question the solution is quite simple - it's given in the comments in the Magento code:
In the comment for the method addAttributeToFilter() in Mage/Eav/Model/Entity/Collection/Abstract.php (which finally gets called) you can read:
/**
* Add attribute filter to collection
*
* If $attribute is an array will add OR condition with following format:
* array(
* array('attribute'=>'firstname', 'like'=>'test%'),
* array('attribute'=>'lastname', 'like'=>'test%'),
* )
*/
Wasn't quite clear to me what this was supposed to mean, but it's as simple: If you want to add an OR condition between the attributes in your statement, then you have to give your parameter for the addAttributeToFilter() method in this way, so in your case:
$collection->addFieldToFilter(
array(
array('attribute'=>'firstname', 'like'=>$post['firstName']),
array('attribute'=>'lastname', 'like'=>$post['lastName'])
));
You can follow this if you look inside the addAttributeToFilter() method:
if (is_array($attribute)) {
$sqlArr = array();
foreach ($attribute as $condition) {
$sqlArr[] = $this->_getAttributeConditionSql($condition['attribute'], $condition, $joinType);
}
$conditionSql = '('.implode(') OR (', $sqlArr).')';
For you googlers, the rigth way to to it is:
$collection->addFieldToFilter(
array('first_name','last_name'),
array(
array('like' => '%'.$post['firstName'].'%'),
array('like' => '%.'$post['lastName'].'%')
)
);
You should pass an array of fields and an array of conditions.
Generated SQL:
... WHERE ((first_name LIKE '%xxxx%') OR (last_name LIKE '%yyy%')) ...
addFieldToFilter( 'sku', array( "like"=>'abc123' ) )
Here you can read much more about Magento collections: http://alanstorm.com/magento_collections
For example, see section named "AND or OR, or is that OR and AND?" in this article.

sfValidatorRegex not working

I am trying validate a username field on a register form so that a username can only be made up of letters. However, when I use the sfValidatorRegex(), it always returns invalid ("johnny" will return invalid, as does "JoHnNy"). Here's the code I'm using:
// From RegisterForm.class.php
$this->validatorSchema['username'] = new sfValidatorRegex(
array(
'pattern' => '[A-Za-z]',
),
array(
'invalid' => 'only lowercase letters',
)
);
What am I missing?
Didn't understand whether you wanted lower or uppercase but try:
Only lowercase: '/^[a-z]*$/'
Lower or uppercase: '/^[A-z]*$/i'

Where statements are not being appended

I am trying to build a query dynamically. It's initial state is fine. This is the initial where clause that should be present on every query
$qb->add('where', $qb->expr()->andx(
$qb->expr()->eq('s.competitor', $competitor),
$qb->expr()->eq('s.ignored', $ignored),
$qb->expr()->eq('s.id', $params['s_id']),
$qb->expr()->eq('s.id', 'k.targetSite')
), true);
But the app I am building allows users to filter. When that happens, I want to add additional where clauses into my query builder. When this line is executed later in the code, it overwrites the above where statement.
$qb->add('where', $qb->expr()->like($col, $val), true );
From what I have read, the 3rd parameter $append should keep the previous statements, but that's not happening. In Doctrine 1.2, I could just do something like this:
foreach($filter as $col => $val) {
$dql->addWhere($col = ?, array($val));
}
How do I dynamically add where clauses to my QueryBuilder?
Update
Here is a full statement
$where = array('col' => 'k.text', 'val' => 'some word%');
$qb = $this->entityManager->createQueryBuilder()
->select('s, sc')
->from('Dashboard\Entity\Section', 'sc')
->innerJoin('sc.keyword', 'k')
->innerJoin('sc.site', 's')
->leftJoin('k.keywordCategory', 'kc')
->leftJoin('k.keywordSubCategory', 'ksc');
$qb->add('where', $qb->expr()->andx(
$qb->expr()->eq('s.competitor', $competitor),
$qb->expr()->eq('s.ignored', $ignored),
$qb->expr()->eq('s.id', $params['s_id']),
$qb->expr()->eq('s.id', 'k.targetSite')
), true);
if ($where) {
$qb->add('where', $qb->expr()->andx(
$qb->expr()->like($where['col'], $where['val'])
), true);
}
$qb->addGroupBy('k.id');
$qb->addGroupBy('s.id');
$qb->setFirstResult( $params['start'] )
->setMaxResults( $params['limit'] );
$q = $qb->getQuery();
echo $q->getSql();
And the output is
SELECT s0_.id AS id0, k1_.id AS id1, k1_.name AS name2, k2_.id AS id3, k2_.name AS name4, k3_.id AS id5, k3_.text AS text6, k3_.search_vol AS search_vol7, s4_.id AS id8, s4_.sub_domain AS sub_domain9, MIN(s0_.rank) AS sclr10, MAX(s0_.created) AS sclr11
FROM section s0_
INNER JOIN keyword k3_ ON s0_.k_id = k3_.id
INNER JOIN site s4_ ON s0_.s_id = s4_.id
LEFT JOIN keyword_category k1_ ON k3_.k_cat_id = k1_.id
LEFT JOIN keyword_sub_category k2_ ON k3_.k_subcat_id = k2_.id
WHERE k3_.text LIKE 'some word%'
GROUP BY k3_.id, s4_.id LIMIT 25 OFFSET 0
If I don't add in that if ($where) clause, then the first andx where statements are still in place. But when I try to dynamically add them, only the final WHERE statement is added, all others are cleared. I should also add, that I tried it like this as well.
if ($where) {
$qb->add('where', $qb->expr()->like($where['col'], $where['val']), true);
}
I can successfully use
$qb->andWhere( $qb->expr()->like($where['col'], $where['val']) );
But the API docs for Query Builder state the way I am trying to use it should be valid too. Wanted to make sure I was doing it right, or if it was a bug.
You're able to use the ->andWhere() normally (and it will also fixes your issue).
Doctrine 2 QueryBuilder is a rather innovative concept (because it mixes both programmatic and fluent styles), and there are likely bugs associated to it.
One point that you should notice in your code: Instead of play with ->add('where', ...), you should think programmatic. Add more items to andX() object and at the end associate to the ->add('where', ...) (or even better: ->where(...))
Looking at the code, it seems like what you're trying to do won't work, though whether that's documented anywhere or is just a bug is open to question, I think.
In add(), the DQL part you pass in seems only ever to be appended if the part is already stored as an array in the query builder:
$isMultiple = is_array($this->_dqlParts[$dqlPartName]);
...
if ($append && $isMultiple) {
if (is_array($dqlPart)) {
$key = key($dqlPart);
$this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key];
} else {
$this->_dqlParts[$dqlPartName][] = $dqlPart;
}
} else {
$this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart;
}
And the WHERE clause, unlike most of the other DQL parts, is not initialised to an array:
private $_dqlParts = array(
'select' => array(),
'from' => array(),
'join' => array(),
'set' => array(),
'where' => null,
'groupBy' => array(),
'having' => null,
'orderBy' => array()
);
Now, this looks a bit like it's by design, but Doctrine 2 is fairly new and this bit seems to have been evolving recently. Personally, I'd raise a bug against this behaviour and see what the project people say. If nothing else, you might end up with improved documentation...

Is it possible to customise drupal node reference and pass your search and a argument from another field

I'm trying to create a bespoke form in drupal, with a node reference field.
I'd like to add a little extra functionality to the node reference auto complete. I've created a view, that contains an argument. I'd like to be able to pass that argument from a drop down as well as the typed text into the autocomplete script.
Does anyone know how I'd start this off.
/* FIELD 1 - the drop down */
$sql = "SELECT nid, title FROM node where type='resourcetype' AND status =1 order by title
";
$result = db_query($sql);
$counter = 0 ;
$options = array();
while ($data = db_fetch_array($result)) {
// krumo ($data);
$options[$data[nid] ] = $data[title] ;
if ($counter ==0 ) {$df = $data[nid]; }
$counter ++;
}
/* FIELD 2 - the node reference field */
$form['sor']['type'] = array(
'#type' => 'select',
'#title' => t('Resource type'),
'#required' =>TRUE,
'#options' => $options,
) ;
$form['sor']['field_asor_sors'] = array(
'#type' => 'textfield',
'#title' => t('Add a SOR item to this job'),
'#autocomplete_path' => 'nodereference/autocomplete/field_asor_sors',
'#element_validate' => array('myelement_validate_is_valid_noderef'),
'#required' =>TRUE,
);
Many Thanks
Matt
AFAIK there is no easy way to do this.
I wanted to do something similar a while ago (using different arguments based on node context), but refrained from doing it, since it would've needed some significant changes of the autocomplete callback logic. You'd need to change several nodereference functions to add support for passing an argument to the initial callback nodereference_autocomplete(), passing it on from there to _nodereference_potential_references(), and finally to _nodereference_potential_references_views(), while ensuring that the changes don't break anything else.
If you want to try nonetheless, you should take a look at the patches in this thread, as they also want to do something like that and might contain some useful hints/examples.
A potentially easier alternative might be to exchange the #autocomplete_path callback of the nodereference field with your own custom version that would generate the result, while adding js logic to your dropdown to add an additional argument to that path when the selection changes.
If you go into the nodereference field configuration form, and scroll all the way to the bottom, there's a fieldset (which may be collapsed) that is titled 'Advanced - Nodes that can be referenced (View)'. You can use this to select a view and have that view be the source of the nodereference choices without writing any new code.

Resources