Couchbase view not updating after set from PHP SDK - php

I am working on an application, where I work mainly with views for getting the data.
What I try to do is, to store a document, redirect and check if the stored document is available(this is done via view, because I am not looking for the document key but for a different value).
What am I using?
PHP(5.4.21), Couchbase(2.1.1) and the PHP SDK for Couchbase(1.1) on a Debian Wheezy.
So what's happening?
I store a document with the API function set($id, $document), then trigger an update of the view with the API function view($designDocument, $viewName, $options), where the $options contain at least 'stale'=>false, and then redirect to another page where I check for the newly added document/or simply want to display it.
But the newly added document is not always shown or not always passes my check for existence.
Following the code I am using in more detail:
public function store(AbstractDocument $document)
{
$result = $this->bucket->storeDocument($document->getId(),
json_encode($document));
$this->afterSave();
return $result;
}
public function storeDocument($id, $document)
{
if (!is_string($document)) {
$document = json_encode($document);
}
$res = $this->couchbase->set($id, $document);
return $res;
}
public function afterSave()
{
$this->bucket->triggerViewIndexing(
$this->getDesignDocumentName(),
Config::KEY_COUCHBASE_VIEW_USER_USERLIST
);
}
public function triggerViewIndexing($designDocument, $viewName)
{
$options = ['limit' => 1, 'stale' => false];
$res = $this->couchbase->view($designDocument, $viewName, $options);
}
As shown in the code, I am setting the stale parameter to false, to ensure an updated index.
But it appears not to be working.
Before writing this question, I looked at quite some threads in the Couchbase forum, posts on Stackoverflow, the documentation of the PHP SDK for Couchbase and the general documentation of Couchbase.
So I think I understand how stale is supposed to work and what limitations there seem to be.
If my assumption that stale works, but only when the document is no longer in the memory but is being written to disk(or already has been), is wrong, please feel free to correct me.
As what I am trying to do is not working, I tried different things, that also have been mentioned in several explanations and documentations to be supposed to help achieve my desired outcome.
For example, I thought if the default behaviour of stale is update_after, then triggering an update of the index twice, would solve the problem.
Well, it didn't.
The other notable things are
$this->couchbase->keyDurability($id,$res);
$this->couchbase->observe($id, $res);
I used those directly after storing the document with set, separately and out of desperation in combination.
Didn't do the trick either.
My assumption what is wrong here is, that the PHP SDK either is somehow not passing through the stale=false parameter and keyDurability doesn't do what it is supposed to. As I pass stale=false in when checking for the newly created document as well, of course both(trigger and check) works on the same bucket and view.
Or I am doing something horribly wrong without noticing it.
I'd be glad if someone could point me in the right direction and hopefully explain what's going wrong, as I can't grasp what's happening. According to my understanding, everthing should work with keyDurability and stale at least.

The "problem" about updating views is, that it only uses data that is persisted to disk.
You can use keyDurability() to check if this has happened, BUT you need the id and the cas of the element you just wrote.
I would change your triggerViewIndexing function to look like this.
/**
* #param $designDocument
* #param $viewName
* #param null $id
* #param null $cas
*/
public function triggerViewIndexing($designDocument, $viewName, $id = null, $cas = null)
{
if (!empty($id) && !empty($cas)) {
$details = [
'persist_to' => 1, //The number of nodes the document should be persisted to
'timeout' => 2000, // The max time to wait for durability
'interval' => 100 //The interval between checking the state of the document
];
$this->couchbase->keyDurability($id, $cas, $details);
}
$options = ['limit' => 1, 'stale' => false];
$res = $this->couchbase->view($designDocument, $viewName, $options);
}
This will check every 100ms for a maximum of 2000ms if the item is written to disk.
After that, triggering the view will refresh the data.

Related

Wrong date submitted to Database

