<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: HergenD</title>
    <description>The latest articles on DEV Community by HergenD (@hergend).</description>
    <link>https://dev.to/hergend</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F471258%2F8a1b816f-eda9-4967-b050-271e4024b658.jpeg</url>
      <title>DEV Community: HergenD</title>
      <link>https://dev.to/hergend</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hergend"/>
    <language>en</language>
    <item>
      <title>Processing Identity Documents in Laravel</title>
      <dc:creator>HergenD</dc:creator>
      <pubDate>Sun, 02 May 2021 12:45:29 +0000</pubDate>
      <link>https://dev.to/hergend/processing-identity-documents-in-laravel-4g1p</link>
      <guid>https://dev.to/hergend/processing-identity-documents-in-laravel-4g1p</guid>
      <description>&lt;p&gt;If you work in any kind of business that require you to verify a users identity, you might have noticed that most solutions can be quite pricey. In this blog post I'll go over the solution  &lt;a href="https://github.com/365Werk"&gt;we&lt;/a&gt; came up with that makes this process completely free (depending on your configuration) and integrated in your own application.&lt;/p&gt;

&lt;p&gt;I'll also go over how some parts of it work, how you can use this solution &lt;strong&gt;very easily&lt;/strong&gt; to handle document processing for your use cases and a cost comparison.&lt;/p&gt;

&lt;p&gt;If you however wish to skip some explanation and go straight to implementation, take a look at the package  &lt;a href="https://github.com/365Werk/identitydocuments"&gt;here&lt;/a&gt;, which has a readme that should be sufficient to get you started.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Short Introduction to Identity Documents
&lt;/h2&gt;

&lt;p&gt;What helps us most in processing documents is that most countries in the world issue Machine Readable Travel Documents (MRTD) that follow standards set by the &lt;a href="https://en.wikipedia.org/wiki/ICAO"&gt;ICAO&lt;/a&gt;. If you wish to read more about the specifications you can do so  &lt;a href="https://www.icao.int/publications/documents/9303_p1_cons_en.pdf"&gt;here&lt;/a&gt;, but what is most important for us to know is that every MRTD contains a Machine Readable Zone (MRZ). The format of the MRZ depends on the document's size of which there are five. What all of the MRZ have in common is that they are specifically designed to provide what we most need, a part of the document that is easily readable for machines (alpha A-Z, numeric 0-9 and &lt;em&gt;filler&lt;/em&gt; &amp;lt; only) and contains the most important information about the document. A few things that the MRZ will include are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document holder's name&lt;/li&gt;
&lt;li&gt;Document holder's nationality&lt;/li&gt;
&lt;li&gt;Document holder's date of birth&lt;/li&gt;
&lt;li&gt;Document holder's sex&lt;/li&gt;
&lt;li&gt;Document holder's personal number (TD3 only)&lt;/li&gt;
&lt;li&gt;Document number &lt;/li&gt;
&lt;li&gt;Issuing country&lt;/li&gt;
&lt;li&gt;Document expiry date&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another important part of the MRZ is the existence of &lt;em&gt;check digits&lt;/em&gt;. These digits exist to mathematically verify the information read from the MRZ. You can read more about how the check digits work  &lt;a href="https://www.icao.int/publications/Documents/9303_p3_cons_en.pdf"&gt;here&lt;/a&gt; (page 20), but for us it's just important to know that they exist and that we can verify data parsed from the MRZ.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding and Parsing the MRZ
&lt;/h2&gt;

&lt;p&gt;I'll start off by saying that there are multiple ways to approach the issue of extracting information from an image reliably. However, my main focus was to make this solution both &lt;strong&gt;easy&lt;/strong&gt; to use and &lt;strong&gt;flexible&lt;/strong&gt;. This rules out any machine vision that would specifically detect the MRZ through AI. Instead, I decided to create an algorithm that can detect the MRZ in text. This means that you can use any OCR API or solution that you prefer (more about that later), and simply let the package do the rest of the work.&lt;/p&gt;

&lt;p&gt;So then how can we detect an MRZ in text? Let's start with what we know about MRZs. First of all, MRZ start with a document key. This key can be either P, I, A, C, or V, and indicate the type of document, where P is a passport, V is a VISA and the remaining keys indicate an official travel document (usually identity cards). Finding one of these keys will give us a starting point to find the complete MRZ.&lt;/p&gt;

