Skip to content
intermediate Phase 43 · Catalog Features

Catalog Collections

ProductCollection, CategoryCollection, filtering, joins, and performance optimization

45m
0 problems
Topic Progress 0%

Collection Basics

Product Collection

// Get product collection
$collection = $objectManager->get(
    \Magento\Catalog\Model\ResourceModel\Product\Collection::class
);

// Select specific attributes
$collection->addAttributeToSelect(['name', 'price', 'sku', 'image']);

// Filter by attribute
$collection->addFieldToFilter('status', 1);
$collection->addFieldToFilter('visibility', ['in' => [2, 4]]);

// Sort
$collection->setOrder('price', 'ASC');

// Paginate
$collection->setPageSize(20)->setCurPage(1);

// Load
$collection->load();

// Iterate
foreach ($collection as $product) {
    echo $product->getName();
    echo $product->getPrice();
}

Category Collection

// Get category collection
$collection = $objectManager->get(
    \Magento\Catalog\Model\ResourceModel\Category\Collection::class
);

// Filter active categories
$collection->addFieldToFilter('is_active', 1);

// Filter by level
$collection->addFieldToFilter('level', ['gteq' => 1]);
$collection->addFieldToFilter('level', ['lteq' => 3]);

// Add product count
$collection->addCountToSelect();

// Sort by position
$collection->setOrder('position', 'ASC');

foreach ($collection as $category) {
    echo $category->getName();
    echo $category->getProductCount();
}

Filtering and Sorting

Filter Conditions

// Simple filter
$collection->addFieldToFilter('status', 1);

// In filter
$collection->addFieldToFilter('entity_id', ['in' => [1, 2, 3]]);

// Range filter
$collection->addFieldToFilter('price', ['gteq' => 10]);
$collection->addFieldToFilter('price', ['lteq' => 50]);
$collection->addFieldToFilter('price', ['from' => 10, 'to' => 50]);

// Like filter
$collection->addFieldToFilter('name', ['like' => '%T-Shirt%']);

// Null filter
$collection->addFieldToFilter('special_price', ['null' => true]);

// Not null filter
$collection->addFieldToFilter('special_price', ['notnull' => true]);

// OR conditions
$collection->addFieldToFilter([
    ['attribute' => 'status', 'eq' => 1],
    ['attribute' => 'visibility', 'eq' => 4]
]);

Sorting

// Sort by attribute
$collection->setOrder('price', 'ASC');
$collection->setOrder('name', 'DESC');

// Multiple sort
$collection->setOrder('position', 'ASC');
$collection->setOrder('price', 'ASC');

// Sort by raw SQL
$collection->getSelect()->order(new \Zend_Db_Expr('RAND()')); // Random

Pagination

// Set page size
$collection->setPageSize(20);

// Set current page
$collection->setCurPage(1);

// Get total count
$count = $collection->getSize(); // or count($collection)

// Get last page number
$lastPage = $collection->getLastPageNumber();

// Check if page exists
if ($collection->getCurPage() <= $collection->getLastPageNumber()) {
    // Load page
}

Joins and Custom Queries

Joining Tables

// Join with another table
$collection->getSelect()->join(
    ['additional' => 'additional_table'],
    'additional.entity_id = e.entity_id',
    ['additional_column']
);

// Left join
$collection->getSelect()->joinLeft(
    ['additional' => 'additional_table'],
    'additional.entity_id = e.entity_id',
    ['additional_column']
);

// Join with alias
$collection->getSelect()->join(
    ['inv' => $collection->getResource()->getTable('cataloginventory_stock_item')],
    'inv.product_id = e.entity_id',
    ['qty' => 'inv.qty']
);

Custom WHERE Conditions

// Add raw WHERE
$collection->getSelect()->where('e.entity_id > ?', 100);

// Add WHERE with AND
$collection->getSelect()->where('e.status = ?', 1);
$collection->getSelect()->where('e.price > ?', 10);

// Add WHERE with OR
$collection->getSelect()->where('e.status = ? OR e.visibility = ?', [1, 4]);

// Add HAVING
$collection->getSelect()->having('COUNT(*) > ?', 5);

Custom SELECT Columns