I have inherited a CakePHP 3 project handling some readings at certain dates. We have an API connected to a phone app that takes the well readings and inserts them to the database.
The readings as well as created by and updated by timestamps work correctly, but in this inherited project they have a separate date column 'reading_date' that is giving me 0015-10-05. The other aspects of the API controller work fine, but this continues to give me issues. I have the code for the add() method below
/**
* Add method
*
* #return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
*/
public function add()
{
$wellReading = $this->WellReadings->newEntity();
if ($this->request->is('post')) {
$data = $this->request->getData();
$this->log($this->request->getAttribute('params'));
$this->ats_log($this->request->getAttribute('params'));
$data['reading_date'] = date('Y-m-d', strtotime('Today'));
$data['reading_time'] = date('G:i:s');
$wellReading = $this->WellReadings->newEntity($wellReading, $data);
if ($this->WellReadings->save($wellReading)) {
// we're going to add some related info in here so we can return it with the inserted record in the API
$operator = $this->WellReadings->Operators->findById($wellReading->operator_id)->first();
$well = $this->WellReadings->Wells->findById($wellReading->well_id)->first();
$wellReading->reading_date = date('Y-m-d');
$wellReading->operator = $operator->name;
$wellReading->well = $well->identifier;
$wellReading->region_id = $well->region_id;
$this->set(['success' => true, 'data' => $wellReading, '_serialize' => ['success', 'data']]);
} else {
$this->set(['success' => false, 'errors' => $wellReading->errors(), '_serialize' => ['success', 'errors']]);
}
}
}
I have removed the UseLocaleParser from bootstrap.php as I have read that his causes issues in older CakePHP versions.
Also, $this->log() does not give me anything, no matter what I try. I have tried to use the data variable, and many different iterations of $this->request->, Params, Attribute, getData, nothing will show up in my error logs.
The ats_log method should take $data and write whatever is in that variable to a separate log file, we had this idea when nothing showed up in the Cake log function, but everything in that file is NULL. Still, the readings show up in the DB, and only the reading_date column is wrong.
Any help is greatly appreciated.
EDIT: I have been informed by my lead dev we have to keep the LocaleParser on.
So, this code did not show the whole picture after working into the project more.
My senior dev had already changed previous code from
$data['reading_date'] = date('d/m/Y', strtotime('Today'));
to what I have above. This caused the validator on the WellReadingsTable to throw an error. In the validator, the accepted format was 'mdY'. I am not sure why someone would allow invalid date format into the DB but after switching it to 'Ymd' data is being entered correctly.
Second part, instead of using $this->log(); to capture data, I was able to use debug(); to get data to show up in a separate log in the filestructure.

Eloquent DB::Post->where()->update() returns 1 always regardless of any changes made

