Magento: Iterate through existing/newly saved objects or use addCommitCallback? - magento

I have a query regarding magento 1.6.
I have a number of objects that I am creating, calling a method on and saving to the database with the Mage_Core_Model_Resource_Transaction transaction model.
The method now requires the primary key value of the object, which obviously isn't going to be available until after the object has been saved to the database.
Can I just loop through the created objects after they've been saved to the database? Instead, do I need to use the transaction's addCommitCallback method?
In other words, can I do:
$collection = [];
$transaction = Mage::getModel('core/resource_transaction');
for ($i = 0; $i <= 10; $i++) {
$obj = new ObjectFoo;
$transaction->addObject($obj);
$collection[] = $obj;
}
$transaction->save();
foreach($collection as $collected) {
$this->doSomething($collected);
}
(And assume that each $collected item will now have a valid id value)
Or do I need to use something like:
$transaction->addCommitCallback(array($obj, 'doSomething'));
One thing that I have noticed with addCommitCallback is that you can not specify any parameters for the callback, which is rather... restrictive.

Yes, each collected item should contain the id after saving the transaction.
http://www.php.net/manual/en/language.oop5.references.php

Related

How to Update a row if at least one field has changed - and return back all changes in laravel 5.7?

I am following this post in order to update a row if any field has changed.
//Retrieve an array from an API
$fields = ['field1'=>var1, 'field2'=>var2, ...];
$row = MyModel::ofField1($this->field1)->ofField2($fields['field2'])->first(); //Selects the row under field1 AND field2 condition. It is working.
$row->updateOrCreate($fields);//Should assign the new values to the model (but not persist)
//Next line should compare the model with what we have in the
//database. At least 1 value from the incomming array is different from what
//we have in the database - so Update the row with the new value.
if($row->isDirty()){
$row->save();
}
This is not working what I am missing?
->updateOrCreate() will persist the data (which you have mentioned you do not want to do).
This method has two arguments:
One to query rows with certain field values
The other for data to be used to update/create a record
In this case, if you are just checking if one of two fields have been updated, you should check if the specific field has been updated using ->isDirty($field):
$row = MyModel::first();
if ($row->isDirty('field1')) {
$row->field1 = $fields['field1'];
}
if ($row->isDirty('field2')) {
$row->field2 = $fields['field2'];
}
$row->save();
As you may have many fields returned from the api, you could loop through and check:
$row = MyModel::first();
foreach ($fields as $field => $value) {
if ($row->isDirty($field)) {
$row->$field = $value;
}
}
$row->save();

How to add data to relative table Laravel?

I have model Object and model Prototype.
Incoming data($_POST) is array prototype_id[]:
prototype_id[] = 1;
prototype_id[] = 2;
prototype_id[] = 3;
That should be saved in table ObjectPrototypes. No i dont have this model.
So, in controller I do:
$object = new Object();
$object->name = "Object";
How to add/attach prototype_id[] to saved object $object?
Important:
Object can has one or more prototypes
One (quick) solution could be to do raw insert on this table. Assuming pivot table name is object_prototype, here is what you could do:
$object = new Object();
$object->name = "Object";
$object->save();
foreach($request->input('prototype_id') as $p) {
\DB::table('object_prototype')->insert([
'object_id' => $object->id,
'prototype_id' => $prototype
]);
}
It should work, thought it's not really Laravelish.
You may also work with pivot, but it requires more boilerplate. You may have a look here: https://laravel.com/docs/5.4/eloquent-relationships#updating-many-to-many-relationships and here: https://laravel.com/docs/5.4/eloquent-relationships#one-to-many-inverse That's what I do usually but it's a bit too long for a short answer and I think Laravel Doc is more complete.

One to Many relation - how to implement push

I am trying to do an insert which will create a parent record for one table and then insert records that link back to the parent record into another.
In other words this: User completes Course information form, then completes a series of questions on the same page. On submission, the course information is inserted into its own table then questions are inserted into a separate one.
My Course model is this:
class CourseVerification extends Eloquent
{
public function courseverificationqanda()
{
return $this->hasMany('CourseVerificationQandA', 'id', 'verification_id');
}
My Question model is this:
class CourseVerificationQandA extends InnovedBaseModel
{
public function courseverification()
{
return $this->belongsTo('CourseVerification');
}
On form submission, my controller is doing this:
// create course verification record first
$veri = new CourseVerification;
$veri->verification_date = $input['verification_date'];
// create collection to store questions
$collection = new Illuminate\Database\Eloquent\Collection();
// loop through submitted questions and push them to the collection
for($i = 0; $i < count(Input::get('question_id')); $i++) {
$answer = new CourseVerificationQandA;
$answer->question_id = $input['question_id'][$i];
$answer->answer = $input['answer'][$i];
$answer->additional_notes = $input['additional_notes'][$i];
$collection->push($answer);
}
// add collection to quesetion relation
$veri->courseverificationqanda = $collection;
// insert both course record and questions
$veri->push();
The push method then errors and debugs the SQL command
insert into `CourseVerification`
(`verification_date`, `topic_id`, `course_id`, `verifier_id`,`iv_status`,
`verification_type`, `courseverificationqanda`, `created_by`)
values
(29/10/2014, 1294, 47, 1, 1, I, [{"question_id":"2","answer":"0","additional_notes":""},
{"question_id":"3","answer":"0","additional_notes":""},
{"question_id":"4","answer":"0","additional_notes":""}], 1)
As you can see, the assignment of the collection to $veri->courseverificationqanda is then getting treated as a table column in the SQL query when it is actually a relationship to the question table.
Any ideas?
You have a few mistakes there.
You don't assign collection to the relation. You need to load that relation (as a collection in this case) and push on it, then just save everything:
$veri = new CourseVerification;
$veri->verification_date = $input['verification_date'];
// Save the parent model before you can add its children
if ($veri->save())
{
// loop through submitted questions and push them to the collection
for($i = 0; $i < count(Input::get('question_id')); $i++) {
$answer = new CourseVerificationQandA;
// associate with the parent model
// explicitly
$answer->verification_id = $veri->getKey();
// or the eloquent way
// $answer->courseverification()->associate($veri);
$answer->question_id = $input['question_id'][$i];
$answer->answer = $input['answer'][$i];
$answer->additional_notes = $input['additional_notes'][$i];
$veri->courseverificationqanda->push($answer);
}
}
// save both course record and questions
$veri->push();
Another thing are your relations, which are both wrong:
// this is how it should look like
// CourseVerification
public function courseverificationqanda()
{
return $this->hasMany('CourseVerificationQandA', 'verification_id', 'id');
}
// QandA
public function courseverification()
{
return $this->belongsTo('CourseVerification', 'verification_id');
}
In firsts case you swapped the keys, so it would work but not the way it should.
In second case you didn't specify the foreign key at all, so Eloquent would look for courseverification_id in the table (method_name_id).

How can I check if an order with a given increment id already exists in magento?

I am new to Magento.
What's the proper way to check if an order with a given increment id already exists ?
The obvious way:
$order = Mage::getModel('sales/order')->loadByIncrementId($reservedOrderId);
if ($order) {
Mage::log('already have order with id ' . $reservedOrderId);
return $order;
}
does not work, because I get a new and empty model instance back.
What's the correct way in magento to see if I have no such model for that id ?
The most common approach I've seen in core code just load()s a model and checks if there was a primary key assigned. In your case this would look like the following - note the very slight adjustment to the logical condition ($object->getId() vs. $object):
$order = Mage::getModel('sales/order')->loadByIncrementId($reservedOrderId);
if ($order->getId()) {
Mage::log('already have order with id ' . $reservedOrderId);
return $order;
}
It's a simple mistake, but remember that a call to load data on a Magento data model will always return the object instance. It's only if there is a result from the storage backend that the object would be decorated with data and therefore a primary key.
In my experience there are two ways to do this:
if ($order->hasData()) {
// order already exists
}
or, by using a collection;
$collection = Mage::getModel('sales/order')->getCollection()->addFieldToFilter('increment_id', $reservedOrderId);
if ($collection->count()) {
// order already exists
}
In your case, probably best to use the first one.
There's multiple ways to approach this. First, since you know the increment ID to expect, you could check for it after you get your model back
$increment_id = '100000002';
$order = Mage::getModel('sales/order')->loadByIncrementId($increment_id);
if($order->getIncrementId() == $increment_id)
{
var_dump("Increment IDs match, that means there's an order");
}
else
{
var_dump("Increment IDs don't match, that means there's no order");
}
Similarly, although there's a model returned even if there's no match, you could check that model's data — an empty array means nothing was loaded
$increment_id = '100000002';
$order = Mage::getModel('sales/order')->loadByIncrementId($increment_id);
if($order->getData())
{
var_dump("Data array means there's an order");
}
else
{
var_dump("Empty data array means there's no order");
}
Finally, you can load a collection with an increment id filter, and check how many items it contains
$increment_id = '100000002';
$c = Mage::getModel('sales/order')->getCollection()
->addFieldToFilter('increment_id',$increment_id);
if(count($c) > 0)
{
var_dump("A collection with more than zero items means the order exists");
}
else
{
var_dump("An empty collection means it does not");
}
I prefer the last approach for a simple "does/does-not" exists check, as a collection doesn't trigger a model's after load method which means it's theoretically more performant. That said, no approach is more valid than the other — just try to use the same technique everywhere for more readable code.

Looping through Magento attribute values skips the first item

This is an ajax file running the following code:
$model = Mage::getModel('catalog/product'); //getting product model
foreach ($violins as $k => $v)
{
$_product = $model->load($v); //getting product object for particular product id
$violinmodel = $_product->getAttributeText('Violinmodel'); //grabbing the violinmodel attribute value
echo $violinmodel;
}
$violin contains an array with three product ID's. My output is echoing the attribute value for the second and third ID's fine, but is NOT echoing the first ID!
I don't get this at all! Why would it completely skip the first ID in the loop and not echo anything, while echoing the following ID's without a problem?
The attributes are set up properly and no matter how I rearrange the ID's in the $violins array, the first attribute value is always skipped. What am I missing?
Initializing $model outside of your loop is unsafe. You might think that you are being more efficient with memory and/or function calls but you're asking for trouble. The Mage_Catalog_Model_Product object is being loaded at that time, and calling ->load() isn't giving you a new object, it's just setting the data of your existing one. Except, you'll get strange behavior when not all of the data gets overwritten (for example if ProductA has a Violinmodel attribute and ProductB doesn't... it will look like ProductA.Violinmodel == ProductB.Violinmodel). For that reason, you should always put your model inside your loop.
foreach ($violins as $k => $v) {
$_product = Mage::getModel('catalog/product')->load($v); //getting product
if ($_product->getId() == $v) { // sanity check
$violinmodel = $_product->getAttributeText('Violinmodel'); //grabbing the violinmodel attribute value
echo $violinmodel;
}
}
Or, as benmarks suggested, load this data via a collection:
$_products = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('Violinmodel')
->addIdFilter($violins);
foreach ($_products as $_product) {
echo $_product->getAttributeText('Violinmodel'));
}
When you iterate over a product collection in Magento, the items it contains are actually product object instances. What you are doing here (hitting the database multiple times, loading all attribute) is unnecessary given what you are trying to accomplish (getting one attribute). Try adding the attribute to the collection to begin with and iterate over it:
$coll = Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('Violinmodel');
//be certain that the attribute code is capitalized...
foreach ($coll as $product) {
//var_dump($product->debug()); //for example
var_dump($product->getAttributeText('Violinmodel));
}

Resources