Menu

How to save text editor content and images in Laravel using DOM Document

Rich text / WYSIWYG editors give great flexibility to write anything for your websites but when you save the content of the text editor, it’s not that simple as it looks. The content encodes some special characters into its own encoding and the encoded images take a lot of space in the database.

By default, all the images you’re adding into your content using a rich text editor are encoded. A better way to handle images uploaded using a text editor is to store them in your project directory and use that short directory path as your image source. This way, you’ll end up saving a lot of space in your database and the process makes you a bit better software engineer. As the famous saying says “A fast web app with a slow database is a slow web app”. The end goal is to keep the database queries as fast as we can.

I am using Laravel, a PHP framework and I'm presuming that you know how to submit a form using html and Laravel. So we are starting from when the request is being submitted and we’re trying to save a blog post content in the database. Name of my text editor input is “content”.

Let’s see how our code looks like

public function submit(Request $request){
  //Write your own logic here
  
  $content = $request->content; //Storing text editor content in a variable
  $dom = new \DomDocument(); //You can read more about DomDocument class here - https://www.php.net/manual/en/class.domdocument.php
  $dom->loadHtml($content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD); //Loading html content of text editor in dom for further processing
  $images = $dom->getElementsByTagName('img'); //Getting all the img tags of content
        
  /* Now we’ll traverse all images one by one */
  foreach($images as $k => $img){
    $data = $img->getAttribute('src'); //Getting src data of image
    list($type, $data) = explode(';', $data); //Getting image type and encoded image data in two variables
    $data = base64_decode($data); //Decoding image data
    $image_name= "/postImages/" . time().$k.'.jpg'; //creating a unique name for your image file
    $path = public_path() . $image_name; //appending in public folder
    file_put_contents($path, $data); //storing the image
    $img->removeAttribute('src'); //removing the existing src attribute
    $img->setAttribute('src', $image_name); // adding the img src attribute with the new local path of your image
    $img->setAttribute('style', 'margin: auto; width: 100%;'); //optional to set any styles to your image
  }
}

Now we have our $dom variable ready, we just need to save it using a function.

$content = utf8_decode($dom->saveHTML($dom)); //decoding the content while saving to avoid the change in special characters while rendering.

Save this variable in the article field of your database

We’ve saved our content and images. Now you can render this in your front end. It’ll work perfectly fine but we’re leaving one edge case in our logic.

Suppose you’ve just submitted your content and you already had one or more images in your content and you want to edit that and add one more image to existing content and all the existing images in content have a local path in the src attribute.

While iterating over submitted images we wrote

list($type, $data) = explode(';', $data); //Getting image type and encoded image data in two variables

But this will get us the wrong image type and data while reading the existing image src path which is not encoded because we have already given them a new local path of our system. So we need to tell the system to not decode the images which have already been decoded in the last submission.

Before assigning the variable $type and $data using a php list, we’ll check if this img src need to be decoded or not.

  /* Now we’ll traverse all images one by one */
  foreach($images as $k => $img){
    if(count(explode(';', $data))>1){ 
        $data = $img->getAttribute('src'); //Getting src data of image
        list($type, $data) = explode(';', $data); //Getting image type and encoded image data in two variables
        $data = base64_decode($data); //Decoding image data
        $image_name= "/postImages/" . time().$k.'.jpg'; //creating a unique name for your image file
        $path = public_path() . $image_name; //appending in public folder
        file_put_contents($path, $data); //storing the image
        $img->removeAttribute('src'); //removing the existing src attribute
        $img->setAttribute('src', $image_name); // adding the img src attribute with the new local path of your image
        $img->setAttribute('style', 'margin: auto; width: 100%;'); //optional to set any styles to your image
    }
  }

This if condition goes more than 1, only in case, where “;” semicolon is twice or more in the image src but in our local path there is no semicolon so we will skip all those images which have already been decoded.

Now we’re completely done with handling the data and images of any rich text editor or WYSIWYG editor. Hope it adds something to your knowledge. Do drop your queries in the comments below.

Posted In:
Laravel

0 Comments