Update method custom data source not called in cakephp - php

I am trying to update data using custom datasource. But when i call save after setting the id, it is calling the create method. I have also logged the $Model and in this the id is coming null. As id field is null, create method is called instead of update.
Below is the value of id when i log $Model in Model beforesave
[useTable] => applications
[id] => 191124
[data] => Array
(
[Application] => Array
(
[id] => 191124
[Field] => Array
(
[0] => Array
(
[#id] => 13312
[#type] => 1
[#value] => 10317
)
)
)
)
and when i log the $Model in create id field is null as
[useTable] => applications
[id] =>
[data] => Array
(
[Application] => Array
(
[id] => 191124
[Field] => Array
(
[0] => Array
(
[#id] => 13312
[#type] => 1
[#value] => 10317
)
)
)
)
I am calling save using below code
$data = array(
'id' => $archer_id,
'Field' => array(
array(
'#id' => 13312,
'#type' => 1,
'#value' => 10317
)
)
);
$this->Application->id = $archer_id;
$this->Application->save($data,false);
Model which i am using is
class Application extends ArcherAppModel {
public $hasMany = array('Archer.Assessment');
public $_schema = array(
'Field' => array('type' => 'array')
);
public function beforeSave( $options = array() ) {
$this->log(array("message" => "Data in application before save ","data" => $this->data),'debug');
return true;
}
}
Read function in datasource code is
public function read ( Model &$Model, $queryData = array(), $recursive = null ) {
$this->Model = $Model;
try {
$mid = $this->get_module_id();
$key = $Model->alias . ".id";
if ( array_key_exists( $key, $queryData['conditions'] ) ) $rid = $queryData['conditions'][$key];
elseif ( $Model->id ) $rid = $Model->id;
else $params = $queryData['conditions'];
$params = $this->_params(array(
"moduleId" => $mid,
"contentId"=>$rid
));
$results = $this->_getSoap('records')->__soapCall('GetRecordById', array('GetRecordById' => $params));
if ( property_exists( $results, 'GetRecordByIdResult' ) ) return Xml::build($results->GetRecordByIdResult);
return array();
} catch ( Exception $e ) {
$this->log("OH No...");
$this->log($e->getMessage());
return array();
}
}
Thanks.

Passing an id does not automatically mean it will be an update. Consider the case of saving with mysql where the primary key field is not autoincrement - This (extremely common) case is also supported by the model layer of CakePHP, where inserting and updating with a fixed primary key value both work.
The way the save method works is to check if the id exists, and if it does not the save method clears the model id:
if (!$exists && $count > 0) {
$this->id = false;
}
The consequence of that being an insert, not an update.
if (!empty($this->id)) {
... update ...
} else {
... insert ...
}
The custom datasource mentioned in the question is not shown, but it would appear it does not implement "does this id exist?". That or the model's exists method (which is just a count, if it issues a query to the datasource) is returning false for a different reason.
Forcing an update
To determine yourself whether it is an insert of update, either check why the find-by-primary-key call in Model::exists doesn't work and fix the datasource, or override ModelName::exists to implement whatever logic is necessary i.e.:
// Application Model
public function exists($id = null) {
if (should be an update) {
return true;
}
return false;
}

Related

User login based on SOAP service

Problem
I am working on creating a user application which I am connecting to a service via their SOAP API service; I have been able to get that working, and I get the desired response okay; but I am now facing a security concern when making the application login framework...
I would love to create my own code; but something Laravel would also work, for secure a development environment, which already the user login system, but I am not sure how I could go about connecting the service to the SOAP instance, instead of MySQL database, which is what I currently use.
Should I create a user on MySQL database based on the SOAP response? Or is there some way to create the instance based on the response from the service without having to continually remake the call?
Response
These are the responses I receive from the service based.
This is what I receive back from it - GIVEN THAT the user login information is CORRECT:
Array(
['columnList'] => Array(
[0] => 'Column1',
[1] => 'Column2',
[2] => 'Column3',
[3] => 'Column4',
[4] => 'Column5'
// ... etc
),
['data'] => array(
[0] => array(
[0] => 'Value_1',
[1] => 'Value_2',
[2] => 'Value_3',
[3] => 'Value_4',
[4] => 'Value_5'
// ... etc
),
// if the user has more entries then we it will create more arrays below
)
);
If the user's info is wrong or the user does not match I just get a string response
[string] 'nomatch'
Attempt
I have attempted creating $_SESSION variables from the response I receive from the service:
The function user_print is my attempt to make a unique finger print for that user which I can use to pull all of their application data, and use as their identifier.
public function user_print( $array )
{
if ( is_array( $array ) || is_object( $array ) )
{
// call back to handle function
// cannot use $this since this is in the SONIS class scope
$combine = applicant::handle_obj( $array );
// set session variable to user
$_SESSION['user_print'] = md5 ( $combine['SOC_SEC'] . $combine['OA_PIN'] . $combine['FIRST_NAME'] . $combine['LAST_NAME'] . $combine['E_MAIL'] . $combine['APP_START_DATE'] . $_SERVER['HTTP_USER_AGENT'] );
$_SESSION['userid'] = $combine['SOC_SEC'];
$_SESSION['email'] = $combine['E_MAIL'];
$user_browser = $_SERVER['HTTP_USER_AGENT'];
$_SESSION['login_string'] = hash ( 'sha512', $combine['OA_PIN'] . $user_browser );
return true;
}
else
{
if ( $array == 'nomatch' )
{
$message = array(
'is_error' => 'warning',
'message' => 'User information is incorrect.'
);
return $message;
exit;
}
else
{
$message = array(
'is_error' => 'danger',
'message' => 'We just hit a snag...'
);
return $message;
exit;
}
}
}
To handle the response from my service I have created a unique function:
private function handle_obj( $array )
{
// if it's an object we treat it as such
if ( is_object( $array ) )
{
// reduce array->data to simpler format
$call_data = array_reduce( $array->data, 'array_merge', [] );
// combine both arrays
$combine = array_combine ( $array->columnList, $call_data );
}
// if it's not an object, treat it as array.
else if ( is_array( $array ) )
{
// reduce array->data to simpler format
$call_data = array_reduce( $array['data'], 'array_merge', [] );
// combine both arrays
$combine = array_combine ( $call['columnList'], $call_data );
}
else
{
$combine = '';
}
return $combine;
}
I could make that code work; but I have fear of that not being the safest and really the most appropriate way to resolve this issue.
I would appreciate any help!
Thank you

PHP How to save data from array to mysql using laravel 5

Im getting an array within array of 'Cylinders' data from POST:
Array
(
[serie] => Array
(
[0] => 1234
[1] => 3545
)
[seriesap] => Array
(
[0] => 1234234
[1] => 345345
)
[type] => Array
(
[0] => 4546
[1] => csdfwe
)
[admission] => Array
(
[0] => 04-05-2015
[1] => 04-05-2015
)
[invoice] => Array
(
[0] => fei76867
[1] => feiasodjf
)
)
Now, the fields inside the keys: serie, type, admission, etc dont change, but the info inside those key do change, i mean there could be even 15 items in there.
At the end i need to save to the database:
$cylinder = new Cylinder();
$cylinder->serie = ??;
$cylinder->seriesap = ??;
$cylinder->type = ??;
$cylinder->admission = ??;
$cylinder->invoice = ??;
$cylinder->save
How can i accomplish this task and save all the cylinders?
I have tried all the foreach's that i could think of nothing seems to work.
/edit/
This is what Im doing so far:
$cyldata = $_POST['cylinder']; //this is the post from top.
$num_elements = 0;
while($num_elements < count($cyldata['serie'])){
$cylinder = new Cylinder();
$cylinder->serie = $cyldata['serie'][$num_elements];
$cylinder->type = $cyldata['type'][$num_elements];
$cylinder->admission = $cyldata['admission'][$num_elements];
$cylinder->seriesap = $cyldata['seriesap'][$num_elements];
$cylinder->save
$num_elements++;
}
But it feels ugly, all those saves doesnt feel right. Dirty solution if you ask me.
First, you need to convert you input data to another format:
$cyldata = $_POST['cylinder']; //this is the post from top.
$num_elements = 0;
$sqlData = array();
while($num_elements < count($cyldata['serie'])){
$sqlData[] = array(
'serie' => $cyldata['serie'][$num_elements],
'type' => $cyldata['type'][$num_elements],
'admission' => $cyldata['admission'][$num_elements],
'seriesap' => $cyldata['seriesap'][$num_elements],
'invoice' => $cyldata['invoice'][$num_elements], // you miss this field, aren't you?
'created_at' => Carbon\Carbon::now(), // only if your table has this column
'updated_at' => Carbon\Carbon::now(), // only if your table has this column
);
$num_elements++;
}
Second, use the Fluent query builder to do a batch insert:
DB::table('table_name')->insert($sqlData);
Note: the created_at and updated_at appear here if your table has these field. When working with Eloquent model, these field is updated automatically. However, we do not use Eloquent, so that we have to assign the value to these field manually.
I don't know what forced you to use $_POST global array in order to receive the data from the user.
Perhaps, this is what you want.
/**
* Store the form inputs in the table
*
* #param Request $request
*/
public function store( Request $request ) {
$data = Input::get();
for($i = 0; $i < count($data['serie']); $i++) {
$c = new Cylinder();
$c->serie = $data['serie'][$i];
$c->type = $data['type'][$i];
$c->admission = $data['admission'][$i];
$c->seriesap = $data['seriesap'][$i];
$c->save(); // fixed typo
}
}
Because you are having an array to insert the data to database, you can try the create method from the model:
Cylinder::create($array);
but it actually needs the key of the array to be the field_name in your database. Or you can do this with the query builder:
DB::table('table_name')->insert($array);
and again it is required to set the key of the array to be the field_name in your database.

PHP ObjectId group a number of Ids

I have a two collections one of all the people I am following and another of what they have been posting on social networking sites like Twitter and Facebook.
The following collection has a subarray of the _id of the feed collection of each user which each status has the word owner and that has the ObjectId that the owner which is the same as the following key. Here is an example.
'_id' => new MongoId("REMOVED"),
'following' =>
array (
'0' => 'ObjectId("53bf464ee7fda8780c8b4568")',
'1' => 'ObjectId("53b00ab5e7fda8304b8b4567")',
),
'owner' => new MongoId("53b9ea3ae7fda8863c8b4123"),
and in the feed you will see that the following.0 status below
array (
'_id' => new MongoId("REMOVED"),
'owner' => new MongoId("53bf464ee7fda8780c8b4568"),
'status' => ' love this video - Pedigree Shelter dogs http://youtube.com/watch?v=5v5Ui8HUuN8',
'timestamp' => new MongoDate(1405044327, 565000),
)
While I can loop through one by one, I can't for some reason do an $or search. I am not quite understanding how I loop through the following array and add it to the search query before I ran the query.
collection = static::db()->feed;
$where=array( '$or' => array(array('owner' => new MongoId($following.0)))));
$feed = $collection->find($where);
return $feed;
now I understand I will somehow have to loop the $where=array( '$or' => array(array('owner' => new MongoId($following.0))))); But I am just not 100% sure how to do this.
Update
As per the answer below I had to edit the array that was returned - now I have only got this working manually and can't seem to get the PHP script to do it.
Answer Returns
Array ( [owner] => Array ( [$in] => Array ( [0] => new MongoId("53bf464ee7fda8780c8b4568") [1] => new MongoId("53b00ab5e7fda8304b8b4567") ) ) )
Correct:
Array ( "owner" => Array ( '$in' => Array ( "0" => new MongoId("53bf464ee7fda8780c8b4568"), "1" => new MongoId("53b00ab5e7fda8304b8b4567") ) ) )
I am not sure how else to get this to work.
current PHP
$collection = static::db()->following;
$following = $collection->findOne(array ('owner' => new MongoId($_SESSION['user_information'][0]['_id'])));
$follow = $following['following'];
$collection = static::db()->feed;
$where=array("owner" => array( '$in' =>$follow));
print_r($where);
$feed = $collection->find($where);
print_r($feed);
return $feed;
I have fixed a small issue with the collection and now the return array shows
Array ( [owner] => Array ( [$in] => Array ( [0] => MongoId Object ( [$id] => 53bf464ee7fda8780c8b4568 ) [1] => MongoId Object ( [$id] => 53b00ab5e7fda8304b8b4567 ) ) ) )
However, I still can't get it to return the feed like this one:
array (
'_id' => new MongoId("53bf4667e7fda8700e8b4567"),
'owner' => new MongoId("53bf464ee7fda8780c8b4568"),
'status' => ' love this video - Pedigree Shelter dogs http://youtube.com/watch?v=5v5Ui8HUuN8',
'timestamp' => new MongoDate(1405044327, 565000),
)
I am presuming here that this is just a PHPism in the way things are displayed and that your following array is an actual array and not a hash/map, which would generally look like this in a JSON representation:
{
"following": [
ObjectId("53bf464ee7fda8780c8b4568"),
ObjectId("53b00ab5e7fda8304b8b4567"),
],
"owner": ObjectId("53b9ea3ae7fda8863c8b4123"),
}
In which case the "following" is already an actual array, and if you just want to .find() all the "feed" items for the people you are following, then you just pass that to the $in operator for your query selection:
$where = array( "owner" => array( '$in' => $following ) );
$feed = $collection->find($where);
return $feed;
The returned cursor will only contain results from the feed where the "owner" is present in your "following" array from the other collection item.
Watch this code:
$list = array(new MongoId(), new MongoId, new MongoId());
$doc = array( "owner" => array( '$in' => $list ));
echo json_encode( $doc, JSON_PRETTY_PRINT );
Despite how this serializes for JSON by this method the equivalent JSON is:
{
"owner": {
"$in": [
ObjectId("53bf8157c8b5e635068b4567"),
ObjectId("53bf8157c8b5e635068b4568"),
ObjectId("53bf8157c8b5e635068b4569")
]
}
}
That is how the BSON will serialize and is the correct query.
(Answer added on behalf the question author to move it to the answer space).
The issue was fixed when I used the following:
var_dump(iterator_to_array($feed));

CakePHP - Select box item not properly appearing in POSTED data

I have the following select box with CakePHP.
//Users/settings.ctp
$options = array('NYC', 'LA');
echo $this->Form->create('Location');
echo $this->Form->input('Location', array('type' => 'select', 'options' => $options));
echo $this->Form->end(__('Submit'));
In the UsersController I have
public function settings($id = null){
if ($this->request->is('post')) {
$location = $this->request->data;
//$location = $location['Location']['Location'][0];
$this->Session->setFlash( __(print_r($location))); //displays '1' in the alert no matter the selection
}
}
When I use print_r on the raw data it shows the following.
Array ( [Location] => Array ( [Location] => 0 ) )
So I have two problems
The index of the item is being selected, not the item itself
The setFlash window always displays '1'. I need to do some string manipulation after I get the listbox working and it's nice to see the output.
update - I went into /Cake/View/Helper/FormHelper.php and did some digging
I did a print_r on the following line
$attributes = $this->_initInputField($fieldName, array_merge(
(array)$attributes, array('secure' => self::SECURE_SKIP)
));
Which resulted in
Array ( [value] => 0 [class] => [name] => data[Users][Location] [id] => UsersLocation )
The value being passed is 0, I don't see the location anywhere
You're creating a form for the "Location" model, but you need to specify which field the select is for:
echo $this->Form->input('Location.location', array('type' => 'select', 'options' => $options));
Or, if it's not actually for the "Location" model, then either replace the "Location" in the form create() with the correct model, or use null if it's not a model at all:
$this->Form->create(null);
I figured it out and decided someone else might find it useful so.
The array needed to be formatted as
$options = array(
'value' => 'Display');
So, I used
$options = array(
'NYC' => 'NYC',
'LA' => 'LA');

get mongodb _id object after upsert with php

is it possible to get the new/updated _id after the query?
example code:
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$mongodb->db->collection->update( $key, $data, array( 'upsert' => true ) );
$key is not holding the new/old _id object and i assume that $data will not either because its just an instruction.
Yes -- It is possible using a single query.
MongoDB includes a findAndModify command that can atomically modify a document and return it (by default it actually returns the document before it's been modified).
The PHP drivers don't include a convenient method for this on the collection class (yet -- check out this bug), but it can still be used (note that my PHP is terrible, so I may very well have made a syntax error in the following snippet):
$key = array( 'something' => 'unique' );
$data = array( '$inc' => array( 'someint' => 1 ) );
$result = $mongodb->db->command( array(
'findAndModify' => 'collection',
'query' => $key,
'update' => $data,
'new' => true, # To get back the document after the upsert
'upsert' => true,
'fields' => array( '_id' => 1 ) # Only return _id field
) );
$id = $result['value']['_id'];
Just in case someone stumbles across this question like I did, Mongo will actually modify the input array when you call MongoCollection->save(); - appending the id to the end.
So, if you call:
$test = array('test'=>'testing');
mongocollection->save($test);
echo $test['_id'];
You will have the mongo id for that object.
I ran into this issue and worked around it by querying back the _id after the upsert. I thought I'd add some of my findings in case they're useful to anyone who comes here searching for info.
When the upsert results in a new document being created in the collection, the returned object contains the _id (here's a print_r of an example):
Array
(
[updatedExisting] => 0
[upserted] => MongoId Object
(
[$id] => 506dc50614c11c6ebdbc39bc
)
[n] => 1
[connectionId] => 275
[fsyncFiles] => 7
[err] =>
[ok] => 1
)
You can get the _id from this:
$id = (string)$obj['upserted'];
However, if the upsert resulted in an existing document being updated then the returned object does not contain _id.
Give this a shot :
function save($data, $id = null) {
$mongo_id = new MongoId($id);
$criteria = array('_id' => $mongo_id);
// Wrap a '$set' around the passed data array for convenience
$update = array('$set' => $data);
$collection->update($criteria, $update, array('upsert' => true));
}
So lets say the passed $id is null, a fresh MongoId is created, otherwise it just converts the existing $id to a MongoId object.
Hope this helps :D
The update method returns an array with the ID of the document UPSERTED:
Array
(
[ok] => 1
[nModified] => 0
[n] => 1
[err] =>
[errmsg] =>
[upserted] => MongoId Object
(
[$id] => 5511da18c8318aa1701881dd
)
[updatedExisting] =>
)
You can also set fsync to true in an update/upsert, to get the _id returned to the object that has been passed to the update.
$save = array ('test' => 'work');
$m->$collection->update(criteria, $save, array('fsync' => true, 'upsert' => true));
echo $save['_id']; //should have your _id of the obj just updated.

Categories