Php MVC + ajax

#1

I am trying create a simple PHP MVC framework for learning purposes and have come to the point of implementing ajax in the system.
I have looked around at different forums and answers but i don’t see a real definitive answer as to how to use ajax with an MVC type system.

Ill explain how i use controllers, and maybe this is actually the problem:

I have a post form page under the url /posts/new. This uses the controller PostsController, and the method “new”.
What the method “new” does is it sets up 2 arrays, a view settings array, and a view data array.
The view settings array sends certain settings about how the page should look to the view (which view file to include, position of the sidebar, type of sidebar, which nav menu to show, etc.).
The view data array gets all of the needed data for the view and sends it to it (this includes data for the actual main page, and the sidebar - if viewing a post list page, and the sidebar also has posts in it or data from the database, this method will send all of that data together to the view in the view data array).

Now is the issue i am having: How do i call /posts/new with ajax, without loading the full view?

Here are the options i know of from what i read and my concerns with them:

1. Create a separate controller for ajax requests:
So all ajax requests will go to this controller, but that would mean that there will be a lot of duplicate code since i would be using mostly the same method from the PostsController, just with different view settings and data (since if ajax, it wouldn’t need the template or the sidebar data).
Wouldn’t that beat the point of reusable code, and make things harder to maintain?

2. In PostsController, have a separate method for ajax requests:
This seems to be the least common solution i have read about, but it just seems more reasonable since you will be able to use the same code since this method will be in the same controller as the main method.

Is it possible that my use of controller is not an efficient way and brings up issues when implementing ajax, or do i just not understand how ajax should be implemented in MVC?

#2

There is no any difference between AJAX and common requests. Method for AJAX request handling returns another response format, e.g. JSON and that’s all.

#3

I understand that. My question is how to implement ajax in the MVC system.

Yes i could just load /posts/new with ajax in to the container element i want, but that would load the whole page view including the template (header, sidebar, footer, etc.).
I want to load only part of that page, and not the whole page.

So my question is how should AJAX be implemented.

#4

Any request, AJAX or not, should have ist own method in your Controller.

  1. You call /post/new - response is HTML.
  2. You call /post/refresh-list - response is JSON.
#5

I have read of an option to include another method in router that checks if the current request is an ajax request, and if so then to add a prefix to the method name, for example “ajax_method_name”.

And that method will be similar to the non-ajax method, just dedicated to ajax, so its functionality will be slightly different.

Though, again i have read more recommendations for create a separate ajax controller that will handle all ajax requests.

So i’m still really not sure what the better approach is here.

#6

Well, if you implement MVC correctly, you wouldn’t need to load the full view. Ajax would be in your View since it’s part of the presentation section. Your Ajax should just call a controller that specifically deals with Ajax calls. In my MVC application, I how I did it was rely on the second segment of a URL to determine which method I should call. That method has to exist in the controller or a 404 error page would be thrown. If it’s a private or protected method, a 404 error will also be thrown. If that method is public and exists, then I create a completely different View file for that specific Ajax result/call.

1 Like
#7

This brings up two thoughts though:

  1. What if you want to use the actual link for both regular requests an ajax requests?
    For example, i have a link to the post form which is /posts/new, clicking on the link could either open the full view for that method, or, in your case open the ajax controller, and the “new” method, but what if you have other links that also have a method named “new”, for example, /comment/new, you would have to change the names of the methods/links then no?

  2. What about the duplicate data issue? if you have a dedicated controller just for ajax requests, then dont you have duplicate code if loading the same page from two different controllers?

#8

Ill give my actual example, i have a PostsController that has a method called “new”, this method load a post form.

The method, with the help of other methods and the PostModel, does this:

  1. Sets up the view settings
  2. Sets up the view data

The view data includes a list of all categories to choose from, the current selected category if posting already inside one, form data if the form has been posted (gets it from session).

If i had a separate controller for ajax requests, wouldnt that mean that i would have to duplicate all of this code?

#9

What if you want to use the actual link for both regular requests an ajax requests?

Then you need to use Content Negotiation.

What about the duplicate data issue?

Since the view only handles the presentation logic and not the business logic, you don’t have duplicate code.

#10

I may be misunderstanding what you mean, but this is what i’m confused about in an example:

class AjaxController{

public function new(){
//This method is supposed to be for posts/new
}

public function new(){
//This method is supposed to be for comments/new
}
}

So if you have one controller for the all ajax requests, how do you set up methods with the same name, would you not have to rename the methods and their links?

And about the duplicate data, i don’t really understand what you mean.
If the above is the controller for all ajax requests, and we have a method for the post form, then that would be a duplicate of the method for the post form from the PostsController, just with a few small changes to a variable or two, but most of it would be the same because we are still going to get the categories, form session data, etc…, no?

I’m puzzled by this, thanks for the help!

#11

Then you’re not using MVC. Generally it’s not a good idea to put all functionality in a single class (cf. SOLID).

#12

Thats why i started this topic, because it doesnt seem to me like creating one controller for all of the ajax functionality is the efficient way to go because i think it goes against the concept of reusable code, but i keep reading suggestions saying to have one controller for ajax requests, so im confused by this topic and trying to learn more about whats more efficient.

#13

That’s not entirely true. MVC is a different pattern than SOLID. If you say it’s bad to create a single controller for specific tasks then CodeIgniter and many other MVC applications are also doing it wrong as well since they also incorporate this kind of idea. I will post more when I get home and have time.

#14

Thank you!
At the moment, my temporary solution until i figure something better out is to detect if the request is an ajax request in the router, and if it is then i add a prefix to what ever the method is, for example ajax_new, instead of the method just being new.
And then i just create a new method called ajax_new in the same controller the main method is in, that way i can use all of its other methods.

But even doing this feels that its not the most efficient way of doing it.

#15

Well, if you put all AJAX requests in one controller, you should put all non-AJAX requests in a single other controller. Indeed, neither makes sense.

The convention is to create controllers for different section of your application. So for a very simple example if you articles which people can comment on you might have an ArticleController and CommentController.

Now when you add AJAX in the mix things become a bit muddy.

Personally I’d like to use the same controllers, so for ajax action on comments, use the CommentController. Then you can do it two ways:

  1. Call a different action of the controller when an AJAX request was detected
  2. Within the controller detect if AJAX was used to send the request and act differently if it was

Which of the two is appropriate mainly depends on how much the AJAX flows differs from your regular flow. And that also depends on how much you’ve abstracted away everything. Can’t really comment anymore without seeing your code.

#16

Thanks for that answer! That’s also what i thought would be a better approach rather then having a dedicated controller to all ajax requests.

Depending on the method and what its for, the ajax version vs the regular version could change quite a bit, so to be on the safe side i would think that it would be best for the ajax requests to have their own methods.

Or maybe even combine both options for different situations?

I have actually set up option 1 at the moment, my router detects if the request was made with ajax, and if so then it adds a prefix to the method name its calling (ajax_method_name for example), and that method is the ajax method.

#17

I also don’t think it makes sense to create 1 single controller for 1 single use and nothing else. That would mean that you would have a million controllers because if any Ajax call shouldn’t be put in the same Ajax controller then you would have ajax_create for 1 controller and specifically only doing a “create” task with that single use. Then if you wanted an Ajax delete feature/task, then you’d have another controller specifically only for that task like ajax_delete. That’s going to bloat your application which I wouldn’t find very efficient. It wouldn’t make much sense to create millions of controllers just to do 1 single task. That’s not very efficient at all. I say if the task is handled the same way, I personally would rather have it in the same controller.

Since everyone has differences of opinions, I’ll show you how I would approach this. My approach isn’t for everyone and the examples I will be showing is examples of pseudo code from my MVC application so I don’t recommend copying and pasting any of the codes. I’m going to just use my codes to demonstrate what I would do in your situation using my MVC application.

So let’s say for example, I have the below layout for my home page. The User List consists of a list of users that is paginated and I have a “search” feature a user can use to search for different users. I don’t want the search feature to open up in a new window so we’ll use Ajax to reload the content with the User List container.

layout

$.ajax({
	url: 'jquery/searchUsers',
	type: 'POST',
	data: $(this).serialize(),
	success: function(data) {
		$('.user_list').html(data);
	}
});

And in my Jquery controller I can have something like so

class Jquery extends Controller {

	public function mainFunction() {

		... Main function goes here

	}