I'm trying to figure out how to properly code the update() function in eloquent to return either 0 or 1 based on user input in a form. For example, if I hit the update button without making any changes, it returns 1. Shouldn't it return 0?
I tried researching for solutions like here in stackoverflow to see if anyone has the same problem as I am facing. But so far not luck. I also tried modifying the code, but no luck.
public function update(Request $request, $id)
{
$UCPost = UCPost::find($id);
$UCPost->gown_2019 = $request->input('Gown2019');
$UCPost->gown_2017_2018 = $request->input('Gown20172018');
$UCPost->gown_2016 = $request->input('Gown2016');
$UCPost->gown_2015 = $request->input('Gown2015');
$UCPost->Light_Blue = $request->input('LightBlue');
$UCPost->Seconds = $request->input('Seconds');
$UCPost->Velveteen = $request->input('Velveteen');
$UCPost->Velveteen_discolored = $request->input('Velveteen_discolored');
$UCPost->Rental = $request->input('Rental');
$UCPost->Rentals_Out = $request->input('Rentals_Out');
$UCPost->Rentals_Left = $request->input('Rentals_Left');
return $UCPost->where('id', $id)->update(
[
'gown_2019' => $UCPost->gown_2019,
'gown_2017_2018' => $UCPost->gown_2017_2018,
'gown_2016' => $UCPost->gown_2016,
'gown_2015' => $UCPost->gown_2015,
'Light_Blue' => $UCPost->Light_Blue,
'Seconds' => $UCPost->Seconds,
'Velveteen' => $UCPost->Velveteen,
'Velveteen_discolored' => $UCPost->Velveteen_discolored,
'Rental' => $UCPost->Rental ,
'Rentals_Out' => $UCPost->Rentals_Out,
'Rentals_Left' => $UCPost->Rentals_Left
]
);
}
The code above as I mentioned earlier, it always returns 1 regardless of any changes made to the form. I want it to return 0 if there are no changes by the user or if they accidentally hit the update button. I'm trying to find the equivalent of mysqli_affected_rows in Eloquent.
You are calling the update() method on your model. This will return 1 if the update was successful. It doesn't matter if there were any changes made, always 1 on successful update.
If you would like to only save to the database if there are changes, you can use the save() method instead, which checks to see if changes are present, and only writes to the database if the data is different. You have already created code to do this, by setting the model to have all of the new data from the sheet, so a simple save() at the end (with a check to see if it saves), will do what you want.
However, your current code is doing a lot of extra work. You are assigning all the variables to the model, and then updating based on that assignment of data to the model. You don't have to do all that assignment again in the update method. Once you have set the fields, you can save immediately, you don't have to re-assign all the variables. So:
$UCPost->gown_2019 = $request->input('Gown2019');
// etc..
$UCPost->Rentals_Left = $request->input('Rentals_Left');
$UCPost->save();
Will get you where you want to go and will only save if different.
If you have control over your form, and can change the form element names to match your database, this can be even easier. You can do all of this in one line:
$UCPost->update($request->all());
If you want to check if the model is dirty just call isDirty():
if($UCPost->isDirty()){
// changes have been made
}
Finally, if you want to verify if anything was changed after either method (save or update):
if ($UCPost->wasChanged()) {
// changes have been made
}
Hope this helps

How to return once from database and store in memory in Laravel

What I want to do:
Return a bunch of rows from database, convert in a array stored in memory and make this array visible from the whole project in such a way that other controllers for example can read it. My function is simple as that:
class BoardController extends Controller
{
/*
* returns something like
* ['name' => 'description',
...
]
*
* */
public static function getAll()
{
$boards = Board::orderBy('ordem')->get();
$retorno = array();
foreach($boards as $b)
{
$retorno[$b->sigla] = $b->nome;
}
return $retorno;
}
}
If I just keep calling BoardController::getAll() it will again read from database again. I also tried making this call inside a config file into a variable and returning it there but laravel gave me a 500 error. So, what is the best practice/way to do it?
If you don't want to call the database everytime then the best approach that can be followed here is to use caching and cache the results.
The Approach is simple, You make a Database call once and cache the reaults and the next time you hit the same function you check the cache first whether its empty or not. If its not empty, then return the cached results.
Remember, the cache has a time limit otherwise if you change/update anything in the database then you'll have to clear the cache that is already stored.
Laravel has some features for caching the results. You can see it Here.
Also You can also view this link for more effective implementation of cache in Laravel.
Hope this helps.

Understanding the passing of data/life of a script in web development/CodeIgniter

