zend, how can i get data to layouts or views without assigning it everytime - zend-framework

i want to be able to get data from my controllers to be made accessible from all views or layouts.. so that i dont have to assign a variable to each controller everytime..
in my case, in my layout.phtml, theres a list of categories direct from the db, for now, i am assigning this to every controller:
class productsController extends Zend_Controller_Action {
public function init() {
Zend_Layout::getMvcInstance()->assign('categories',$dbArrayCategories));
}
}
but i dont want to repeat it every time for each controller..

This sounds like a case where I would use a ViewHelper: http://framework.zend.com/manual/en/zend.view.helpers.html. This will let you use it in any view or layout. I typically pass a model into my helper, but you can definitely have it load one up by default.

You might extend Zend_Controller_Action with you own base class and assign the values directly in the constructor or init method.

Related

Command class in Caliburn Micro

On my first project trying out Caliburn.Micro, I like a lot of the things :-)
One thing I miss (or havn't discovered yet) is how to separate the viewmodel and a command.
CM doesn't support ICommand, as it's way of doing things is superior. I'm sure it's true, so I would love a small push in the right direction to achieve this or perhaps discover a better way.
As I understand you have to put the "Execute" method and "CanExecute" property directly in the viewmodel, named to match the control in the view, to get the magic to work.
I would like to put the "Execute" and "CanExecute" in a different object that is property on the viewmodel and then CM would automatically bind to that object, using the control name and property names as usually.
Repost from the forum on Caliburn Micro, I didn't get any answers so I'm trying my luck here.
You should try to avoid invalidating the Law of Demeter. Therefore, on your view model you can have an execute method, and a CanExecute property (usually calculated), and these can call into the containing model where appropriate, e.g:
public void Save
{
// .. save logic
}
public bool CanSave
{
get
{
return this.model.CanSave ... and other logic etc.
}
}
You must remember to notify a change in the calculated property when the can save state changes, e.g:
public void CodeThatGetsRunWhenAPropertyOfTheModelChanges()
{
this.NotifyOfPropertyChanged(() => this.CanSave);
}
If you have e.g. a Button on your view with x:Name="Save", then Caliburn.Micro will automatically invoke your Save verb on the view model when the button is clicked, and will automatically enable and disable the button when the CanSave property value changes.
To avoid fat ViewModels you also need to avoid fat Views. Caliburn.Micro allows you to compose Views/ViewModels as described in Screens, Conductors and Composition.
The short version is, you can include a "DetailView" and "DetailViewModel" pair in a "MasterView"/"MasterViewModel" shell by defining a DetailViewModel-typed property in MasterViewModel and adding a ContentControl named after it in MasterView. Binding and actions work as usual, so you avoid both fat models/views and routing of commands.
Another option is to bind a MasterView element to a DetailViewModel property or action, by prepending the detail's property to the target's name. I can't find the specific URL yet, so the example is from memory.
Assuming you have the following classes:
public class MasterViewModel:Screen
{
public property DetailViewModel MyDetails{get;set;}
}
and
public class DetailViewModel:Screen
{
public property string SomeText{get;set;}
public void DoTheBoogie(){}
}
You can add a control in you MasterView named 'MyDetails_SomeText' to bind to the DetailViewModel.SomeText. You can also bind to DoTheBoogie the same way.
I prefer to create a separate View though, named DetailView and add a ContentControl named "MyDetails" in MasterView. This results in a cleaner and more modular design

Simplifying ICommand/RelayCommand in a MVVM approach

I'm pushing myself to make the applications I write simpler, and I've taken some steps to do that, but I'm left with an interesting problem that doesn't at all feel like it would be unique to me. I'm wondering what I'm doing wrong.
I have a ViewModel that keeps a collection of model objects. The view is a ListView that displays all of the objects in the collection. The model objects have all the logic in them to manipulate them. Inside the ListView row for each item I have a button, and that button needs to be wired to call a method on the model object.
To get this to work I need to add a command binding, but to the parent window data context, that passes a parameter of the model object in the row, all so that model object can be used inside the ViewModel (the parent window data context) to call the method on the model object that's being passed in.
This seems really much more complex than it needs to be. I'm willing to throw out anything I've done already, there are no sacred cows, I just want this to be done in a simpler method that will be easy to look back on in a year and figure out what I was doing.
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.MyCommand}
Create a presenter class in your ViewModel for the model objects and have a collection of those. You can then put the ICommand property on those instead and pass a reference to the method you want to call in the parent datacontext.
Perhaps something like the following:
public class ModelPresenter : INotifyPropertyChanged
{
private Model _model;
public ModelPresenter(Model model, Action<Model> parentAction)
{
_model = model
_action = parentAction;
}
public ICommand MyAction
{
get { return new RelayCommand(() => _parentAction(_model)); }
}
...
}
It also sounds like you might be binding to Properties of your model your view. You shouldn't do this as it can cause a memory leak if your models aren't implementing INotifyPropertyChanged (see: http://support.microsoft.com/kb/938416/en-us).

Initiating objects in Zend Framework?

How can I eliminate to write $object = new Application_Model_Database() in every controller?
For example for an article controller, I have to type $articles = new Application_Model_Articles() for every controller. Should I put it under viewer controller, action helpers, or any other way?
Your question almost sounds like an OOP best practices question as opposed to a Zend Framework specific question. Regardless of whether or not I'm using a framework, and regardless of what framework I choose, I base when and where I create new objects on testability how many times I have to write $object = new My_Random_Object();.
Speaking specifically to the Zend Framework: Objects I'm going to use everywhere, or almost everywhere, get created in Bootstrap.php. These objects generally include a database adapter, logger, view object, and any plugins I might use. To access these across the application, I'll create private properties in the appropriate controllers and assign the objects to those properties in the controller's init() method.
class ExampleController extends Zend_Controller_Action
{
public function init()
{
$bootstrap = $this->getInvokeArg('bootstrap');
$this->_db = $bootstrap->getResource('db');
$this->_log = $bootstrap->getResource('log');
// and so on, and so forth
}
}
Ideally, models, services, daos, etc, will all be relatively tightly grouped by controller and by action. In my experience, and this is speaking generally, if I have the same model or service class showing up across all of the controllers in my application, I have an organization problem. That being said, any model that shows up in only one action gets created in that action. If it's across actions in a controller, it gets created in the init() method and assigned to a property. If it shows up across multiple controllers, it gets created in my Bootstrap.php.
(Ideally, everything gets created in the Bootstrap.php, so you can swap out that bootstrap for testing purposes. Sadly, I don't always do that, and I most often use the principles I outlined above.)
Well do you really need it in every controllers? Because that's pretty much by design. You implement models when you need them. Its not that much code really.
Now if its to be used across actions from a controller you could always:
class MyController extends Zend_Controllers{
$protected $_articleModel;
...
and in your constructor or __init() function initialize it so you can use it in every action thru $this->_articleModel
If you REALLY want it everywhere in your application just initialize it in your bootstrap and store it in the registry.
public function __initModels(){
$articles = new Application_Model_Articles()
Zend_Registry::set('articles', $articles );
}
And access it in your controllers like so:
Zend_Registry::get('articles')->fetchAll();
But then your still writing a couple of characters.
Hope this help!
IF you want to use models in the controllers you must call it..anyway some shortcuts are here
1.You can initialize it in the init section of your controller like
public function init(){
$this->object = new Application_Model_Database();
}
So that the this->object is available in all the actions of that particular controller
2.Use Zend_registry as suggested in the above answer
Another possibility is to use a Dependency Injection container, such as the Symfony DI component. It takes care of instantiating your objects, and you get some additional benefits:
Separation of concerns. You have a component devoted to create your object tree.
Easier testability of the objects.
Last, but not least, the performance benefits given by lazy instantiation (objects are created only when you ask for them). Thus, if some object is not used by the particular controller serving your request, it's not instantiated).
It's a bit more laborious than the above solutions, but much more flexible if you need to maintain and extend your application in the future.
Hope that helps,
If you are using this object to just display data in your view and are using your controller to grab the data and assign it to your view, like so:
//someControllerAction
$object = new Application_Model_Articles();
$object->fetchAll();
//assign to view
$this->view->articles = $object;
You might be better off making a view helper similar to:
//Articles.php put in /application/views/helpers
class Zend_View_Helper_Articles extends Zend_View_Helper_Abstract {
public function Articles() {
$articles = new Application_Model_Articles();
$articles->fetchAll();
//return rowset object
return $articles;
Then in your view (phtml) you could do something like:
//someView.phmtl
<?php $articles = $this->Articles(); ?>
<h1><?php echo $this->escape($articles->title); ?></h1>
<p><?php echo $this->escape($articles->body); ?></p>
building a view helper allows you to bypass the controller completely if you just need to display data from the model. This is a very simple example and can be used with partials and partialLoops.
REF:ZF reference Custom View Helper
ZF partial view helper reference

How to create instance to zend controller

I have a controller named class TestController which extends some Zend_Controller_Action. Now I would like to use create an instance of TestController in TestForms (a Zend_Form). I want to populate a Zend_Form_Element_Select dynamically.
Please suggest how I can do this. Thanx in advance.
Where are you instantiating the form - is it in the controller? Instead of having the form call an action on the controller to dynamically get the values, you should look at setting the values on the form after it has been instantiated.
A quick and dirty way of doing that would be to grab the values in the controller and assign it to the element via:
$values = $db->query('query');
$element = $form->getElement('dynamicSelect');
$element->setValue($values);
Of course having DB queries to a table in your controller isn't exactly best practice... Per philistyne's suggestion, I use a a form builder class to build forms dynamically from my models. I have mappers for each model, and I pass in the mapper to the form builder class so it can dynamically populate my select elements.
A couple of things to try (passing a controller into a form or instantiating from within one is not recommended):
Use a model to access the dynamic values you want to put into your Zend_Form_Element_Select.
If the form is complex, create a form builder class to take care of, and separate out, the heavy lifting of the form construction.
Create customised form elements by extending from Zend_Form_Element_(Radio, Select, etc etc) if you feel you need very fine control over the form element's construction/behaviour/appearance, but wish to be able to reuse that element elsewhere.

How to handle Zend_Exception's in preDispatch or init of an action when using a base controller?

As mentioned in the Zend Framework manual, I created a base controller.
Subclassing the Action Controller
By design, Zend_Controller_Action must
be subclassed in order to create an
action controller. At the minimum, you
will need to define action methods
that the controller may call.
Besides creating useful functionality
for your web applications, you may
also find that you're repeating much
of the same setup or utility methods
in your various controllers; if so,
creating a common base controller
class that extends
Zend_Controller_Action could solve
such redundancy.
But it turns out, that Exceptions are not being called properly from the base... To replicate this, create a file:
/path/to/workspace/library/Joe/Controller.php
Then:
class Joe_Controller extends Zend_Controller_Action
{
public function init()
{
Throw new Zend_Exception('test', 500);
parent::init();
}
}
Then in your controller directory, IndexController.php extends off the base:
class IndexController extends Joe_Controller
{
You will find that the exception is uncaught.
If however, you don't extend off the base controller, then throw an exception in init or preDispatch, it will be caught and forwarded to the ErrorController.
Anyone have an idea on getting the exceptions caught from the Base Controller?
Thanks.
UPDATING TO TACKLE THIS ANOTHER WAY
After looking at the various articles and official documentation on how to structure the directories, I went with putting the base controller in library/Joe/...but maybe that's part of the problem...there's no guidance in the manual on how to name and place the base controller. What do the experts with a base controller do?
Another Update
Looking at my code some more, and reading on the net, seems like people suggested in should be:
abstract class Joe_Controller_Action extends Zend_Controller_Action.
Except changing it did not solve the problem...
Now considering that there are articles suggesting to use Base Controllers including the manual, would this be considered a bug in Zend Framework?
To replicate, just throw a Zend_Exception of any kind in the base init or preDispatch. Imagine you poll the database in there, (which is what I'm doing)...and the database is down. No error controller. That's not a good situation unless I'm doing something incorrectly.
My suspicion is that this is a new bug... I don't recall this problem before Zend_Application and I've been using a base controller since ZF 1.5.
Two thoughts off the top of my head:
Controller names in the default module are usually named like SomethingController. Your name Joe_Controller suggests the module Joe and an empty controller name.
In routing/dispatch, doesn't it look for a matching action before dispatch to the controller? Since the samples have no actions, might you be triggering ControllerNotFound exception?
Just throwin' out some ideas.
in Zend philosophy, they provide the init() method to avoid the hassle of
public class Module_TotoController extends Zend_Action_Controller {
public function __construct(Zend_Controller_Request_Abstract $request, Zend_Controller_Response_Abstract $response, array $invokeArgs = array())
parent::__construct($request, $response, invokeArgs);
// some init code here
}
}
just use :
public class Module_TotoController extends Zend_Action_Controller {
public function init ()
// some init code here
}
}
Now, If you look at the Zend_Controller_Action class source, you will notice that the following methods are empty:
init()
preDispatch ()
postDispatch ()
This means, it is useless to call parent:: in your specific controller
public class Module_TotoController extends Zend_Action_Controller {
public function init ()
parent::init(); // useless ;-) but you can go for it
// some init code here
}
}
Now, if you want to put an intermediate class between Module_TotoController and Zend_Action_Controller, you expose yourself to a big hassle as:
Some controllers won't extend your "base" controller (e.g your ErrorController in your question)
so it is not a really a base of your application, is it ?
If you put some logic in your init() of that "base",
you must call the parent:: in your init(),
all the developers in the project need to be aware of that
You will ever add another little features to your "Base" controller
This will result to a big bloated file
Loading/Initializing plenty of stuff you might not really need in that lambda controller
Do you need you database on every page ?
No: doesn't a $this->_helper->connect(); look nice instead ?
Yes: use a controller plugin
This "Base" controller won't fit your other projects needs, so that class won't be reusable
Action helpers will
Controller Plugins will
Hope it makes sense
Don't go for a base or whatever controller
As documentation says :
By design, Zend_Controller_Action must be subclassed in order to create an action controller.
Agreed, this is misleading
What they meant is
class Module_TotoController extends Zend_Controller_Action {}
class Module_TotoController extends Joe_Controller is plain wrong (sorry no offence)
as you said your ErrorController can't extend your intermediate Joe_Controller class
because your exception will be thrown again in the instantiation of the ErrorController class (as excepted !!!)
The uncaught Exception error is the result of a protection to avoid loops (dig Zend\Controller\Plugin\ErrorHanlder.php line 200-ish)
If you require something to be done for every action, use a Zend_Controller_Plugin and register it with the frontController