How to exclude a category from a magento getCollection - magento

this might be a silly question, but well. I have the following code that retrieves all the products on my shop.
$products = Mage::getModel('catalog/product')->getCollection();
$products->addAttributeToFilter('status', 1);
$products->addAttributeToFilter('visibility', 4);
$products->addAttributeToFilter('type_id', 'simple');
$products->addAttributeToSelect('*');
$products->addStoreFilter($storeId);
$prodIds = $products->getAllIds();
Im aware of the:
$category = Mage::getModel('catalog/category')->load(9);
$products->addCategoryFilter($category);
to filter by a category ID, but how to get all products except one specific category ID ? (Magento 1.6.2)

I think this should work, presuming you know what category ID you want to filter out, but I can't test it right now
$catId = 9;
/* I'm almost positive 'e' is the alias used for catalog_product_entity, check your
query with echo (string) $products->getSelect(); if it doesn't work */
$products->getSelect()->join(array('cats' => 'catalog_category_product'), 'cats.product_id = e.entity_id');
$products->getSelect()->where('cats.category_id', array('neq' => $catId));

This is what I used:
$_productCollection = Mage::getModel('catalog/product')
->getCollection()
->addAttributeToSelect('*')
->addUrlRewrite();
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($_productCollection);
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($_productCollection);
$_productCollection->load();
$_productCollection->getSelect()->join(array('cats' => 'catalog_category_product'), 'cats.product_id = e.entity_id');
$_productCollection->getSelect()->where('cats.category_id not in (41)');

Here is the logic I came up with to resolve this problem.
Note: Should work in Magento 1.X
It dynamically pulls table names so that it would work in any installation.
It validates that it is a correct catalog/category entity ID.
It uses a unique table alias by category ID when performing the MySQL LEFT JOIN. This allows us to exclude the collection from multiple category IDs.
It validates that we do not attempt to exclude the collection from the same category id twice.
public function addCategoryExclusionFilter(Mage_Catalog_Model_Resource_Product_Collection $collection, $category_id)
{
/* #var $resource Mage_Core_Model_Resource */
/* #var $category Mage_Catalog_Model_Category */
$resource = Mage::getModel('core/resource');
$category = Mage::getModel('catalog/category')->load($category_id);
$select = $collection->getSelect();
$tblccp = $resource->getTableName('catalog_category_product');
$tblAlias = $tblccp.'_'.$category_id;
if (! $category->getId()) {
Mage::throwException("Invalid `{$resource->getTableName('catalog/category')}`.`entity_id` value ({$category_id}).");
}
if (strpos($select->__toString(), $tblAlias) !== false) {
Mage::throwException("Category (ID: {$category->getId()}) already excluded from collection");
}
$select->joinLeft(array($tblAlias => $tblccp), "(`{$tblAlias}`.`product_id` = `e`.`entity_id` AND `{$tblAlias}`.`category_id` = '{$category->getId()}')", array());
$select->where("`{$tblAlias}`.`category_id` IS NULL");
}
Example of the MySQL query afterwards:
SELECT
`e`.*
FROM
`catalog_product_entity` AS `e`
LEFT JOIN
`catalog_category_product` AS `catalog_category_product_28` ON (
`catalog_category_product_28`.`product_id` = `e`.`entity_id` AND
`catalog_category_product_28`.`category_id` = '28'
)
WHERE
(`catalog_category_product_28`.`category_id` IS NULL)
What we are doing here is performing a join on the table which holds the relationship between product entities and category entities, BUT only where the record's category_id is equal to the category id which we are looking to exclude. This is important because the catalog_product_entity table has a 1:M relationship to the catalog_category_product table (at the time of posting this answer, I do not see the other answers here addressing this). Then, we add a WHERE declaration that we only want to select records where the category_id column for the joined table IS NULL (because there exists no record in the joined table, for product entities which we wish to select).

Related

Laravel 5 - How to Query if DB Value is Array

In database, I have "categories" and "products" table. In "products" table, there is a category_ids column which is an array, because the product has more than one categories.
What I want is to query the product that has category_ids of 1. I am trying to do like this, it is works, but I want a simpler way how to do.
$catId = 1; //category ID
$allProducts = Product::all(); // get all products
foreach ($allProducts as $product) { //loop all the products
if(in_array($catId, $product->category_ids)) { // check if category ID is in array of its product categories
$products[] = Product::find($product->id);
}
}
As you can see above,I must loop through all the products to check if category ID is exist in product categories array.
Is there a better way so it is not checking all the products?
All code is OK except one line .
$catId = 1; //category ID
$allProducts = Product::all(); // get all products
foreach ($allProducts as $product) { //loop all the products
$cats = explode(',',$product->category_ids);
if(in_array($catId, $cats)) { // check if category ID is in array of its product categories
$products[] = Product::find($product->id);
}
}
I added this line. I assume that multiple categories of a product will be stored in a single field with comma separated (Although its not the correct way, you need to create a M2M table)
$cats = explode(',',$product->category_ids);
So In in_array function, 2nd element should be an array. and Because of output of explode, $cats is an array.
in_array($catId, $cats)

getStoreCategories() to get categories by ids

