QuickBase foreach insert - php

I'm having an issue using the Quickbase API to perform the following:
SELECT 1, 2, 3 FROM table AA that has column BB = 1
foreach record {
Insert 1, 2, 3 into table ZZ.
}
function add_children($opportunity_id) {
global $config;
$qbc = new QuickBase($_SESSION['qb_username'] ,
$_SESSION['qb_password'],
true,
$config['AA'],
$config['debug'],
$config['app_token']);
$xml = $qbc->do_query("{'" . $config['AA'] . "'.EX.''}", 0, 0, 'a', 0, '', '');
$records = array();
foreach($xml->record as $record) {
$r = array();
$r['record_id'] = $record->record_id_;
$r['account_number'] = $record->account_number;
$records[] = $r;
$xml = $qbc->add_record($records[]);
}
}

First, I'm assuming that you're using this PHP SDK by QuickbaseAdmirer https://github.com/QuickbaseAdmirer/QuickBase-PHP-SDK. There are a few potential problems with your code.
Double check that your constructor is correct. Unless you've modified it, the Quickbase constructor in the SDK (again that I'm assuming you're using) takes user name, password, xml, database id, and then token in that order. Whatever value is in $config['debug'] may be taken as the token and the value of $config['app_token'] may be taken as your realm. Also, $config['AA'] as used in the constructor should be a string of random seeming characters like "bbqn1y5qv". Here's the constructor in the SDK for reference:
public function __construct($un, $pw, $usexml = true, $db = '', $token
= '', $realm = '', $hours = '')
Your query $xml = $qbc->do_query("{'" . $config['AA'] . "'.EX.''}", 0, 0, 'a', 0, '', ''); is not returning any records because $config['AA'] is both being used as your DBID (in the constructor) and your field ID in the query. The DBID must be a string and the field ID must be an integer that corresponds to the field you're making the query for. For example, if you wanted to return records created today your query would be '{1.IR.today}' because 1 is always the field ID for date created. It's also not returning any records because the SDK requires queries be passed as an array of arrays. So, my records created today query needs to be rewritten as:
$query= array(
array(
'fid' => '1',
'ev' => 'IR'),
'cri' => 'today'),
);
You'll also need to pass a string of period separated values to the clist parameter of the method or leave it blank for the table defaults. For example, if I wanted to get the date created and record ID for all records in this table sorted by date ascending, I would use this:
$query= array(
array(
'fid' => '3',
'ev' => 'GT'),
'cri' => '0'),
);
$xml = $qbc->do_query($query, '', '', '1.3', '1', '', 'sortorder-A');
You can read up more on the Quickbase API, and do_query specifically, here http://www.quickbase.com/api-guide/index.html#do_query.html
The add record API call takes pairs of field IDs and values. The SDK handles that by taking an array of arrays with 'fid' and 'value' pairs. Assuming you want to put the value of $record->record_id_ in field #37 and $record->account_number in field #30 your code should look like this:
foreach($xml->record as $record) {
$records= array(
array(
'fid' => '37', //Whatever field you want to store the value to
'value' => $record->record_id_),
array(
'fid' => '30',
'value' => $record->account_number),
);
$xml = $qbc->add_record($records);
}
Throw in a print_r($xml); at the end and you can see any response from Quickbase for debugging. You should get something like this for a success:
SimpleXMLElement Object ( [action] => API_AddRecord [errcode] => 0 [errtext] => No error [rid] => 81 [update_id] => 1436476140453 )
The way your code is presented, you may not get the results you expect. Your do query and add record method calls are performed on the same table and that isn't normally what someone would want. Usually, the goal is to perform a do query on one table and then use that data to add records in a different table. If that's the case, you'll need to change the database ID in your $qbc object before you preform the add record call. This is easy enough to do with $qbc->set_database_table('DBID'); where DBID is the target table ID (which should be a string of random seeming characters like "bbqn1y5qv").
Best of luck!