&lt;p&gt;Once we've found a bunch of (possible) document keys in the complete text, we'll be able to iterate over them to see if they are indeed the start of the MRZ. How we do this is by using the next important thing we know about every MRZ: &lt;em&gt;check digits&lt;/em&gt;. For every document (end therefor MRZ) format we know the &lt;em&gt;relative&lt;/em&gt; positions of these digits from the document key. For example a passport (TD3 format), has digits on position 53, 63, 71, 86 and 87. To make our list of possible document keys shorter quickly, we'll first check if the characters in those positions are actually digits (or the letter O due to common OCR error). If these are indeed all digits, the document key passes this first rudimentary test, and it will progress to a more thorough and robust test.&lt;/p&gt;

&lt;p&gt;The next thing we'll do it the actual mathematical test for each check digit. We once again know the check digit's position, but we also know the position of the substring this digit has to check over. If we take the characters that fit these positions relative to the key, we can then use the check algorithm to verify the check digit. If all check digits pass, we can confidently assume we have indeed found the correct document key. After this, all that's left is creating a substring from our full OCR text using the found position and the known MRZ length (varies per format).  &lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing Your OCR Service
&lt;/h2&gt;

&lt;p&gt;Having a MRZ finding algorithm that works well on simply any text gives you the huge advantage of being able to use pretty much any OCR service you wish. This means that you'll be able to use an external API like Google Vision, or run your own OCR solution through something like Tesseract. Keeping this flexibility in mind, the package includes a service class for the Google Vision API, but also commands to create your own &lt;code&gt;ServiceClass&lt;/code&gt;. I've personally tested this with tesseract running locally on my PC, and although the results were not as accurate as the Google API (due to my lack of knowledge about tesseract), it did show me how easily I could set up any alternative service.&lt;/p&gt;

&lt;p&gt;More information on how to set up your own custom service can be found in the  &lt;a href="https://github.com/365Werk/identitydocuments/"&gt;readme&lt;/a&gt;, but to demonstrate how easy it can be, let's go through building a tesseract service together.&lt;/p&gt;

&lt;h4&gt;
  
  
  Building a Tesseract OCR Service
&lt;/h4&gt;

&lt;p&gt;The first thing we need is obviously our main package, which we'll install using&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;composer require werk365/identitydocuments
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, let's create a new OCR service&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;php artisan &lt;span class="nb"&gt;id&lt;/span&gt;:service Tesseract OCR
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new &lt;code&gt;Tesseract&lt;/code&gt; class in your &lt;code&gt;App\Services&lt;/code&gt; namespace, which will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Intervention\Image\Image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Werk365\IdentityDocuments\Interfaces\OCR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Werk365\IdentityDocuments\Responses\OcrResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tesseract&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;OCR&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt; &lt;span class="nv"&gt;$image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;OcrResponse&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// TODO: Add OCR and return text&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OcrResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Response text'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is to use Tesseract in our PHP class, to do that we'll use  &lt;a href="https://github.com/thiagoalessio/tesseract-ocr-for-php"&gt;this&lt;/a&gt; excellent package&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;composer require thiagoalessio/tesseract_ocr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Using this package, we'll be able to get an OCR result easily. In the following example we'll first save the image to the local temp storage folder, and then parse the image using Tesseract, after which we return the result&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Intervention\Image\Image&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;thiagoalessio\TesseractOCR\TesseractOCR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;Werk365\IdentityDocuments\Interfaces\OCR&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Werk365\IdentityDocuments\Responses\OcrResponse&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Tesseract&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;OCR&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;ocr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Image&lt;/span&gt; &lt;span class="nv"&gt;$image&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;OcrResponse&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Store your image in a temp folder&lt;/span&gt;
        &lt;span class="nv"&gt;$imagePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;sys_get_temp_dir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;microtime&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;random_bytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'.jpg'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$image&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$imagePath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Use Tesseract to create text response&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;TesseractOCR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$imagePath&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Return the new response&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;OcrResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After doing this, you'll be able to load this service class and use it in the package as described by the readme. Although this is just an example and has not been tested enough at the moment, using Tesseract gives you the benefit of both being able to keep all documents within your own ecosystem (might be important due to regulations) and it being free to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost comparison
