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.
Related
I am creating my custom activity logs and everything works fine except the part that works with the Livewire. My product edit page is whole made with the livewire (10 components separated) and when I try to make some changes (send update method request) I get the black popup like the one when we dd something, just without any messages inside. Since the log making is the last part of the save method in LW, product gets saved and all the code is running fine except the log making.
Black popup
My code for making the log is in the model as a static method:
// Helper methods
public static function log($message, $model_id, $model_type, $query_info, $view_route = '')
{
// Check if something is created by the System or by the logged user
if (Auth::check()) {
$author = Auth::user()->id;
} else {
$author = 'System';
}
self::create([
'user_id' => $author,
'loggable_id' => $model_id,
'loggable_type' => $model_type,
'action_executed' => $message,
'view_route' => $view_route,
'query_info' => json_encode($query_info),
'description' => 'Automated system log',
'executed_at' => now()
]);
}
And this is how it is called:
Changelog::log('Product updated ID ' . $this->product->id, $this->product->id, $this->model,DB::getQueryLog(), 'admin.product.view');
Am I missing something?
I have tried to dd line by line to see where it gets broken, and seems like everything is fine until code reaches self::create in the model.
FIXED
Hello guys, so if anyone is working on something like this, the issue was with fetching the exceptions like wrong/missing route, etc.
Usually, when you catch exceptions it will have "previous" since I did not know that some exceptions can come without it, and that was the issue that caused errors.
if($exception)
{
Changelog::log($exception->getMessage(), null, get_class($exception->getPrevious()), DB::getQueryLog(), '');
}
Here is the code I am using just in case you are wondering how can you catch it ^^
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
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.
I have built a simple Notification system in my Cake app that I want to have a function that will create a new notification when I call a certain method. Because this is not something the user would actually access directly and is only database logic I have put it in the Notification model like so:
class Notification extends AppModel
{
public $name = 'Notification';
public function createNotification($userId, $content, $url)
{
$this->create();
$this->request->data['Notification']['user_id'] = $userId;
$this->request->data['Notification']['content'] = $content;
$this->request->data['Notification']['url'] = $url;
$result = $this->save($this->request->data);
if ($result)
{
$this->saveField('datetime', date('Y-m-d H:i:s'));
$this->saveField('status', 0);
}
}
}
And then whenever I want to create a notification within my app I just do:
$this->Notification->createNotification($userId,'Test','Test');
However this doesn't work! The controller is talking to the model fine, but it doesn't create the row in the database... I'm not sure why... but it would seem I'm doing this wrong by just doing all the code in the model and then calling it across the app.
Edit: Based on answers and comments below, I have tried the following the code to create a protected method in my notifications controller:
protected function _createNotification($userId, $content, $url)
{
$this->Notification->create();
$this->request->data['Notification']['user_id'] = $userId;
$this->request->data['Notification']['content'] = $content;
$this->request->data['Notification']['url'] = $url;
$result = $this->save($this->request->data);
if ($result)
{
$this->saveField('datetime', date('Y-m-d H:i:s'));
$this->saveField('status', 0);
}
}
Now the thing that is stumping me still (apologies if this is quite simple to others, but I have not used protected methods in CakePHP before) is how do I then call this from another controller? So for example If have a method in my PostsController and want to create a notification on successful save, how would I do this?
I thought about in my PostsController add method:
if($this->save($this->request-data){
$this->Notification->_createNotification($userId,'Test','Test');
}
But being protected I wouldn't be able to access the method from outside of the NotificationsController. Also I'm using the same syntax as if I was calling a function from a model so again it doesn't feel right.
Hopefully someone can help me out and get me back on track as this is a new area to me.
the controller should pass all data to the model
$this->createNotification($this->request->data);
the model then can use the data:
public function createNotification(array $data) {
$key = $data[$this->alias]['key'];
$data[...] = ...;
$this->create();
return $this->save($data);
}
you never ever try to access the controller (and/or its request object) from within a model.
you can also invoke the method from other models, of course:
public function otherModelsMethod() {
$this->Notification = ClassRegistry::init('Notification');
$data = array(
'Notification' => array(...)
);
$this->Notification->createNotification($data);
}
and you can make your methods verbose, but that usually makes it harder to read/understand/maintain with more and more arguments:
public function createNotification($userId, $content, $url) {
$data = array();
// assign the vars to $data
$data['user_id'] = $userId;
...
$this->create();
return $this->save($data);
}
so this is often not the cake way..
Methods in a model are not "publicly accessible" by definition. A user cannot call or invoke a method in a model. A user can only cause a controller action to be initiated, never anything in the model. If you don't call your model method from any controller, it's never going to be invoked. So forget about the "non-public" part of the question.
Your problem is that you're working in the model as if you were in a controller. There is no request object in a model. You just pass a data array into the model method and save that array. No need for $this->request. Just make a regular array(), put the data that was passed by the controller in there and save it.
The whole approach is totally wrong in the MVC context IMO and screams for the use of the CakePHP event system. Because what you want is in fact trigger some kind of event. Read http://book.cakephp.org/2.0/en/core-libraries/events.html
Trigger an Event and attach a global event listener that will listen for this kind of events and execute whatever it should do (save something to db) when an event happens. It's clean, flexible and extendible.
If you did a proper MVC stack for your app most, if not all, events aka notifications should be fired from within a model like when a post was saved successfully for example.
This is what I have ended up doing. While it certainly isn't glamorous. It works for what I want it to do and is a nice quick win as the notifications are only used in a few methods so I'm not creating a large amount of code that needs improving in the future.
First to create a notification I do the following:
$notificationContent = '<strong>'.$user['User']['username'].'</strong> has requested to be friends with you.';
$notificationUrl = Router::url(array('controller'=>'friends','action'=>'requests'));
$this->Notification->createNotification($friendId,$notificationContent,$notificationUrl);
Here I pass the content I want and the URL where the user can do something, in this case see the friend request they have been notified about. The url can be null if it's an information only notification.
The createNotification function is in the model only and looks like:
public function createNotification($userId, $content, $url = null)
{
$this->saveField('user_id',$userId);
$this->saveField('content',$content);
$this->saveField('url',$url);
$this->saveField('datetime', date('Y-m-d H:i:s'));
$this->saveField('status', 0);
}
This creates a new record in the table with the passed content, sets its status to 0 (which means unread) and the date it was created. The notification is then set as read when a user visits the notifications page.
Again this is most probably not an ideal solution to the problem outlined in this question... but it works and is easy to work with And may prove useful to others who are learning CakePHP who want to run functions from models when building prototype apps.
Remember nothing to stop you improving things in the future!
First of all, you can improve your last solution to do one save() (instead of 5) the following way:
public function createNotification($userId, $content, $url = null){
$data = array(
'user_id' => $userId,
'content' => $content,
'url' => $url,
'datetime' => date('Y-m-d H:i:s'),
'status' => 0
);
$this->create();
$this->save($data);
}
When I began programming CakePHP(1.3) more than a year ago I also had this problem.
(I wanted to use a function of a controller in any other controller.)
Because I didn't know/researched where to place code like this I've done it wrong for over a year in a very big project. Because the project is really really big I decided to leave it that way. This is what i do:
I add a function (without a view, underscored) to the app_controller.php:
class AppController extends Controller {
//........begin of controller..... skipped here
function _doSomething(){
//don't forget to load the used model
$this->loadModel('Notification');
//do ur magic (save or delete or find ;) )
$tadaaa = $this->Notification->find('first');
//return something
return $tadaaa;
}
}
This way you can access the function from your Notification controller and your Posts controller with:
$this->_doSomething();
I use this kind of functions to do things that have nothing to do with data submittance or reading, so i decided to keep them in the app_controller. In my project these functions are used to submit e-mails to users for example.. or post user actions to facebook from different controllers.
Hope I could make someone happy with this ;) but if you're planning to make a lot of these functions, it would be much better to place them in the model!
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.