Magento
Adobe's feature-rich e-commerce platform. Capable of handling complex B2B and B2C requirements.
CMS
Magento
Overview
Magento is Adobe's feature-rich e-commerce platform. Capable of handling complex B2B and B2C requirements, it's an enterprise solution for large-scale e-commerce sites.
Details
Magento is an open-source e-commerce platform released in 2008 and acquired by Adobe in 2018. Developed in PHP and built on the Zend Framework, it offers two editions: Community Edition (free) and Commerce Cloud (paid). Used by over 250,000 sites with a 7% market share, it provides enterprise-level features including multi-store management, B2B functionality, advanced inventory management, price management, marketing tools, and PWA (Progressive Web App) support. It employs advanced development patterns such as dependency injection (DI), plugins (interceptors), and event/observer patterns. Supporting both GraphQL and REST APIs, it enables headless commerce and omnichannel strategies. The latest PWA Studio enables mobile-first development.
Pros and Cons
Pros
- Feature-rich: Extensive enterprise-level features
- Scalability: Can handle high-traffic volumes
- B2B Support: Comprehensive B2B features including quotes and approval workflows
- Customizability: High extensibility with modular architecture
- PWA Support: Enables mobile-first development
- Internationalization: Multi-language, multi-currency, and multi-tax support
- Adobe Integration: Seamless integration with Adobe Experience Cloud
Cons
- High Complexity: Steep learning curve requires time to master
- High Resource Consumption: Requires substantial server resources
- Steep Learning Curve: Developer training requires time and cost
- Development Costs: Customization can be expensive
- Hosting Requirements: Needs high-spec servers
Key Links
- Magento Official Site
- Adobe Commerce Developer Documentation
- Magento GitHub Repository
- Magento DevDocs
- Magento Marketplace
- Magento Community
Usage Examples
Basic Magento Module Structure
// app/code/Vendor/Module/registration.php
<?php
use Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(
ComponentRegistrar::MODULE,
'Vendor_Module',
__DIR__
);
// app/code/Vendor/Module/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Vendor_Module" setup_version="1.0.0">
<sequence>
<module name="Magento_Catalog"/>
<module name="Magento_Customer"/>
</sequence>
</module>
</config>
// app/code/Vendor/Module/etc/di.xml - Dependency injection configuration
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Vendor\Module\Api\ProductInterface"
type="Vendor\Module\Model\Product" />
<type name="Vendor\Module\Model\Product">
<arguments>
<argument name="logger" xsi:type="object">Psr\Log\LoggerInterface</argument>
</arguments>
</type>
</config>
Custom Model and Repository Pattern
// Api/Data/CustomProductInterface.php
<?php
namespace Vendor\Module\Api\Data;
interface CustomProductInterface
{
const ENTITY_ID = 'entity_id';
const SKU = 'sku';
const CUSTOM_ATTRIBUTE = 'custom_attribute';
/**
* @return int|null
*/
public function getId();
/**
* @param int $id
* @return $this
*/
public function setId($id);
/**
* @return string
*/
public function getSku();
/**
* @param string $sku
* @return $this
*/
public function setSku($sku);
/**
* @return string|null
*/
public function getCustomAttribute();
/**
* @param string $customAttribute
* @return $this
*/
public function setCustomAttribute($customAttribute);
}
// Model/CustomProduct.php
<?php
namespace Vendor\Module\Model;
use Magento\Framework\Model\AbstractModel;
use Vendor\Module\Api\Data\CustomProductInterface;
class CustomProduct extends AbstractModel implements CustomProductInterface
{
protected function _construct()
{
$this->_init(\Vendor\Module\Model\ResourceModel\CustomProduct::class);
}
public function getSku()
{
return $this->getData(self::SKU);
}
public function setSku($sku)
{
return $this->setData(self::SKU, $sku);
}
public function getCustomAttribute()
{
return $this->getData(self::CUSTOM_ATTRIBUTE);
}
public function setCustomAttribute($customAttribute)
{
return $this->setData(self::CUSTOM_ATTRIBUTE, $customAttribute);
}
}
// Api/CustomProductRepositoryInterface.php
<?php
namespace Vendor\Module\Api;
use Vendor\Module\Api\Data\CustomProductInterface;
use Magento\Framework\Api\SearchCriteriaInterface;
interface CustomProductRepositoryInterface
{
/**
* @param int $id
* @return \Vendor\Module\Api\Data\CustomProductInterface
* @throws \Magento\Framework\Exception\NoSuchEntityException
*/
public function getById($id);
/**
* @param \Vendor\Module\Api\Data\CustomProductInterface $product
* @return \Vendor\Module\Api\Data\CustomProductInterface
* @throws \Magento\Framework\Exception\CouldNotSaveException
*/
public function save(CustomProductInterface $product);
/**
* @param \Magento\Framework\Api\SearchCriteriaInterface $searchCriteria
* @return \Vendor\Module\Api\Data\CustomProductSearchResultsInterface
*/
public function getList(SearchCriteriaInterface $searchCriteria);
}
Plugin (Interceptor) Implementation
// etc/di.xml - Plugin configuration
<type name="Magento\Catalog\Api\ProductRepositoryInterface">
<plugin name="vendor_module_product_save_plugin"
type="Vendor\Module\Plugin\ProductSavePlugin"
sortOrder="10" />
</type>
// Plugin/ProductSavePlugin.php
<?php
namespace Vendor\Module\Plugin;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Catalog\Api\Data\ProductInterface;
use Psr\Log\LoggerInterface;
class ProductSavePlugin
{
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
/**
* Before plugin - Pre-execution processing
*/
public function beforeSave(
ProductRepositoryInterface $subject,
ProductInterface $product,
$saveOptions = false
) {
$this->logger->info('Before product save: ' . $product->getSku());
// Automatic price adjustment (example)
if ($product->getPrice() < 100) {
$product->setPrice($product->getPrice() * 1.1);
}
return [$product, $saveOptions];
}
/**
* After plugin - Post-execution processing
*/
public function afterSave(
ProductRepositoryInterface $subject,
ProductInterface $result,
ProductInterface $product,
$saveOptions = false
) {
$this->logger->info('After product save: ' . $result->getId());
// Custom processing (cache clearing, notifications, etc.)
$this->clearCustomCache($result->getId());
return $result;
}
/**
* Around plugin - Wrap around processing
*/
public function aroundDelete(
ProductRepositoryInterface $subject,
\Closure $proceed,
ProductInterface $product
) {
$this->logger->info('Starting product deletion: ' . $product->getSku());
// Pre-deletion validation
if ($this->canDelete($product)) {
$result = $proceed($product);
$this->logger->info('Product deletion completed');
return $result;
}
throw new \Magento\Framework\Exception\LocalizedException(
__('This product cannot be deleted.')
);
}
}
GraphQL API Implementation
// etc/schema.graphqls
type Query {
customProducts(
filter: CustomProductFilterInput
pageSize: Int = 20
currentPage: Int = 1
): CustomProducts @resolver(class: "Vendor\\Module\\Model\\Resolver\\CustomProducts")
}
type CustomProducts {
items: [CustomProduct]
page_info: SearchResultPageInfo
total_count: Int
}
type CustomProduct {
id: Int
sku: String
name: String
price: Float
custom_attribute: String
categories: [CategoryInterface]
}
input CustomProductFilterInput {
sku: FilterTypeInput
name: FilterTypeInput
price: FilterRangeTypeInput
custom_attribute: FilterTypeInput
}
// Model/Resolver/CustomProducts.php
<?php
namespace Vendor\Module\Model\Resolver;
use Magento\Framework\GraphQl\Config\Element\Field;
use Magento\Framework\GraphQl\Query\ResolverInterface;
use Magento\Framework\GraphQl\Schema\Type\ResolveInfo;
use Magento\Framework\GraphQl\Query\Resolver\Value;
use Magento\Framework\GraphQl\Query\Resolver\ValueFactory;
class CustomProducts implements ResolverInterface
{
private $valueFactory;
private $customProductRepository;
public function __construct(
ValueFactory $valueFactory,
\Vendor\Module\Api\CustomProductRepositoryInterface $customProductRepository
) {
$this->valueFactory = $valueFactory;
$this->customProductRepository = $customProductRepository;
}
public function resolve(
Field $field,
$context,
ResolveInfo $info,
array $value = null,
array $args = null
) {
$searchCriteria = $this->buildSearchCriteria($args);
$searchResult = $this->customProductRepository->getList($searchCriteria);
$products = [];
foreach ($searchResult->getItems() as $product) {
$products[] = [
'id' => $product->getId(),
'sku' => $product->getSku(),
'name' => $product->getName(),
'price' => $product->getPrice(),
'custom_attribute' => $product->getCustomAttribute()
];
}
return [
'items' => $products,
'page_info' => [
'page_size' => $searchCriteria->getPageSize(),
'current_page' => $searchCriteria->getCurrentPage(),
'total_pages' => ceil($searchResult->getTotalCount() / $searchCriteria->getPageSize())
],
'total_count' => $searchResult->getTotalCount()
];
}
}
Event and Observer Pattern
// etc/events.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="checkout_cart_product_add_after">
<observer name="vendor_module_cart_add_observer"
instance="Vendor\Module\Observer\CartAddObserver" />
</event>
<event name="sales_order_place_after">
<observer name="vendor_module_order_place_observer"
instance="Vendor\Module\Observer\OrderPlaceObserver" />
</event>
</config>
// Observer/CartAddObserver.php
<?php
namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;
use Psr\Log\LoggerInterface;
class CartAddObserver implements ObserverInterface
{
private $logger;
private $messageManager;
public function __construct(
LoggerInterface $logger,
\Magento\Framework\Message\ManagerInterface $messageManager
) {
$this->logger = $logger;
$this->messageManager = $messageManager;
}
public function execute(Observer $observer)
{
$item = $observer->getEvent()->getQuoteItem();
$product = $observer->getEvent()->getProduct();
// Custom logic (e.g., inventory check, price adjustment, etc.)
if ($item->getQty() > 10) {
// Apply bulk purchase discount
$price = $item->getProduct()->getPrice();
$discountedPrice = $price * 0.9; // 10% discount
$item->setCustomPrice($discountedPrice);
$item->setOriginalCustomPrice($discountedPrice);
$item->getProduct()->setIsSuperMode(true);
$this->messageManager->addSuccessMessage(
__('Bulk purchase discount applied!')
);
}
$this->logger->info(
'Product added to cart: ' . $product->getSku() . ', Quantity: ' . $item->getQty()
);
}
}
B2B Feature Implementation Example
// B2B Quote Request
<?php
namespace Vendor\Module\Model;
use Magento\Framework\Model\AbstractModel;
class QuoteRequest extends AbstractModel
{
protected function _construct()
{
$this->_init(\Vendor\Module\Model\ResourceModel\QuoteRequest::class);
}
/**
* Create quote request
*/
public function createQuoteRequest($customerId, $items)
{
$this->setCustomerId($customerId);
$this->setStatus('pending');
$this->setCreatedAt(date('Y-m-d H:i:s'));
// Save product information
$itemsData = [];
foreach ($items as $item) {
$itemsData[] = [
'product_id' => $item['product_id'],
'qty' => $item['qty'],
'requested_price' => $item['requested_price'] ?? null
];
}
$this->setItems(json_encode($itemsData));
return $this->save();
}
/**
* Approve quote
*/
public function approveQuote($adminUserId, $approvedPrices)
{
$this->setStatus('approved');
$this->setApprovedBy($adminUserId);
$this->setApprovedAt(date('Y-m-d H:i:s'));
$this->setApprovedPrices(json_encode($approvedPrices));
// Post-approval processing (email sending, order creation, etc.)
$this->_eventManager->dispatch('quote_request_approved', ['quote' => $this]);
return $this->save();
}
}
// Controller/Adminhtml/Quote/Approve.php
<?php
namespace Vendor\Module\Controller\Adminhtml\Quote;
use Magento\Backend\App\Action;
use Magento\Backend\App\Action\Context;
class Approve extends Action
{
private $quoteRequestFactory;
private $jsonHelper;
public function __construct(
Context $context,
\Vendor\Module\Model\QuoteRequestFactory $quoteRequestFactory,
\Magento\Framework\Json\Helper\Data $jsonHelper
) {
parent::__construct($context);
$this->quoteRequestFactory = $quoteRequestFactory;
$this->jsonHelper = $jsonHelper;
}
public function execute()
{
$quoteId = $this->getRequest()->getParam('quote_id');
$approvedPrices = $this->jsonHelper->jsonDecode(
$this->getRequest()->getParam('approved_prices')
);
try {
$quote = $this->quoteRequestFactory->create()->load($quoteId);
$quote->approveQuote(
$this->_auth->getUser()->getId(),
$approvedPrices
);
$this->messageManager->addSuccessMessage(__('Quote has been approved.'));
} catch (\Exception $e) {
$this->messageManager->addErrorMessage(__('Error occurred: %1', $e->getMessage()));
}
return $this->_redirect('*/*/index');
}
}
These examples demonstrate Magento's major development patterns. From module development, dependency injection, plugin system, GraphQL API, event/observer patterns, to B2B features, they cover the functionality needed for enterprise-level e-commerce site development.