Related
Database Structure
I have two tables, users and nicks.
In users table I have a field username and in nicks table I have a field nick_name
There is a hasMany association between users and nicks
public $hasMany = array(
'Nick' => array(
'className' => 'Nick',
'foreignKey' => 'user_id',
'dependent' => true
)
);
In the User model I am validating to allow only unique username during registration by
'username must be unique' => array(
'rule' => 'isUnique',
'message' => 'username is already taken'
)
but I also don't want to allow users to register any previously used nick names as their username. for that.
'somebody use that name as a nickname' => array(
'rule' => 'checkNickName',
'message' => 'That name is already in use'
)
and
public function checkNickName(){
$nick2 = $this->find('all');
$nickNames = array();
foreach($nick2 as $name2){
foreach($name2['Nick'] as $name1){
array_push($nickNames,strtolower($name1['nick_name']));
}
}
$username = strtolower($this->data['User']['username']);
return in_array($username,$nickNames);
}
But that's not working. What should I to make it work?
If I understand correctly, you want to ensure the username is unique but also that the username has not been used as a nickname by anybody also, so why not use something like this?
public function checkNickIsUnique($check = array()) {
$value = array_values($check);
$nicknameExists = $this->Nick->find('count', array(
'conditions' => array(
'Nick.nick_name' => $value[0]
)
));
return ($nicknameExists > 0) ? false : true;
}
And in your user model, assuming it's directly related to nick, have this in the validation.
public $validate = array(
'username' => array(
'isUnique' => array(
'rule' => 'isUnique',
'message' => 'That username has already been taken'
),
'checkNickIsUnique' => array(
'rule' => array('checkNickIsUnique'),
'message' => 'Your username has already been taken as a nickname'
)
),
);
All it's doing is passing the value from the validation to the method, and checking if that value exists as a nickname within the nicks table, if it does it fails validation otherwise it passes.
First find the nicknames.
$nick2 = $this->Nick->find('all');
Then the result array will be
nick2 = array(
0 => array( //first foreach
'Nick' => array(
nickname => 'TEST'
)
)
)
so remove the second foreach and save the value as using $name2['Nick']['nickname']
public function checkNickName(){
$nick2 = $this->Nick->find('all');
$nickNames = array();
foreach($nick2 as $name2){
array_push($nickNames,strtolower($name2['Nick']['nick_name']));
}
$username = strtolower($this->data['User']['username']);
return in_array($username,$nickNames);
}
just need use the username of the input form.
in your view example:
<?php echo $this->Form->input('username', array(
'label' => array(
'text' => 'Provide your username'
)
)); ?>
and in your model you need change
'username' => array(
'nonEmpty' => array(
'rule' => array('notEmpty'),
'message' => 'not empty please',
'allowEmpty' => false
),
'unique' => array(
'rule' => array('isUniqueUsername'),
'message' => 'username is already taken'
),
),
'nickname' => array(
'nonEmpty' => array(
'rule' => array('notEmpty'),
'message' => 'not empty',
'allowEmpty' => false
),
'unique' => array(
'rule' => array('checkNickName'),
'message' => 'That name is already in use'
)
)
your condition example for is validation is unique user name, use this:
EDIT
'username' => array(
'unique' => array(
'rule' => array('checkNickName'),
'message' => 'username is already taken'
),
),
in your function:
public function checkNickName($check) {
$this->loadModel('Nick');
$username = $this->Nick->find(
'first',
array(
'fields' => array(
'Nick.id',
'Nick.nickname'
),
'conditions' => array(
'Nick.nickname' => $check['username']
)
)
);
if(!empty($username)){
if($this->data[$this->alias]['id'] == $username['User']['id']){
return true;
}else{
return false;
}
}else{
return true;
}
}
A better solution that doesn't require tons of additional code is this:
$valid = $this->User->saveAll($this->request->data, array(
'atomic' => false,
'validate' => 'only'
));
debug($valid);
It will validate the record and all other records attached to it as well, no more code required, just make sure you have the isUnique rule in your associated nicknames model. It will as well invalidate the correct fields in your form.
Instead of my custom validation message being show upon submitting a form, the default "This field cannot be left blank" is being shown. Any ideas?
Here is my validation:
public $validate = array(
'id' => array(
'rule' => 'notEmpty'
),
'first_name' => array(
'rule' => 'notEmpty'
),
'last_name' => array(
'rule' => 'notEmpty'
),
'accused_of' => array(
'rule' => 'notEmpty'
),
'last_4_of_ssn' => array(
'rule' => '/^[0-9]{4}$/', // NEED TO DOUBLE CHECK THIS
'messsage' => 'Exactly 4 digits'
),
'date_of_accusation' => array(
'rule' => array('date', 'ymd'), // NEED TO DOUBLE CHECK THIS
'message' => 'Date must be in YYYY-MM-DD format.'
),
'monitoring' => array(
'rule' => 'notEmpty'
)
);
And here is my form:
echo $this->Form->create('Offender');
echo $this->Form->input('first_name', array('label' => 'First name:'));
echo $this->Form->input('last_name', array('label' => 'Last name:'));
echo $this->Form->input('accused_of', array('label' => 'Accused of:'));
echo $this->Form->input('monitoring', array('label' => 'Monitoring (enter as comma seperated list. ex: BAC, location):'));
echo $this->Form->input('last_4_of_ssn', array('label' => 'Last 4 digits of SSN:') );
echo $this->Form->input('date_of_accusation', array('label' => 'Date of accusation:'));
echo $this->Form->end('Save New Offender');
It appears for some fields you have not set any custom messages please go to this URL http://book.cakephp.org/2.0/en/models/data-validation.html
You can also create a custom validation for this purpose like in validation array do like this
'last_4_of_ssn' => array(
'rule' => array('digitValidate'),
'messsage' => 'Exactly 4 digits'
)
and in Model you can create a public function with the same name.
public function digitValidate() {
$pattern = '/^[0-9]{4}$/';
if (!preg_match($pattern,$this->data['last_4_of_ssn'])) {
return false;
}
return true;
}
I hope this will help you.
Hopefully I'm missing something simple here. I'm using the CakeDC Search and Tags plugin for my cake (2.3.4) app.
Along with generic search by field functionality I want the user to be able to search by tags. I've almost got this working but the search will only display results if you search for a single tag not multiples. For example, if I add an article with the following tags - black, white, red. The article will only show in the search results if I search for a single tag (say, black) and not all 3, or even 2... What am I missing?
Heres my code:
Article.php model
class Article extends AppModel {
public $actsAs = array(
'Upload.Upload' => array(
'screenshot1' => array (
'fields' => array (
'dir' => 'dir'
),
'thumbnailMethod' => 'php',
'thumbnailSizes' => array(
'xvga' => '1024x768',
'vga' => '640x480',
'thumb' => '80x80'
),
),
),
'Search.Searchable',
'Tags.Taggable'
);
// Search plugin filters
public $filterArgs = array(
'title' => array('type' => 'like'),
'environment' => array('type' => 'like'),
'description' => array('type' => 'like'),
'error' => array('type' => 'like'),
'cause' => array('type' => 'like'),
'resolution' => array('type' => 'like'),
'live' => array('type' => 'value'),
'synced' => array('type' => 'value'),
'tags' => array('type' => 'subquery', 'method' => 'findByTags', 'field' => 'Article.id'),
array('name' => 'search', 'type' => 'query', 'method' => 'filterQuery'),
);
// This is the OR query that runs when the user uses the client side signle field search
public function filterQuery($data = array()) {
if(empty($data['search'])) { // search is the name of my search field
return array();
}
$query = '%'.$data['search'].'%';
return array(
'OR' => array(
'Article.title LIKE' => $query,
'Article.description LIKE' => $query,
'Article.error LIKE' => $query,
)
);
}
// Find by tags method
public function findByTags($data = array()) {
$this->Tagged->Behaviors->attach('Containable', array('autoFields' => false));
$this->Tagged->Behaviors->attach('Search.Searchable');
$query = $this->Tagged->getQuery('all', array(
'conditions' => array('Tag.name' => $data['tags']),
'fields' => array('foreign_key'),
'contain' => array('Tag')
));
return $query;
}
public $hasAndBelongsToMany = array(
'Category' => array(
'className' => 'Category',
'joinTable' => 'articles_categories',
'foreignKey' => 'article_id',
'associationForeignKey' => 'category_id',
'unique' => 'keepExisting',
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
),
'Tag' => array('with' => 'Tagged')
);
Controller method
public function admin_advancedSearch() {
// Disable validation for this action
$this->Article->validate = array();
// For search plugin
$this->Prg->commonProcess();
// Set searched for details
$this->set('searchedFor', $this->passedArgs);
// If passed args are all empty
if ($this->passedArgs) {
if (empty($this->passedArgs['title']) AND
empty($this->passedArgs['environment']) AND
empty($this->passedArgs['description']) AND
empty($this->passedArgs['error']) AND
empty($this->passedArgs['cause']) AND
empty($this->passedArgs['resolution']) AND
empty($this->passedArgs['live']) AND
empty($this->passedArgs['synced']) AND
empty($this->passedArgs['tags'])) {
$this->Session->setFlash('Please enter at least one search criteria', 'flash_failure');
// Set this var for checks in view
$this->set('emptySeach', true);
} else {
$paginateConditions = $this->Article->parseCriteria($this->passedArgs);
$this->paginate = array('conditions' => array(
$paginateConditions),
'limit' => 10,
'order' => array('Article.modified' => 'DESC'));
$this->set('articles', $this->paginate());
// Count number of results
$count = 0;
foreach ($this->paginate() as $result) {
$count++;
}
$this->set('resultsCount', $count);
// Search was not empty - set flag for view
$this->set('emptySeach', false);
}
}
// Set layout
$this->layout = 'admin';
//debug($this->passedArgs);
}
All plugins are loaded successfully from bootstrap, searches work fine without tags. Searches with tags only work if one tag is entered....
*EDIT *
If I debug $query from the findByTags method I get this:
'SELECT `Tagged`.`foreign_key` FROM `knowledgebase`.`tagged` AS `Tagged` LEFT JOIN `knowledgebase`.`tags` AS `Tag` ON (`Tagged`.`tag_id` = `Tag`.`id`) WHERE `Tag`.`name` = 'kia, rio, red''
So it looks like its trying to find a single tag with all the searched for tags in its name. How can I make the WHERE part an IN?
For example:
WHERE `Tag`.`name` IN ('kia', 'rio', 'red')
Thanks
Use the "method" https://github.com/cakedc/search#behavior-and-model-configuration key her to pass the search args to a customized method.
The example here shows you exactly how this works https://github.com/cakedc/search#full-example-for-modelcontroller-configuration-with-overriding
public $filterArgs = array(
'some_related_table_id' => array('type' => 'value'),
'search'=> array('type' => 'like', 'encode' => true, 'before' => false, 'after' => false, 'field' => array('ThisModel.name', 'OtherModel.name')),
'name'=> array('type' => 'query', 'method' => 'searchNameCondition')
);
public function searchNameCondition($data = array()) {
$filter = $data['name'];
$cond = array(
'OR' => array(
$this->alias . '.name LIKE' => '' . $this->formatLike($filter) . '',
$this->alias . '.invoice_number LIKE' => '' . $this->formatLike($filter) . '',
));
return $cond;
}
Inside your custom method explode() the tags and make them an array so that CakePHP is using them as IN() or better (in can become slow) make it a chain of AND or OR.
I try to print a drupal 'select option' element in a form .I think drupal_render not applying #default_value.every thing is ok except #default_value not applied.
where is the problem?anybody know how i can do this?
do #default_value accept string value?
this is pseudo of my codes:
function test_menu(){
$items=array();
$items['admin/config/regional/test']=array(
'title' => 'test',
'description' => t('test'),
'page callback' =>'drupal_get_form',
'page arguments' => array('test_function'),
);
$items[]=array();
return $items;
}
function test_function(){
$header = array
(
'test1' => t('test1'),
'test2'=> t('test2'),
);
$a=(1,2,3);
$$options=array();
foreach($a as $i=>$v)
{
$f['type'] = array(
'#type' => 'select',
'#options' => array(1,2,3,4),
'#default_value'=>1,
);
$options += array($name=>array( 'test1' => $v,
'test2'=> drupal_render($f['type']) ,
}
$form['table'] = array
(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#multiple' => FALSE
//'#empty' => t('No users found'),
);
$form['submit'] = array
(
'#type' => 'submit',
'#value' => t('Submit'),
);
return $form;
}
I test textfield but its also not work and not accept #default_value in drupal_render
$f['test3']=array(
'#type'=>'textfield',
'#title'=>'test3',
'#default_value' =>'aaa',
);
I suppose this is beacuse using drupal_render .anybody have a solution?
In Drupal_render 's used in drupal_get_form , #default_value not set use must use #value instaed of it.
$f['type'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array('1','2','3','4')),
'#value'=> '1',
);
The following code doesn`t work:
$form['title'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array(t('opt1'), t('opt3'), t('opt4'), t('opt5'), t('opt6'))),
'#default_value' => array($default_value_key));
$form['title1'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array(t('opt1'), t('opt3'), t('opt4'), t('opt5'), t('opt6'))),
'#default_value' => array($default_value_key));
return $form;
But then i did the following:
$form['group'] = array('#tree' => TRUE);
$form['group']['title'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array(t('opt1'), t('opt3'), t('opt4'), t('opt5'), t('opt6'))),
'#default_value' => array($default_value_key));
$form['group']['title1'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array(t('opt1'), t('opt3'), t('opt4'), t('opt5'), t('opt6'))),
'#default_value' => array($default_value_key));
return $form;
And default values now works.
I have the same problem. finally, i found one method. As you said, the default_value does not work. So make the default_value fixed as 0. and change the options array, put the default value on the top.
If you look at the example from Drupal's Form API, you'll see that the #options setting takes a key-value pair array and in the #default_value, you should specify the key of the default value, not the string value.
Also, according to documentation for the #options setting, #options is expecting String values. So your select should be more like:
$f['type'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(array('1','2','3','4')),
'#default_value'=> '1',
);
Hie guys i need help with my code. I have a form where a student selects subjects, marks and grades they obtained. Subjects and grades are a dropdown menu and they are in a loop. I want a student to enter at least five subjects including english language. each subject is referenced by a subject code. Can you help me do this?
My Controller function is
subject_code' => $this->data['ApplicantOlevelQualification']['subject_code'][$i],
'grade' => $this->data['ApplicantOlevelQualification']['grade'][$i],
My model is as follows
public $validate = array(
'grade' => array(
'notempty' => array(
'rule' => array('notempty'),
// extra keys like on, required, etc. go here...
),
'ruleName2' => array(
'rule' => array('inList', array('A', 'B','C','D','E')),
'message' => 'Not in range.',
),
),
'subject_code' => array(
'numeric' => array(
'rule' => array('numeric'),
//'message' => 'Your custom message here',
//'allowEmpty' => false,
//'required' => false,
//'last' => false, // Stop validation after this rule
//'on' => 'create', // Limit validation to 'create' or 'update' operations
),
),
You need to create a custom validation.
Have a look here:
http://book.cakephp.org/1.3/view/1179/Custom-Validation-Rules
An example:
<?php
class User extends AppModel {
var $name = 'User';
var $validate = array(
'promotion_code' => array(
'rule' => array('limitDuplicates', 25),
'message' => 'This code has been used too many times.'
)
);
function limitDuplicates($check, $limit){
//$check will have value: array('promomotion_code' => 'some-value')
//$limit will have value: 25
$existing_promo_count = $this->find( 'count', array('conditions' => $check, 'recursive' => -1) );
return $existing_promo_count < $limit;
}
}
?>