	public function searchUsers() {

		if(REQUEST_METHOD == 'POST') {

			... Do some validation

			$user = $_POST['user'];

			$data = $this->model->userClass->searchUsers($user);
			if($data == false) {

				... Return a single HTML message through the render using the list layout

				$this->render->view('user/error');

			} else {

				... Return the $data variable and pass it through the render

				$this->render->view('user/found', [
					'data' => $data
				]);

			}

		}

	}

	... Other methods go down here

}

Then in my user/error View, I can just do something like so

	<li>
		<h1>User not fond</h1>
		<p>The user you searched for could not be found.</p>
	</li>

In my user/found View, I would do something like so

<?php
foreach($data AS $d) {
?>
	<li>
		<h1><?php print $this->validate->html_escape($d->name); ?></h1>
		<p><?php print $this->validate->html_escape($d->email); ?></p>
	</li>
<?php
}

In my home layout, the only thing I would replace/reload would be the User List container so it would be something like this

... A bunch of HTML
<ul class="user_list">
	... default loaded user list
</ul>
... A bunch of other HTML

So when I search for a user then the only thing that would actually change would be the list itself and not the whole page. If I were to create a different Ajax call for a different reason, I can just do something like so

class Jquery extends Controller {

	public function mainFunction() {

		... Main function goes here

	}

	public function searchUsers() {

		if(REQUEST_METHOD == 'POST') {

			... Do some validation

			$user = $_POST['user'];

			$data = $this->model->userClass->searchUsers($user);
			if($data == false) {

				... Return a single HTML message through the render using the list layout

				$this->render->view('user/error');

			} else {

				... Return the $data variable and pass it through the render

				$this->render->view('user/found', [
					'data' => $data
				]);

			}

		}

	}

	public function anotherAjaxCall() {

		... Do more ajax related stuff just like the above with a completely different set of validations and/or render page(s)

	}

	... Other methods go down here

}

So if the anotherAjaxCall method requires the same View file from searchUsers, I don’t need to create a completely different View file for that, I would just do something like so

	public function anotherAjaxCall() {

		... Do more ajax related stuff just like the above with a completely different set of validations and/or render page(s)

		$this->render->view('user/error');

		$this->render->view('myOtherAjax/stuff');

	}

Basically, I can reuse the same View file without having to create a completely new View file with the same exact elements. That’s how I would approach it in my MVC application. Not everyone’s MVC application runs the same way so I can’t speak for everyone in this thread.

1 Like
#18

I found using AJAX was not easy and would first:

  1. try to create a simple example, validate, test thoroughly and ensure everything is ok.

  2. Next I would introduce classes, validate…

  3. Next add the second AJAX call for the comments, test…

Submitting each step would help others in testing the scripts.

#19

Wait, you’re not really calling your controller that, are you? That doesn’t say anything about what the controller does, only who’s calling it. Discoverability zero.

Worst case I might think you’ve recreated jQuery in PHP to manipulate in-memory DOMs for some reason :eek:

#20

Ah, but again that depends on what you’re doing :wink:

A nice pattern to use here would be the command pattern, where you create a command, send it off to a command bus, and let a command handler handle it down the line.

class CommentController
{
    public function addComment(Request $request): Response
    {
        if ($request->getMethod() !== 'POST') {
            // show error
        }

        $command = new AddComment($request->data->get('name'), $request->data->get('comment'));

        $this->commandBus->execute($command);

        // redirect back to article
    }

    public function addCommentAjax(Request $request): Response
    {
        if ($request->getMethod() !== 'POST') {
            // return error page
        }

        $data = json_decode($request->getContent(), true); // read from JSON data

        $command = new AddComment($data['name'], $data['comment']);

        $this->commandBus->execute($command);

        // return rendered comment
    }
}

class AddCommentHandler implements CommandHandler
{
    public function __invoke(AddComment $command): void
    {
        $comment = new Comment($command->name, $command->comment);
        
        $this->repository->add($comment);
    }
}

(here Request is an instance of the Symfony HttpKernel’s Request)

You could use a package like Tactician for the command bus.

This example is quite small, but you will see that the more logic is needed, the bigger the CommandHandler will get, but the controller will stay small. This is a nice way of keeping the controller in control of the flow only, without being aware of any actual logic.

Now say for example you wanted to create a comment via a CLI tool, no problem, let it create the Command and fire it to the CommandBus, no need to do anything more :slight_smile:

If you’re interested, this ties into the Hexagonal architecture

1 Like