Is there a better loop I could write to reduce database queries? - php

Below is some code I've written that is effective, but makes too many database queries. Is there a way I could optimize and reduce the number of queries but have conditional statements still be as effective as below?
I pasted the code repeated a few times just for good measure.
echo "<h3>Pool Packages</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE && $query->category == "Pool Packages") {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Pool Packages") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { }
endforeach;
echo "</ul>";
echo "<h3>Water Features</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE && $query->category == "Water Features") {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Water Features") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { }
endforeach;
echo "</ul>";
echo "<h3>Waterfall Rock Work</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE) {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Waterfall Rock Work") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { }
endforeach;
echo "</ul>";
echo "<h3>Sheer Descents</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE && $query->category == "Sheer Descents") {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Sheer Descents") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { }
endforeach;
echo "</ul>";
echo "<h3>Booster Pump</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE && $query->category == "Booster Pump") {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Booster Pump") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { }
endforeach;
echo "</ul>";
echo "<h3>Pool Concrete Decking</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE && $query->category == "Pool Concrete Decking") {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Pool Concrete Decking") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { }
endforeach;
echo "</ul>";
echo "<h3>Solar Heating</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE && $query->category == "Solar Heating") {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Solar Heating") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { }
endforeach;
echo "</ul>";
echo "<h3>Raised Bond Beam</h3>";
echo "<ul>";
foreach ($items as $item):
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($item['quantity'] > 1 && $item['quantity'] == TRUE && $query->category == "Raised Bond Beam") {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
else {
$newprice = $query->price;
$totals[] = $newprice;
}
if ($query->category == "Raised Bond Beam") {
echo "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>";
}
else { echo "<li>None</li>"; }
endforeach;
echo "</ul>";
It goes on beyond this to several more categories, but I don't know how to handle looping through this best. Thanks!

You could build the html in a variable so you only loop once. Here's a quick and dirty example just to show you what I'm talking about:
$html = '';
$oldCat = '';
foreach ($items as $item) {
$this->db->where('id', $item['id']);
$query = $this->db->get('items')->row();
if ($oldCat != $query->category) {
$html .= "</ul>\n";
$html .= "<h3>".$query->category."</h3>\n<ul>\n";
$oldCat = $query->category;
}
if ($item['quantity'] > 0) {
$newprice = $item['quantity'] * $query->price;
$totals[] = $newprice;
}
$html .= "<li>" . $query->name . " (QTY: " . $item['quantity'] . " x = " . str_ireplace(" ", "", money_format('%(#10n', $newprice)) . ")</li>\n";
}
// strip leading /ul, append a /ul, echo html

You could store all the rows into a separate array during the first loop and then reference the array throughout all the other loops rather than fetching the same information over and over, assuming you're select * which you probably are.
Or, if there are not many items more than the ones you're fetching, you could use a single query to fetch all of the rows at once (you're using only one query) and loop through that to store all the values in an array $array[$row['id']] = $row (or something similar) then simply reference all those rows in the array in each of your loops.

