We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

call an action of another controller

Hi, what is the best way to call a function from another controller beside forward? There is a stackoverflow discussion, is the solution from the stackoverflow discussion good or bad?



2.2k

Why do you want to call another controller function? It might be bad calling another controller function.



25.7k

I would like to split a task into smaller tasks and let each controller do their part, I gather the results from each of them and decide the next logic to run.

A controller action should be self-contained and atomic. By that I mean a single action method should do a single "thing", and only that "thing".

My guess is that some of the functionality you've put in your action, should probably sit in your model somewhere. Can you give us more details on what exactly you're trying to accomplish?

I would like to split a task into smaller tasks and let each controller do their part, I gather the results from each of them and decide the next logic to run.

As @kenjis said this isn't a good use of controllers.

Controllers are merely a means to handling routing logic

Create a file, in another other folder, and then call functions from that folder

Examples can be seen in the vokuro project with an Auth class that handles authentication The session controller calls the auth class, and the auth class handles the logic. This allows for moving the logic out of the controller.

https://github.com/phalcon/vokuro/blob/master/app/library/Auth/Auth.php https://github.com/phalcon/vokuro/blob/master/app/controllers/SessionController.php



25.7k

Thanks for your replies quasipickle and David Duncan, very valuable information. Say there are 3 tables: staffs, personal_infos and system_users. Each of them have their own controller. When I create/edit a row in staffs, I'm also creating/editing a row in personal_infos and a row in system_users; there are some verification have to be done when creating/editing the personal_infos and system_users, when all three tables return true, create/edit is successed.

Well right off a problem is you're mapping a table to a controller. You should have 1 model per table, not a controller. All the row creation and data verification should be in the model, not the controller. I hope I don't sound condescending - but perhaps you should do a little more research on the MVC design pattern.



25.7k
edited Jan '15

I'm studying with the Phalcon examples. Like INVO and vokuro, they do not create inside the model classes, but inside the controllers. Validations are inside models, however they do not do complicated logic. Take a look at this one https://github.com/phalcon/vokuro/blob/master/app/models/Users.php

That class doesn't do complicated validation because complicated validation isn't necessary. There's nothing saying it can't though. Following proper MVC, the controller shouldn't know about the inner workings of a model - it shouldn't be responsible for determining if the data in the model is correct. Only the model should know that stuff. Otherwise, you'll have to duplicate data validation every time you create a new instance of that model.

What validation the controller can do, is validate the form data that gets sent to it from the view. Though, if depending on how you've built your model, that might not be necessary either.



25.7k
edited Jan '15

Although I'm not very good at Phalcon, I believe it is the controller decide how to use the validation; again take a look at their example https://github.com/phalcon/vokuro/blob/master/app/controllers/ProfilesController.php. Creation, modification, what to do after the validation is also done inside controller. Furthermore if you look at the code, "the inner workings" is not done by the controller, it is the controller calling the model validation function, so there is technically no duplicated data validation. By the way, complicated validation may or may not be necessary depending on the actual need.

Like I said - the controller does do some validation, but only of the form data submitted.

Let's say you're creating a user. Every user needs a username and password right? When the form is submitted, the controller would check the POSTed form data - it would check if the username and password were both provided. That's done in the controller because that's all the controller knows about. The controller will then pass that data to a new User object. That User object would then do stuff like check if the password is good enough. That's not done in the controller, because the controller doesn't know about the restrictions the User class has in place for the password. Those restrictions are out of the controller's scope.

In this example, there is no reason for the Controller object to call a method in a different controller



25.7k
edited Jan '15

look at those actions:

  • // delete action
    • // find a row by id
    • // see if the row exist (validation), if fail
      • // do something
    • // row exisit (validation pass), then try delete, if delete fail
      • // do something
    • // if delete success
      • // do something

In this delete action, every time it needs to validate the row before doing the "delete"; programmer decide this complicated logic, design how and when to validate, what to do after; in their examples, although they do not explicity "1-1 mapping" a table to controller, basically each controller don't take multiple tables, and I believe it's for the purpose of avoiding the duplication of "complicated logic". When a whole logic needs to move on to another controller, they do call a method (dispatdch forward) in a different controller and may do so even with arbitrary parameters.

"Validation" generally refers to checking data to ensure it meets a required format. Verifying if a row exists is generally not referred to as "validation", so I can see where the miscommunication has happened. Logic like you've shown is supposed to be in the controller, because the model can't do it.

You said in your original post that you wanted to use functionality in other controllers without using forward. Forwarding is different than "calling" a method in another controller. Basically when you forward, you're passing control of the program flow on to another method in another controller. That's perfectly fine because in those instances, you would be wanting the user to be treated as if they had called that action directly.

In this conversation, it has seemed like you want to stay in one controller/action, but just call functionality from different controller/action - basically treating other controller like a function library.



25.7k
edited Jan '15

Correct, I actually would like to use another controller like a function library, otherwise the same logic is going to be somewhat duplicated. However based on what it wrote from the doc, it doesn't seem like forwarding means making the user to be treated as if they had called that action directly; it is passing the flow to another controller, or more precise let another controller to get involve and continue (depending on the return value).

Well I probably wasn't clear, so let's try an example.

Say you have an application that allows people to sell chairs, and sell fruit. There is a page where the user can sell chairs, a page where users can sell fruit, and a page where users can sell either. While it's incredibly bad design, let's assume each selling page uses a different controller.

A person going to the chair selling page will see a form where they can put in their information, and upon successful completion of the form, they see a message saying "Your chair has been sold". The fruit page is similarly set up.

When a user goes to the page where they can sell either - you're using a different controller than the chairs or fruit page. However, you don't want to duplicate the functionality. What you would do is - when a user submits the form, you forward the program flow to the chairs controller. The chairs controller & action then behave just like they would if the user had tried to sell a chair directly.

If you want to use a function library, that's fine - there is a time and place for that. Trying to make a controller and action act like a function library is, in my opinion, bad design.

Why make your controller into a library for other controllers to use? You could use a PHP trait, or regular class that you call on demand when you need that same functionality. Also some stuff is available within a model sub class directly by accessing $this->getDI(), like flash or flashSession messaging, but some other stuff like the dispatcher seems to be blank when accessed by like that.



25.7k

thanks for your info Edward. Currrently I'm using static functions and I personally think that it is clean and rational. I shall think about your suggestion if I see flaw in the future.

edited Feb '15

What you are trying to do is scrapping the MVC structure in your app when you try calling a function in another functions all based in controllers. Just put all the functions you need to call in a model. Let the MVC logic remain and you will find it easy for you along.

Unless you want to continue to manipulate your app data in some ways in which case you should use the Dispatcher component where you will forward the operations.