Assign products to category programatically in magento2

You need to get category Ids and product Id to set data: impliment this :

$this->getCategoryLinkManagement()->assignProductToCategories(
                $product->getSku(),
                $product->getCategoryIds()
            );

also impliment this function :

private function getCategoryLinkManagement()
{
    if (null === $this->categoryLinkManagement) {
        $this->categoryLinkManagement = \Magento\Framework\App\ObjectManager::getInstance()
            ->get('Magento\Catalog\Api\CategoryLinkManagementInterface');
    }
    return $this->categoryLinkManagement;
}

rest dependency you should manage for : Magento\Catalog\Api\CategoryLinkManagementInterface

intialize :

protected $categoryLinkManagement;

Direct use of \Magento\Framework\App\ObjectManager::getInstance() is not valid as per magento so you can inject it in Constructor

This answer is for below magento 2.2 versions, so please take a note


I think, this needs an updated answer which does not make use of the object manager. Also, there are some kinks involved which are not mentioned anywhere.

In your constructor, inject the CategoryLinkManagementInterface:

protected $categoryLinkManagement;

public function __construct(
    ...
    \Magento\Catalog\Api\CategoryLinkManagementInterface $categoryLinkManagementInterface,
    ...
) {
    $this->categoryLinkManagement = $categoryLinkManagementInterface;
    ...
}

Later in your code, assign categories the following way:

$product = $this->productRepository->getById(1337); // or any other way to get a product model/interface
$categoryIds = [
    42,
    606
];
$this->categoryLinkManagement->assignProductToCategories(
    $product->getSku(),
    $categoryIds
);

This will replace all previous category assignments. If you want to keep the existing category assignments, use something like this:

$categoryIds = array_unique(
    array_merge(
        $product->getCategoryIds(),
        $categoryIds
    )
);

Be aware: The link management defers the category assignment (for the product attribute) to the scheduled indexer. This means that if you make other changes to the product and save it after assignProductToCategories()

$product = $this->productRepository->save($product);

the category assignments will be gone as $product contains either null (if it was a newly created product) or only the previously assigned categories for its attribute. Also,

$product = $this->productRepository->getById($product->getId());

right after assignProductToCategories() will not help for the same reason mentioned above. Either assign categories at the latest possible point in time (when you do not save the product afterwards) or assign the attribute manually before saving again

$product->setCategoryIds($categoryIds);

If you opt to use the latter, you could probably fall back to only using setCategoryIds(). I have not tested either case (assignProductToCategories + setCategoryIds + save or setCategoryIds only + save) for performance impact, so I can not comment on that, but I think the whole circumstance was important to mention.


Assign Products To Category

 <?php
 $new_category_id = array('100','101');
 $sku = 'sku of product';

 $objectManager = \Magento\Framework\App\ObjectManager::getInstance();

 $CategoryLinkRepository = $objectManager->get('\Magento\Catalog\Api\CategoryLinkManagementInterface');
 $CategoryLinkRepository->assignProductToCategories($sku, $new_category_id);

Remove Products From Category

 <?php
 $category_id = 101;
 $sku = 'sku of product';

 $objectManager = \Magento\Framework\App\ObjectManager::getInstance();

 $CategoryLinkRepository = $objectManager->get('\Magento\Catalog\Model\CategoryLinkRepository');
 $CategoryLinkRepository->deleteByIds($category_id ,$sku);