Using Froala Image Manager with Laravel 5

Previously, we looked at using Froala with Laravel 5.2, including the image upload feature. As promised, it's time to look at using the Froala image manager as well! Moving forward, I'll assume you looked at the first article for getting Froala setup initially, and enabling image upload.

In our image upload feature, we initiated Froala on our create article form with the following javascript:

<script>
    // Replace the <textarea id="body"> with a Froala
    // instance, using default configuration.
	$('#body, #excerpt').froalaEditor({
		toolbarButtons: ['undo', 'redo', 'html', '-', 'fontSize', 'paragraphFormat', 'align', 'quote', '|', 'formatOL', 'formatUL', '|', 'bold', 'italic', 'underline', '|', 'insertLink', 'insertImage', 'insertTable'],
		heightMin: 300,
		imageMove: true,
		imageUploadParam: 'file',
		imageUploadMethod: 'post',
		// Set the image upload URL.
		imageUploadURL: '/department/{{ $department->id }}/images',
		// Pass the department id
		imageUploadParams: {
                            froala: 'true',               // This allows us to distinguish between Froala or a regular file upload.
			_token: "{{ csrf_token() }}"  // This passes the laravel token with the ajax request.
		}
	});
</script>

To add the image manager functionality, we will need to use the imageManagerLoadURL to specify the url to get the images from, the imageManagerDeleteURL  to specify where to send the delete request, the imageManagerDeleteMethod  to specify the request type (in our case, DELETE), and finally the imageManagerDeleteParams  to specify which parameters to pass through. We must pass through the csrf_token or Laravel will reject the request and it will fail. Adding these things will look something like this:

// URL to get all department images from
imageManagerLoadURL: '/department/{{ $department->id }}/images',
// Set the delete image request URL.
imageManagerDeleteURL: "/department/{{ $department->id }}/images/",
// Set the delete image request type.
imageManagerDeleteMethod: "DELETE",
imageManagerDeleteParams: {
	_token: "{{ csrf_token() }}"
},

So, after adding these things, the entire Froala editor with image upload and image manager javascript would be like the following:

<script>
    // Replace the <textarea id="body"> with a Froala
    // instance, using default configuration.
	$('#body, #excerpt').froalaEditor({
		toolbarButtons: ['undo', 'redo', 'html', '-', 'fontSize', 'paragraphFormat', 'align', 'quote', '|', 'formatOL', 'formatUL', '|', 'bold', 'italic', 'underline', '|', 'insertLink', 'insertImage', 'insertTable'],
		heightMin: 300,
		imageMove: true,
		imageUploadParam: 'file',
		imageUploadMethod: 'post',
		// Set the image upload URL.
		imageUploadURL: '/department/{{ $department->id }}/images',
		// Pass the department id
		imageUploadParams: {
                            froala: 'true',               // This allows us to distinguish between Froala or a regular file upload.
			_token: "{{ csrf_token() }}"  // This passes the laravel token with the ajax request.
		},
		// URL to get all department images from
		imageManagerLoadURL: '/department/{{ $department->id }}/images',
		// Set the delete image request URL.
		imageManagerDeleteURL: "/department/{{ $department->id }}/images/",
		// Set the delete image request type.
		imageManagerDeleteMethod: "DELETE",
		imageManagerDeleteParams: {
			_token: "{{ csrf_token() }}"
		}
	});
</script>

But that is the easy part (not that any of this is largely difficult; the hardest part was during my brain fart (yes I said brain fart, and yes I am doing nested parenthesis) about the laravel token on forms). Now we must actually make those urls do something.

In my case, I had all the uploaded files stored in the database under a table. For now lets call that table uploaded_images . Because of this table, we must have a model called UploadedImage (technically you can call it whatever you want and point it to the table). Keep this in mind in the upcoming code. Now onto the controller... after we register the route! In my app, I had images that belonged to specific departments, hence the /department/{{ $department->id }} in the midst of my url. To register the routes that match the JavaScript code above, we would open routes.php:

	Route::post('/department/{id}/images', 'FeaturedImagesController@store');
	Route::get('/department/{id}/images', 'FeaturedImagesController@index');
	Route::DELETE('/department/{id}/images', 'FeaturedImagesController@destroy');

This registers our post, where we upload the images (left that out last post), our get where we get all of the images for the manager, and our delete where we handle the delete request.

Now, finally, we can move onto our controller! First, let's handle showing our images in the image manager. First, let's briefly look at how it works.

Froala's image manager works similar to the image upload: it uses JSON to fetch links to the images it needs to handle. In this case, we must return a JSON array (Froala documentation does very well showing the format it needs to be returned in) of the image links. So, in our index() method on our controller (check the routes for why we are using index here), we will fetch all image links from the database that belong to the department we are currently accessing:

/**
 * This returns a json array of links to fill the image manager in Froala forms. DO NOT CHANGE.
 * @param  [int] $id Department id from URL
 * @return JSON Returns a json array of links.
 */
public function index($id){
	$images = UploadedImage::where('department_id', $id)->get();
	$list = array();
	foreach($images as $image){
		$img = new \StdClass;
		$img->url  = "http://example.com/" . $image->path;
		$img->thumb = "http://example.com/" . $image->path;
		$img->id = $image->id;
		$list[] = $img;
	}
	return stripslashes(response()->json($list)->content());
}

Here, we simply get all images (the links to them) from the database, as long as they belong to the current department (you will likely not need that step), then do a foreach through the returned collection to create our own array of images, which we then format into the proper json format for froala to use, returning the proper json array of image links. Froala will then use this array to fill our image manager with images found through the links you provided, and now all we have left to do is add in the delete functionality!

The delete functionality with Froala works similar to any other delete request you would send with Laravel. In our JavaScript, we specified to send a DELETE request to /department/{{ $department->id }}/images/ when the delete button is clicked. At the time of writing this, Froala currently does not automatically pass through the id of the image you click delete on, though I'm told they're working on releasing an update soon with that feature (which will be handy!). So here is what i did:

/**
 * Find and delete the deleted image.
 * @param  Request  $request 	[description]
 * @param  int  	$id      	Department ID
 */
public function destroy(Request $request, $id){
	$input = $request->all();
	$url = parse_url($input['src']);
	$splitPath = explode("/", $url["path"]);
	$splitPathLength = count($splitPath);
	$file = FeaturedImage::where('path', 'LIKE', '%' . $splitPath[$splitPathLength-1] . '%')->where('department_id', $id)->delete();
}

I grabbed the input from the delete request, which contained only the url of the image. To get the correct image, I then used PHP's functions to break the url into an array of the main parts: suffix, url, and path. I then grabbed the path and searched the database for the image that contained that path. At first I was cautious about this, but I realized that if two images were uploaded to the same path (two images of the same name within the same department) then one would overwrite the other anyway, so there couldn't be two of the same image. So I used the path to find the image, then used Laravel's delete function to delete the image.

**NOTE: This will NOT delete the file on your server. I used SoftDelete functionality to update the deleted_at column in the database, then it will be there. If you want to fully delete it, you must pair this request with other code to delete the actual image file. **

And voila! You should have a working Froala image manager with uploading and deleting capabilities!