I would like to get an array of categories for my magento store.
I need to use following unit for my website to work:
$categories = $helper->getStoreCategories('name', true, true);
However this lists all categories.
I would like to select categories that are important for me. I think I could do that by selecting ids of the categories or names of the categories but I don't know how to do it.
Could anybody help me please?
Let's say you have an array with category ids like this:
$ids = array(6,8,99);
You can get the category objects like this:
$collection = Mage::getModel('catalog/category')->getCollection()
->addAttributeToSelect('*')
->addAttributeToFilter('entity_id', $ids);
if you want only active categories add this line also
$collection->addAttributeToFilter('is_active', 1);
Use this function and just pass category Id:
function getCategoryData($_categoryId=null) {
// For category Collection
$category = Mage::getModel('catalog/category')->load($_categoryId);
// For product Collection category wise
$prodCollection = $category->getProductCollection();
return $prodCollection;
}

How to assign categories for products in magento Programmatically

I am a newbie in magento. Basically, I want to assign multiple products to multiple categories. I have followed this post and I have done the following code which is working fine:
$collection = Mage::getModel('catalog/product')->getCollection();//my coustom collection
$categorys_ids = array(1,2,3,4,5);//Array of ids etc
if ($categorys_ids != NULL && $collection->getData()!= NULL)
{
foreach ($collection as $product)
{
$categories_pd = $product->getCategoryIds();
$product->setCategoryIds(array_merge($product->getCategoryIds(),array($categorys_ids)));
$product->save();
}
}
Now, the main issue is that when I assign set category id for the products it takes a lot of time. I have 200 products and this takes up to two minutes or so, which is a lot of time.
I was wondering if there is a way that I can assign categories to a products array instead of assigning products to categories or something that can be optimized and take less time.
Here is how you can assign multiple products to a category and merge with the existing products.
The example is for one category but you can turn it into a loop to make it work for more.
$categoryId = 6;
$category = Mage::getModel('catalog/category')->setStoreId(Mage_Core_Model_App::ADMIN_STORE_ID)->load($categoryId);
//get the current products
$products = $category->getProductsPosition();
//now attach the other products.
$newProductIds = array(1,2,3,4,5);
foreach ($newProductIds as $id){
$products[$id] = 1;//you can put any other position number instead of 1.
}
//attach all the products to the category
$category->setPostedProducts($products);
//save the category.
$category->save();
If you want an even faster way of doing it you can do direct inserts in the table catalog_category_product.
Just make sure you reindex when you are done.
Here is how I did it, using some fast array management features from newer PHP versions:
<?php
require_once '../../app/Mage.php';
Mage::app();
Mage::app()->setCurrentStore(Mage::getModel('core/store')->load(Mage_Core_Model_App::ADMIN_STORE_ID));
$storeCode = Mage::app()->getStore()->getStoreId();
function addProductsToCategoryId($mergeProductIds, $categoryId, $storeCode) {
// load the $category by $categoryId
$category = Mage::getModel('catalog/category')->setStoreId($storeCode)->load($categoryId);
// build a flipped array of two merged arrays (1) array keys from flipped $mergeProductIds, (2) array keys from product_id keyed array in $category
$categoryProductIds = array_flip(array_merge(array_keys(array_flip($mergeProductIds)),array_keys($category->getProductsPosition())));
// combine array_keys from resulting merge with a matched index array filled with '0'
// THIS resets position of product within category, change this logic if desired
$categoryProductIds = array_combine(array_keys($categoryProductIds), array_fill(0, count($categoryProductIds), '0'));
$category->setPostedProducts($categoryProductIds);
$category->save();
// optional
// return $categoryProductIds;
}
// optional array of category IDs to test against for nin (not in) or in a find_in_set array test
// in the optional example line below, nin (not in) is used
$categoryIds = array(5,8,9,10,11,12,45,46);
$collectionIds = Mage::getModel('catalog/product')->getCollection()
->setStoreId($storeCode)
// optional inclusion of join for category_id
->joinField('category_id', 'catalog/category_product', 'category_id', 'product_id = entity_id', null, 'left')
// optional logic to only gather ids that are, or are not in a given categoryIds array, nin (not in) is shown in example
// ->addAttributeToFilter('category_id', array('nin' => array('finset' => $categoryIds)))
// optional line to test whether product is associated to ANY category
->addAttributeToFilter('category_id', array('null' => true))
// example qualifiers to affect gathered IDs
->addAttributeToFilter('sku', array('like' => 'M-H%'))
->addAttributeToFilter('sku', array('nlike' => '%E'))
->addAttributeToFilter('sku', array('nlike' => '%E#'))
->addAttributeToFilter('sku', array('nlike' => '%Euro'))
->addAttributeToFilter('sku', array('nlike' => '%Euro#'))
->getAllIds()
;
// if using a return value, you can set the results of this to a variable
// to perform further operations against the resulting data
addProductsToCategoryId($collectionIds, 8, $storeCode);
Please note, by default my method DOES NOT preserve any position for products within categories you had set. IT WILL set all positions back to a default of '0'.
Works beautifully. Requires a reindex afterward, fast mass adding. The code is a little complex, so explaining the code in direct context comments made more sense to me in this case.
I included a lot of optional extras in here, but they are all flagged as such and fully explained.
The following code worked for me:
include '../../app/Mage.php';
Mage::app('admin');
Mage::app()->setCurrentStore(Mage_Core_Model_App::ADMIN_STORE_ID);
$product_id = Mage::getModel("catalog/product")->getIdBySku($sku);
$product = Mage::getModel('catalog/product')->load($product_id);
$product->setCategoryIds($category_id);
$product->save();

