Magento 2 Product Collection Data

Magento developers may find themselves with a task of working with collection class whether that be trying to add a product programmatically, or set an attribute value against it. Here is a simple overview how to manipulate Magento 2 product collection data.

Magento has its very own ProductFactory class to obtain the product collection. This can be used in a block class such as the example below.

// app/code/[Vendor]/[Module]/Block/Template.php
<?php
namespace [Vendor]\[Module]\Block;

use Magento\Framework\View\Element\Template as MagentoTemplate;

class Template extends MagentoTemplate
{
    protected $_productFactory;

    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Catalog\Model\ProductFactory $productFactory
    )
    {
        $this->_productFactory = $productFactory;
        parent::__construct($context);
    }

    public function getCollection()
    {
        return count($this->_productFactory->create()->getCollection());
    }

}

Installing the Magento sample data, the getCollection() method returns 2046 products.

To load and save individual products in Magento 1, the load() and save() methods of the Mage_Core_Model_Abstract class.

Within Magento 2, we have a Magento\Catalog\Api\ProductRepositoryInterface interface. The interface contains the following methods.

<?php

namespace Magento\Catalog\Api;

interface ProductRepositoryInterface
{
    /**
     * Create product
     *
     * @param \Magento\Catalog\Api\Data\ProductInterface $product
     * @param bool $saveOptions
     * @return \Magento\Catalog\Api\Data\ProductInterface
     * @throws \Magento\Framework\Exception\InputException
     * @throws \Magento\Framework\Exception\StateException
     * @throws \Magento\Framework\Exception\CouldNotSaveException
     */
    public function save(\Magento\Catalog\Api\Data\ProductInterface $product, $saveOptions = false);

    /**
     * Get info about product by product SKU
     *
     * @param string $sku
     * @param bool $editMode
     * @param int|null $storeId
     * @param bool $forceReload
     * @return \Magento\Catalog\Api\Data\ProductInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function get($sku, $editMode = false, $storeId = null, $forceReload = false);

    /**
     * Get info about product by product id
     *
     * @param int $productId
     * @param bool $editMode
     * @param int|null $storeId
     * @param bool $forceReload
     * @return \Magento\Catalog\Api\Data\ProductInterface
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     */
    public function getById($productId, $editMode = false, $storeId = null, $forceReload = false);

    /**
     * Delete product
     *
     * @param \Magento\Catalog\Api\Data\ProductInterface $product
     * @return bool Will returned True if deleted
     * @throws \Magento\Framework\Exception\StateException
     */
    public function delete(\Magento\Catalog\Api\Data\ProductInterface $product);

    /**
     * @param string $sku
     * @return bool Will returned True if deleted
     * @throws \Magento\Framework\Exception\NoSuchEntityException
     * @throws \Magento\Framework\Exception\StateException
     */
    public function deleteById($sku);

    /**
     * Get product list
     *
     * @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
     * @return \Magento\Catalog\Api\Data\ProductSearchResultsInterface
     */
    public function getList(\Magento\Framework\Api\SearchCriteriaInterface $searchCriteria);
}

Within the collection retrieved in the module’s block class, you can inject the interface into the block’s constructor and use the getById() and save() methods to load and save a product.

<?php

....

class Template extends MagentoTemplate
{
    protected $_productFactory;

    protected $_productRepository;

    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Catalog\Model\ProductFactory $productFactory,
        \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
    )
    {
        $this->_productFactory = $productFactory;
        $this->_productRepository = $productRepository;
        parent::__construct($context);
    }

    public function getCollection()
    {
        $collection = $this->_productFactory->create()->getCollection();

        foreach ($collection as $product) {
            $product = $this->_productRepository->getById($product->getId());
            echo $product->getName(); exit(); // Joust Duffle Bag
        }
    }
}

We could change the product name by using the setName() method.

The ‘get’ and ‘set’ methods in Magento 1 were from the Varien_Object class via PHP’s __call() magic method.

Within Magento 2, the Magento\Framework\DataObject class is used and contains the __call() method.

public function getCollection()
{
    $collection = $this->_productFactory->create()->getCollection();

    foreach ($collection as $product) {

        // Load the product
        $product = $this->_productRepository->getById($product->getId());
        echo $product->getName(); // Joust Duffle Bag

        // Set the product name
        $product->setName($product->getName() . ' and more!');

        // Save the product
        $this->_productRepository->save($product);
        echo $product->getName(); exit(); // Joust Duffle Bag and more!
    }
}

Assuming you don’t want to load all 2046 products in the collection or only select certain attributes, you can use filters to reduce the collection size.

The following code:

$collection = $this->_productFactory->create()->getCollection()
    ->addAttributeToSelect('*');
echo $collection->getSelect();

Will print out this query:

SELECT `e`.* FROM `catalog_product_entity` AS `e`

This means that when you loop around the product collection, you can print out all of the attributes of a product.

$collection = $this->_productFactory->create()->getCollection()
    ->addAttributeToSelect('*');

foreach ($collection as $product) {
    echo $product->getId(); // 2
    echo $product->getSku(); // 24-MB04
    echo $product->getName(); exit(); // Strive Shoulder Pack
}

However, if we specify no attributes or only certain attributes within the addAttributeToSelect() method, the data will not be included within the collection.

$collection = $this->_productFactory->create()->getCollection()
    ->addAttributeToSelect(array());

foreach ($collection as $product) {
    echo $product->getId(); // 2
    echo $product->getSku(); // 24-MB04
    echo $product->getName(); exit(); // Nothing printed!
}

We can also use the addAttributeToFilter() and addFieldToFilter() methods to filter number of records within the collection.

$collection = $this->_productFactory->create()->getCollection();
echo count($collection); // 2046

$collection2 = $this->_productFactory->create()->getCollection()
    ->addAttributeToFilter('sku', ['eq' => '24-MB04']);
echo count($collection2); // 1

Note: This article is based on Magento CE version 2.1.