I hope I worded the title accurately enough but I typically use Java and don't have much experience in Web Development/PHP/CodeIgniter. I have a difficult time understanding the life cycle of a script as I found out trying to implement a certain feature to a website I am developing (as a means of learning how to). I'll first describe the feature I tried implementing and then the problem I ran into that made me question my fundamental understanding of how scripts work since I'm used to typical OOP.
Ok so here goes...
I have a webpage that has 2 basic tasks a user can do, create and delete an entry. What I attempted to implement was a way to time a user how long it takes them to complete a certain task. The way I did this was have a homepage where there would be a list of tasks a user to choose from (in this case 2, create and delete). A user would click a task which would link to the 'true' homepage where the user then would be expected to complete the task. My script looks like this:
<?php
class Site extends CI_Controller {
var $task1;
var $tasks = array(
"task1" => NULL,
"date1" => 0,
"date2" => 0,
"diff" => 0);
function __construct()
{
parent::__construct();
include 'timetask.php';
$this->task1 = new TimeTask("create");
}
function index()
{
$this->tasks['task1'] = $this->task1->getTask();
$this->tasks['diff'] = $this->task1->getTimeDiff();
if($this->tasks['diff'] == NULL)
{
$this->tasks['diff'] = 0;
}
$this->load->view('usability_test', $this->tasks);
}
function origIndex()
{
$this->task1->setDate1(new DateTime());
$this->tasks['date1'] = $this->task1->getDate1()->getTimestamp();
$data = array();
if($q = $this->site_model->get_records())
{
$data['records'] = $q;
}
$this->load->view('options_view', $data);
}
function create()
{
$this->task1->setDate2(new DateTime());
$this->tasks['date2'] = $this->task1->getDate2()->getTimestamp();
$data = array(
'author' => $this->input->post('author'),
'title' => $this->input->post('title'),
'contents' => $this->input->post('contents')
);
$this->site_model->add_record($data);
$this->index();
}
I only included create to keep it short. Then I also have the TimeTask class, that actually another StackOverflow so kindly helped me with:
<?php
class TimeTask
{
private $task;
/**
* #var DateTime
*/
private $date1, $date2;
function __construct($currTask)
{
$this->task = $currTask;
}
public function getTimeDiff()
{
$hasDiff = $this->date1 && $this->date2;
if ($hasDiff) {
return $this->date2->getTimestamp() - $this->date1->getTimestamp();
} else {
return NULL;
}
}
public function __toString()
{
return (string) $this->getTimeDiff();
}
/**
* #return \DateTime
*/
public function getDate1()
{
return $this->date1;
}
/**
* #param \DateTime $date1
*/
public function setDate1(DateTime $date1)
{
$this->date1 = $date1;
}
/**
* #return \DateTime
*/
public function getDate2()
{
return $this->date2;
}
/**
* #param \DateTime $date2
*/
public function setDate2(DateTime $date2)
{
$this->date2 = $date2;
}
/**
* #return get current task
*/
public function getTask()
{
return $this->task;
}
}
?>
I don't think posting the views is necessary for the question but here is atleast how the links are made.
<?php echo form_open('site/create');?>
...and...
<?php echo anchor("site/delete/$row->id", $row->title); ?>
Now there's no error in the code but it doesn't do what I expect of it and the reason I assume why is because that each time a function of the script is called via a new page it is NOT the same instance of the script called previously so any previously created objects are no longer there. This confuses me and leaves me quite unsure of how to implement this gracefully. Some ways I would guess of how to do this is by passing the necessary data through the URL or have data saved in a database and retrieve it later to compare the times. What would be a recommended way to do, not just this, but anything that needs previously created data? Also, am I correct to think that a script is only 'alive' for one webpage at a time?
Thanks!
Web development is a bit different to "standard" development - principally because of the nature of HTTP. Each request to the web application has to travel across the network using HTTP, which, as all web developers know, is stateless. What this means is that web servers do not have to remember anything about previous HTTP requests. Usually, webdevs get round this using cookies in one way or another - where a cookie is some bit of data, coded as a text string, which is sent back to the browser so that it can resend it to the application on the next request. Like that, a cookie is a kind of transferable memory.
So, each time you make a request, unless you transfer some data using a cookie (either an HTTP cookie, or what is sometimes called a URL cookie - state data coded in the URL), it looks to the web application like a brand new request, unrelated to any past request. So, for your application to work, you need to use a cookie in some way to remember or recover the start time when you detect that the user has finished a task. You can either (i) use CI's built-in facilities for remembering data (flashdata, as mentioned above, or userdata from the CI Session class - see http://codeigniter.com/user_guide/libraries/sessions.html), which are built on top of CI cookies), (ii) do this using your own cookie data (not recommended - why use the framework in that case?), or (iii) use hidden form fields - an oldie but sometimes goldie technique that requires the PHP script generating a view to write hidden form fields whose values are the data you want to remember and have sent back to you on the next request.
This kind of problem is something you'll come across again and again in web development - so get to know the problem and its solutions well!
You can use flashdata to make data available for next server request.
Reference: http://codeigniter.com/user_guide/libraries/sessions.html
You can go the extra step with sessions and implement the database table to store the current sessions. This gives you the ability to validate the id and confirm it's a valid session and not an old session accidentially restored via modded cookies for example.
It's very straight forward.
Create the table:
CREATE TABLE IF NOT EXISTS `ci_sessions` (
session_id varchar(40) DEFAULT '0' NOT NULL,
ip_address varchar(45) DEFAULT '0' NOT NULL,
user_agent varchar(120) NOT NULL,
last_activity int(10) unsigned DEFAULT 0 NOT NULL,
user_data text NOT NULL,
PRIMARY KEY (session_id),
KEY `last_activity_idx` (`last_activity`)
);
Modify the config file(application/config/config.php):
$config['sess_use_database'] = TRUE;
$config['sess_table_name'] = 'ci_sessions';
And then, use the standard methods to set and get the values.
Here is the guide: CI User Guide - Session Class

Object property saving via $object->save(); problems

I'm a magento programmer and I've been loosing several long minutes to figure out why a property on an object was not saved in the database.
Let's explain, here are 3 pieces of code that I would expect to do the same thing :
First code
$order = Mage::getModel('sales/order')->load(1873);
$myInfo = 'important piece of information';
$order->getPayment()->setAdditionalInformation('my_info',$myInfo);
$order->getPayment()->save(); //No information in the database is saved
No value saved in database.
Second code
$order = Mage::getModel('sales/order')->load(1873);
$myInfo = 'important piece of information';
$payment = $order->getPayment();
$payment->setAdditionalInformation('my_info',$myInfo);
$payment->save(); //No information in the database is saved
No value saved in database.
Third code
$order = Mage::getModel('sales/order')->load(1873);
$myInfo = 'important piece of information';
$order->getPayment()->setAdditionalInformation('my_info',$myInfo)->save(); //YEAHHH ! It works ! I now have that in my database.
Finally, I got it !
The code from setAdditionalInformation
/**
* Additional information setter
* Updates data inside the 'additional_information' array
* or all 'additional_information' if key is data array
*
* #param string|array $key
* #param mixed $value
* #return Mage_Payment_Model_Info
* #throws Mage_Core_Exception
*/
public function setAdditionalInformation($key, $value = null)
{
if (is_object($value)) {
Mage::throwException(Mage::helper('sales')->__('Payment disallow storing objects.'));
}
$this->_initAdditionalInformation();
if (is_array($key) && is_null($value)) {
$this->_additionalInformation = $key;
} else {
$this->_additionalInformation[$key] = $value;
}
return $this->setData('additional_information', $this->_additionalInformation);
}
note: The final setData() always returns $this
Question, Why ?
I think I've forgot some specificities about the way PHP works, especially for the first code. I would understand that it doesn't work because of some memory stuff with PHP.
But the two other pieces of code, why doesn't it work ?
Thanks,
Hugues.
These pieces of code are identical from Magento view - you didn't forget anything about how PHP works. With default Magento installation all 3 snippets must produce same results.
If the results of those code blocks are different, then you should:
a) turn off all custom extensions you use and try your code blocks without them - maybe some of extensions modify the default behavior of Order or Payment models.
b) check that your code snippets are really same as presented in this question - maybe there were other code lines that you thought of as non-important and didn't include in this question
c) check that you update view in your MySQL client after executing each code snippet - maybe you see just some old information in payment table
c2) check that you don't use replicated MySQL severs - maybe you update information on master DB, but sees payment table from slave DB, where these changes haven't yet been synced to
d) check that no other code executes after yours - maybe some other model or controller modifies additional_information and so deletes all your changes. Try to insert 'exit' just after your code so you'll be sure about it.
Not a Magento user, but it looks as if each method is returning an object which is required by the next method in the chain.
If you call each method individually, the object they create or modify won't contain any changes made by the previous method calls. By chaining the method calls, each one picks up the changes made by the previous call.

Categories