I've created a module to notify the customer when a custom carrier has been added. I've used an observer on the sales_order_shipment_save_after event but the tracking info and items ordered are empty in the shipment email. I'm guessing I need to load the tracking info and order items into the $shipment object?



namespace Example\SendShipmentTracking\Observer;

use Example\SendShipmentTracking\Helper\Data;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Serialize\Serializer\Json;
use Magento\Sales\Model\Order\Shipment\Notifier;
use Psr\Log\LoggerInterface;

class SendShipmentTrackingProcessor implements ObserverInterface
     * @var LoggerInterface
    protected LoggerInterface $logger;

     * @var Json
    protected Json $jsonHelper;

     * @var Data
    protected Data $helperData;

     * @var Notifier
    protected Notifier $shipmentNotifier;

     * @param LoggerInterface $logger
     * @param Json $jsonHelper
     * @param Data $helperData
     * @param Notifier $shipmentNotifier
    public function __construct(
        LoggerInterface $logger,
        Json $jsonHelper,
        Data $helperData,
        Notifier $shipmentNotifier
    ) {
        $this->logger = $logger;
        $this->jsonHelper = $jsonHelper;
        $this->helperData = $helperData;
        $this->shipmentNotifier = $shipmentNotifier;

     * @param Observer $observer
     * @return void
    public function execute(Observer $observer)
        if ($this->helperData->isEnabled()) {
            try {
                $shipment = $observer->getEvent()->getShipment();

                if ($shipment && $this->hasShipmentTracking($shipment)) {
                    $order = $shipment->getOrder();
                    $this->logger->debug('shipment data ' . $this->jsonHelper->serialize($shipment->getData()));
                    $this->shipmentNotifier->notify($order, $shipment);
            } catch (\Exception $e) {

     * @param $shipment
     * @return bool
    private function hasShipmentTracking($shipment): bool
        $tracksCollection = $shipment->getTracksCollection();
        $trackingNumber = [];

        foreach ($tracksCollection->getItems() as $track) {
            if ($track->isCustom()) {
                $trackTile = $track->getTitle();
                $carrierServices = $this->helperData->getCarriers();

                if (in_array($trackTile, $carrierServices) && $track->getTrackNumber()) {
                    $trackingNumber[] = $track->getTrackNumber();

        return count($trackingNumber) > 0;

1 Answer 1


After get shipment by this line code:

$shipment = $observer->getEvent()->getShipment();

You can get items of shipment by using method:

$shipment->getItems() // return record in table `sales_shipment_item`

and get tracks of this shipment by using method:

  • With using these methods $shipment->getItems() and $shipment->getAllTracks() I get empty arrays? Commented May 23, 2023 at 8:21
  • I think you should comment code of this observer and check default flow create shipment first. After create new shipment, check information of that new shipment. If everything working right, it should have data (you can check in shipment detail page admin or table sales_shipment_item). If not, maybe the reason you can not get item and get all tracks because your another code.
    – Shou
    Commented May 23, 2023 at 8:43

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.