// Add columns
$collection->getSelect()->columns([
    'entity_id',
    'name',
    'price',
    'IF(special_price IS NOT NULL, special_price, price) AS final_price'
]);

// Use expressions
$collection->getSelect()->columns([
    'price_with_tax' => new \Zend_Db_Expr('price * 1.2')
]);

// Aggregate functions
$collection->getSelect()->columns([
    'avg_price' => new \Zend_Db_Expr('AVG(price)')
]);

Query Building

// Get SQL query
$sql = $collection->getSelect()->__toString();

// Debug query
error_log($sql);

// Count query
$countSql = 'SELECT COUNT(*) FROM (' . $sql . ') AS count_table';

Collection Performance

Performance Optimization

// 1. Select only needed attributes
$collection->addAttributeToSelect(['name', 'price', 'sku']);
// Avoid: addAttributeToSelect('*')

// 2. Use flat table for listing
$flatCollection = $objectManager->get(
    \Magento\Catalog\Model\ResourceModel\Product\Flat\Collection::class
);

// 3. Limit result set
$collection->setPageSize(20)->setCurPage(1);

// 4. Use indexes
$collection->addAttributeToFilter('status', 1); // Uses index

// 5. Avoid N+1
// BAD: foreach ($collection as $product) { $product->getName(); }
// GOOD: Load attributes in collection

// 6. Clear when done
$collection->clear();

Monitoring Queries

// Enable query logging
$profiler = $objectManager->get(
    \Magento\Framework\Profiler::class
);
$profiler->start('catalog_collection');

$collection->load();

$profiler->stop('catalog_collection');

// Log queries
$logger = $objectManager->get(
    \Psr\Log\LoggerInterface::class
);
$logger->info($collection->getSelect()->__toString());

Common Patterns

// Get products in category with price filter
$collection = $productCollection->create();
$collection->addAttributeToSelect(['name', 'price', 'image'])
    ->addFieldToFilter('status', 1)
    ->addFieldToFilter('visibility', ['in' => [2, 4]])
    ->joinField(
        'price',
        'catalog_product_index_price',
        'price',
        'entity_id=entity_id',
        ['customer_group_id' => 0, 'website_id' => 1]
    )
    ->addFieldToFilter('price', ['gteq' => 10])
    ->addFieldToFilter('price', ['lteq' => 50])
    ->setOrder('price', 'ASC')
    ->setPageSize(20);

// Get product count per category
$category->getProductCount();

// Get categories for product
$product->getCategoryIds();

Quiz

1. What method loads a collection?

Question 1 options

2. How do you filter by multiple values?

Question 2 options

3. How do you get total count without loading?

Question 3 options

Flashcards

Question

What is a collection?

Answer

A set of entities loaded from database with filtering, sorting, and pagination

Question

How to filter a collection?

Answer

addFieldToFilter('attribute', value) or addAttributeToFilter()

Question

How to get collection count?

Answer

$collection->getSize() or count($collection)

Question

How to paginate?

Answer

$collection->setPageSize(20)->setCurPage(1)

Question

How to join tables?

Answer

$collection->getSelect()->join(['alias' => 'table'], 'condition', ['columns'])

Revision Notes

Key Takeaways

  • 1. Collections provide filtering, sorting, and pagination for entity sets
  • 2. Use addAttributeToSelect() to load only needed attributes
  • 3. Filter with addFieldToFilter() using various conditions
  • 4. Join tables with getSelect()->join() or joinLeft()
  • 5. Optimize: select specific attributes, use flat tables, limit results

Interview Tips

  • Demonstrate filtering and sorting on collections
  • Explain performance optimization techniques
  • Describe joining tables in collections

Cheat Sheet

Collection Methods:
  addAttributeToSelect(['attr1', 'attr2'])
  addFieldToFilter('attr', value)
  setOrder('attr', 'ASC'|'DESC')
  setPageSize(N)->setCurPage(P)
  getSize() → Total count
  load() → Execute query

Filter Conditions:
  eq, neq, in, nin, gt, gteq, lt, lteq
  like, null, notnull, from/to

Joins:
  join(['alias' => 'table'], 'condition', ['cols'])
  joinLeft(['alias' => 'table'], 'condition', ['cols'])

Debug:
  $collection->getSelect()->__toString()