I had to create method in Boostrap which bootstraps Layout resource and registers some view helpers.
protected function _initViewHelpers() {
$this->bootstrap('layout');
$layout = $this->getResource('layout');
$view = $layout->getView();
$view->registerHelper(new Application_View_Helper_LoadMenu, 'loadMenu');
$view->registerHelper(new Application_View_Helper_InfoLink, 'infoLink');
$view->registerHelper(new Application_View_Helper_InfoData, 'infoData');
}
Now, I am passing some variables to layout (to Zend_View instance, as always), but layout doesn't recognize that it has them.
When I move code which registers helpers, to init() method in controller, everything is ok. Is it ZF error or I did sth wrong?
In Your Controller (or wherever you have view)
$view->layout()->some_var = "Some Value";
In Your Layout
<?php echo $this->layout()->some_var; ?>
if i'm missing some part of your question let me know.
Edit: failing the above, the other correct way to do this would be to use the placeholder helper (http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.placeholder)
Edit 2: Make sure you are bootstrapping view as well.
$this->bootstrap('view');
$view = $this->getResource('view');
Do your view-helpers implement the setView() method, either directly or perhaps as subclasses of Zend_View_Helper_Abstract?
If you look at the code for Zend_View_Abstract::registerHelper($helper, $name) method, you will see that it checks for the presence of a setView() method on the helper. If it finds such a method, then it calls $helper->setView($this), where $this is the $view.
This is where the connection takes place. In the absence of this call, it seems that although the view will be aware of the helper (after all, you did just register it), the helper will be unaware of the view. If the helper tries to access the view, it ends up creating a new view object, which is not the one you configured way back in Bootstrap.
tl;dr: There is probably no need to explicitly register the helpers. With the default resource-autoloader in place and the class/method naming convention you seem to be using, you can probably allow the built-in plugin-loader to handle all the instantiation. Simply call $this->myHelperMethod() in your layouts or view-scripts and all should be cool.
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.
This might be a bit hard to comprehend so I apologize in advance if this is not clear enough.
I'm writing my own MVC framework and am once again stuck.
I am in the process of writing the controller classes for the framework. Basically this is how it works:
Instantiate class coreController which extends abstract class
coreController sets controller to be loaded by interpreting query string
query string values stored in variables
other variables assigned values
new controller is loaded
new controller checks if an action object needs to be instantiated.
new actioncontroller is loaded
action controller checks if it is the final object required.
action controller is returned as an object to be referenced during the rest of the script.
generic $controller->method() can be called and references final controller loaded.
Another overview:
coreController
pageController
pageControllerActionAdd
return as object to start
$controller->something(); //References pageControllerActionAdd
Esentially what I want to be able to do is be able enter a url like:
http://www.mywebsite.com/page/modify/
and have the script pull up the PageModifyController as a variable so I can execute it's methods.
If you can tell me a better method for what I am doing please go ahead. You don't have to write any code, just the idea would be great. It is just that the way I am currently doing is very confusing and hard to debug. I will end up with multiple nested objects and I don't like the concept of that.
I've been reading a lot of other source code and found that it too can be quite sophisticated.
I actually created a framework that works along the lines you are trying to implement. I think what you are missing is a RoutingHandler class. Routing is the physical manipulation of the URL, which tells your application which Controller to load, and which Action to run.
In my world I also have Modules, so the basic routing scheme is
Module -> Controller -> Action
These three items map to my URI scheme in that fashion. Variables can be appended also like so...
http://www.domain.com/module/controller/action/var1/val1/var2/val2
So, what happens after the URI is parsed, and control is passed over to the appropriate controller and action? Let's make some code up to demonstrate a simple example...
<?php
class indexController extends Controller {
protected function Initialize() {
$this->objHomeModel = new HomeModel;
$this->objHeader = new Header();
$this->objFooter = new Footer();
$this->objHeader
->SetPageId('home');
}
public function indexAction() {
$this->objHeader->SetPageTitle('This is my page title.');
}
// other actions and/or helper methods...
}
?>
In the Initialize method, I'm setting some controller-wide stuff, and grabbing an instance of my Model to use later. The real meat is in the indexAction method. This is where you would set up stuff to use in your View. For example...
public function randomAction() {
$this->_CONTROL->Append($intSomeVar, 42);
}
_CONTROL is an array of values that I manipulate and pass onto the View. The Controller class knows how to find the right template for the View because it is named after the Action (and in a sibling directory).
The Controller parent class takes the name of the action method and parses it like so...
indexAction -> index.tpl.php
You can also do some other fun stuff here, for example...
Application::SetNoRender();
...would tell the Controller not to render inside a template, but just complete the method. This is useful for those situations where you don't actually want to output anything.
Lastly, all of the controllers, models, and views live inside their own Module directory like so...
my_module
controllers
indexController.class.php
someotherController.class.php
:
:
models
HomeModel.class.php
:
:
templates
index.tpl.php
someother.tpl.php
:
:
I can have as many Modules as I need, which means I can separate functionality out by Module and/or Controller.
I could go on, but I'm writing this from memory, and there are some wrinkles here and there, but hopefully this gives you food for thought.
I am new to PHP and I have a few questions that follows:
Do I need the init function or can I do the job (whatever I need to do in my code) without the init function?
I am saying this because the NetBeans "kinda" created/added automatically the init() function in my project.
In my code I am suppose to create the CRUD functionality in it.
If I don't use it what's the problems I might have and the downsides?
As the official docs would say:
The init() method is primarily intended for extending the constructor. Typically, your constructor should simply set object state, and not perform much logic. This might include initializing resources used in the controller (such as models, configuration objects, etc.), or assigning values retrieved from the front controller, bootstrap, or a registry.
You can have controllers that don't override the init() method, but it will be called under the sheets anyways.
If you are new to PHP, do not start by using a framework. Instead you should learn the language itself.
There is nothing significant about init() function. It is not a requirement for classes in PHP. Hell .. even __construct() is not mandatory in PHP.
That said, Zend Framework executes it right after the controller is created. It is required if you are using ZF.
You can read more about it here.
init() in Zend_Framework for most practical purposes is where you would put code that you need to affect all of the actions in that controller.(at least to test against all of the actions).
For example I often use the init() method to set up the the flashmessenger helper and to set the session namespace I want to be used.:
public function init() {
if ($this->_helper->FlashMessenger->hasMessages()) {
$this->view->messages = $this->_helper->FlashMessenger->getMessages();
}
//set the session namespace to property for easier access
$this->_session = new Zend_Session_Namespace('location');
}
Also Netbeans did not make this method or the controller, Zend_Tool made the controller and the methods utilizing the interface that Netbeans provided. That's why in your PHP settings for Netbeans you have to provide the path to the ZF.bat file and click the register provider button if you change your ZF install.
One more thing...Be aware that there more methods available to the controller that provide hooks into different parts of the dispatch cycle. You may not need them very often but you need to know they are there.
Simply its a constructor for that class(controller)...
init(){
$this->a = 1; //If we set something like this in the init
}
public function fooAction(){
echo $this->a; //1
}
public function barAction(){
echo $this->a; //1
}
ie the variables,objects..that is initialised in init will be available to all the actions in that controller
I am using the zend framework and trying to create and render a view from inside a controller. Normally this process is handled by the framework but I thought I could do it myself too as this part of the documentation states.
Unfortunately there is something wrong as the framework is still trying to load the default view as well. Here's my controller
<?php
class ViewController extends Zend_Controller_Action {
private $viewsFolder = null;
public function init()
{
$this->viewsFolder = realpath(dirname(__FILE__)) . '/../views/custom/';
}
public function indexAction()
{
// using a custom view (initialization and rendering executed by hand)
$view = new Zend_View();
$view->setScriptPath($this->viewsFolder);
$view->assign(array(
"dev_name" => "Fabs",
"framework" => "Zend frmwrk"
));
echo $view->render('customView.phtml');
}
}
and here is the error I get
Message: script 'view/index.phtml' not found in path (/home/ftestolin/stuff/rubrica/application/views/scripts/)
It looks like the normal view rendering cannot be suppressed. Any idea how to do it?
Probably better to disable the ViewRenderer rather than remove it. In controller:
$this->_helper->viewRenderer->setNoRender(true);
Remember that the ViewRenderer is where Zend_Form instances pull their default view for their own rendering. Removing the ViewRenderer means that it has to be re-instantiated later when the form needs to render. But when it does so, it recreates a brand new Zend_View instance. Any settings you have applied to your view - say, at bootstrap, setting doctype, etc - will be lost.
ok I will answer myself :)
one has to prevent the normal behaviour which is handled by Zend_Controller_Action_Helper_ViewRenderer
you can do it like that in the controller and then do your View instantiation business.
$this->_helper->removeHelper('viewRenderer'); // stop the default views rendering process
cheers
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.