&lt;/h2&gt;

&lt;p&gt;As cost was one of the motivators for me to write this package, I feel like it's good to do a quick comparison. All prices are excluding server cost as that depends on your own infrastructure.&lt;/p&gt;

&lt;p&gt;In this list I'll just go over some of that APIs and services you can use with this package, and not over other external Identity Document Verification services, as their prices and functionalities differ greatly. It goes without saying though, that running your own solution will be &lt;strong&gt;much&lt;/strong&gt; cheaper in most cases.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;OCR Service&lt;/th&gt;
&lt;th&gt;Price&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tesseract&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Free&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Vision&lt;/td&gt;
&lt;td&gt;Free for the first 1000, after that starting from $1.5 per 1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Amazon Textract&lt;/td&gt;
&lt;td&gt;Free for the first 1000, after that starting from $1.5 per 1000 (depends on region)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Azure Computer Vision&lt;/td&gt;
&lt;td&gt;Free for the first 5,000 (rate limited), after that starting from $1 per 1000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you're currently using or researching another solution, you'll see that this can make a big difference. What should be noted is that although the package supports an option that merges your front and backside images of the documents (for cost saving), by default the OCR will be done in two separate calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;

&lt;p&gt;If you just want to get started using this package and you have no preference on what API to use, using the Google Vision API will be the easiest as the package comes pre-configured for it. This way you can also use the Face Detection functionality that is supported by the build in Google Service (although you can create a custom service for this too of course, in a similar manner as the OCR service).&lt;/p&gt;

&lt;p&gt;Installing the package and getting it running should be quite painless, and a walkthrough can be found in the readme of the package:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/365Werk/identitydocuments"&gt;https://github.com/365Werk/identitydocuments&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>passport</category>
      <category>ocr</category>
    </item>
    <item>
      <title>Caching your Laravel API with ETag and Conditional Requests</title>
      <dc:creator>HergenD</dc:creator>
      <pubDate>Sat, 20 Feb 2021 01:28:57 +0000</pubDate>
      <link>https://dev.to/hergend/caching-your-laravel-api-with-etag-and-conditional-requests-3b1</link>
      <guid>https://dev.to/hergend/caching-your-laravel-api-with-etag-and-conditional-requests-3b1</guid>
      <description>&lt;p&gt;When writing an application with a decoupled front- and backend, you'll have to start considering the requests your frontend client makes to the API. Fetching data again from the backend, even when you only want to verify that your frontend's cache is up to date can quickly add up. &lt;br&gt;
To combat this, you can make use of the &lt;code&gt;ETag&lt;/code&gt; header and  &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Conditional_requests#conditional_headers"&gt;conditional requests&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this blogpost I'll give a quick summary on what &lt;code&gt;ETag&lt;/code&gt;, &lt;code&gt;If-None-Match&lt;/code&gt; and &lt;code&gt;If-Match&lt;/code&gt; headers do, and then go over how I approached implementing this into our  &lt;a href="https://github.com/365Werk/etagconditionals"&gt;package&lt;/a&gt; which would be the quickest way of implementing it in your own application.&lt;/p&gt;

&lt;p&gt;Skip straight to the implementation&lt;/p&gt;
&lt;h2&gt;
  
  
  What
&lt;/h2&gt;

&lt;p&gt;Let's start with the header that is at the core of all of this, the &lt;code&gt;ETag&lt;/code&gt; header. This header is meant to be a value that represents the response body in the exact state it is in. In many cases, the &lt;code&gt;ETag&lt;/code&gt; value will be a hash of the content, since this is easiest to generate and guarantees a unique identifier for the response data.&lt;/p&gt;

