Is there any way to speed up this Magento code? Currently it is looking through about 2k products and takes approximately 20 minutes to run.
I'm guessing the issue is around the product->load() call, but I'm not familiar enough Magento to know the overhead it takes.
Thank you.
from controller
Mage::dispatchEvent(
'category_rule_save',
array(
'rule_id' => $id,
'attribute_code' => $data['attribute_code'],
'operator' => $data['operator'],
'value' => $data['value'],
'category_id' => $data['category'],
'store_id' => $data['store_id']
)
);
from Observer.php
public function onCategoryRuleSave($observe)
{
$collection =
Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect($observe['attribute_code']);
$write = Mage::getSingleton('core/resource')->getConnection('core_write');
foreach ($collection as $product) {
$productId = $product->getId();
$product = $product->load($productId);
$productAttributeValue = '';
$productAttributeValue =
Mage::getModel('catalog/product')->load($productId)
->getAttributeText( $observe['attribute_code'] );
$r = 0;
if ( is_numeric($observe['value']) ) {
switch($observe['operator']) {
case "<":
$r = ($productAttributeValue < $observe['value']) ? 1 : 0;
break;
case ">":
$r = ($productAttributeValue > $observe['value']) ? 1 : 0;
break;
case "<=":
$r = ($productAttributeValue <= $observe['value']) ? 1 : 0;
break;
case ">=":
$r = ($productAttributeValue >= $observe['value']) ? 1 : 0;
break;
case "==":
$r = ($productAttributeValue == $observe['value']) ? 1 : 0;
break;
case "!=":
$r = ($productAttributeValue != $observe['value']) ? 1 : 0;
break;
}
}
else {
switch($observe['operator']) {
case "==":
$r = (
strcmp(strtolower($productAttributeValue) , strtolower($observe['value'])) == 0
) ? 1 : 0;
break;
case "!=":
$r = (
strtolower($productAttributeValue) != strtolower($observe['value'])
) ? 1 : 0;
break;
}
}
if ($r==1) {
$write->query(
"REPLACE INTO `catalog_category_product` (`category_id`, `product_id`)
VALUES (" . $observe['category_id'] . "," . $product->getId() . ")"
);
}
}
}
Try replacing the product loading with:
...
foreach ($collection as $product) {
$productAttributeValue = $product->getAttributeText( $observe['attribute_code'] );
$r = 0;
...
You're loading a product object 2 extra times. The $product variable in the foreach already is the loaded product with the attribute that you need to work with. Loading a $product object with all of its attributes is expensive given Magento's EAV database structure.
1.$collection =
Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect($observe['attribute_code']);
2.Mage::getModel('catalog/product')->load($productId)
->getAttributeText( $observe['attribute_code'] );
3.$product = $product->load($productId);
That is really awful :)
$collection =
Mage::getModel('catalog/product')->getCollection()
->addAttributeToSelect('*');//this string should load all of the product attributes
There also were some methods to add product websites, product store and some others.
Related
So I have a request-response that looks like this
$json='[ {"item_cat":"Stationary","items":[{"item_name":"A4 Paper","qty":"2"},{"item_name":"Test Paper","qty":"6"}],"total":"2"},
{"item_cat":"Computer Accessory ","items":[{"item_name":"Power pack","qty":"2"}],"total":"1"},
{"item_cat":"Material","items":[{"item_name":"T-Shirt","qty":"3"},
{"item_name":"Cap","qty":"5"}],"total":"2"}]';
I'm trying to get each item_name and qty so I can use them to manipulate my db. Here is what I've done
$data = json_decode($json, true);
$len = count($data);
for($i =0; $i< $len; $i++){
$item_length = count($data[$i]['items']);
for($c=0; $c < $item_length; $c++){
foreach ($data[$i]['items'][$c] as $key => $value ) {
$qty = '';
if($key == "qty"){
$qty = $data[$i]['items'][$c];
}
if($key == 'item_name'){
$item_name = "$value";
}
$sql= $db->query("SELECT `stock` from `inventory` WHERE `item_name` = '$item_name'");
while ($sql1 = $sql->fetch_assoc()) {
$stock = $sql1['stock'];
}
if($stock > $qty ){
$stock_balance = $stock - $qty;
$quantity = (int)$qty;
$db->query("UPDATE `inventory` SET `stock` = (`stock` - '$quantity') WHERE `item_name` = '$item_name'");
}else{
echo "<h3> This Operation Not Allowed: Stock Balance Is Less Than The Request <h3>";
}
}
}
}
A non-numeric value encountered, which is as a result of $qty because I'm not able to return just qty value. I've tried several other means. I'm really exhausted. Would appreciate any help please. Cheers!
Let's decompose your code.
This is json:
[
{
"item_cat":"Stationary",
"items":[
{
"item_name":"A4 Paper",
"qty":"2"
},
{
"item_name":"Test Paper",
"qty":"6"
}
],
"total":"2"
},
{
"item_cat":"Computer Accessory ",
"items":[
{
"item_name":"Power pack",
"qty":"2"
}
],
"total":"1"
},
{
"item_cat":"Material",
"items":[
{
"item_name":"T-Shirt",
"qty":"3"
},
{
"item_name":"Cap",
"qty":"5"
}
],
"total":"2"
}
]
Now the array loop without the SQL (to ensure that it works as expected):
$data = json_decode($json, true);
$len = count($data);
for($i =0; $i< $len; $i++){
$item_length = count($data[$i]['items']);
for($c=0; $c < $item_length; $c++){
foreach ($data[$i]['items'][$c] as $key => $value ) {
$qty = '';
if($key == "qty"){
$qty = $data[$i]['items'][$c];
}
if($key == 'item_name'){
$item_name = "$value";
}
The problems here are: un-human variable names and not correct working with JSON object.
First of all, let us rename variables to something readable.
Example:
$data[$i] will be $catalog_entry (object)
$data[$i]['items'] will be $catalog_entry_items (array)
$data[$i]['items'][$c] will be $catalog_entry_item (one item, object)
Let's change the code with new variables:
$data = json_decode($json, true);
$len = count($data);
for($i =0; $i< $len; $i++) {
$catalog_entry = $data[$i];
$catalog_entry_items = $data[$i]['items'];
for($c=0; $c < sizeof($catalog_entry_items); $c++) {
$catalog_entry_item = $data[$i]['items'][$c];
$qty = $catalog_entry_item['qty'];
$item_name = $catalog_entry_item['item_name'];
echo $item_name . ' : ' . $qty . "\n"; // <-- this is for testing
}
}
Run this code and see the expected result:
A4 Paper : 2
Test Paper : 6
Power pack : 2
T-Shirt : 3
Cap : 5
Good, now we have qty and item_name.
Let's make queries. First look at your code:
$sql= $db->query("SELECT `stock` from `inventory` WHERE `item_name` = '$item_name'");
while ($sql1 = $sql->fetch_assoc()) {
$stock = $sql1['stock'];
}
Two strange things: 1) possible SQL injection, 2) replace $stock variable many times with new value (if we have more than 1 row for item in inventory).
Anyway, if this code is working (I can't check), then we come to next part:
if ($stock > $qty ) {
$stock_balance = $stock - $qty;
$quantity = (int)$qty;
$db->query("UPDATE `inventory` SET `stock` = (`stock` - '$quantity') WHERE `item_name` = '$item_name'");
} else {
echo "<h3> This Operation Not Allowed: Stock Balance Is Less Than The Request <h3>";
}
First, unnecessary cast to integer, so remove line $quantity = (int)$qty; and put $stock_balance into query. We will have:
if ($stock >= $qty ) {
$stock_balance = $stock - $qty;
$db->query("UPDATE `inventory` SET `stock` = $stock_balance WHERE `item_name` = '$item_name'");
} else {
echo "<h3> This Operation Not Allowed: Stock Balance Is Less Than The Request <h3>";
}
...well... not only you are exhausted, so I will end now. Ask if something is not correct or not understandable.
I want to transform query string like pending, shipped or cancel to number status.
$q = strtolower($keyword);
if($q == 'pen' or $q == 'pend' or $q == 'pending' ) {
$d = 1;
} elseif($q == 'shi' or $q == 'ship' or $q == 'shipped') {
$d = 2;
} elseif($q == 'can' or $q == 'cancel' ) {
$d = 3;
} else {
$d = 4;
}
$query->whereStatus($d);
Current query working fine but too much or. It's possible to do in shorten way?
str_is(query, stringToSearch) will probably be enough:
if (str_is('pen*', $q)) {
$d = 1;
}
Else you could parse them from arrays:
$pendingArray = ['pen', 'pend', 'pending'];
if (in_array($q, $pendingArray)) {
$d = 1;
}
If these are all the conditions you need, you could always use a switch.
$q = strtolower($keyword);
$d = 4;
switch($q) {
case 'pen':
case 'pend':
case 'pending':
case 'pen':
$d = 1;
break;
case 'shi':
case 'ship':
case 'shipped':
$d = 2;
break;
case 'can':
case 'cancel':
$d = 3;
break;
}
$query->whereStatus($d);
If this needs to be called on a model, it could be saved to a Laravel scope function like so:
on Laravel model
public function scopeSearchStatus($query, $keyword) {
/** All the code above **/
}
Then it can be called cleanly anywhere you'd like:
SomeModel::searchStatus($keyword);
You could also try this:
<?php
$q = strtolower($keyword);
$d = (preg_match('/\b(pen|pend|pending)\b/', $q)) ? 1 : 4;
$d = (preg_match('/\b(shi|ship|shipped)\b/', $q)) ? 2 : 4;
$d = (preg_match('/\b(can|cancel|)\b/', $q)) ? 3 : 4;
$query->whereStatus($d);
I am trying to evaluate the content of an array. The array contain water temperatures submitted by a user.
The user submits 2 temperaures, one for hot water and one for cold water.
What I need is to evaluate both array items to find if they are within the limits, the limits are "Hot water: between 50 and 66", "Cold water less than 21".
If either Hot or Cold fail the check flag the Status "1" or if they both pass the check flag Status "0".
Below is the code I am working with:
$row_WaterTemp['HotMin'] = "50";
$row_WaterTemp['Hotmax'] = "66";
$SeqWaterArray new(array);
$SeqWaterArray = array("58", "21");
foreach($SeqWaterArray as $key => $val) {
$fields[] = "$field = '$val'";
if($key == 0) {
if($val < $row_WaterTemp['HotMin'] || $val > $row_WaterTemp['Hotmax']) {
$Status = 1;
$WaterHot = $val;
} else {
$Status = 0;
$WaterHot = $val;
}
}
if($key == 1) {
if($val > $row_WaterTemp['ColdMax']) {
$Status = 1;
$WaterCold = $val;
} else {
$Status = 0;
$WaterCold = $val;
}
}
}
My question is:
When I run the script the array key(0) works but when the array key(1) is evaluted the status flag for key(1) overrides the status flag for key0.
If anyone can help that would be great.
Many thanks for your time.
It seems OK to me, exept for the values at limit, and you can simplify
$row_WaterTemp['HotMin'] = "50";
$row_WaterTemp['Hotmax'] = "66";
$SeqWaterArray = array("58", "21");
$Status = array() ;
foreach($SeqWaterArray as $key => $val) {
if($key == 0) {
$Status = ($val >= $row_WaterTemp['HotMin'] && $val <= $row_WaterTemp['Hotmax']) ;
$WaterHot = $val;
} else if($key == 1) {
$Status += ($val >= $row_WaterTemp['ColdMax']) ;
$WaterCold = $val;
}
}
If one fails, $Status = 1, if the two tests failed, $Status = 2, if it's ok, $Status = 0.
<?php
// this function return BOOL (true/false) when the condition is met
function isBetween($val, $min, $max) {
return ($val >= $min && $val <= $max);
}
$coldMax = 20; $hotMin = 50; $hotMax = 66;
// I decided to simulate a test of more cases:
$SeqWaterArray['john'] = array(58, 30);
$SeqWaterArray['martin'] = array(34, 15);
$SeqWaterArray['barbara'] = array(52, 10);
foreach($SeqWaterArray as $key => $range) {
$flag = array();
foreach($range as $type => $temperature) {
// here we fill number 1 if the temperature is in range
if ($type == 0) {
$flag['hot'] = (isBetween($temperature, $hotMin, $hotMax) ? 0 : 1);
} else {
$flag['cold'] = (isBetween($temperature, 0, $coldMax) ? 0 : 1);
}
}
$results[$key]['flag'] = $flag;
}
var_dump($results);
?>
This is the result:
["john"]=>
"flag"=>
["hot"]=> 1
["cold"]=> 0
["martin"]=>
"flag" =>
["hot"]=> 1
["cold"]=> 0
["barbara"]=>
"flag" =>
["hot"]=> 0
["cold"]=> 0
I don't think that you need a foreach loop here since you are working with a simple array and apparently you know that the first element is the hot water temperature and the second element is the cold water temperature. I would just do something like this:
$row_WaterTemp['HotMin'] = 50;
$row_WaterTemp['HotMax'] = 66;
$row_WaterTemp['ColdMax'] = 21;
$SeqWaterArray = array(58, 21);
$waterHot = $SeqWaterArray[0];
$waterCold = $SeqWaterArray[1];
$status = 0;
if ($waterHot < $row_WaterTemp['HotMin'] || $waterHot > $row_WaterTemp['HotMax']) {
$status = 1;
}
if ($waterCold > $row_WaterTemp['ColdMax']) {
$status = 1;
}
You can combine the if statements of course. I separated them because of readability.
Note that I removed all quotes from the numbers. Quotes are for strings, not for numbers.
You can use break statement in this case when the flag is set to 1. As per your specification the Cold water should be less than 21, I have modified the code.
<?php
$row_WaterTemp['HotMin'] = "50";
$row_WaterTemp['Hotmax'] = "66";
$row_WaterTemp['ColdMax'] = "21";
$SeqWaterArray = array("58", "21");
foreach($SeqWaterArray as $key => $val) {
$fields[] = "$key = '$val'";
if($key == 0) {
if($val < $row_WaterTemp['HotMin'] || $val > $row_WaterTemp['Hotmax']) {
$Status = 1;
$WaterHot = $val;
break;
} else {
$Status = 0;
$WaterHot = $val;
}
}
if($key == 1) {
if($val >= $row_WaterTemp['ColdMax']) {
$Status = 1;
$WaterCold = $val;
break;
} else {
$Status = 0;
$WaterCold = $val;
}
}
}
echo $Status;
?>
This way it would be easier to break the loop in case if the temperature fails to fall within the range in either case.
https://eval.in/636912
I need a function or an array that gives me random numbers from 1 - 47, so I can use it for my code below.
public function output($question_id, $solution) {
$yes = ($GLOBALS['TSFE']->sys_language_uid == 0) ? 'Ja' : 'Oui';
$no = ($GLOBALS['TSFE']->sys_language_uid == 0) ? 'Nein' : 'No';
switch ($question_id) {
case 1:
$arg = array(
'main_content' => $this->build_html(1, '01_0429_04_14_Psori_Arthro.jpg', $yes, $no, $solution)
);
break;
case 2:
$arg = array(
'main_content' => $this->build_html(2, '02_0342_05_14_Psori_Arthropathie.jpg', $yes, $no, $solution),
);
break;
case 3:
$arg = array(
'main_content' => $this->build_html(3, '03_0255_05_14_Psori_Arthropathie.jpg', $yes, $no, $solution),
);
break;
}
}
Example
This
case 1:
$arg = array(
'main_content' => $this->build_html(1, '01_0429_04_14_Psori_Arthro.jpg', $yes, $no, $solution)
);
should look somehow like this:
case $random_id:
$arg = array(
'main_content' => $this->build_html($random_id, '01_0429_04_14_Psori_Arthro.jpg', $yes, $no, $solution)
);
So that means, each case and each first parameter of the function build_html should get a unique random id.
Of course I could use rand() but then it is quite possible that I get duplicated values.
Any help would be appreciated!
Create a class like follows:
class SamplerWithoutReplacement {
private $pool;
public __construct($min,$max) {
$this->pool = range($min,$max);
}
public function next() {
if (!empty($this->pool)) {
$nIndex = array_rand($this->pool);
$value = $this->pool[$nIndex];
unset($this->pool[$nIndex]);
return $value;
}
return null; //Or throw exception, depends on your handling preference
}
}
Use it as:
$sampler = new SamplerWithoutReplacement(1,47);
//do things
$value = $sampler->next();
The $sampler will draw random samples between 1-47 without replacing them in the pool and therefore they'll be unique.
Not tested but I think it should work:
$numbers = [];
function genenerate_nr_not_in_list($list, $min = 1, $max = 47) {
$number = rand($min, $max);
while (false !== array_search($number, $list)) {
$number = rand($min, $max);
}
return $number;
}
for ($i = 0; $i < 47; $i++) {
$numbers[] = genenerate_nr_not_in_list($numbers);
}
With this solution you can generate numbers in each range ($min and $max parameters). But it should be at least as big as the number of values you need ($max).
I have this odd situation and I can't think of a solution for it.
I have a variable $cat_count = 1; and I use it inside a loop and then do $cat_count++ somewhere below where I've used it.
Then I have another alphabetical counter which works the following way:
I have $alpha_string = 'abcdefghijklmnopqrstuvwxyz'; and $alpha_counter = 0;. I use this the following way - $alpha = $alpha_string{$alpha_counter}. I want my alphabetical counter to start counting from a, whenever $cat_count gets incremented by one.
So for example we would have this:
$cat_count = 1
$alpha = a
$alpha = b
$cat_count = 2
$alpha = a
$alpha = b
What I get momentarily is this:
$cat_count = 1
$alpha = a
$alpha = b
$cat_count = 2
$alpha = c
$alpha = d
Ideas?
Thank you.
following my answer in comments..
$counter = 0;
$cat_count = 1;
$alpha_count = 'abcdefghijklmnopqrstuvwxyz';
$rule_id = null;
$public_cats = array();
while ($row = $db->sql_fetchrow($result))
{
if ($rule_id != $row['rule_id'])
{
$group_ids = array_map('intval', explode(' ', $row['groups']));
$is_grouped = false;
// Check if user can see a specific category if he is not an admin or moderator
if (!$auth->acl_get('a_') && !$auth->acl_get('m_'))
{
$is_grouped = (group_memberships($group_ids, $user->data['user_id'], true)) ? true : false;
}
else
{
$is_grouped = true;
}
// Fill $public_cats with boolean values
if ($is_grouped !== false)
{
$public_cats[] = $is_grouped;
}
$rule_id = $row['rule_id'];
$template->assign_block_vars('rules', array(
'RULE_CATEGORY' => $row['rule_title'],
'ROW_COUNT' => $cat_count,
'CAN_SEE_CAT' => $is_grouped
));
$cat_count++;
$counter = 0;
}
$uid = $bitfield = $options = '';
generate_text_for_storage($row['rule_desc'], $uid, $bitfield, $options, $row['bbcode'], $row['links'], $row['smilies']);
$template->assign_block_vars('rules.rule', array(
'RULE_DESC' => generate_text_for_display($row['rule_desc'], $uid, $bitfield, $options),
'ALPHA_COUNT' => $alpha_count{$counter}
));
$counter++;
}