I know what you are thinking. You are thinking what’s even the need of this topic. I mean sure with Laravel’s in-built Validator class, validation at server side has become easier as ever. But, Laravel’s validation class does screw from time to time and then you have to resort to some custom solution, like I did.

You see, you can learn all about Laravel’s validation from the below documentation page of Laravel, explaining the Laravel’s validation class uses:
https://laravel.com/docs/5.3/validation
It’s pretty self explanatory, but there are certain things that you don’t learn in the books.

So, I was working on an application and in that application the user was required to upload audio files in a form via jQuery ajax but the audio files could only be in the mp3 or ogg format as they had to be used in the jPlayer plugin. Now normally the first thing that would come to your mind will be the

mimes: mp3, ogg

validation method, and so I thought of it too. But even after clearly and rightly mentioning the file formats to be allowed, to be only mp3 and ogg, the upload was still giving the error for mp3 validation.

I was working on Laravel 5.2.24 and couldn’t figure out the reason at first since I was new at Laravel 5.x. The first thing that came to my mind was that there might be something wrong with Laravel itself, so I changed the validation as

mimes: jpg, png

, and sure enough, it worked all well for that. So, after messing around a bit more I concluded that it was again the time to ask Lord Google for help. To my surprise a lot of people out there were facing the same problem and had some very interesting solution for it.

The first solution that worked for someone else was using

mimes:mpga

as validation or so I believe. I actually think that the questioner was trying to do something custom by using

$file->guessExtension()

or maybe just checking things out. Anyhow, the reason the solution provider gave the questioner was as below:

“The main extension for the audio/mpeg mime type in the Apache list is mpga, not mp3. So if this is the actual logic they use, you need to use mpga to allow the audio/mpeg mime type, not mp3.”

You can check out more about the above case at: Github link
And it’s solution at: Github link

This didn’t work for me, so the hunt continued. There was another post with the same issue, kind of. The questioner however also mentioned that he thought that the reason that Laravel validation as not working because the mime ‘type’ of the upload was not understood by Laravel as ‘audio/mpeg’ but as ‘application/octet-stream’. So I checked this theory out as well since I thought that may be cause I’m uploading via jQuery ajax ‘FormData’ object from Firefox browser, it could be possible, and found that this was not the case for me as well. But for those who also think the same might be happening in their case, the below was the solution or an explanation given for the case:

“Laravel’s getMimeType() builds on top of the native mime_content_type() method, which has trouble detecting certain mp3 files.

Your best bet is to use a custom MIME detector like this one to do the job.”

To read more about the above case, you can visit: Stackoveflow link

Now all this said and done, I couldn’t find any relatable issue and any working solution. So, instead of wasting any more time I focused on looking into how to create a custom validation in Laravel based on file extension of the uploaded files. After going through the Laravel documentaion I found that you can use Validator::extend() method to add some custom rules. You can check out the whole rule from the below link:
Laravel Custom Validation rules

So, here is what I did that worked for me. First I opened the file:
..\app\Providers\AppServiceProvider.php

And, in the boot method I added the following code:

public function boot()
	{
		//Added by B.Singh
		Validator::extend('mp3_ogg_extension', function($attribute, $value, $parameters, $validator) {
			
			if(!empty($value->getClientOriginalExtension()) && ($value->getClientOriginalExtension() == 'mp3' || $value->getClientOriginalExtension() == 'ogg')){
				return true;
			}else{
				return false;
			}
			
        });
	}

The ‘mp3_ogg_extension’ in the above code is the name of the validation rule that I may apply in the controller file to the input file name. The second parameter is a function which deals with the validation process. As per the Laravel documentation:

The custom validator Closure receives four arguments: the name of the $attribute being validated, the $value of the attribute, an array of $parameters passed to the rule, and the Validator instance.

You may also pass a class and method to the extend method instead of a Closure:

Validator::extend('foo', 'FooValidator@validate');

The ‘$value’ here contains the uploaded file object. Since I was using ajax I checked the object value in the console of firebug. I used the in-built Laravel function getClientOriginalExtension() to check for the uploaded file extensions and then validate them as being mp3 or ogg.

But you are not done yet. You also have to specify the default error message that the user will receive when the validation fails. You can either do this using the custom error message way in Laravel as given at:
Custom Error Messages

that is, as:

$messages = [
		  audio.mp3_ogg_extension' => "The audio must be a file of type: mp3, ogg."
		];

or the way I did, like, opening the file:
..\resources\lang\en\validation.php
and adding the key and value to the ‘return’ array as:

return [

	/*
	|--------------------------------------------------------------------------
	| Validation Language Lines
	|--------------------------------------------------------------------------
	|
	| The following language lines contain the default error messages used by
	| the validator class. Some of these rules have multiple versions such
	| as the size rules. Feel free to tweak each of these messages here.
	|
	*/
	'mp3_ogg_extension'    => "The :attribute must be a file of type: mp3, ogg", //added by B.Singh
	"accepted"             => "The :attribute must be accepted.",
        .....
        .....

This is it. Now wherever the validation rule ‘mp3_ogg_extension’ is applied for file uploads it’ll work and validate for the mp3 or ogg files only. Keep in mind that I was uploading via ajax so in my controller all I had to do was convert all the validation error in JSON format and return it in the ajax ‘error’ case and deal it with there. I’m also going to share my jQuery code here and my controller code too as it might help in you better understanding, but if you think it’s making you more confused you can leave right here. Feel free to ask anything you want clarification on

So, here is the code form my jQuery js file:

var baseUrl = $('#base_url').data('base-url');

$("#upload_song").click(function(){
		
		
		var audioForm = document.getElementById('upload-audio-form');
		var file_data = $("#upload-audio-form input[type=\'file\']").prop("files")[0]; // Getting the properties of file from file field
		
		
		var form_data = new FormData(audioForm); // Creating object of FormData class
			        form_data.append("audioFile", file_data);
			

		$.ajax({
	        type: 'POST',
	        url: baseUrl+'/playlist/storeAudio',
	        data: form_data,
			dataType: 'json',
			cache: false,
			contentType: false,
			processData: false,
	        success: function(result) {
				
				var id = result.data.id;
				window.location = baseUrl+'/playlist';
				
				
							
	        },
			error: function(result){
				
				$("div[class^='error_']").html('');
				
				if(result.responseJSON){
				
					
						 $.each(result.responseJSON.errors, function(idx, obj){ 
							if($(obj.length > 0)){
								$.each(obj, function(key, value){
									if($("#upload-audio-form input[name="+idx+"]").length > 0){
										$('.error_'+idx).html('<p class="help-block help-block-modal">'+value+'</p>');
									}						
								});
							}	
						});
					
				}
			}
	    });
		return false;
	});

This might not work as it is if you apply in your code as I have deleted some stuff I think is just junk in the code but might be required for proper functioning.

Noe the code in my controller file:

if($request->ajax()){
  $rules = [
          'audio' => 'required|max:10240|mp3_ogg_extension'
        ];

  $messages = [];
			
  $validator = Validator::make($request->all(), $rules, $messages);
			

  if ($validator->fails()){
	return response() ->json(array(
					'success' => false,
					'errors' => $validator->getMessageBag()->toArray()
				), 400); // 400 being the HTTP code for an invalid request.
					die(); //To stop execution of code in case there is a validation error when posting via ajax 
  }
		

}

Now, I’m sure that there are a lot of other ways in which this could’ve been handled and I would love to hear all of them. This was just my take on the issue in the given time.

That’s it. Happy coding 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *