It may be a simple question, but I can't find the answer. How can I know if my Collection has no data ?
I do $datas = Mage::getModel('zzz/zzz')->getCollection() if I do a $datas->getData() it returns an empty array, but how do I know if my collection has no data without doing foreach or getData ?
You should avoid using count or your Collections. Here's why:
the Mage_Core_Model_Resource_Db_Collection_Abstract (Collection Model that is inherited by almost all Magento Collections) does not have count() defined, so using count on your Collection you'll most likely end up with Varien_Data_Collection::count() which is very bad option, since it does a collection load() and then counts the loaded objects:
/**
* Retireve count of collection loaded items
*
* #return int
*/
public function count()
{
$this->load();
return count($this->_items);
}
Having a large collection (especially EAV collection) will make result in loading ALL of your Collection data - this can take a lot of time.
Instead you should use Varien_Data_Collection_Db::getSize() method, which will run the SQL query to get count only, much more optimized compared to retrieving all kind of data for Collection load:
/**
* Get collection size
*
* #return int
*/
public function getSize()
{
if (is_null($this->_totalRecords)) {
$sql = $this->getSelectCountSql();
$this->_totalRecords = $this->getConnection()->fetchOne($sql, $this->_bindParams);
}
return intval($this->_totalRecords);
}
In addition to that, after load collection can not be modified in any way. For example you won't be able to apply additional filters of change sort order at any point after using count().
So correct answer should be:
$collection = Mage::getModel('zzz/zzz')->getCollection();
var_dump($collection->getSize());
You can easily just do an if statement like so:
if (!$datas->getData() || empty($datas->getData())) {
// do things
}
In addition to the accepted answers see benchmarks:
Tested for 750 products
$collection->getData()
Total Incl. Wall Time (microsec): 67,567 microsecs
Total Incl. CPU (microsecs): 67,599 microsecs
Total Incl. MemUse (bytes): 11,719,168 bytes
Total Incl. PeakMemUse (bytes): 11,648,152 bytes
Number of Function Calls: 1,047
$collection->getSize()
Total Incl. Wall Time (microsec): 6,371 microsecs
Total Incl. CPU (microsecs): 4,402 microsecs
Total Incl. MemUse (bytes): 140,816 bytes
Total Incl. PeakMemUse (bytes): 96,000 bytes
Number of Function Calls: 191
$collection->count() or sizeof($collection)
Total Incl. Wall Time (microsec): 2,130,568 microsecs
Total Incl. CPU (microsecs): 2,080,617 microsecs
Total Incl. MemUse (bytes): 12,899,872 bytes
Total Incl. PeakMemUse (bytes): 13,002,256 bytes
Number of Function Calls: 101,073
So you should go with getSize().
Form: https://magento.stackexchange.com/questions/179028/how-to-check-if-a-collection-has-items/179035#179035
/**
* Retrieve collection all items count
*
* #return int
*/
$collection = Mage::getModel('aaa/bbb')->getCollection()->getSize();
This is the code that's used in pagination etc and is recommended.
where
/**
* Retireve count of collection loaded items
*
* #return int
*/
public function count()
will be useful to check for loaded items data.
you can use;
$collection = Mage::getModel('zzz/zzz')->getCollection();
var_dump($collection->count());
Running a simple, standard PHP count() on the collection is fine here. As long as you have properly filtered your collection, which you should always have done before getting to the point of counting it calling the ->count() method on a collection is fine also. As soon as you manipulate the collection in any way, it will load regardless of the method you use, so a standard PHP count(), calling the ->count() method on the object, running through the collection with a foreach() will all load the collection in the same way as as load(), in fact if you trace the load() method back, you will see it actually runs a standard PHP foreach() to load collection data.
So it doesn't matter how you do it, you still can't count your collection until you know how many results have been returned from the database, so the method above is fine, but does mean extra DB calls, first to count, then to load. A better method is just to ensure that you make your SELECT statements as specific as possible by narrowing them with WHERE clauses and so on. If you pull the select object from a collection you have access to all of the Zend_Db_Select methods shown here, i.e.
$collection->getSelect()->where('......... = ?', $var);
Suppose the product collection is $pro_collection
Now apply the following code..
<?php
if(isset($pro_collection) && count($pro_collection) > 0) {
/* Your code here */
}
?>
Related
I want to decrease quantity from products table when order is added to cart ,right now when I add something to cart, correct quantity is not decreasing. For example if a I place order of 6 items then a random amount gets decreased from my products table.
Here is my code in order controller of livewire:
public function IncrementQty($cartId)
{
$carts=Cart::find($cartId);
$carts->increment('product_qty',1);
$updatePrice=$carts->product_qty * $carts->product->price;
$carts->update(['product_price'=>$updatePrice]);
$getProductStock=Product::where(['id'=>$carts['product_id']])->first()->toArray();
$newStock=$getProductStock['quantity'] - $carts['product_qty'];
Product::where(['id'=>$carts['product_id']])->update(['quantity'=>$newStock]);
$this->mount();
}
Being a beginner I am unable to understand about what's wrong here. Should I be substracting it from orderDetails table instead of carts table? If yes then how?
A couple of things to note first,
You should not call $this->mount() or any other lifecycle hooks from Livewire. If there are code inside your mount-method you need to run again, then abstract it into a separate method, and call it from both your mount() method and your IncrementQty() method.
You can use model-route-binding to inject the correct model into the method
public function IncrementQty(Cart $cart)
You can then simplify your code by using the product relation on the cart directly, and calling decrement() on that.
public function IncrementQty(Cart $cart)
{
$product = $cart->product;
$cart->increment('product_qty', 1);
$cart->update(['product_price' => $cart->product_qty * $product->price]);
$product->decrement('quantity', 1);
$this->mount(); // Do not call mount manually, abstract the contents you need into a separate method instead
}
If you are still experiencing issues, then it cannot possibly be this piece of code, or you are misreading your data somehow - in any case, if you are still experiencing issues, you will need to provide additional information with your table-structure, models, how you call this method, and data before and after your actions.
it seems you have a problem in this line:
$newStock=$getProductStock['quantity'] - $carts['product_qty'];
$carts is an object not an array, so, do:
$newStock = $getProductStock['quantity'] - $carts->product_qty;
I have this query eloquent in Laravel, I was curious to know. If there is a way to know how many records inserted or how many records ignored to the table?
DB::table('mytable')->insertOrIgnore($data)
Note: One of the manual ways can be, counting records of table before and after process. but this has performance effect, if there be any better way achieving this.
The function insertOrIgnore() returns the affected rows.
/**
* Insert a new record into the database while ignoring errors.
*
* #param array $values
* #return int
*/
public function insertOrIgnore(array $values) {
So you can simply use the returned value and compare it to what was expected to be inserted:
$affected = DB::table('mytable')->insertOrIgnore($data);
$ignored = count($data) - $affected;
I've got a Symfony entity, which has a OneToMany mapping with an OrderBy clause like this:
/**
* #ORM\OneToMany(targetEntity="App\Entity\News", mappedBy="category", orphanRemoval=true)
* #ORM\OrderBy({"id" = "DESC"})
*/
private $news;
Assuming I would like to only display n entries in Twig, I would have the options to either loop over it and ignoring every thing after loop.index n or rather use slice. However these options do have the downside, that if there are a lot of news entries, all of them will be loaded, which isn't very efficient.
Another option would be to use a Criteria in the controller or the entity to limit the amount of loaded entities. If I understood it here correctly, it should modify the doctrine query directly and thus not have any performance impact. Is this the best practice, or would it be better to have a custom query builder in the controller or a function in the repository?
Actually you can set $news relationship as EXTRA_LAZY and use $news->slice() function without triggering a full load as stated in official documentation:
If you mark an association as extra lazy the following methods on
collections can be called without triggering a full load of the
collection:
Collection#contains($entity)
Collection#containsKey($key) (available with Doctrine 2.5)
Collection#count()
Collection#get($key) (available with Doctrine 2.4)
Collection#slice($offset, $length = null)
Therefore your declaration should look like the following:
/**
* #ORM\OneToMany(targetEntity="App\Entity\News", mappedBy="category", orphanRemoval=true, fetch="EXTRA_LAZY")
* #ORM\OrderBy({"id" = "DESC"})
*/
private $news;
I have a Shop with Products that have different Taxrates. Some have 0%, some 19% etc...
For Shipping im using Tablerates, for example:
up to 2000€ = 10€ shipping cost
up to or over 2400€ = 15€ shipping cost
Now comes the thing that at first i just want to understand, so there is not really a bug to fix here. I just need to know how it works to plan my tablerates accordingly.
I have orders with a total over 2400€ (incl. Tax), but the customer still gets the 10€ Shipping rate. This can only be if the System is using the Price without Tax to check against the table rates. Because only then would the price be in the Tablerate range for the lower rate. And yes i double checked the Tablerates Setup (not my first Magento Install).
1) Is this assumption correct that table rates are checked against the total without taxes?
2) Is there a way to set up tablerates to check against the cart total including taxes?
Anyone got any info on how that works in the background? I couldnt find anything as when youre searching the topic you mostly get tutorial on how to set up table rates.
Super thankful for any tipps or maybe other threads or places i could check for detailed infos on how that works.
note: i use Magento 1.9.2.1
For Question 1:
It would appear to be without tax.
Mage_Shipping_Model_Shipping::collectRatesByAddress calls setPackageValue to set the package value for the rate request.
This gets passed to Mage_Shipping_Model_Carrier_Tablerate::collectRates.
collectRates subtracts free shipping from the package value.
Then Mage_Shipping_Model_Carrier_Tablerate::getRate is called
$result = Mage::getModel('shipping/rate_result');
$rate = $this->getRate($request);
...
public function getRate(Mage_Shipping_Model_Rate_Request $request)
{
return Mage::getResourceModel('shipping/carrier_tablerate')->getRate($request);
}
This calls Mage_Shipping_Model_Resource_Carrier_Tablerate::getRate which binds the condition value to the query. (The condition name should be package_value in your case).
// Render condition by condition name
if (is_array($request->getConditionName())) {
$orWhere = array();
$i = 0;
foreach ($request->getConditionName() as $conditionName) {
$bindNameKey = sprintf(':condition_name_%d', $i);
$bindValueKey = sprintf(':condition_value_%d', $i);
$orWhere[] = "(condition_name = {$bindNameKey} AND condition_value <= {$bindValueKey})";
$bind[$bindNameKey] = $conditionName;
$bind[$bindValueKey] = $request->getData($conditionName);
$i++;
}
if ($orWhere) {
$select->where(implode(' OR ', $orWhere));
}
} else {
$bind[':condition_name'] = $request->getConditionName();
$bind[':condition_value'] = $request->getData($request->getConditionName());
$select->where('condition_name = :condition_name');
$select->where('condition_value <= :condition_value');
}
While it is possible to modify the order, baseSubtotal should be before tax.
See collectRatesByAddress:
$request->setBaseSubtotalInclTax($address->getBaseSubtotalInclTax()
+ $address->getBaseExtraTaxAmount());
As for your question 2.
As noted above, we have the data in the request, but we do not have an easy touchpoint.
One suggestion is to rewrite the Mage_Shipping_Model_Carrier_Tablerate and override getRate. What you would do would be to set the BaseSubtotal to the BaseSubtotalInclTax, call parent, then reset the request.
public function getRate(Mage_Shipping_Model_Rate_Request $request)
{
// TODO: wrap in try catch, to reset the value ??
// TODO: clone the request ??
// TODO: Test This
$oldPackageValue = $request->getPackageValue();
$request->setPackageValue($request->getBaseSubtotalInclTax());
$returnvalue = Mage::getResourceModel('shipping/carrier_tablerate')->getRate($request);
$request->setPackageValue($oldPackageValue);
return $returnvalue;
}
This is hacky, but minimally intrusive. It should withstand unrelated changes without forcing you to modify the code.
Another method involves rewriting and modifying Mage_Shipping_Model_Resource_Carrier_Tablerate::getRate to use the value you want.
NOTE: Neither of these methods are tested.
I have implemented simple pagination using laravel. It works fine. However, I want to add total number of records and trying to use the method getTotal() but it returns null value.
$records = DB::table('tablename')
->where(condition)
....
....
->simplePaginate(10);
In the view, adding links works fine.
{{$records->links();}}
When I use,
{{$records->getTotal();}}
it returns null.
If I use,
{{$records->count();}}
it returns the count of records for a given page.
Any insights please?
That's how simplePaginate works. From the docs:
If you are only showing "Next" and "Previous" links in your pagination view, you have the option of using the simplePaginate method to perform a more efficient query. This is useful for larger datasets when you do not require the display of exact page numbers on your view.
The simple method is simple because it doesn't need to do the extra, inefficient-on-large-tables count query to obtain the total.
There is method total() in that pagination object. Just call that object. You will get total count.
ex: $records->total();
I had the same problem, what I did was
$records = DB::table('tablename')
->where(condition)
....
....
->paginate(1); //set as 1
$total = $records->getTotal(); //get total
Then returning it like this(I am using ajax that is why I return it as array):
return Response::JSON(array(
'pagination' => (string) $records->links('pagination::simple'),
'total_records' => $total
));