Related

Appending value to DynamoDB number set using aws-php-sdk

I am using aws-php-sdk with dynamoDB to store a set of unique numerical values for a key.
I want to be able to append a new value to the set - this is easy to achieve with a list however this also results in duplicate values so I would rather use a set.
Because of the way dynamoDB is charged I don’t want to have to read all the values, insert unique value and write the whole set back because that would be very costly when scaled out. Docs seem to suggest appending to a number set is possible but doesn’t give much help in making it work.
$UpdateExpression = "
SET #subscribers = list_append(if_not_exists(#subscribers, :empty_list), :subscriber)
";
// This condition should match if the value already exists.
$ConditionExpression = "not contains (#subscribers,:subscriberStr)";
$ExpressionAttributeNames = [
"#subscribers" => "subscribers"
];
$ExpressionAttributeValues = $marshaler->marshalJson('
{
":subscriber": ['.$subscriber.'],
":empty_list": [],
":subscriberStr": "'.$subscriber.'"
}
');
$tableName = 'publisherRelationships';
$key = $marshaler->marshalJson('
{
"publisher": '.$publisher.'
}
');
$params = [
'TableName' => $tableName,
'Key' => $key,
'UpdateExpression' => $UpdateExpression,
'ConditionExpression' => $ConditionExpression,
'ExpressionAttributeNames' => $ExpressionAttributeNames,
'ExpressionAttributeValues' => $ExpressionAttributeValues
];
$result = $client->updateItem($params);

Insert dynamic Object property names into Database

Alright so I have an insert query that I would like to run but the issue I am having is with getting object properties/values that I need to insert.
Say I have a query that looks like the one below.
$this->db->insert('tblitems_in', array(
'platform' => $item['Platform'],
'ram' => $item['RAM'],
'qty' => $item['qty'],
'rate' => number_format($item['rate'], 2, '.', ''),
'rel_id' => $insert_id,
'rel_type' => 'estimate',
'item_order' => $item['order'],
'unit' => $item['unit']
));
This works fine when the person chooses RAM on the webpage which sets the $item Objects property 'RAM' to the value that was picked. Now if they choose HardDrive, that properties name is now sent as 'HardDrive' with the value they chose. Is there a way that i Could replace the 'ram' and 'RAM' from the below example with a variable so I could change what the property name is that I would like to insert and insert into the corresponding db column?
EDIT:
I should have added that the options on the webpage are also dynamically created from a database so I do not know at the time of coding what the property names are. They could be RAM, HardDrive, Processor, maybe even Elephant. I was hoping I could use variables so that I could look at the DB used to create the webpage so that I know the property names and then dynamically add those names into the query.
EDIT:
Right now I am using the following code in order to get all the possible options that can be received from the webpage from a DB the webpages uses to create itself.
$plat_options = $this->db->get('tblplatform_options')->row()->name;
In the database right now it is only populated with names RAM and HardDrive to make things known for testing purposes. So this returns $plat_options = {RAM, HardDrive}. I now have to figure out how to test is $item has these(RAM and HardDrive) as properties and if $item does have them then add them into the query previously shown.
You can set an array of key => variable names, then loop over those values to see if they exist in the $item variable and, if so, add that value to the data to be inserted into the db:
//default array of data to insert
$data = [
'platform' => $item['Platform'],
'qty' => $item['qty'],
'rate' => number_format($item['rate'], 2, '.', ''),
'rel_id' => $insert_id,
'rel_type' => 'estimate',
'item_order' => $item['order'],
'unit' => $item['unit']
];
//Get column names from db
$plat_options = $this->db->get('tblplatform_options')->row()->name;
// $plat_options = [RAM, HardDrive]
//Check if $item[$name] exists. If it does, add that to the
// array of data to be inserted
foreach($plat_options as $key) {
if(array_key_exists($key, $item)) {
$data[$key] = $item[$key];
}
}
$this->db->insert('tblitems_in', $data);
edit
I'm not sure this will work (I don't understand the use case).
It is possible, using array_diff_key to get a list of array keys that exist in $item but not in $data. With this array of keys, you can add the missing keys.
I have altered my previous code to demonstrate this.
You could create the array one element at a time based on whatever field data you received. I used a switch statement, but it could be a simple if/then/else as well.
$data_array = array();
$data_array['platform'] = $item['Platform']
switch($item['Object'] {
case 'HardDrive':
$data_array['harddrive'] = $item['HardDrive'];
break;
case 'RAM':
$data_array['ram'] = $item['RAM'];
break;
}
$data_array['qty'] = $item['qty'];
$data_array['rate' = number_format($item['rate'], 2, '.', '');
$data_array['rel_id'] = $insert_id;
$data_array['rel_type' = 'estimate';
$data_array['item_order'] = $item['order'];
$data_array['unit'] = $item['unit'];
$this->db->insert('tblitems_in', $data_array);

php: Database Class with PDO. How can i make it better?

So I started to get a little more practice in php and want to create a object oriented forum. Therefor I want to have a Database class such like:
<?php
class Database {
public $databaseConnection;
function __construct(){
$this->databaseConnection = new PDO('sqlite:test.sq3', 0, 0);
$this->databaseConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$params = NULL;
$pdoStatement = $this->databaseConnection->prepare('CREATE TABLE IF NOT EXISTS user(
id INTEGER PRIMARY KEY,
username VARCHAR(40) NOT NULL UNIQUE,
numberoflogins INTEGER DEFAULT 0,
bannedstatus BOOLEAN DEFAULT FALSE,
dateofjoining TIME
)');
$pdoStatement->execute(array_values((array) $params));
}
function query($sql, $params = NULL){
$s = $this->databaseConnection->prepare($sql);
$s->execute(array_values((array) $params));
return $s;
}
function insert($table, $data){
self::query("INSERT INTO $table(" . join(',', array_keys($data)) . ')VALUES('. str_repeat('?,', count($data)-1). '?)', $data);
return $this->databaseConnection->lastInsertId();
}
}
Then I do this in the same script:
$database = new Database();
$database->insert('user',array( 'id' => 0,
'username' => 'gulaschsuppe',
'numberoflogins' => 23,
'bannedstatus' => TRUE,
'dateofjoining' => time()));
$searchID = 0;
$userData = $database->query('SELECT username FROM user WHERE id = 0');
$username = $userData->fetchAll();
print_r(array_values($username));
?>
I just wanted to see how things working. The most important part of the code is the class. I needed a little bit time to figure out how I get the information I wanted. This is what I get.
Array ( [0] => Array ( [username] => gulaschsuppe [0] => gulaschsuppe ) )
Everythings working, but I don´t think this is best way to get the informations. I get an Array with an Array. Also, now there is no validation but first I want to focus on the functions query and insert.
So, can you explain how the last part with username => g. [0] => g. occured ?
I would like to know how I can improve the functions and please tell me when I do something totally wrong.
Array (
[0] => Array (
[username] => gulaschsuppe
[0] => gulaschsuppe
)
)
You're getting the result with both names columns (so you could do $row['username']) and numerically (so you can do $row[0]). This is the default PDO behavior. You need to use the PDO::FETCH_* constants in order to change the fetched results. PDO::FETCH_BOTH is the default value.
You can either set it when you're fetching:
$username = $userData->fetchAll(PDO::FETCH_ASSOC);
Or globally at some point:
$this->databaseConnection->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
In the case that you want only a single column, you can use this fetch mode:
$usernames = $userData->fetchAll(PDO::FETCH_COLUMN);
// $usernames = array('gulaschsuppe');
This fetches only a single column for each row, without wrapping each result in another array (column 0 is fetched by default).
See the PDOStatement::fetch() documentation for more details.

$this->Model->id not working before saveAll in CakePHP

I have the following code in CakePHP 2:
$this->Order->id = 5;
$this->Order->saveAll(array(
'Order' => array(
'person_id' => $this->Session->read('Person.id'),
'amount' => $total,
'currency_id' => $code
),
'Lineitem' => $lineitems /* a correctly-formatted array */
));
I would expect this to update the row with the Primary Key of 5 in the Order table and then insert the Lineitem rows with an order_id of 5.
However, all it does is create a new row in Order and then use the new id from the new Order record to create the Listitem rows.
Note: I'm only setting the ID as above for debugging purposes and to easily demonstrate this question. In my final code, I'll be checking to see if there's already a pending order with the current person_id and doing $this->Order->id = $var; if there is and $this->Order->create(); if there isn't.
In other words, sometimes I will want it to INSERT (in which case I will issue $this->Order->create(); ) and sometimes I will want it to UPDATE (in which case I will issue $this->Order->id = $var; ). The test case above should produce an UPDATE but it's producing an INSERT instead.
Any idea what I am doing wrong here?
The array you pass to Model->saveAll() doesnt't contain the order's id, so Cake creates a new one. If you wanto to update an existing record, either you set the order id in the passed array, or you retrieve it with a find. The documentation explicitly remarks
If you want to update a value, rather than create a new one, make sure
your are passing the primary key field into the data array
$order = $this->Order->findById(5);
// ... modify $order if needed
$this->Order->saveAll(array('Order' => $order, 'LineItem' => $items));
In your case, you may want to use something like the following to be as concise as possible. Model::saveAssociated() is smart enough to create or update depending on the id, but you must provide suitable input. Model::read($fields, $id) initializes the internal $data: for an existing record all fields will be read from the database, but for a nonexistent id, you'll need to supply the correct data for it to succeed. Assuming an order belongsTo a customer, I supply the customer id if the order doesn't exist
// set the internal Model::$data['Order']
$this->Order->read(null, 5);
// You may want to supply needed information to create
// a new order if it doesn't exist, like the customer
if (! $this->Order->exists()) {
$this->Order->set(array("Customer" => array("id" => $customer_id)));
}
$this->Order->set(array('LineItem' => $items));
$this->Order->saveAssociated();
As a final note, it seems you are implementing a shopping cart. If that's the case, maybe it'd be clearer to use a separate ShoppingCart instead of an Order with a finalized flag.
Have you tried following:
$this->Order->saveAll(array(
'Order' => array(
'id' => 5,
'person_id' => $this->Session->read('Person.id'),
'amount' => $total,
'currency_id' => $code
),
'Lineitem' => $lineitems /* a correctly-formatted array */
));
Its pretty much the same what you did with :
$this->Order->id = 5;
Maybe that would fix your problem.
Cake is checking if you set id field and if its there it updates record, if not found it creates new record instead.
update:
Then maybe check before you saveAll if there is id field, then save result of check to some boolean and create array to save determined by this boolean for example:
if($id_exist) $order['Order']['id'] = 5;
$order['Order']['id'] = 5;
$order['Order']['person_id'] = $this->Session->read('Person.id'),
$order['Order']['amount'] = $total;
$order['Order']['currency_id'] = $code;
$this->Order->saveAll(array(
'Order' => $order,
'Lineitem' => $lineitems /* a correctly-formatted array */
));

join decomposition inside PHP

I came across an article about Join decomposition.
SCENARIO #1 (Not good):
Select * from tag
Join tag_post ON tag_post.tag_id=tag.id
Join post ON tag_post.post_id=post.id
Where tag.tag='mysql'
SCENARIO #2 (good):
Select * from tag where tag='mysql'
Select * from tag_post Where tag_id=1234
Select * from post where post.id in (123,456,9098,545)
It was suggested to stick to scenario #2 for many reasons specially caching.
The question is how to join inside our application. Could u give us an example with PHP
after retrieving them individually?
(I have read MyISAM Performance: Join Decomposition?
but it did not help)
You COULD use an SQL subselect (if I understand your question). Using PHP would be rather odd while SQL has all the capabilities.
SELECT *
FROM `post`
WHERE `id` IN (
SELECT `post_id`
FROM `tag_post`
WHERE `tag_id` = (
SELECT `tag_id`
FROM `tag`
WHERE `tag` = 'mysql'
)
)
I'm not sure how your database structure looks like, but this should get you started. It's pretty much SQL inception. A query within a query. You can select data using the result of a subselect.
Please, before copying this SQL and telling me it's not working, verify all table and column names.
Before anyone starts to cry about speed, caching and efficiency: I think this is rather efficient. Instead of selecting ALL data and loop through it using PHP you can just select smaller bits using native SQL as it was ment to be used.
Again, I highly discourage to use PHP to get specific data. SQL is all you need.
edit: here's your script
Assuming you have some multi-dimensional arrays containing all data:
// dummy results
// table tag
$tags = array(
// first record
array(
'id' => 0,
'tag' => 'mysql'
),
// second record
array(
'id' => 1,
'tag' => 'php'
)
// etc
);
// table tag_post
$tag_posts = array(
// first record
array(
'id' => 0,
'post_id' => 0, // post #1
'tag_id' => 0 // has tag mysql
),
// second record
array(
'id' => 1,
'post_id' => 1, // post #2
'tag_id' => 0 // has tag mysql
),
// second record
array(
'id' => 2,
'post_id' => 2, // post #3
'tag_id' => 1 // has tag mysql
)
// etc
);
// table post
$posts = array(
// first record
array(
'id' => 0,
'content' => 'content post #1'
),
// second record
array(
'id' => 1,
'content' => 'content post #2'
),
// third record
array(
'id' => 2,
'content' => 'content post #3'
)
// etc
);
// searching for tag
$tag = 'mysql';
$tagid = -1;
$postids = array();
$results = array();
// first get the id of this tag
foreach($tags as $key => $value) {
if($value['tag'] === $tag) {
// set the id of the tag
$tagid = $value['id'];
// theres only one possible id, so we break the loop
break;
}
}
// get post ids using the tag id
if($tagid > -1) { // verify if a tag id was found
foreach($tag_posts as $key => $value) {
if($value['tag_id'] === $tagid) {
// add post id to post ids
$postids[] = $value['post_id'];
}
}
}
// finally get post content
if(count($postids) > 0) { //verify if some posts were found
foreach($posts as $key => $value) {
// check if the id of the post can be found in the posts ids we have found
if(in_array($value['id'], $postids)) {
// add all data of the post to result
$results[] = $value;
}
}
}
If you look at the length of the script above, this is exactly why I'd stick to SQL.
Now, as I recall, you wanted to join using PHP, rather doing it in SQL. This is not a join but getting results using some arrays. I know, but a join would only be a waste of time and less efficient than just leaving all results as they are.
edit: 21-12-12 as result of comments below
I've done a little benchmark and the results are quite stunning:
DATABASE RECORDS:
tags: 10
posts: 1000
tag_posts: 1000 (every post has 1 random tag)
Selecting all posts with a specific tag resulted in 82 records.
SUBSELECT RESULTS:
run time: 0.772885084152
bytes downloaded from database: 3417
PHP RESULTS:
run time: 0.086599111557
bytes downloaded from database: 48644
Please note that the benchmark had both the application as the database on the
same host. If you use different hosts for the application and the database layer,
the PHP result could end up taking longer because naturally sending data between
two hosts will take much more time then when they're on the same host.
Even though the subselect returns much less data, the duration of the requests is nearly 10 times longer...
I've NEVER expected these results, so I'm convinced and I will certainly use this information when I know that performance is important however I will still use SQL for smaller operations hehe...

Categories