I've got a form for a job application, where administrator needs to pick from the list of certain user ids. The list contains only user ids of type "employer", however I want to validate the administrator input, so if he manually inserts an id that doesn't exist or is for user of different type than "employer", the validation should fail. I thought that code to do this would be:
new Zend_Validate_Db_RecordExists(
array(
'table' => 'users',
'field' => 'id',
'exclude' => "mode != 'employer'"
)
)
so, I'm searching for all the records in table users, excluding those where mode != 'employer' - if such record exists, where id is equal to the one picked from input, it passes the validation. However, the code above doesn't work - I have to do 'exclude' => "mode = 'employer'", so exclude actually equals where statement. My understanding of the logic here is wrong - can somebody tell me why?
PHP: 5.2.17, Zend: 1.10.4
EDIT: (the comment to #ro ko enquiries, as it probably clears things out)
Please find the table and sample code here: http://pastebin.com/C7AXMNTZ . In my understanding this should return valid for Joker (is employer), but false for Kingpin (not employer) and Poison Ivy (not in the db) - as you can see the results are not what I'm expecting.
A) 'exclude' => "mode != 'employer'"
$id = new Zend_Form_Element_Select("id");
$id->setRegisterInArrayValidator(false);
$id->addValidator(new Zend_Validate_Db_RecordExists(array(
'table' => 'villains',
'field' => 'id',
'exclude' => "mode != 'employer'"
)));
Produces the following query:
SELECT `villains`.`id`
FROM `villains`
WHERE (`id` = :value) AND (mode != 'employer')
LIMIT 1
B) 'exclude' => "mode = 'employer'"
$id = new Zend_Form_Element_Select("id");
$id->setRegisterInArrayValidator(false);
$id->addValidator(new Zend_Validate_Db_RecordExists(array(
'table' => 'villains',
'field' => 'id',
'exclude' => "mode = 'employer'"
)));
Produces the following query:
SELECT `villains`.`id`
FROM `villains`
WHERE (`id` = :value) AND (mode = 'employer')
LIMIT 1
C) 'exclude' => array("field" => "mode", "value" => "employer")
$id = new Zend_Form_Element_Select("id");
$id->setRegisterInArrayValidator(false);
$id->addValidator(new Zend_Validate_Db_RecordExists(array(
'table' => 'villains',
'field' => 'id',
'exclude' => array(
"field" => "mode",
"value" => "employer"
)
)));
Produces the following query:
SELECT `villains`.`id`
FROM `villains`
WHERE (`id` = :value) AND (`mode` != 'employer')
LIMIT 1
Outcomes
You want B. It is confusing and arguably the logic and behaviour of the component is backwards. Nonetheless, the behaviour you want is from example B.
Appendix
We can hack a test (and I really mean hack together) to check that the above works as expected.
Both test1 and test2 pass, but as you can see from the providers, they both produce different results.
class SO14706653Test extends PHPUnit_Framework_TestCase
{
/**
* #var Zend_Test_PHPUnit_Db_Connection
*/
public $dbConnection;
public function getRowCount($tableName) {
$query = "SELECT COUNT(*) FROM ".$this->dbConnection->quoteSchemaObject($tableName);
return (int) $this->dbConnection->getConnection()->query($query)->fetchColumn();
}
// hack a very quick setup for tests
public function setup() {
$app = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/configs/application.ini');
$app->bootstrap();
$dbAdapter = $app->getBootstrap()->getResource('db'); /* #var $db Zend_Db_Adapter_Pdo_Mysql */
$this->dbConnection = new Zend_Test_PHPUnit_Db_Connection($dbAdapter, 'unittests');
$dbAdapter->exec("CREATE TABLE IF NOT EXISTS `villains` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`mode` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1");
$dbAdapter->exec('DELETE FROM villains'); // clean out db data
$dbAdapter->exec("
INSERT INTO `villains` VALUES(1, 'Joker', 'employer');
INSERT INTO `villains` VALUES(2, 'Kingpin', '');
INSERT INTO `villains` VALUES(3, 'Penguin', '');
");
}
// ensure the above setup is working as expected
public function assertPreConditions() {
$this->assertEquals(3, $this->getRowCount('villains'));
}
public function provideTest1()
{
return [
// form data is valid? isRequired?
[['id' => '1'], false, false],
[['id' => '2'], true, false],
[['id' => '3'], true, false],
[['id' => ''], true, false],
[[], true, false],
[['id' => '856'], false, false],
[['id' => '856'], false, true],
[['id' => ''], false, true],
[[], false, true],
];
}
public function provideTest2()
{
return [
// form data is valid? isRequired?
[['id' => '1'], true, false],
[['id' => '2'], false, false],
[['id' => '3'], false, false],
[['id' => ''], true, false],
[[], true, false],
[['id' => '856'], false, false],
[['id' => '856'], false, true],
[['id' => ''], false, true],
[[], false, true],
];
}
/**
* #dataProvider provideTest1
*/
public function test1(array $data, $isValid, $isRequired)
{
$form = new Zend_Form();
$id = new Zend_Form_Element_Select("id");
$id->setRegisterInArrayValidator(false);
$id->addValidator(new Zend_Validate_Db_RecordExists(array(
'table' => 'villains',
'field' => 'id',
'exclude' => "mode != 'employer'"
)));
$id->setRequired($isRequired);
$form->addElement($id);
// produces the query
// SELECT `villains`.`id`
// FROM `villains`
// WHERE (`id` = :value) AND (mode != 'employer')
// LIMIT 1
$this->assertSame($isValid, $form->isValid($data));
}
/**
* #dataProvider provideTest2
*/
public function test2(array $data, $isValid, $isRequired)
{
$form = new Zend_Form();
$id = new Zend_Form_Element_Select("id");
$id->setRegisterInArrayValidator(false);
$id->addValidator(new Zend_Validate_Db_RecordExists(array(
'table' => 'villains',
'field' => 'id',
'exclude' => "mode = 'employer'"
)));
$id->setRequired($isRequired);
$form->addElement($id);
// produces the query
// SELECT `villains`.`id`
// FROM `villains`
// WHERE (`id` = :value) AND (mode = 'employer')
// LIMIT 1
$this->assertSame($isValid, $form->isValid($data));
}
}
I think you got confused here. Logic is fine with the validator.
How?
Well exclude here adds negation to your condition.
'exclude' => "mode = 'employer'"
OR
'exclude' => array(
'field' => 'mode',
'value' => 'employer'
)
is interpreted as validator should stand valid for other than mode = employer condition.
EDIT: If you see In the manual you'll see a description: "The above example will check the table to ensure no records other than the one where id = $user_id contains the value $username." this statement should clear things for you I assume.
You pass an array in the exclude option. Eg.
$this->getElement('username')->addValidator(
new Zend_Validate_Db_NoRecordExists(
array(
'table' => 'user',
'field' => 'username',
'exclude' => array(
'field' => 'username',
'value' => $username
)
)
)
);
Check here for more info.
Edit: After reading the question again, I see I didn't actually answer it. The only other advice I would give is using the quoteInto statement as in the documentation.
$email = 'user#example.com';
$clause = $db->quoteInto('email != ?', $email);
$validator = new Zend_Validate_Db_RecordExists(
array(
'table' => 'users',
'field' => 'username',
'exclude' => $clause
)
);
Also here is a link to another question about Zend_Validate_Db_RecordExistswhich has a very good answer.
How to modify zend Db_RecordExists validator where clause?
As per documentation (search for The above example will check the table to ensure no records other than the one where id = $user_id contains the value $username.) the exclude option indeed works as WHERE statement.
I can only say that whoever though that using such keyword for this functionality is a good idea had a wicked sense of humor.
Related
I want use combine MongoDB\Driver\Query and MongoDB\Driver\Command. But it does not work. Who can help me. Thanks!
$filter = ['column1' => ['$ne' =>'abc']];
$options = [
'limit' => 1000,
/* Only return the following fields in the matching documents */
'projection' => [
'column2' => 1,
'column3' => 1,
'column4' => 1,
],
/* Return the documents in descending order of views */
'sort' => [
'created_date' => -1
],
];
$query = new MongoDB\Driver\Query($filter, $options);
$cmd = new MongoDB\Driver\Command([
// build the 'distinct' command
'distinct' => 'collection', // specify the collection name
'key' => 'column5', // specify the field for which we want to get the distinct values
'query' => $query // criteria to filter documents,
]);
$cursor = $mgClient->executeCommand('mydb', $cmd); // retrieve the results
$arr = current($cursor->toArray())->values; // get the distinct values as
I recently built a website for work because our techs need an easy way to find out what keys go to which properties we provide service to. I've gotten the site working with live search (Ajaxlivesearch.com) and it's linked to my MySQL database. Everything works great except it only searches one column currently, the Address column. I'd like to be able to search two columns, Address and Property_Name at the same time.
Here's the current code, or at least the part that deals with searching the db.
<?php
namespace AjaxLiveSearch\core;
if (count(get_included_files()) === 1) {
exit('Direct access not permitted.');
}
/**
* Class Config
*/
class Config
{
/**
* #var array
*/
private static $configs = array(
// ***** Database ***** //
'dataSources' => array(
'ls_query' => array(
'host' => '',
'database' => '',
'username' => '',
'pass' => '',
'table' => '',
// specify the name of search columns
'searchColumns' => array('Address'),
// specify order by column. This is optional
'orderBy' => '',
// specify order direction e.g. ASC or DESC. This is optional
'orderDirection' => '',
// filter the result by entering table column names
// to get all the columns, remove filterResult or make it an empty array
'filterResult' => array(),
// specify search query comparison operator. possible values for comparison operators are: 'LIKE' and '='. this is required.
'comparisonOperator' => 'LIKE',
// searchPattern is used to specify how the query is searched. possible values are: 'q', '*q', 'q*', '*q*'. this is required.
'searchPattern' => 'q*',
// specify search query case sensitivity
'caseSensitive' => false,
// to limit the maximum number of result uncomment this:
'maxResult' => 10,
// to display column header, change 'active' value to true
'displayHeader' => array(
'active' => true,
'mapper' => array(
'Property_Name' => 'Property Name',
'Address' => 'Address',
'Key' => 'Key',
'Property_Manager' => 'Property Manager',
'Door_Code' => 'Door Code'
)
),
// add custom class to <td> and <th>
// To hide a column use class 'ls_hide'
'columnClass' => array(
'Count' => 'ls_hide',
'Reserve' => 'ls_hide'
),
'type' => 'mysql',
),
I've taken the connection info out for obvious reasons.
I tried 'searchColumns' => array('Address' AND 'Property_Name'), thinking that would search both columns but that didn't work at all.
I'm not familiar with Ajaxlivesearch, but it looks like searchColumns takes an array, so:
'searchColumns' => array('Address', 'Property_Name'),
will probably work.
(array('Address' AND 'Property_Name') is a syntax error.)
And of course as soon as I post the question I figure it out, maybe it will help someone else though. To look at multiple columns in one array you need to separate them with a comma (,) the working code is:
<?php
namespace AjaxLiveSearch\core;
if (count(get_included_files()) === 1) {
exit('Direct access not permitted.');
}
/**
* Class Config
*/
class Config
{
/**
* #var array
*/
private static $configs = array(
// ***** Database ***** //
'dataSources' => array(
'ls_query' => array(
'host' => '',
'database' => '',
'username' => '',
'pass' => '',
'table' => '',
// specify the name of search columns
'searchColumns' => array('Address', 'Property_Name'),
// specify order by column. This is optional
'orderBy' => '',
// specify order direction e.g. ASC or DESC. This is optional
'orderDirection' => '',
// filter the result by entering table column names
// to get all the columns, remove filterResult or make it an empty array
'filterResult' => array(),
// specify search query comparison operator. possible values for comparison operators are: 'LIKE' and '='. this is required.
'comparisonOperator' => 'LIKE',
// searchPattern is used to specify how the query is searched. possible values are: 'q', '*q', 'q*', '*q*'. this is required.
'searchPattern' => 'q*',
// specify search query case sensitivity
'caseSensitive' => false,
// to limit the maximum number of result uncomment this:
'maxResult' => 10,
// to display column header, change 'active' value to true
'displayHeader' => array(
'active' => true,
'mapper' => array(
'Property_Name' => 'Property Name',
'Address' => 'Address',
'Key' => 'Key',
'Property_Manager' => 'Property Manager',
'Door_Code' => 'Door Code'
)
),
// add custom class to <td> and <th>
// To hide a column use class 'ls_hide'
'columnClass' => array(
'Count' => 'ls_hide',
'Reserve' => 'ls_hide'
),
'type' => 'mysql',
),
Run code snippetCopy snippet to answer
I`ve been trying to setup my migrate module for a while without any success to associate taxonomy terms to node.
Lets first start by saying, its not D2D migration, source is old symfony database!
First I`ve exported taxonomy terms successfuly:
*-.migrate.inc:
...
'symfony_db_countries' => array(
'class_name' => 'MigrateSymfonyCountriesMigration',
'group_name' => 'symfony_db_loc',
),
'symfony_db_cities' => array(
'class_name' => 'MigrateSymfonyCitiesMigration',
'group_name' => 'symfony_db_loc',
'dependencies' => array('symfony_db_countries'),
),
'symfony_db_sights' => array(
'class_name' => 'MigrateSymfonySightsMigration',
'group_name' => 'symfony_db_loc',
),
...
Cities.inc:
class MigrateSymfonyCitiesMigration extends MigrateSymfonyCitiesMigrationBase {
const TABLE_NAME = 'ya_loc_city';
public function __construct($arguments = array()) {
parent::__construct($arguments);
$this->description = t('Migrate cities from Symfony DB.');
$query = Database::getConnection('symfony', 'default')->select('ya_loc_city', 'c');
$query->leftJoin('ya_loc_city_translation', 'ct', 'ct.id = c.id');
$query->fields('c', array());
$query->fields('ct', array());
$this->source = new MigrateSourceSQL($query, array(), NULL, array('map_joinable' => FALSE));
$this->destination = new MigrateDestinationTerm('cities');
$this->map = new MigrateSQLMap($this->machineName,
array(
'id' => array(
'type' => 'int',
'not null' => TRUE,
'description' => 'City ID',
),
),
MigrateDestinationTerm::getKeySchema()
);
$this->addFieldMapping('name', 'name');
$this->addFieldMapping('parent', 'country_id')->sourceMigration('symfony_db_countries');
}
}
Then Im creating nodes from another table, which have a entityReference field (field_city) to the taxonomy term.
<?php
class MigrateSymfonySightsMigration extends Migration {
public function prepareRow($row) {
parent::prepareRow($row);
$row->point = 'point (' . $row->longitude . ' ' . $row->latitude. ')';
}
public function __construct($arguments = array()) {
//entry is the name of a group.
parent::__construct($arguments);
$this->description = 'Migration sights';
/*************SOURCE DATA*************/
...
$this->source = new MigrateSourceSQL($query, array(), NULL,
array('map_joinable' => FALSE));
$this->destination = new MigrateDestinationNode('sight');
/*************MAPPING*************/
$this->addFieldMapping('field_city', 'name')->sourceMigration('symfony_db_cities');
$this->map = new MigrateSQLMap($this->machineName,
array(
'id' => array('type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
)
),
MigrateDestinationNode::getKeySchema()
);
}
}
From symfony table there is a column "name", which contains the city name like "Paris".
How to associate this column ('name') value ('Paris') with taxonomy term? Following doesnt work:
$this->addFieldMapping('field_city', 'name')->sourceMigration('symfony_db_cities');
Firstly, make sure your "field_city" field accepts "cities" terms.
Then make sure MigrateSymfonyCitiesMigration is a dependency of MigrateSymfonySightsMigration.
According to the documentation, the term reference field migration has a "source_type" of term name by default. Make sure "ignore_case" is set appropriately.
If all else fails, then change the MigrateSymfonyCitiesMigration map sourceid1 to the "name" instead of "id" and try again (will have to reregister migration):
$this->map = new MigrateSQLMap($this->machineName,
array(
'name' => array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'description' => 'City Name',
),
),
MigrateDestinationTerm::getKeySchema()
);
I have problem, becouse only last item from loop is saved in database.
Im using CakePhp 2.x
Controller:
for ($x=1; $x <= count($this->request->data['Goodsandoffer'])/3;$x++){
$promID = $this->request->data['Goodsandoffer']['promotionaloffer_id_'.$x];
if($this->request->data['Goodsandoffer']['cenaPromocyjna_'.$x] != ''){
$helperReqestTable3 = array('promotionaloffer_id'=>$this->request->data['Goodsandoffer']['promotionaloffer_id_'.$x],'good_id'=>$this->request->data['Goodsandoffer']['good_id_'.$x],'cenaPromocyjna'=>$this->request->data['Goodsandoffer']['cenaPromocyjna_'.$x]);
$helperReqestTable['Goodsandoffer']=$helperReqestTable3;
debug($helperReqestTable);
$this->Goodsandoffer->save($helperReqestTable);
}
}
Here is how look my debug in loop:
array(
'Goodsandoffer' => array(
'promotionaloffer_id' => '7',
'good_id' => '18',
'cenaPromocyjna' => '1'
)
)
And in next interation:
array(
'Goodsandoffer' => array(
'promotionaloffer_id' => '7',
'good_id' => '19',
'cenaPromocyjna' => '2'
)
)
In database is created only one row with last item.
Model:
class Goodsandoffer extends AppModel {
public $displayField = 'id';
public $belongsTo = array(
'Promotionaloffer' => array(
'className' => 'Promotionaloffer',
'foreignKey' => 'promotionaloffer_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Good' => array(
'className' => 'Good',
'foreignKey' => 'good_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
Have you tried calling $this->Goodsandoffer->create() before you call save? That way you're definitely telling Cake to create a new record each time.
The general process of creating and saving data in Cake is:
$this->Model->create()
$this->Model->set($data_array)
$this->Model->save()
You can also eliminate step 2 above by passing you $data_array to the save() function:
$this->Model->create();
$this->Model->save($data_array);
NOTE: from the manual (if you aren't using create()):
When calling save in a loop, don’t forget to call clear().
Another way you could create new data would be to make sure the primary key for your model is null in the data set you pass into save, although this is a little less obvious and probably best to stick to the create/[set/]save flow:
$data = array('id' => null, 'somefield' => 'foobar');
$this->Model->save($data); // new record created
In cakephp 2.0 $this->Model->create() create work fine. But if you are using cakephp version 3 or greater then 3. Then follow the below code
$saveData['itemId'] = 1;
$saveData['qty'] = 2;
$saveData['type'] = '0';
$saveData['status'] = 'active';
$saveData = $this->Model->newEntity($saveData);
$this->Model->save($materialmismatch);
In normal case we use patchEntity
$this->Model->patchEntity($saveData, $this->request->data);
It will only save last values of array so you have to use newEntity() with data
I am working on my first project experimenting with CakePHP. Basically I have a site where I want users to be able to search through different workouts stored in a MySQL database.
When users enter a search term, I want to simply return any workouts whose names contain the search term. On top of that, I want them to be able to apply filters to narrow down the search to specific muscle groups.
The filters are passed to the search action through a querystring, and the search term is passed using the post method.
First off, here are the two models I care about right now:
class Workout extends AppModel {
public $name = 'Workout';
public $hasAndBelongsToMany = array(
'MuscleGroup' =>
array(
'className' => 'MuscleGroup',
'joinTable' => 'workouts_muscle_groups',
'foreignKey' => 'workout_id',
'associationForeignKey' => 'muscle_group_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
class MuscleGroup extends AppModel {
public $name = 'MuscleGroup';
public $hasAndBelongsToMany = array(
'Workout' =>
array(
'className' => 'Workout',
'joinTable' => 'workouts_muscle_groups',
'foreignKey' => 'muscle_group_id',
'associationForeignKey' => 'workout_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
Now below is the search action in my WorkoutsController:
function search() {
$hasFilters = false;
$filters = array(
"abs" => false,
"back" => false,
"biceps" => false,
"chest" => false,
"forearms" => false,
"legs" => false,
"shoulders" => false,
"triceps" => false
);
if(isset($this->params['url']['abs']))
if($this->params['url']['abs'] == "1")
$filters['abs'] = $hasFilters = true;
if(isset($this->params['url']['back']))
if($this->params['url']['back'] == "1")
$filters['back'] = $hasFilters = true;
if(isset($this->params['url']['biceps']))
if($this->params['url']['biceps'] == "1")
$filters['biceps'] = $hasFilters = true;
if(isset($this->params['url']['chest']))
if($this->params['url']['chest'] == "1")
$filters['chest'] = $hasFilters = true;
if(isset($this->params['url']['forearms']))
if($this->params['url']['forearms'] == "1")
$filters['forearms'] = $hasFilters = true;
if(isset($this->params['url']['legs']))
if($this->params['url']['legs'] == "1")
$filters['legs'] = $hasFilters = true;
if(isset($this->params['url']['shoulders']))
if($this->params['url']['shoulders'] == "1")
$filters['shoulders'] = $hasFilters = true;
if(isset($this->params['url']['triceps']))
if($this->params['url']['triceps'] == "1")
$filters['triceps'] = $hasFilters = true;
$query = $this->request->data['search'];
//insert code here to actually perform the search!
}
I was really hoping for an easy way to do this but I have been poking around the documentation all night and can't seem to find any relevant examples. In EntityFramework I could simply say something like:
List<Workout> results = context.Workouts.Where(w => w.Name.Contains(searchTerm) && w.MuscleGroups.Any(g => g.Id == mg_id)).ToList();
Note: no idea if that C# syntax is anywhere near correct, but the ease of accessing data from the model was somewhere close to that from what I remember.
How can I get the equivalent of that from CakePHP?
Try to use MuscleGroup IDs for filtering, instead of their names. I guess the user will select them from a list, anyway.
Then, the find() should work like this:
$this->Workout->find(
'all',
array(
'conditions' => array(
'Workout.name LIKE' => '%'.$searchTerm.'%',
// List of included MuscleGroups:
'MuscleGroup.id' => array(1, 5, 9, 17)
)
)
);
Note that you need to take care that the related MuscleGroup is included in the Workout query, either by setting $this->Workout->recursive = 1 or (recommended) by using the more flexible ContainableBehavior.