How to use jQuery/ajax with php MVC

I am used to using procedural php and jQuery/ajax. Here is a simple example of how I would usually use them together.

Let's say I have a page that just has an input field.

<input type="text" name="name" id="name">

Open in new window


Then to keep it simple, I have some jQuery at the bottom of the page:

// code here to trigger the ajax call on button click/form submit etc.
		var form = $( "#loginForm" ).serialize();	
			$.ajax({
				url: 'functions/some-file.php',
				type: 'POST',
				dataType: 'json',
				data: form,
				beforeSend: function() {
					$( "#login" ).hide();
					$( ".spinner" ).html("<img src='img/spinner.gif'>");
				},
			})
                     // .done and .fail go here

Open in new window


The some-file.php file would look something like:

<?php

if($_POST) {

	$response = array();
	$message = "";

if(empty($_POST['name'])) {
		
		$message .= "Name required";
	}

if($message) {
	
		$response['success'] = false;
		$response['message'] = $message;
	
	} else {

                // add data to database
                $response['success'] = true;
		$response['message'] = "Success";
		
	}
	
	echo json_encode($response);
}

Open in new window


This works fine. I have a basic MVC framework that I built from scratch using only php based on a course I did (no templating engines or anything, just plain php) and don't know how to do the above in MVC.

At this point I have the view and the controller. I am not onto the model yet. At this point I have a form with email and password fields and I am just trying to do the validation. Here I am just checking if the email field is empty or not to keep it simple.

In my controller I just have:

      
public function login() {
		
		if($_POST) {
			
			
			$data = [
				
				'email' => trim($_POST['email']),
				'email_err' => '' 
			];
			
			if(empty($_POST['email'])) {
				
				$data['email_err'] = 'Email required';
			}
			
			if(empty($data['email_err'])) {
				
				//call login model
				

			} else {
				
				$this->view('users/login', $data);
			}
			
		} else {
			
			$data = [
				
				'email' => '',
				'email_err' => ''
			];
			
			$this->view('users/login', $data);
		}
	}

Open in new window


And my view:

<form action="<?php echo URLROOT;?>/users/login" method="post">
	<input class="form-email" type="email" placeholder="Email Address" name="email" autocomplete="off">
	<input class="form-password" type="password" placeholder="Password" name="password">
	<input class="login-btn btn-filled" id="login" type="submit" value="Login">
	<div class="spinner"></div>
	<input type="hidden" name="form_token" id="form_token" value="<?php //echo make_form_token(); ?>">
	<input type="text" name="some-field" style="display:none;">
</form>
<div class="alert alert-danger">

Open in new window


So, the question is, how on earth do I make the ajax call using the controller and send the data to the model?
LVL 1
Black SulfurAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Leonidas DosasCommented:
My first thought is to use two ajax call methods to do this.The first is to validate the form and the second to send data.See the example below:
function ajaxCall1(){
    $.ajax({
        success: function(){
            ajaxCall2();
        }
    });
}
function ajaxCall2(){
    $.ajax({
				url: 'functions/some-file.php',
				type: 'POST',
				dataType: 'json',
				data: form,
				beforeSend: function() {
					$( "#login" ).hide();
					$( ".spinner" ).html("<img src='img/spinner.gif'>");
				},
			})
                     // .done and .fail go here
}

Open in new window

0
Julian HansenCommented:
What you are running into here is a common issue with MVC / AJAX. MVC is a pattern for rendering out a view based on a user input and a data model.
AJAX is more geared to the client side model using services to provide the data that is required.

In the MVC I built I had a flag I used in the URL that told the master (index.php) to not load the main template but but rather just call the controller. This worked well but it was not optimal.

My strategy now is to maintain a separate service architecture. AJAX calls are made to a REST service interface that takes the input based on GET/PUT/DELETE/POST etc and an optional action and returns the data required.
The service can share models with the MVC code.

If I can find the time I will put together an article on the Service approach. I am personally moving away from server side MVC and focusing more on SPA's which use a client based framework instead.
0
Black SulfurAuthor Commented:
Hmm. Well this has thrown a spanner in the works. I thought moving to MVC was going to be the 'right' thing to do but not being able to easily use ajax is putting me off. Now I really don't know where to go from here in order to move forward.....
0
Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

Julian HansenCommented:
Apologies if my post threw a spanner - I was attempting to paint the landscape.

The question you need to ask - is why do you want your AJAX to go through your MVC process - if there is a valid reason for it - you are requesting views from the server then the solution is dependent on how you implemented you MVC

In my framework it starts by calling a master template that defines the common areas and from there loads the right controller - which loads the model and the view.

Here is where the first decision needs to be made. When you query the server by AJAX for a view (or sub view) do you want to only get that view or the whole page. Obviously, in most instances, we don't want the whole page loaded into our existing page but jQuery has some nice features where we can get the whole page and only load a portion of that into our page for example

$('#target').load('http://server/page #targetview");

Open in new window

As long as your page renders out with an element that has an id="targetview" the above will get the whole page, do a find on #targetview discard the rest and place the html from #targetview into #target.

With the above scenario you don't need to change anything - simply call the page as you would normally.

If you want to return only a sub-view (i.e. bypass the master template) then you need to add a parameter to your AJAX call that asks only for the content of the view - not the content inside the master template.

So for instance in my framework - in the master template I have this
<!-- menu and header here -->
<div class="content">
   <?php $mainframe->render() ;?>
</div>
<!-- footer and closing here -->

Open in new window


$mainframe is set up in the master script (index.php)

In index.php I check for a parameter (content) if this is present then I simply call $mainframe->render() directly - this invokes the controller and the view - draws only the view part.
Otherwise I include the master template and it calls $mainframe->render()

Now, if your AJAX call is not to return a view but to operate as a service i.e. validate data, return data etc then my advice would be to bypass your MVC and build a REST service which runs in parallel with the MVC code. The service code provides the service architecture for your AJAX calls, MVC provides the view.

Hopefully that clears it up a bit.
1

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Black SulfurAuthor Commented:
Hi Julian,

As you know, I am still learning so I try to keep everything as basic as possible. The main reason I use ajax is just so the user doesn't have to have a page refresh for everything. I seldom use jQuery and ajax to actually generate html or add content to a page/view. I really only use it when it comes to any form submissions. Login, contact, register, any forms I would like to use jQuery and ajax. All that ajax call does is validation and returns error messages to the user in a div like <div class='alert alert-danger' id="err"></div> and if there are no validation issues then the form data should be inserted into the database. But from the little I have learnt about MVC, the validation can be done and errors displayed via the controller but when it's time for the data to go into the database, it needs to be sent to the model and inserted into the database there.

So, I am just trying to wrap my mind around how to do this. If for arguments sake I have the ajax call in the view, I don't think I can just choose any php page to do the ajax call to. I need to do it in the controller. But that just seems really confusing to me. I am pretty sure this is possible as I can't believe that people don't use ajax with MVC but I guess it's just a bit too advanced for me at this stage.

Anyway, maybe some code might be able to give you an idea of what I have in order to point me in the right direction. Otherwise I will just give up for now, haha!

This is my User.php controller which I have made as basic as possible with literally one field.

class Users extends Controller {
	
	public function __construct() {
		
		
	}
	
	public function login() {
		
		if($_POST) {
			
			
			$data = [
				
				'email' => trim($_POST['email']),
				'email_err' => '' 
			];
			
			if(empty($_POST['email'])) {
				
				$data['email_err'] = 'Email required';
			}
			
			if(empty($data['email_err'])) {
				
				//send data to model

			} else {
				
				$this->view('users/login', $data);
			}
			
		} else {
			
			$data = [
				
				'email' => '',
				'email_err' => ''
			];
			
			$this->view('users/login', $data);
		}
	}
}

Open in new window


My view:

<form action="<?php echo URLROOT;?>/users/login" method="post">
	<input class="form-email" type="email" placeholder="Email Address" name="email" autocomplete="off">
	<input class="login-btn btn-filled" id="login" type="submit" value="Login">
</form>
// if I was using ajax I would output the error to this form-error div
<div class="form-error"></div>
<div class="alert alert-danger">
	<?php echo $data['email_err']; ?>
</div>

Open in new window


I know I should have my jQuery in a scripts.js file but let's just pretend I have my jQuery below the html and when I click login the ajax fires and it needs a url. That's where I get stuck. I can't just send it to any php file like I did before, I need to get it to the controller. In your post you said I should bypass MVC and build a REST service that runs in parallel with the MVC. I honestly don't know what that means. So, is there no way to send that ajax call to the controller?
0
Julian HansenCommented:
What you are telling me is that you want to use AJAX to access services - code that does something with data rather than returns a view to show on the screen.

It is difficult to advise you on how to do this as I am not familiar with your MVC code - from what you have posted I am going to guess that you don't want to do lines 30 and 42 - don't call into your view but rather construct your return and send it back directly.

Views are for when you change what is visible in the browser - when you validate a login you are not doing that - you are returning a status and optionally some error information - most likely as a JSON return - so I would make it that you send that output directly and leave the view out of it.

As I said before - I keep my service code separate from the MVC - because I see it as an API - that might be used outside of the MVC application. By putting it into an independent REST based service I isolate the service functionality from the view - although they can share code in terms of how they process the data.
0
Black SulfurAuthor Commented:
What you are telling me is that you want to use AJAX to access services - code that does something with data rather than returns a view to show on the screen.

Yeah, that's it. The only thing it should send to the view is the validation error message or success message unless the user is meant to be redirected after success.

So, I added this jQuery to my view:

$(document).ready(function(){
	$( "#login" ).on("click", function(e) {
		e.preventDefault();
		var form = $( "#loginForm" ).serialize();
		$.ajax({
			url: '../controllers/Users.php',
			type: 'POST',
			dataType: 'json',
			data: form,
		})
		.done(function (data) {
			if(!data.success) {
				
                               // append message to error div
				console.log(data.message);
				
			} else {
				
                                //append message to success div
				console.log(data.message);
			}
		})
		.fail(function (jqXHR, textStatus, errorThrown) {
				console.log(textStatus + ': ' + errorThrown);
				console.warn(jqXHR.responseText);
			});
	});
});

Open in new window

I am posting it to my Users controller (Users.php) , which I showed in my previous EE post. This obviously doesn't work as is.

Had I not been using MVC, the php in the page it would have posted to would have been something like:

if($_POST) {

	$response = array();
	$message = "";

if(empty($_POST['name'])) {
		
		$message .= "Name required";
	}

if($message) {
	
		$response['success'] = false;
		$response['message'] = $message;
	
	} else {

               // add data to database, send to model?
               $response['success'] = true;
		$response['message'] = "Success";
		
	}
	
	echo json_encode($response);
}

Open in new window


Is it possible to use the above code in the controller, and where the $response['success'] code is, send the data to the model from there?
0
Julian HansenCommented:
Is it possible to use the above code in the controller, and where the $response['success'] code is, send the data to the model from there?
Yes, as I have already mentioned - as long as your controller does not invoke a view it can output what it wants directly to the calling entity.
0
Black SulfurAuthor Commented:
I seem to have figured this out to an extent. I found a very small MVC project on github that said it had basic ajax functionality and I managed to implement it into my project.

What I did was:

1. in my footer I added this line:
<script> var url = "<?php echo URLROOT; ?>" </script>

Open in new window


2. add a file called Ajax.php in my controllers folder

3. In that controller I created a method called ajaxGet. I just echoed some text in there for now.

4. In my scripts file I just wrote some jQuery for when a button is clicked to make the ajax call

$.ajax(url + '/Ajax/ajaxGet')
//. done code here etc.

Open in new window


The ajax call was successful and my code was appended to the div I created. I will have to try expand on this and get it to call database records and other things that I was used to doing with procedural, non php applications.

Anyway, I hope this helps someone.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
PHP

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.