&lt;p&gt;To make the &lt;code&gt;ETag&lt;/code&gt; header useful, we'll have to use conditional requests. The first of two we'll go over is the &lt;code&gt;If-None-Match&lt;/code&gt; header. This is a request header, and is meant to be used on &lt;code&gt;GET&lt;/code&gt; requests. When your backend receives this header, it should compare it's value to the value of the current content. If these values match, nothing but a &lt;code&gt;304&lt;/code&gt; status code should be returned, resulting in a response that is tiny compared to fetching the entire resource.&lt;br&gt;
The implementation of this is dead easy: if your first &lt;code&gt;GET&lt;/code&gt; request to a resource gave you a response with the &lt;code&gt;ETag&lt;/code&gt; header set, your browser will automatically set the &lt;code&gt;If-None-Match&lt;/code&gt; header on subsequent requests to the resource.&lt;/p&gt;

&lt;p&gt;This means that if you simply implement the &lt;code&gt;ETag&lt;/code&gt; and &lt;code&gt;If-None-Match&lt;/code&gt; on your backend, the amount of data transferred from your API to your frontend can be reduced by quite a bit.&lt;/p&gt;

&lt;p&gt;The second conditional request uses the &lt;code&gt;If-Match&lt;/code&gt; header. This is used to prevent &lt;strong&gt;mid-air collisions&lt;/strong&gt;. Simply put, if we want to update data in the backend, but our frontend data is outdated, the update should be halted and our frontend should be notified. This works in a similar way as &lt;code&gt;If-None-Match&lt;/code&gt;. After fetching a resource and obtaining the resource's &lt;code&gt;ETag&lt;/code&gt; value, you can make a &lt;code&gt;PATCH&lt;/code&gt; request to this resource and set the &lt;code&gt;If-Match&lt;/code&gt; value equal to the &lt;code&gt;ETag&lt;/code&gt; you previously received. The backend will then check if the &lt;code&gt;ETag&lt;/code&gt; value of the resource currently available on the server matches the one you send. If these match, your update will be allowed. If there is no match, &lt;code&gt;412&lt;/code&gt; will be returned, letting the frontend know that the condition has not been met.  &lt;/p&gt;
&lt;h2&gt;
  
  
  How
&lt;/h2&gt;

&lt;p&gt;If all you want to do is use conditional requests in Laravel, you can simply run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;composer require werk365/etagconditionals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After which you can add the &lt;code&gt;etag&lt;/code&gt; middleware to your route and you'll be good to go. If you're curious about how the middlewares work or how you could implement this without using our package, keep reading!&lt;/p&gt;

&lt;h3&gt;
  
  
  SetEtag Middleware
&lt;/h3&gt;

&lt;p&gt;As you might have guessed, implementing this one was the most simple of the middlewares. Laravel actually provides an option to set the &lt;code&gt;ETag&lt;/code&gt; header through the &lt;code&gt;SetCacheHeaders&lt;/code&gt; middleware, but it does not support &lt;code&gt;HEAD&lt;/code&gt; requests. The contents of the &lt;code&gt;SetEtag&lt;/code&gt; middleware looks something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle request&lt;/span&gt;
        &lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Support using HEAD method for checking If-None-Match&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HEAD'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;//Handle response&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Setting etag&lt;/span&gt;
        &lt;span class="nv"&gt;$etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setEtag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$etag&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing we do is getting the method of the request in case we want to modify it. Then if we're dealing with a &lt;code&gt;HEAD&lt;/code&gt; request, we'll change it to a &lt;code&gt;GET&lt;/code&gt; request to make sure the content is loaded and a hash can be made. After this, we skip to the response where we'll take the response body and hash it using the &lt;code&gt;md5()&lt;/code&gt; function. We'll set this hash as the &lt;code&gt;ETag&lt;/code&gt; header and make sure the original request method is set back before returning the response.&lt;/p&gt;

&lt;h3&gt;
  
  
  IfNoneMatch Middleware
&lt;/h3&gt;

&lt;p&gt;This is another relatively straight forward one. Let's view the code first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Handle request&lt;/span&gt;
        &lt;span class="nv"&gt;$method&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMethod&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Support using HEAD method for checking If-None-Match&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'HEAD'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;//Handle response&lt;/span&gt;
        &lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nv"&gt;$etag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$noneMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getETags&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;in_array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$etag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$noneMatch&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setNotModified&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;setMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$method&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The start of this looks familiar to the &lt;code&gt;SetEtag&lt;/code&gt; middleware, we'll ensure we can handle &lt;code&gt;HEAD&lt;/code&gt; requests again, and we generate a hash based on the response content. Note that in this case we'll add double quotes  around the hash. &lt;code&gt;ETag&lt;/code&gt; headers are supposed to be wrapped in double quotes, and the &lt;code&gt;setEtag()&lt;/code&gt; method wrapped our hash automatically in the &lt;code&gt;SetEtag&lt;/code&gt; middleware. After we have the hash, we can simply compare it to the &lt;code&gt;If-None-Match&lt;/code&gt; header. Since this header can actually contain any number of hashes, and the &lt;code&gt;getETags()&lt;/code&gt; method will return them as an array, we'll simply check if our newly generated has exists in this array. If we do indeed have a match, we can use &lt;code&gt;setNotModified()&lt;/code&gt; to set a &lt;code&gt;304&lt;/code&gt; status code on the response.&lt;/p&gt;

&lt;h3&gt;
  
  
  IfMatch Middleware
&lt;/h3&gt;

&lt;p&gt;Handling &lt;code&gt;If-Match&lt;/code&gt; will be slightly more complicated. The What it all comes down to is that we have to find a way to get the current version of the content that should be updated. This can be done in multiple ways. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You could use a HTTP client and make an external &lt;code&gt;GET&lt;/code&gt; request for the same resource&lt;/li&gt;
&lt;li&gt;You could look at the action that will be performed by the current request and instead call the &lt;code&gt;GET&lt;/code&gt; request equivalent of that (for example calling the &lt;code&gt;show()&lt;/code&gt; method on a controller) &lt;/li&gt;
&lt;li&gt;Or you can make a new internal &lt;code&gt;GET&lt;/code&gt; request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When building this middleware I started off trying to use the second option. This for some reason seemed like the best option to me. I managed to create a fully working version, but could not be happy with the result. To make it work I had to make some assumptions, had some limitations and do too much work that would be done for me would I simply handle it by creating a new request. &lt;/p&gt;

&lt;p&gt;So let's look at the code when we create a new request to fetch the current version of a resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Closure&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Next unless method is PATCH and If-Match header is set&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isMethod&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PATCH'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'If-Match'&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="c1"&gt;// Create new GET request to same endpoint,&lt;/span&gt;
        &lt;span class="c1"&gt;// copy headers and add header that allows you to ignore this request in middlewares&lt;/span&gt;
        &lt;span class="nv"&gt;$getRequest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getRequestUri&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'GET'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$getRequest&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$getRequest&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'X-From-Middleware'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'IfMatch'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$getResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$getRequest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Get content from response object and get hashes from content and etag&lt;/span&gt;
        &lt;span class="nv"&gt;$getContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$getResponse&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getContent&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$getEtag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'"'&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;md5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$getContent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="mf"&gt;.&lt;/span&gt;&lt;span class="s1"&gt;'"'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nv"&gt;$ifMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nb"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'If-Match'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// Compare current and request hashes&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$getEtag&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$ifMatch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;412&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All of this middleware will run at the start of the request lifecycle. First, we'll filter out any non-&lt;code&gt;PATCH&lt;/code&gt; requests or ones that don't have the &lt;code&gt;If-Match&lt;/code&gt; header set. After this, we'll make a new &lt;code&gt;GET&lt;/code&gt; request to the same endpoint, and duplicate the headers from the initial request so the new one can pass through things like auth middleware and other constraints.&lt;/p&gt;

&lt;p&gt;Using the response of this new request, we'll once again generate a hash that we can compare to the hash sent. If the hashes match, the request will be allowed through the middleware. If there is no match, a response with status code &lt;code&gt;412&lt;/code&gt; will be returned.&lt;/p&gt;

&lt;p&gt;With these 3 middlewares, you'll be able to handle etags and conditional requests easily within your Laravel application.&lt;/p&gt;

&lt;p&gt;Package:  &lt;a href="https://github.com/365Werk/etagconditionals"&gt;https://github.com/365Werk/etagconditionals&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Original post:&lt;br&gt;
&lt;a href="https://hergen.nl/caching-your-laravel-api-with-etag-and-conditional-requests"&gt;https://hergen.nl/caching-your-laravel-api-with-etag-and-conditional-requests&lt;/a&gt;&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>cache</category>
      <category>php</category>
    </item>
  </channel>
</rss>
