Site icon dev98

Get PDF files by Magento Webapi

Magento 2 comes with a modern REST interface. One of the advantages of the REST interface is that it can handle multiple response types. A client can request data from the server with a list of acceptable response formats. Out of the box Magento 2 supports two types. It comes with JSON and XML support.

You can test it with a simple call to your local store.

curl -X GET --header "Accept: application/json" "http://<store-baseurl>/rest/default/V1/categories"

If you omit the accept header the server will return JSON as default. Let’s change the accept header to “application/json”.

curl -X GET --header "Accept: application/xml" "http://<store-baseurl>/rest/default/V1/categories"

Now you should see a well formed XML document:

Extend the list of acceptable mime types

Magento 2 can handle JSON and XML. What if we want to add an alternative return format? The bad news… it cannot handle it by default. The good news… You can add the support by yourself.
The response is generated internally by response renderers which must implement the RendererInterface.

interface RendererInterface
{
    /**
     * Render content in a certain format.
     *
     * @param object|array|int|string|bool|float|null $data
     * @return string
     */
    public function render($data);

    /**
     * Get MIME type generated by renderer.
     *
     * @return string
     */
    public function getMimeType();
}

The interface is really simple. A mime type must be defined. In our example we intend to handle the mime type “application/pdf”. The main idea of this demo is to return an existing invoice directly as printable PDF document instead of the JSON or XML data.

## Request Workflow

A request by a browser or a HTTP client (i.e. curl) is sent to the server. The request is dispatched by a special REST FrontController. The FrontController executes some business logic and passes the generated output data to a response object. The response object is generated by a RendererFactory which creates a renderer object based on the mime types of the request accept header.

Webapi Response Rendering Overview

If we want to add our own renderer for a specific mime type we can use the Magento 2 Dependency Injection for that. The RendererFactory gets a list of available renderers via di.xml. The next thing we need to do is to create an own module and to add the di.xml in our module with following content:

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <type name="Magento\Framework\Webapi\Rest\Response\RendererFactory">
        <arguments>
            <argument name="renders" xsi:type="array">
                <item name="application_pdf" xsi:type="array">
                    <item name="type" xsi:type="string">application/pdf</item>
                    <item name="model" xsi:type="string">\N98\WebapiRestPdf\Response\Renderer\PdfRenderer</item>
                </item>
            </argument>
        </arguments>
    </type>

</config>

Now our response renderer must be created. It must implement the renderer interface. Firstly we add the getMimeType method and let them return the value “application/pdf”. Secondly we implement the render method which returns the PDF data. In our example we only support the REST URL “/V1/invoices”.

<?php
namespace N98\WebapiRestPdf\Response\Renderer;

use Magento\Framework\Webapi\Exception;
use Magento\Framework\Webapi\Rest\Request;
use Magento\Framework\Webapi\Rest\Response\RendererInterface;
use Magento\Sales\Api\InvoiceRepositoryInterface;
use N98\WebapiRestPdf\Service\InvoicePdfGeneratorService;

class PdfRenderer implements RendererInterface
{
    /**
     * @var \Magento\Framework\Webapi\Rest\Request
     */
    private $request;

    /**
     * @var \N98\WebapiRestPdf\Service\InvoicePdfGeneratorService
     */
    private $invoicePdfGeneratorService;

    /**
     * @var \Magento\Sales\Api\InvoiceRepositoryInterface
     */
    private $invoiceRepository;

    /**
     * Pdf constructor.
     * @param \Magento\Framework\Webapi\Rest\Request $request
     * @param \N98\WebapiRestPdf\Service\InvoicePdfGeneratorService $invoicePdfGeneratorService
     * @param \Magento\Sales\Api\InvoiceRepositoryInterface $invoiceRepository
     */
    public function __construct(
        Request $request,
        InvoicePdfGeneratorService $invoicePdfGeneratorService,
        InvoiceRepositoryInterface $invoiceRepository
    ) {
        $this->request = $request;
        $this->invoicePdfGeneratorService = $invoicePdfGeneratorService;
        $this->invoiceRepository = $invoiceRepository;
    }

    /**
     * Render content in a certain format.
     *
     * @param object|array|int|string|bool|float|null $data
     * @return string
     * @throws \Magento\Framework\Webapi\Exception
     */
    public function render($data)
    {
        if (!strstr($this->request->getPathInfo(), '/V1/invoices')) {
            throw new Exception(__('PDF rendering is not supported for this URI'));
        }

        if (isset($data['entity_id'])) {
            $invoice = $this->invoiceRepository->get($data['entity_id']);
            $pdf = $this->invoicePdfGeneratorService->execute($invoice);

            return $pdf->render();
        }
     
        return null;
    }

    /**
     * Get MIME type generated by renderer.
     *
     * @return string
     */
    public function getMimeType()
    {
        return 'application/pdf';
    }
}

For a better software architecture we place the PDF generation code in an own InvoicePdfGeneratorService class.

<?php
namespace N98\WebapiRestPdf\Service;

use Magento\Sales\Api\Data\InvoiceInterface;

class InvoicePdfGeneratorService
{
    /**
     * @var \Magento\Sales\Model\Order\Pdf\Invoice
     */
    private $invoicePdf;

    /**
     * PdfGeneratorService constructor.
     * @param \Magento\Sales\Model\Order\Pdf\Invoice $invoicePdf
     */
    public function __construct(\Magento\Sales\Model\Order\Pdf\Invoice $invoicePdf)
    {
        $this->invoicePdf = $invoicePdf;
    }

    /**
     * @param \Magento\Sales\Api\Data\InvoiceInterface $invoice
     * @return \Zend_Pdf
     */
    public function execute(InvoiceInterface $invoice)
    {
        return $this->invoicePdf->getPdf([$invoice]);
    }
}

That’s all we need to code. As you can see Magento 2 is very extendable. If you have ideas for additional formats, create your own renderer.

Testing

Before we start a test of the new functionality, we need an invoice in the database. Simply create a new order in your shop and create an invoice for it in the Magento admin.

To test the new renderer we can use CURL again. To fetch an invoice you need API credentials. The simplest way to do that, is to get an “Access Token”.

Have a look in the documentation to see how you can obtain an access token: http://devdocs.magento.com/guides/v2.1/get-started/authentication/gs-authentication-token.html

On my local machine this CURL command looks like this:

curl -o invoice.pdf -X GET \
--header "Accept: application/pdf" \
--header "Authorization: Bearer 0b4sdr02nis73md8qsg3y3b6uk4hi5k4" \
"http://magento.dev/rest/default/V1/invoices/1"

This command calls the Magento Shop, which should now return the content of a PDF file. The PDF content is written to the file “invoice.pdf”.

My PDF looks like this:

Great! We can now get PDF invoices by the REST API!

Conclusion

Magento 2 can be extended in an easy way. If you like to play a little bit with the code of this blog post, checkout our demo module on Github.

https://github.com/netz98/N98_WebapiRestPdf

Exit mobile version