You need to start thinking in terms of sets instead of loops. Write a stored proc that takes the array either as a varchar (or in SQL Server 2008 you can use a table valued input parameter, don't know about other dbs).
Then split the string into a temp table and return all the records in one select joining to the temp table. Even if you need to return separate record sets, doing it in a stored proc will reduce the network traffic in.

you should use a join from items to category and get all the items, then you can sort them out into a multi-dimensional array and then loop through that for output.
Im not sure what youre classes db connection is doing but but lets assume we want all items with thier category:
$sql = "SELECT item.*, category.name as category from item, category WHERE item.category_id = category.item_id";
// ill use PDO for db access here...
$db = new PDO($connString, $username, $password);
$items = array(); // our array indexed by category.
foreach($db->query($sql) as $item) {
if(!array_key_exists($items, $item['category']) {
$items[$item['category']] = array();
}
$items[$item['category']][] = $item;
}
// now loop through $items using the similar stuff you did for output previously.
// note instead of doing the conditionals for pricing and stuff here you may want to
// do that in the loop above and put it in the array before hand... it will keep the
// output loop cleaner.

Related

Loop while changing the name of a variable

Using FaceBook PHP SDK, when I want to get the next page of results I call something along the likes of $next = $fb->next($response). Now for the next page, I would have to call $next2 = $fb->next($next). I want to put this into a loop as obviously, I will never know beforehand how many times to execute this until I get through all the pages of results. When there are no more pages, the $fb->next() call will = null.
Here is an example without a loop:
$reactions = array();
$response = $response->getGraphEdge();
foreach ($response as $reaction) {
$reactions[] = $reaction['name'] . " - " . $reaction['type'];
}
$next = $fb->next($response);
foreach ($next as $reaction) {
$reactions[] = $reaction['name'] . " - " . $reaction['type'];
}
$next2 = $fb->next($next);
foreach ($next2 as $reaction) {
$reactions[] = $reaction['name'] . " - " . $reaction['type'];
}
$next3 = $fb->next($next2);
foreach ($next2 as $reaction) {
$reactions[] = $reaction['name'] . " - " . $reaction['type'];
}
print_r($reactions);
I was over-complicating this.. I didnt account for the fact that if I keep the variable name the same it gets updated on each iteration of a loop. So this worked:
$reactions = array();
$response = $response->getGraphEdge();
if ($fb->next($response) == null) {
foreach ($response as $reaction) {
$reactions[] = $reaction['name'] . " - " . $reaction['type'];
}
}else {
foreach ($response as $reaction) {
$reactions[] = $reaction['name'] . " - " . $reaction['type'];
}
while ($response = $fb->next($response)) {
foreach ($response as $reaction) {
$reactions[] = $reaction['name'] . " - " . $reaction['type'];
}
}
}
echo "<pre>";
print_r($reactions);
echo "</pre>";

Creating a menu consisting of multi-level structure, how to perform this more officiently? witout hard-coding limits?

I was asked to modify existing code to allow multi-level page structure. Each page can have one parent, and one page can have multiple children.
I will present following code. My question is is there a more efficient way of doing this? I am hard-coding the number of levels that are reachable.
Is there easier way to do this automatically without hard coding? Recursion perhaps?
function createMenu($parents) {
foreach ($parents as $parent) {
$parentName = $this->getPagefromID($parent);
$kids = $this->areThereKids($parent);
if ($kids == "yes") {
echo "<ul class=\"children\">\n";
$query = "SELECT * FROM pages";
$result = mysql_query($query) or die('Query failed: ' . mysql_error());
while ($row = mysql_fetch_assoc($result)) {
$thisparent = $row['Parent'];
$name = $row['Name'];
$id2 = $row['ID'];
if ($thisparent == $parent) {
echo "<li>" . $name . "</li>\n";
if($this->areThereKids($id2) == "yes") {
$children = $this->getMyKids($id2);
foreach($children as $kid) {
$info = $this->getPersonInformation($kid);
$kidid = $info[0]["ID"];
echo "<li>" . $info[0]['Name'] . "</li>\n";
// more sub
if($this->areThereKids($kidid) == "yes") {
$children1 = $this->getMyKids($kidid);
foreach($children1 as $kid1) {
$info1 = $this->getPersonInformation($kid1);
$kidid1 = $info1[0]['ID'];
echo "<li>-" . $info1[0]['Name'] . "</li>\n";
if($this->areThereKids($kidid1) == "yes") {
$children2 = $this->getMyKids($kidid1);
foreach($children2 as $kid2) {
$info2 = $this->getPersonInformation($kid2);
$kidid2 = $info2[0]['ID'];
echo "<li>--" . $info2[0]['Name'] . "</li>\n";
// No need for 6 levels.
}
}
}
}
}
}
}
}
echo "</ul>\n";
}
echo "</li>\n";
}
}
This would be more efficiently done with recursion.
Identify the steps of the code that are repeated and turn them into a subroutine. From your code, we have the following repeated unit:
(...)
if($this->areThereKids($id2) == "yes") {
$children = $this->getMyKids($id2);
foreach($children as $kid) {
$info = $this->getPersonInformation($kid);
$kidid = $info[0]["ID"];
echo "<li>" . $info[0]['Name'] . "</li>\n";
// more sub
if($this->areThereKids($kidid) == "yes") {
(...)
Turn that into a function (assuming you're working with an object):
function dealWithKids($par) {
if ($this->areThereKids($par) == 'yes') {
$children = $this->getMyKids($par);
foreach ($children as $kid) {
$info = $this->getPersonInformation($kid);
$kidid = $info[0]["ID"];
echo "<li>" . $info[0]['Name'] . "</li>\n";
// instead of repeating the same steps again,
// call the dealWithKids function here
$this->dealWithKids($kidid);
}
}
}
...and then call that function instead of the repeated units of code:
while ($row = mysql_fetch_assoc($result)) {
$thisparent = $row['Parent'];
$name = $row['Name'];
$id2 = $row['ID'];
if ($thisparent == $parent) {
echo "<li>" . $name . "</li>\n";
// instead of this:
// if($this->areThereKids($id2) == "yes") {
// do this:
$this->dealWithKids($parent);
}
}

Magento Get Products By Manufacturer

I have the following code which gets me a list of manufacturers:
$attribute = Mage::getModel('eav/entity_attribute')
->loadByCode('catalog_product', 'manufacturer');
$valuesCollection = Mage::getResourceModel('eav/entity_attribute_option_collection')
->setAttributeFilter($attribute->getData('attribute_id'))
->setStoreFilter(0, false);
$preparedManufacturers = array();
foreach($valuesCollection as $value) {
$preparedManufacturers[$value->getOptionId()] = $value->getValue();
}
if (count($preparedManufacturers)) {
echo "<h2>Manufacturers</h2><ul>";
foreach($preparedManufacturers as $optionId => $value) {
echo "<li>" . $value . " - (ID:" . $optionId . ")</li>";
}
echo "</ul>";
}
How do I get the first product of each manufacturer?
Thanks
try this
if (count($preparedManufacturers)) {
echo "<h2>Manufacturers</h2><ul>";
foreach($preparedManufacturers as $optionId => $value) {
<!-- add the this code for get first item id -->
$firstProductId=Mage::getModel('catalog/product')->getCollection()
->addStoreFilter(0)
->addAttributeToFilter('manufacturer',$optionId)->getFirstItem()->getId();
echo "<li>" . $value . " - (ID:" . $optionId . ")</li>";
}
echo "</ul>";
}

how to remove white space in td tag?

I have a html table which i fill in php in a complex logic.
There are 4 foreach loops to fill the data.
Now this works fine but through the loops i get some whitespace in my element.
The browser can display it fine, but as soon as I want to export this to a pdf, it will have some problems.
Anyone knows how to dismiss these whitespaces. Take a look at the image for more clarification:
Here's the code my variables which i echo out dont have any whitespaces. The problem also occur if give out echo "test";:
<div id="autoload-content">
<table class="tg" >
<thead>
<tr>
<th></th>
<th>Montag</th>
<th>Dienstag</th>
<th>Mittwoch</th>
<th>Donnerstag</th>
<th>Freitag</th>
</tr>
</thead>
<tbody>
<?php
$arrNoDoubleEntries = array();
$totalFactorMo = 0;
$stackIrregular = array();
//displays all the different presencetypes
foreach ($types as &$value) {
?>
<tr>
<td><?php echo ($value->Name);?></td>
<td>
<?php //all Monday Childs
if ($value->Name == "Total"){
echo ($totalFactorMo);
}else{
$childrenMonday = (Children::LoadPresence(1, $groupId));
foreach ($childrenMonday as &$child) {
foreach ($irregularPresence as &$irrugPrese){
if (($child->id == $irrugPrese->child_id) && ($monday >= $irrugPrese->datefrom) && ($monday <= $irrugPrese->dateto)){
if ($irrugPrese->away == 1 && ($value->id == $child->presencetype)){
echo("<s>".$child->fullName . " " . $child->factor."</s>");
echo "<br />";
array_push($stackIrregular, $child->id);
}
else{
if (($value->id == $irrugPrese->presencetype_id) AND (!(in_array($child->fullName + $irrugPrese->presencetype_id, $arrNoDoubleEntries)))){
echo("<u>".$child->fullName . " " . $child->factor."</u>");
echo "<br />";
array_push($arrNoDoubleEntries, $child->fullName + $irrugPrese->presencetype_id);
$totalFactorMo = $totalFactorMo + $child->factor;
}
}
}
}
if (!(in_array($child->id, $stackIrregular))) {
//prints the children which dont have some irregular Presences
if (!($child->presencetype == 1) && ($value->id == $child->presencetype)){
echo ($child->fullName . " " . $child->factor);
echo "<br />";
$totalFactorMo = $totalFactorMo + $child->factor;
}
}
}
}
?>
</td>
</tr>
</tbody>
</table>
</div>
in your case try to concatenate html and then show it like
<?php //all Monday Childs
$html = '';
if ($value->Name == "Total"){
$html .= ($totalFactorMo);
}else{
$childrenMonday = (Children::LoadPresence(1, $groupId));
foreach ($childrenMonday as &$child) {
foreach ($irregularPresence as &$irrugPrese){
if (($child->id == $irrugPrese->child_id) && ($monday >= $irrugPrese->datefrom) && ($monday <= $irrugPrese->dateto)){
if ($irrugPrese->away == 1 && ($value->id == $child->presencetype)){
$html .=("<s>".$child->fullName . " " . $child->factor."</s>");
$html .= "<br />";
array_push($stackIrregular, $child->id);
}
else{
if (($value->id == $irrugPrese->presencetype_id) AND (!(in_array($child->fullName + $irrugPrese->presencetype_id, $arrNoDoubleEntries)))){
$html .=("<u>".$child->fullName . " " . $child->factor."</u>");
$html .= "<br />";
array_push($arrNoDoubleEntries, $child->fullName + $irrugPrese->presencetype_id);
$totalFactorMo = $totalFactorMo + $child->factor;
}
}
}
}
if (!(in_array($child->id, $stackIrregular))) {
//prints the children which dont have some irregular Presences
if (!($child->presencetype == 1) && ($value->id == $child->presencetype)){
$html .= ($child->fullName . " " . $child->factor);
$html .= "<br />";
$totalFactorMo = $totalFactorMo + $child->factor;
}
}
}
}
echo $html;
?>
You can trim spaces from the variable before displaying in the table like
$string = preg_replace('/\s+/', '', $string);
or
$string = str_replace(' ', '', $string);
Just strip all the white space before printing the value.
EG:
function cleanSpaces($text)
{
$text = preg_replace('/(xC2xA0/| )','',$text); //remove
$text = preg_replace('/\s+/', '', $loop_variable_value); //remove whitespace
return $text;
}

How To Change Numbers Based On Results

I have a follow up question on something I got help with here the other day (No Table Three Column Category Layout).
The script is as follows:
$res = mysql_query($query);
$system->check_mysql($res, $query, __LINE__, __FILE__);
$parent_node = mysql_fetch_assoc($res);
$id = (isset($parent_node['cat_id'])) ? $parent_node['cat_id'] : $id;
$catalist = '';
if ($parent_node['left_id'] != 1)
{
$children = $catscontrol->get_children_list($parent_node['left_id'], $parent_node['right_id']);
$childarray = array($id);
foreach ($children as $k => $v)
{
$childarray[] = $v['cat_id'];
}
$catalist = '(';
$catalist .= implode(',', $childarray);
$catalist .= ')';
$all_items = false;
}
$NOW = time();
/*
specified category number
look into table - and if we don't have such category - redirect to full list
*/
$query = "SELECT * FROM " . $DBPrefix . "categories WHERE cat_id = " . $id;
$result = mysql_query($query);
$system->check_mysql($result, $query, __LINE__, __FILE__);
$category = mysql_fetch_assoc($result);
if (mysql_num_rows($result) == 0)
{
// redirect to global categories list
header ('location: browse.php?id=0');
exit;
}
else
{
// Retrieve the translated category name
$par_id = $category['parent_id'];
$TPL_categories_string = '';
$crumbs = $catscontrol->get_bread_crumbs($category['left_id'], $category['right_id']);
for ($i = 0; $i < count($crumbs); $i++)
{
if ($crumbs[$i]['cat_id'] > 0)
{
if ($i > 0)
{
$TPL_categories_string .= ' > ';
}
$TPL_categories_string .= '' . $category_names[$crumbs[$i]['cat_id']] . '';
}
}
// get list of subcategories of this category
$subcat_count = 0;
$query = "SELECT * FROM " . $DBPrefix . "categories WHERE parent_id = " . $id . " ORDER BY cat_name";
$result = mysql_query($query);
$system->check_mysql($result, $query, __LINE__, __FILE__);
$need_to_continue = 1;
$cycle = 1;
$column = 1;
$TPL_main_value = '';
while ($row = mysql_fetch_array($result))
{
++$subcat_count;
if ($cycle == 1)
{
$TPL_main_value .= '<div class="col'.$column.'"><ul>' . "\n";
}
$sub_counter = $row['sub_counter'];
$cat_counter = $row['counter'];
if ($sub_counter != 0)
{
$count_string = ' (' . $sub_counter . ')';
}
else
{
if ($cat_counter != 0)
{
$count_string = ' (' . $cat_counter . ')';
}
else
{
$count_string = '';
}
}
if ($row['cat_colour'] != '')
{
$BG = 'bgcolor=' . $row['cat_colour'];
}
else
{
$BG = '';
}
// Retrieve the translated category name
$row['cat_name'] = $category_names[$row['cat_id']];
$catimage = (!empty($row['cat_image'])) ? '<img src="' . $row['cat_image'] . '" border=0>' : '';
$TPL_main_value .= "\t" . '<li>' . $catimage . '' . $row['cat_name'] . $count_string . '</li>' . "\n";
++$cycle;
if ($cycle == 7) // <---- here
{
$cycle = 1;
$TPL_main_value .= '</ul></div>' . "\n";
++$column;
}
}
if ($cycle >= 2 && $cycle <= 6) // <---- here minus 1
{
while ($cycle < 7) // <---- and here
{
$TPL_main_value .= ' <p> </p>' . "\n";
++$cycle;
}
$TPL_main_value .= '</ul></div>'.$number.'
' . "\n";
}
I was needing to divide the resulting links into three columns to fit my html layout.
We accomplished this by changing the numbers in the code marked with "// <---- here".
Because the amount of links returned could be different each time, I am trying to figure out how to change those numbers on the fly. I tried using
$number_a = mysql_num_rows($result);
$number_b = $number_a / 3;
$number_b = ceil($number_b);
$number_c = $number_b - 1;
and then replacing the numbers with $number_b or $number_c but that doesn't work. Any ideas?
As mentioned before, you can use the mod (%) function to do that.
Basically what it does is to get the remainder after division. So, if you say 11 % 3, you will get 2 since that is the remainder after division. You can then make use of this to check when a number is divisible by 3 (the remainder will be zero), and insert an end </div> in your code.
Here is a simplified example on how to use it to insert a newline after every 3 columns:
$cycle = 1;
$arr = range (1, 20);
$len = sizeof ($arr);
for ( ; $cycle <= $len; $cycle++)
{
echo "{$arr[$cycle - 1]} ";
if ($cycle % 3 == 0)
{
echo "\n";
}
}
echo "\n\n";

Categories