ActiveRecord is not updating data - yii

Here is a piece of my code
$file=Files::model()->findByPk($id);
if($file == null) {
throw new CHttpException(404,'Not found');
}
$count = $file->count;
$count++;
$file->count = $count;
$file->save();
$this->redirect(Yii::app()->request->hostInfo."/".$file->path);
The Files model contains a count field.
The code is ok, and there are no warnings, but the save method is not working.

try $file->getErrors() to display after save() and before redirect to see if there are no errors
this would show you what is the problem. Most common is you are not populating the dependencies required for a model to insert a row in database
to see it in well format use
CVarDumper::Dump($file->getErrors(),100,true)

Related

Process multiple records, one at a time, with same form

I have a table that contains multiple records for the same user (each record is slightly different - for a reason).
I need to be able to edit each record, one at a time, using a standard Yii form.
I've tried the following code but I need it to wait for each form to be submitted before the loop continues to display the form for the next record. At the moment, it displays the form for each record, all at once, on one page.
I'm sure I figured out how to do it once many, many projects ago but I can't remember what I did to get it to work right :(
public function actionProcess()
{
$system = get some data;
foreach ($system as $item):
$this->getExperience($item);
endforeach;
}
protected function getExperience($item)
{
$model = get data for the form
if(isset($_POST['the_form'])):
$model->attributes=$_POST['the_form'];
if($model->save())
return true;
endif;
$this->render('the_form', array('model'=>$model,));
}
Thanks for your suggestion. I figured out my own solution which I'll share for the benefit of others:
$_stack = array(); // defined in the Class global space
public function actionProcess()
{
$system = get some data
foreach ($system as $item):
$this->_stack[] = $item->id;
endforeach;
if(!empty($this->_stack))
$this->getExperience();
}
protected function getExperience()
{
if(empty($this->_stack))
$this->redirect(array('somewhere else'));
$model = get data for the form using $_stack[0] as the reference for what to obtain
if(isset($_POST['the_form'])):
$model->attributes=$_POST['the_form'];
if($model->save()):
array_shift($this->_stack);
$this->getExperience();
endif;
endif;
$this->render('the_form', array('model'=>$model,));
}

Doctrine ODM: Cannot prime->(true) getSingleResult(); throws cursor error

I have a document that has a ReferenceMany attribute to another document. The reference is setup fine, and the data is returned from the query fine, but each document in the arraycollection is returned as a proxy. On this site, I saw it was mentioned I should add ->prime(true) in order to return the actual referenced documents.
When that ArrayCollection of documents is returned, I am running a loop of ids I have submitted to the server to remove them from the referenced collection. The removeElement method is not working b/c the returned documents are proxies, and I am comparing an actual document vs. those proxies. So basically I am trying to:
Look up a single document
Force all documents in the ReferenceMany attribute to be actual documents and not Proxy documents
Loop through my array of id's and load each document
Send the document to the removeElement method
On the first getSingleResult query method below, I am getting an error cannot modify cursor after beginning iteration. I saw a thread on this site mention you should prime the results in order to get actual documents back instead of proxies, and in his example, he used getSingleResult.
$q = $this->dm->createQueryBuilder('\FH\Document\Person')->field('target')->prime(true)->field('id')->equals($data->p_id);
$person = $q->getQuery()->getSingleResult();
foreach($data->loc_id as $loc) {
$location = $this->dm->createQueryBuilder('\FH\Document\Location')->field('id')->equals(new \MongoId($loc))->getQuery()->getSingleResult();
$person->removeTarget($location);
}
....
....
....
public function removeTarget($document)
{
$this->target->removeElement($document);
return $this;
}
If I remove ->prime(true) from the first query, it doesn't throw an error, yet it doesn't actually remove any elements even though I breakpoint on the method, compare the two documents, and the data is exactly the same, except in $this->target they are Location Proxy documents, and the loaded one is an actual Location Document.
Can I prime the single result somehow so I can use the ArrayCollection methods properly, or do I need to just do some for loop and compare ids?
UPDATE
So here is an update showing the problem I am having. While the solution below would work just using the MongoId(s), when I submit an actual Document class, it never actually removes the document. The ArrayCollection comes back from Doctrine as a PersistentCollection. Each element in $this->coll is of this Document type:
DocumentProxy\__CG__\FH\Document\Location
And the actual Document is this:
FH\Document\Location
The removeElement method does an array_search like this:
public function removeElement($element)
{
$key = array_search($element, $this->_elements, true);
if ($key !== false) {
unset($this->_elements[$key]);
return true;
}
return false;
}
So because the object types are not exactly the same, even though the proxy object should be inheriting from the actual Document I created, $key always returns 0 (false), so the element is not removed. Everything between the two documents are exactly the same, except the object type.
Like I said, I guess I can do it by MongoId, but why isn't it working by submitting the entire object?
Don't worry about the prime(true) stuff for just now. All that does is tell doctrine to pull the referenced data now, so it doesn't have to make multiple calls to the database when you iterate over the cursor.
What I would do is change your removeTarget method to do the following.
$this->dm->createQueryBuilder('\FH\Document\Person')->field('id')->equals($data->p_id);
$person = $q->getQuery()->getSingleResult();
$person->removeTargets($data->loc_id);
Person.php
public function removeTargets($targets)
{
foreach ($targets as $target) {
$this->removeTarget($target);
}
}
public function removeTarget($target)
{
if ($target instanceof \FH\Document\Location) {
return $this->targets->removeElement($target);
}
foreach ($this->targets as $t) {
if ($t->getId() == $target) {
return $this->targets->removeElement($t);
}
}
return $this;
}
This would mean you don't have to perform the second query manually as doctrine will know it needs to pull the data on that reference when you iterate over it. Then you can make this operation quicker by using the prime(true) call to make it pull the information it needs in one call rather than doing it dynamically when you request the object.

Notes attributes syntax changed? - Notes attributes via API call fails for me

Hello Shopfiy Developers!
I'm having an issue with the notes attributes via API call. It used to work up until a month ago and then things start to go sideways. Has any syntax changed? Here is a snippet of my code that returns an error in the for loop.
Error message "Undefined index: note_attribute right at the foreach line"
// Overwrite custom status field if it's defined in note-attributes
if(array_key_exists('note-attributes', $o))
{
// For whatever reason, the note-attributes are formatted
// differently if there's only one key => value pair
// ( * see examples at end of this file )
// If the note-attribute array has the key 'name' in it, it's just a single pair.
// Otherwise, the note-attribute array would be numerically indexed with keys 0,1,2.. etc
if(array_key_exists('name',$o['note-attributes']['note_attribute']))
{
if($o['note-attributes']['note_attribute']['name'] == "custom_status")
$arr_tmp[7] = $o['note-attributes']['note_attribute']['value'] ;
}
else
{
foreach($o['note-attributes']['note_attribute'] as $na) //Fails here
{
if($na['name'] == "custom_status")
$arr_tmp[7] = $na['value'] ;
}
}
}
Your help is much appreciated. Thank you.
The issue here was due to a change in XML node syntax; Shopify had a regression that changed note-attributes to note_attributes in the response and it was changed back.

Yii model is validating but data could not be saved

I have a yii application. Data is validated properly. the $model->validate() returns true but data is not being saved. Is there any way that I know about the error. It does nothing. neither prints error nor any warning.
if (isset($_POST['Invoice'])) {
$model->validate();
$model->attributes = $_POST['Invoice'];
if (!$model->validate()) {
die(CVarDumper::dump($model->errors,10,true));
}
if ($model->save()) {
die("Data saved");
$this->redirect(array('view', 'id' => $model->id));
} else {
CVarDumper::dump($model->attributes,10,true);
CVarDumper::dump($model->errors,10,true);
}
}
if you override beforeSave or afterFind method in your model,
public function beforeSave() {
return true; //don't forget this
}
public function afterFind() {
return true; //don't forget this
}
make sure you return true for those function
If save() is returning true and there are no errors as such in your database and queries. Only thing, thats possible is you haven't marked some of the column safe for mass assignment via "$model->attributes".
Make sure the column you are trying to save are marked safe in the "rules" function in your model. You can mark columns safe via adding the following rule in "rules" function in the model.
array ( "column_name1, column_name2 ....." , "safe" )
I've just ran into something similar to this. Everything was validating correctly, and $model->save() was returning true, but no data was saved in the database.
The problem and solution was that I was creating the $model object like so:
$model = ClassName::model();
but you need to create the object like so:
$model = new ClassName;
If you have this problem, you replace this:
$model->save(false)
This solves your problem.
If you use $model->save(); the filters is running that is not good for you.
Fire up some logging and see what going on...
I got the same error when I was using reCaptcha. I just did this and it worked:
$model->scenario = NULL;
Make sure you do this AFTER validation.
I had the same issue, my mistake was with the post name in the controller, where I used $model->save. I had given wrong - if(isset($_POST['postname']))
If I am not wrong, you are doing an AR save() in the $model->save() method. You do not get any error, but the data is not saved as well.
If this is the case you would like to do a:
die(CVarDumper::dump($arObj->errors,10,true));
after the $arObj->save(); call. Most of the time this happens because of the Database rejecting the values provided for insert or update.
Also do not override your model constructor:
function __construct() { } // don't do this
The issue for me was that I had a property for the column name in the ActiveRecord class, so it wasn't saving.
You should not declare properties for column names as I guess the magic methods __get() and __set() are used to save data, I guess by checking if there are column changes when you click the save() method to avoid useless SQL queries. In my case, because the column was a user-declared property, it wasn't in the columns list and therefore changes to it were not detected.
Hope this helps other people

How to validate >1 field at a time, in a Zend sub-form?

I've created a 3 screen "wizard" using the Zend_Form_SubForm example from the online reference documentation.
The requirement I'm having trouble meeting is this:
If fields 1, 2, & 3 of the first screen are already in the database, notify the user that they are trying to add a duplicate record. Each of those fields has their own validators. Somehow I need to add this "group validator".
So, at its most basic level, I'm trying to do:
if($field_1_not_in_db && $field_2_not_in_db && $field_3_not_in_db){
return true;//validation OK
} else {
return false;//invalid data
}
I am coming up against several issues, though:
1) Because it applies to multiple fields, I don't know which field to attach it to. Error messages appear beside the field they are attached to, so this is important... unless I can get these "multi-field validator" errors to appear at the top of the screen, which would be ideal.
2) My validator is only receiving a single value (the value of the field I attach it to, not the values of the multiple fields it is supposed to validate).
3) I provide a link to the original (non-duplicate) record in the error message, but it escapes the link, and I can't figure out how to get around that.
The setup I'm currently using (below) actually executes fine, but NewPlace validator receives $_POST['city_fk'] as $fields, instead of the desired group of posted values.
$city_fk = new Zend_Form_Element_Select('city_fk');
$cities = array();
$city_fk->setMultiOptions($cities)
->setLabel('City')
->setDescription('The city this place is in')
->setRequired(true);
$v = array(
'place_is_unique' => array(
'NewPlace',
'fields' => array('place_name','phone_number','phone_extension','street','post_code_name'),
)
);
$city_fk->addValidators($v);
$addressSubForm->addElement($city_fk);
class My_Validate_NewPlace extends Zend_Validate_Abstract
{
public function isValid($fields)
{
$result = false;
if(!$result)
{
$this->_error('sorry, this is duplicate data. see it here');
return false;
}
return true;
}
}
This won't help you decide which field to attach the validation to, but...
There is a thing called a "validation context" that helps.
When you create your custom validator or form IF you specify a second optional parameter ($context = null), then Zend will auto-populate this with the entire array of posted data, which you can use to incorporate other fields values into your validation. Here's a very basic example:
$city_name = new Zend_Form_Element_Text('city_name');
$place_name = new Zend_Form_Element_Text('place_name');
$place_name->addValidator('NewPlace');
class My_Validate_NewPlace extends Zend_Validate_Abstract
{
public function isValid($value, **$context = null**)
{
if(trim($value)!='' && trim($context['city_name']) != '')
{
return true;
}
return false;
}
}