I was wondering where is the correct location to place the App::import on CakePHP2.
I was thinking that it should be better to use it in each function in order not to load if another function doesnt use it.
Something like this:
public function name(){
App::import('Controller', 'Classifiers');
$classifiersController = new ClassifiersController();
$this->request->data['Post'] = $classifiersController->getIdCategory('hola');
}
Instead of using the import at the top of the class.
What do you think?
the correct place for app::import: no where! ;)
you use App::uses() in 2.0 for all app classes (import is only for vendor stuff).
and you would place it at the very top of your file (after the <?php)
in your case:
<?php
App::uses('ClassifiersController', 'Controller');
...
public function name(){
$classifiersController = new ClassifiersController();
$this->request->data['Post'] = $classifiersController->getIdCategory('hola');
}
although I HIGHLY recommend to take a closer look at what you are doing there.
using another controller in a controller is pretty wrong - in your case you would probably want to import a model and use its method. controllers are only for logic of a specific request action. place all other things in the model (fat model, slim controller principle).
Related
Since my app is getting bigger i would like to separate admin prefix actions and views from normal actions and views. The new folder for admin is Controller/admin/UsersController.php.
I would like to change my cakephp controllers and views folder structure to match the prefix I'm using.
Example for admin prefix:
Controller:
app/Controller/UsersController.php (contain view(), index() ...)
app/Controller/admin/UsersController.php (contain admin_view(), admin_index() ...)
View:
app/View/Users/index.ctp (for index() in UsersController.php)
app/View/Users/admin/index.ctp (for admin_index() in admin/UsersController.php)
How can I implement this structure using Cakephp 2.6?
Unlike in 3.x where this is the default behavior for prefixes, this isn't supported in 2.x.
You could try hacking it in using a custom/extended dispatcher (in order to retrieve the desired controller) or even dispatcher filters in case you are adventurous, and in your app controller modify the view template path with respect to the prefix.
That should do it, however I would probably simply go for using plugins instead, this will separate things just fine without any additional fiddling.
If you just want to separate logic you could do something like this. It's untested an just thought to give you just the idea. I'll explain the concept after the code:
public function beforeFilter() {
if ($this->request->prefix === 'foo') {
$name = Inflector::classify($this->request->prefix);
$className = $name . 'ChildController';
App::uses($className, 'Controller/Foo');
$this->ChildController = new $className($this);
}
}
public function __call($method, $args) {
if ($this->request->prefix === 'foo' && method_exists($this->ChildController, $method)) {
call_user_func_array([$this->ChildController, $method], $args);
}
}
Depending on the prefix you can load other classes. How you load that class and how you instantiate it, what params you pass to it is up to you. In my example I'm passing the controller instance directly. I think you could actually init a complete controller here but be aware that components like the Session might cause a problem because they might have been already initiated by the "parent" controller.
When you now call a controller method that doesn't exist it will try to call the same method with the same arguments on the ChildController. Not really a great name for it, but maybe you can come up with something better.
You'll have to implement some logic to load the views from the right place in your classes as well but this shouldn't be hard, just check the controller class.
But actually I don't see your problem, I've worked on an app that hat over 560 tables and not putting the code into sub folders wasn't a problem, it did in fact use a similar solution.
I think you have to much code in your controllers, get more code into your models and the controller shouldn't be a problem.
Another solution might be to think about implementing a service layer in CakePHP which implements the actual business logic while a model is reduced to a data handler. The service would sit between a controller and the model. I've done this a few times as well now and if done right it works very well.
In my current implementation of the MVC design pattern (demonstrated using PHP and CodeIgniter):
Let's say I have a "page" located at "www.mysite.com/blue/page" that is ultimately generated by the following (greatly simplified) files:
/libraries
session_lib.php
/controllers
/red
/white
/blue
page.php
/models
/red
/white
/blue
funky_class.php
page_model.php
/views
/red
/white
/blue
page.php
And here's an easy to understand controller:
// FILE: /controllers/blue/page.php
// Get some paramaters from URL
$params = $this->input->get();
// Use a library to do some stuff with these params with a session
$this->load->library('session_lib');
$this->session_lib->store_in_session($params);
// Use a very page specific class to do some funky stuff with the params
$this->load->model('blue/funky_class');
$funkified_params = $this->funky_class->funkify($params);
// Pass params to a model
$this->load->model('blue/page_model');
$data['output_from_db'] = $this->page_model->get_some_output_from_db($funkified_params);
// Send to view
$this->load->view('blue/page', $data);
And now the question...
What is the best procedure for these "funky" little page specific classes that don't interact with the database? In this example I store the little class with the models, and in some cases might just add additional methods inside the model class to do the funky stuff. Is this good practice? Or should the library and funky class both go inside the model so the controller is even skinnier (however the model might be used in other places without the need for sessions and funkiness)?
I would use a helper.
When a helper is loaded, that function will be available in the global scope so you can call it anywhere.
They're great for small, stand-alone functions that do only one thing that is not necessarily related to code in a model or library
Helpers especially excel for me when converting things between one format or another (slugify, convert_timezone, change_query_string) or doing little tasks that you don't want to think about (force_ssl, is_image, uri_string)
funkify() seems like an appropriate helper function that may prevent a lot of repeated code.
Here's the codeigniter documentation page on them: http://ellislab.com/codeigniter/user-guide/general/helpers.html
If you're in a situation where the helper function is so specific it will be only used in one place, you can still use a helper. Name the helper after your controller, page_helper.php for example.
page_helper.php
function funkify($params) {
// do funky stuff
return $funky_params;
}
then in your controller:
class Page extends CI_Controller {
public function __construct() {
parent::__construct();
$this->load->helper('page');
}
public function page() {
// do stuff
$funky_params = funkify($params);
// do more stuff
$this->load->view('blue/page', $data);
}
I have no excuse for it, but sometimes if I am in a situation where I need a razor specific function that will only be used on one location (say, a controller) ever, I will put it right in the controller's file. You can paste a function outside of the class definition and it will act like a helper and be available globally (as long as that controller is loaded). You can also define functions inside of a view. Yucky, but possible. I don't like to do it often because it's unusual and not expected (by myself or other developers)
I have a project and I'm using ZendFramework, also I'm a newbie in this Framework.
So my problem is,
I want to create a user defined function that I can use in all the controllers. Example: I want to create a function that will validate an input field in which I'll use trim, htmlspecialchars and mysql_real_escape_string. I want to access this function in all the controllers.
I have a solution for this which is I'll create this function in every controller that I have, which I think is not the best solution.
Thanks
A base controller is a solution, but not the best to me. You should write your own Action Helper, they are meant to do it.
Action Helpers allow developers to inject runtime and/or on-demand
functionality into any Action Controllers that extend
Zend_Controller_Action. Action Helpers aim to minimize the necessity
to extend the abstract Action Controller in order to inject common
Action Controller functionality.
More information here in the manual.
Let's see how to register your Action Helper path, add this in your application.ini:
resources.frontController.actionHelperPaths.My_Controller_Action_Helper = "My/Controller/Action/Helper/"
where My is the name of you custom library.
And in the path My/Controller/Action/Helper/ you can add a file MyActionHelper.php as follow:
class My_Controller_Action_Helper_MyActionHelper extends Zend_Controller_Action_Helper_Abstract
{
public function direct($input)
{
$output = mysql_real_escape_string($input);
// trim, etc.
return $output;
}
}
That's all you need to do! Finally, you can access your action helper from any controller using $this->_helper->myActionHelper($input);.
If you need to validate an input coming from a form, take a look at Zend_Form and Zend_Filter. Zend_Filter can natively StripTags and TrimString, it's even a better way to do it.
Create file Util.php put it inside library folder
and add as many functions you want into it then open index.php (inside public folder)
add
require_once 'Util.php';
after line
require_once 'Zend/Application.php';
So for e.g your Util.php will migh look like
function mysql_real_escape_string($value)
{
return $value
}
function logger($value)
{
Zend_Regsitry::get('logger')->log($value);
}
function _T($translate)
{
return Zend_Registry::get('translator')->translate($translate);
}
Now all of these functions are global and you are free to call them from anywhere in your zf application . I do this with my every ZF project . Adding functions here for translation or logging purpose can be really time saver .
The Zend Framework have Zend_Controller_Action_Helper.
I have a Zend project with a lot of controllers and actions using the method
$this->_redirect('/controller_name/action_name');
for redirecting to other actions after data processing. I've observed that this creates a lot of "magic strings" that hurts the DRY principle: if I change a controller or action name I'd have to change this redirection code in all actions that use it. I'd like to know if there is a better way in Zend to avoid this. I've thought about creating constants for all controllers and action names, like this:
$this->_redirect(HOME_CONTROLLER_INDEX);
But I think this approach isn't very good. Is there a better solution?
You could use const instead
class DefaultController extends Zend_Controller_Action {
const IndexAction = '/controller/action/';
...
$this->_redirect(DefaultController::IndexAction);
}
Or SplEnum. See: http://www.php.net/manual/de/class.splenum.php
Your option of defining constant would be a viable one if you don't plan on modifying your router (since you are using the "compiled" url).
$this->_redirect('/controller/action');
Otherwise, you could rely on a few private methods that call the redirector action helper.
public function redirectControllerAction()
{
$this->_helper->redirector('action', 'controller');
}
This way you can have all your redirections localized in your controllers.
If you have to maintain your code in the future you have all your specifics redirection grouped together and you won't cluter your code with a lot of constants that could hinder readability and make it complex to maintain.
You could also use the controller method to send message to your user using the flash messenger without much effort.
public function redirectControllerAction()
{
$this->_helper->flashMessenger('You have been redirected to a better location');
$this->_helper->redirector('action', 'controller');
}
For the destination controller name, you can add this static method:
public static function getName() { return __CLASS__; }
(You need to add code to remove the controller suffix and call Zend_Filter_Word_xxx to perform inflection)
and use it like this:
$this->_redirect('/'.DefaultController::getName().'/action_name');
For projects written in php, can I call more than one (or multiple) controller in class controller? Example in http://img192.imageshack.us/img192/7538/mvc03.gif
ASK: I need to call an action from another controller... And if I do like the picture above, I'm being out-ethics?
Thanks,
Vinicius.
I'm sure that you can do what you want with whichever framework you're using. If you can't do it natively for whatever reason, then you can extend your framework as required.
Having said that, I personally don't like the idea of a controller calling another controller. It seems to somewhat break the MVC paradigm if only from a theoretical standpoint. What I might do instead is build a library class that contains the functionality required and then have both controllers instantiate that class as a member and call the functions required.
For example, using CodeIgniter:
libraries/MyLib.php:
class MyLib
{
public function MyFunc()
{ /* do whatever */ }
}
controllers/ControllerA.php:
class ControllerA extends Controller
{
public function index()
{
$this->load->library('MyLib');
$this->mylib->MyFunc();
}
}
controllers/ControllerB:
class ControllerB extends Controller
{
public function index()
{
$this->load->library('MyLib');
$this->mylib->MyFunc();
}
}
out-ethics? Anywhose... back to reality.
Yes, a controller can call another controller's action. For instance, in cakePHP, this functionality is afforded via requestAction
// pass uri to request action and receive vars back
$ot3 = $this->requestAction('/stories/xenu');
If you're rolling your own, the details of how to implement it will be very specific to your framework.
then you need to modify framework, find place where controller is lounched and add there your second controller.
what framework you are using?
You can do it any way that you want. You don't have to use MVC if you don't want to. However, in MVC you really should only have one controller active at a time. You probably want multiple Views or Models, not another Controller. There is nothing at all wrong in loading, say, a header and footer view for the menu and footer of the site.
If you are building another Controller, then feel that you need to access the functionality of a previous Controller to access its functionality (because it works with a specific / desired Model), then the Model you developed for the latter probably needs to be refactored. IN plain speak, your target Model may be doing too much. Break it up.
You are trying to avoid repeating yourself (DRY) by using calling the methods of a Controller that has already been developed, but in doing so your are creating TIGHT coupling between both controllers! If something changes in the borrowed controller, it will have an effect on the borrowing controller. Not good, Dr. Jones.