Magento - Filter products that have a specific customer group price set (by group code, not id)

I'm looking to filter a product list by those products which have a group_price set at the product level and assigned to a specific customer group.
I was thinking it would be something along the lines of:
$products->addFieldToFilter('group_price', array(
array(
'cust_group' => 2
)
));
But it appears that group_price is not an attribute. Additionally, $_product->getGroupPrice() always returns the product's price despite if a group price is set on the product or not.
Ideally, I'd prefer to filter these by the customer group code (ie. Wholesale, Retail, etc) instead of the group id (simply for the case where someone might delete a customer group and recreate it later and the id changes).
Any thoughts?
I have a similar requirement but the problem is that iterating over a large product collection is way too slow.
The catalog_product_index_price table contains group information so you may try something like:
$productCollection = Mage::getModel('catalog/product')->getCollection();
...
$productCollection->addFinalPrice();
$productCollection->getSelect()->where(
'price_index.customer_group_id = ? AND price_index.group_price IS NOT NULL',
$customer_group_id
);
I ended up using this:
$products = Mage::getModel('catalog/product')->getResourceCollection();
$products
->addAttributeToSelect('*')
->addAttributeToFilter('visibility', array('neq' => 1));
foreach ($products as $product => $value) {
//still not sure how to get 'group_price' without running load() =(
$productModel = Mage::getModel('catalog/product')->load($value->getId());
$groupPrices = $productModel->getData('group_price');
// A helper of mine
// store the customer's groupId to compare against available group prices array below.
$currentGroupId = Mage::helper('user')->getCustomerGroup()->getId();
// remove products w/o configured group prices
if (!count($groupPrices)) {
$products->removeItemByKey($product);
}
foreach ($groupPrices as $key) {
foreach ($key as $subkey => $value) {
if ($subkey == "cust_group") {
$customerGroup = $subkey;
if ($value != $currentGroupId) {
$products->removeItemByKey($product);
}
}
}
}
};
$this->_productCollection = $products;
return $this->_productCollection;

Get users who has bought this product (MAGENTO)

Is it possible in magento to filter user based on the products they have bought?
For eg.
How can I get all the users who have bought product A from category B
mysql query like
SELECT users From table users, table products ..... WHERE user has purchased product A .
Please give some ideas, I needed to make this work.
Thanks
If you want an actual query, you can probably do something as simple as (add additional joins to get customer information from EAV):
SELECT DISTINCT o.customer_id FROM sales_flat_order_item i
INNER JOIN sales_flat_order o ON o.entity_id = i.order_id
WHERE o.customer_id IS NOT NULL
AND i.sku = 'some-product-sku'
Using Magento models, this should work for you:
<?php
require_once 'app/Mage.php';
/*
* Initialize Magento. Older versions may require Mage::app() instead.
*/
Mage::init();
/**
* Get all unique order IDs for items with a particular SKU.
*/
$orderItems = Mage::getResourceModel('sales/order_item_collection')
->addFieldToFilter('sku', 'some-product-sku')
->toArray(array('order_id'));
$orderIds = array_unique(array_map(
function($orderItem) {
return $orderItem['order_id'];
},
$orderItems['items']
));
/**
* Now get all unique customers from the orders of these items.
*/
$orderCollection = Mage::getResourceModel('sales/order_collection')
->addFieldToFilter('entity_id', array('in' => $orderIds))
->addFieldToFilter('customer_id', array('neq' => 'NULL'));
$orderCollection->getSelect()->group('customer_id');
/**
* Now get a customer collection for those customers.
*/
$customerCollection = Mage::getModel('customer/customer')->getCollection()
->addFieldToFilter('entity_id', array('in' => $orderCollection->getColumnValues('customer_id')));
/**
* Traverse the customers like any other collection.
*/
foreach ($customerCollection as $customer) {
var_dump($customer->getData());
}
It's pretty ugly though (instantiates multiple models, executes a bunch of queries under the covers), you could probably write your own model to make this -a lot- prettier.
You have to base your query on orders. If you want to do it by SQL query, you have to it by the following table:
sales_flat_quote, sales_flat_order_item to get the link between customer and product
catalog_category_product to get the link between category and product
catalog_product_entity to get the product id in function of the sku
...
Good luck
try these models
$orders = Mage::getModel('sales/order')->addAttributeToSelect('*')->getCollection();
$order_items = Mage::getResourceModel('sales/order_item_collection')
->addAttributeToSelect('sku')
->addAttributeToSelect('created_at')
->addAttributeToSelect('order_id')
->addAttributeToFilter('order_id', array('in' => $orders_ids))->load();

Resources